specweave 0.1.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 (288) hide show
  1. package/INSTALL.md +848 -0
  2. package/LICENSE +21 -0
  3. package/README.md +675 -0
  4. package/SPECWEAVE.md +665 -0
  5. package/bin/install-agents.sh +57 -0
  6. package/bin/install-all.sh +49 -0
  7. package/bin/install-commands.sh +56 -0
  8. package/bin/install-skills.sh +57 -0
  9. package/bin/specweave.js +81 -0
  10. package/dist/adapters/adapter-base.d.ts +50 -0
  11. package/dist/adapters/adapter-base.d.ts.map +1 -0
  12. package/dist/adapters/adapter-base.js +146 -0
  13. package/dist/adapters/adapter-base.js.map +1 -0
  14. package/dist/adapters/adapter-interface.d.ts +108 -0
  15. package/dist/adapters/adapter-interface.d.ts.map +1 -0
  16. package/dist/adapters/adapter-interface.js +9 -0
  17. package/dist/adapters/adapter-interface.js.map +1 -0
  18. package/dist/adapters/claude/adapter.d.ts +54 -0
  19. package/dist/adapters/claude/adapter.d.ts.map +1 -0
  20. package/dist/adapters/claude/adapter.js +184 -0
  21. package/dist/adapters/claude/adapter.js.map +1 -0
  22. package/dist/adapters/copilot/adapter.d.ts +42 -0
  23. package/dist/adapters/copilot/adapter.d.ts.map +1 -0
  24. package/dist/adapters/copilot/adapter.js +239 -0
  25. package/dist/adapters/copilot/adapter.js.map +1 -0
  26. package/dist/adapters/cursor/adapter.d.ts +42 -0
  27. package/dist/adapters/cursor/adapter.d.ts.map +1 -0
  28. package/dist/adapters/cursor/adapter.js +297 -0
  29. package/dist/adapters/cursor/adapter.js.map +1 -0
  30. package/dist/adapters/generic/adapter.d.ts +40 -0
  31. package/dist/adapters/generic/adapter.d.ts.map +1 -0
  32. package/dist/adapters/generic/adapter.js +155 -0
  33. package/dist/adapters/generic/adapter.js.map +1 -0
  34. package/dist/cli/commands/init.d.ts +6 -0
  35. package/dist/cli/commands/init.d.ts.map +1 -0
  36. package/dist/cli/commands/init.js +247 -0
  37. package/dist/cli/commands/init.js.map +1 -0
  38. package/dist/cli/commands/install.d.ts +7 -0
  39. package/dist/cli/commands/install.d.ts.map +1 -0
  40. package/dist/cli/commands/install.js +160 -0
  41. package/dist/cli/commands/install.js.map +1 -0
  42. package/dist/cli/commands/list.d.ts +6 -0
  43. package/dist/cli/commands/list.d.ts.map +1 -0
  44. package/dist/cli/commands/list.js +154 -0
  45. package/dist/cli/commands/list.js.map +1 -0
  46. package/package.json +90 -0
  47. package/src/adapters/README.md +312 -0
  48. package/src/adapters/adapter-base.ts +146 -0
  49. package/src/adapters/adapter-interface.ts +120 -0
  50. package/src/adapters/claude/README.md +241 -0
  51. package/src/adapters/claude/adapter.ts +157 -0
  52. package/src/adapters/copilot/.github/copilot/instructions.md +376 -0
  53. package/src/adapters/copilot/README.md +200 -0
  54. package/src/adapters/copilot/adapter.ts +210 -0
  55. package/src/adapters/cursor/.cursor/context/docs-context.md +62 -0
  56. package/src/adapters/cursor/.cursor/context/increments-context.md +71 -0
  57. package/src/adapters/cursor/.cursor/context/strategy-context.md +73 -0
  58. package/src/adapters/cursor/.cursor/context/tests-context.md +89 -0
  59. package/src/adapters/cursor/.cursorrules +325 -0
  60. package/src/adapters/cursor/README.md +243 -0
  61. package/src/adapters/cursor/adapter.ts +268 -0
  62. package/src/adapters/generic/README.md +277 -0
  63. package/src/adapters/generic/SPECWEAVE-MANUAL.md +676 -0
  64. package/src/adapters/generic/adapter.ts +159 -0
  65. package/src/adapters/registry.yaml +126 -0
  66. package/src/agents/architect/AGENT.md +416 -0
  67. package/src/agents/devops/AGENT.md +1738 -0
  68. package/src/agents/docs-writer/AGENT.md +239 -0
  69. package/src/agents/performance/AGENT.md +228 -0
  70. package/src/agents/pm/AGENT.md +751 -0
  71. package/src/agents/qa-lead/AGENT.md +150 -0
  72. package/src/agents/security/AGENT.md +179 -0
  73. package/src/agents/sre/AGENT.md +582 -0
  74. package/src/agents/sre/modules/backend-diagnostics.md +481 -0
  75. package/src/agents/sre/modules/database-diagnostics.md +509 -0
  76. package/src/agents/sre/modules/infrastructure.md +561 -0
  77. package/src/agents/sre/modules/monitoring.md +439 -0
  78. package/src/agents/sre/modules/security-incidents.md +421 -0
  79. package/src/agents/sre/modules/ui-diagnostics.md +302 -0
  80. package/src/agents/sre/playbooks/01-high-cpu-usage.md +204 -0
  81. package/src/agents/sre/playbooks/02-database-deadlock.md +241 -0
  82. package/src/agents/sre/playbooks/03-memory-leak.md +252 -0
  83. package/src/agents/sre/playbooks/04-slow-api-response.md +269 -0
  84. package/src/agents/sre/playbooks/05-ddos-attack.md +293 -0
  85. package/src/agents/sre/playbooks/06-disk-full.md +314 -0
  86. package/src/agents/sre/playbooks/07-service-down.md +333 -0
  87. package/src/agents/sre/playbooks/08-data-corruption.md +337 -0
  88. package/src/agents/sre/playbooks/09-cascade-failure.md +430 -0
  89. package/src/agents/sre/playbooks/10-rate-limit-exceeded.md +464 -0
  90. package/src/agents/sre/scripts/health-check.sh +230 -0
  91. package/src/agents/sre/scripts/log-analyzer.py +213 -0
  92. package/src/agents/sre/scripts/metrics-collector.sh +294 -0
  93. package/src/agents/sre/scripts/trace-analyzer.js +257 -0
  94. package/src/agents/sre/templates/incident-report.md +249 -0
  95. package/src/agents/sre/templates/mitigation-plan.md +375 -0
  96. package/src/agents/sre/templates/post-mortem.md +418 -0
  97. package/src/agents/sre/templates/runbook-template.md +412 -0
  98. package/src/agents/tech-lead/AGENT.md +263 -0
  99. package/src/commands/add-tasks.md +176 -0
  100. package/src/commands/close-increment.md +347 -0
  101. package/src/commands/create-increment.md +223 -0
  102. package/src/commands/create-project.md +528 -0
  103. package/src/commands/generate-docs.md +623 -0
  104. package/src/commands/list-increments.md +180 -0
  105. package/src/commands/review-docs.md +331 -0
  106. package/src/commands/start-increment.md +139 -0
  107. package/src/commands/sync-github.md +115 -0
  108. package/src/commands/validate-increment.md +800 -0
  109. package/src/hooks/README.md +252 -0
  110. package/src/hooks/docs-changed.sh +59 -0
  111. package/src/hooks/human-input-required.sh +55 -0
  112. package/src/hooks/post-task-completion.sh +57 -0
  113. package/src/hooks/pre-implementation.sh +47 -0
  114. package/src/skills/ado-sync/README.md +449 -0
  115. package/src/skills/ado-sync/SKILL.md +245 -0
  116. package/src/skills/ado-sync/test-cases/test-1.yaml +9 -0
  117. package/src/skills/ado-sync/test-cases/test-2.yaml +8 -0
  118. package/src/skills/ado-sync/test-cases/test-3.yaml +9 -0
  119. package/src/skills/bmad-method-expert/SKILL.md +628 -0
  120. package/src/skills/bmad-method-expert/scripts/analyze-project.js +318 -0
  121. package/src/skills/bmad-method-expert/scripts/check-setup.js +208 -0
  122. package/src/skills/bmad-method-expert/scripts/generate-template.js +1149 -0
  123. package/src/skills/bmad-method-expert/scripts/validate-documents.js +340 -0
  124. package/src/skills/bmad-method-expert/test-cases/test-1-placeholder.yaml +12 -0
  125. package/src/skills/bmad-method-expert/test-cases/test-2-placeholder.yaml +12 -0
  126. package/src/skills/bmad-method-expert/test-cases/test-3-placeholder.yaml +12 -0
  127. package/src/skills/brownfield-analyzer/SKILL.md +523 -0
  128. package/src/skills/brownfield-analyzer/test-cases/test-1-basic-analysis.yaml +48 -0
  129. package/src/skills/brownfield-analyzer/test-cases/test-2-placeholder.yaml +12 -0
  130. package/src/skills/brownfield-analyzer/test-cases/test-3-placeholder.yaml +12 -0
  131. package/src/skills/brownfield-onboarder/SKILL.md +625 -0
  132. package/src/skills/brownfield-onboarder/test-cases/test-1-placeholder.yaml +12 -0
  133. package/src/skills/brownfield-onboarder/test-cases/test-2-placeholder.yaml +12 -0
  134. package/src/skills/brownfield-onboarder/test-cases/test-3-placeholder.yaml +12 -0
  135. package/src/skills/calendar-system/test-cases/test-1-placeholder.yaml +12 -0
  136. package/src/skills/calendar-system/test-cases/test-2-placeholder.yaml +12 -0
  137. package/src/skills/calendar-system/test-cases/test-3-placeholder.yaml +12 -0
  138. package/src/skills/context-loader/SKILL.md +734 -0
  139. package/src/skills/context-loader/test-cases/test-1-basic-loading.yaml +39 -0
  140. package/src/skills/context-loader/test-cases/test-2-token-budget-exceeded.yaml +44 -0
  141. package/src/skills/context-loader/test-cases/test-3-section-anchors.yaml +45 -0
  142. package/src/skills/context-optimizer/SKILL.md +618 -0
  143. package/src/skills/context-optimizer/test-cases/test-1-bug-fix-narrow.yaml +97 -0
  144. package/src/skills/context-optimizer/test-cases/test-2-feature-focused.yaml +109 -0
  145. package/src/skills/context-optimizer/test-cases/test-3-architecture-broad.yaml +98 -0
  146. package/src/skills/cost-optimizer/SKILL.md +190 -0
  147. package/src/skills/cost-optimizer/test-cases/test-1-basic-comparison.yaml +75 -0
  148. package/src/skills/cost-optimizer/test-cases/test-2-budget-constraint.yaml +52 -0
  149. package/src/skills/cost-optimizer/test-cases/test-3-scale-requirement.yaml +63 -0
  150. package/src/skills/cost-optimizer/test-results/README.md +46 -0
  151. package/src/skills/design-system-architect/SKILL.md +107 -0
  152. package/src/skills/design-system-architect/test-cases/test-1-token-structure.yaml +23 -0
  153. package/src/skills/design-system-architect/test-cases/test-2-component-hierarchy.yaml +24 -0
  154. package/src/skills/design-system-architect/test-cases/test-3-accessibility-checklist.yaml +23 -0
  155. package/src/skills/diagrams-architect/SKILL.md +763 -0
  156. package/src/skills/diagrams-generator/SKILL.md +25 -0
  157. package/src/skills/diagrams-generator/test-cases/test-1.yaml +9 -0
  158. package/src/skills/diagrams-generator/test-cases/test-2.yaml +9 -0
  159. package/src/skills/diagrams-generator/test-cases/test-3.yaml +8 -0
  160. package/src/skills/docs-updater/README.md +48 -0
  161. package/src/skills/docs-updater/test-cases/test-1-placeholder.yaml +12 -0
  162. package/src/skills/docs-updater/test-cases/test-2-placeholder.yaml +12 -0
  163. package/src/skills/docs-updater/test-cases/test-3-placeholder.yaml +12 -0
  164. package/src/skills/dotnet-backend/SKILL.md +250 -0
  165. package/src/skills/e2e-playwright/README.md +506 -0
  166. package/src/skills/e2e-playwright/SKILL.md +457 -0
  167. package/src/skills/e2e-playwright/execute.js +373 -0
  168. package/src/skills/e2e-playwright/lib/utils.js +514 -0
  169. package/src/skills/e2e-playwright/package.json +33 -0
  170. package/src/skills/e2e-playwright/test-cases/TC-001-basic-navigation.yaml +54 -0
  171. package/src/skills/e2e-playwright/test-cases/TC-002-form-interaction.yaml +64 -0
  172. package/src/skills/e2e-playwright/test-cases/TC-003-specweave-integration.yaml +74 -0
  173. package/src/skills/e2e-playwright/test-cases/TC-004-accessibility-check.yaml +98 -0
  174. package/src/skills/figma-designer/SKILL.md +149 -0
  175. package/src/skills/figma-implementer/SKILL.md +148 -0
  176. package/src/skills/figma-mcp-connector/SKILL.md +136 -0
  177. package/src/skills/figma-mcp-connector/test-cases/test-1-read-file-desktop.yaml +22 -0
  178. package/src/skills/figma-mcp-connector/test-cases/test-2-read-file-framelink.yaml +21 -0
  179. package/src/skills/figma-mcp-connector/test-cases/test-3-error-handling.yaml +18 -0
  180. package/src/skills/figma-to-code/SKILL.md +128 -0
  181. package/src/skills/figma-to-code/test-cases/test-1-token-generation.yaml +29 -0
  182. package/src/skills/figma-to-code/test-cases/test-2-component-generation.yaml +27 -0
  183. package/src/skills/figma-to-code/test-cases/test-3-typescript-generation.yaml +28 -0
  184. package/src/skills/frontend/SKILL.md +177 -0
  185. package/src/skills/github-sync/SKILL.md +252 -0
  186. package/src/skills/github-sync/test-cases/test-1-placeholder.yaml +12 -0
  187. package/src/skills/github-sync/test-cases/test-2-placeholder.yaml +12 -0
  188. package/src/skills/github-sync/test-cases/test-3-placeholder.yaml +12 -0
  189. package/src/skills/hetzner-provisioner/README.md +308 -0
  190. package/src/skills/hetzner-provisioner/SKILL.md +251 -0
  191. package/src/skills/hetzner-provisioner/test-cases/test-1-basic-provision.yaml +71 -0
  192. package/src/skills/hetzner-provisioner/test-cases/test-2-postgres-provision.yaml +85 -0
  193. package/src/skills/hetzner-provisioner/test-cases/test-3-ssl-config.yaml +126 -0
  194. package/src/skills/hetzner-provisioner/test-results/README.md +259 -0
  195. package/src/skills/increment-planner/SKILL.md +889 -0
  196. package/src/skills/increment-planner/scripts/feature-utils.js +250 -0
  197. package/src/skills/increment-planner/test-cases/test-1-basic-feature.yaml +27 -0
  198. package/src/skills/increment-planner/test-cases/test-2-complex-feature.yaml +30 -0
  199. package/src/skills/increment-planner/test-cases/test-3-auto-numbering.yaml +24 -0
  200. package/src/skills/increment-quality-judge/SKILL.md +566 -0
  201. package/src/skills/increment-quality-judge/test-cases/test-1-good-spec.yaml +95 -0
  202. package/src/skills/increment-quality-judge/test-cases/test-2-poor-spec.yaml +108 -0
  203. package/src/skills/increment-quality-judge/test-cases/test-3-export-suggestions.yaml +87 -0
  204. package/src/skills/jira-sync/README.md +328 -0
  205. package/src/skills/jira-sync/SKILL.md +209 -0
  206. package/src/skills/jira-sync/test-cases/test-1.yaml +9 -0
  207. package/src/skills/jira-sync/test-cases/test-2.yaml +9 -0
  208. package/src/skills/jira-sync/test-cases/test-3.yaml +10 -0
  209. package/src/skills/nextjs/SKILL.md +176 -0
  210. package/src/skills/nodejs-backend/SKILL.md +181 -0
  211. package/src/skills/notification-system/test-cases/test-1-placeholder.yaml +12 -0
  212. package/src/skills/notification-system/test-cases/test-2-placeholder.yaml +12 -0
  213. package/src/skills/notification-system/test-cases/test-3-placeholder.yaml +12 -0
  214. package/src/skills/python-backend/SKILL.md +226 -0
  215. package/src/skills/role-orchestrator/README.md +197 -0
  216. package/src/skills/role-orchestrator/SKILL.md +1184 -0
  217. package/src/skills/role-orchestrator/test-cases/test-1-simple-product.yaml +98 -0
  218. package/src/skills/role-orchestrator/test-cases/test-2-quality-gate-failure.yaml +73 -0
  219. package/src/skills/role-orchestrator/test-cases/test-3-security-workflow.yaml +121 -0
  220. package/src/skills/role-orchestrator/test-cases/test-4-parallel-execution.yaml +145 -0
  221. package/src/skills/role-orchestrator/test-cases/test-5-feedback-loops.yaml +149 -0
  222. package/src/skills/skill-creator/LICENSE.txt +202 -0
  223. package/src/skills/skill-creator/SKILL.md +209 -0
  224. package/src/skills/skill-creator/scripts/init_skill.py +303 -0
  225. package/src/skills/skill-creator/scripts/package_skill.py +110 -0
  226. package/src/skills/skill-creator/scripts/quick_validate.py +65 -0
  227. package/src/skills/skill-creator/test-cases/test-1-placeholder.yaml +12 -0
  228. package/src/skills/skill-creator/test-cases/test-2-placeholder.yaml +12 -0
  229. package/src/skills/skill-creator/test-cases/test-3-placeholder.yaml +12 -0
  230. package/src/skills/skill-router/SKILL.md +497 -0
  231. package/src/skills/skill-router/test-cases/test-1-basic-routing.yaml +33 -0
  232. package/src/skills/skill-router/test-cases/test-2-ambiguous-request.yaml +42 -0
  233. package/src/skills/skill-router/test-cases/test-3-nested-orchestration.yaml +50 -0
  234. package/src/skills/spec-driven-brainstorming/README.md +264 -0
  235. package/src/skills/spec-driven-brainstorming/SKILL.md +439 -0
  236. package/src/skills/spec-driven-brainstorming/test-cases/TC-001-simple-idea-to-design.yaml +148 -0
  237. package/src/skills/spec-driven-brainstorming/test-cases/TC-002-complex-ultrathink-design.yaml +190 -0
  238. package/src/skills/spec-driven-brainstorming/test-cases/TC-003-unclear-requirements-socratic.yaml +233 -0
  239. package/src/skills/spec-driven-debugging/README.md +479 -0
  240. package/src/skills/spec-driven-debugging/SKILL.md +652 -0
  241. package/src/skills/spec-driven-debugging/test-cases/TC-001-simple-auth-bug.yaml +212 -0
  242. package/src/skills/spec-driven-debugging/test-cases/TC-002-race-condition-ultrathink.yaml +461 -0
  243. package/src/skills/spec-driven-debugging/test-cases/TC-003-brownfield-missing-spec.yaml +366 -0
  244. package/src/skills/spec-kit-expert/SKILL.md +1012 -0
  245. package/src/skills/spec-kit-expert/test-cases/test-1-placeholder.yaml +12 -0
  246. package/src/skills/spec-kit-expert/test-cases/test-2-placeholder.yaml +12 -0
  247. package/src/skills/spec-kit-expert/test-cases/test-3-placeholder.yaml +12 -0
  248. package/src/skills/specweave-ado-mapper/SKILL.md +501 -0
  249. package/src/skills/specweave-detector/SKILL.md +420 -0
  250. package/src/skills/specweave-detector/test-cases/test-1-basic-detection.yaml +37 -0
  251. package/src/skills/specweave-detector/test-cases/test-2-missing-config.yaml +37 -0
  252. package/src/skills/specweave-detector/test-cases/test-3-non-specweave-project.yaml +34 -0
  253. package/src/skills/specweave-jira-mapper/SKILL.md +500 -0
  254. package/src/skills/stripe-integrator/test-cases/test-1-placeholder.yaml +12 -0
  255. package/src/skills/stripe-integrator/test-cases/test-2-placeholder.yaml +12 -0
  256. package/src/skills/stripe-integrator/test-cases/test-3-placeholder.yaml +12 -0
  257. package/src/skills/task-builder/README.md +90 -0
  258. package/src/skills/task-builder/test-cases/test-1-placeholder.yaml +12 -0
  259. package/src/skills/task-builder/test-cases/test-2-placeholder.yaml +12 -0
  260. package/src/skills/task-builder/test-cases/test-3-placeholder.yaml +12 -0
  261. package/src/templates/.env.example +144 -0
  262. package/src/templates/.gitignore.template +81 -0
  263. package/src/templates/CLAUDE.md.template +383 -0
  264. package/src/templates/README.md.template +240 -0
  265. package/src/templates/config.yaml +333 -0
  266. package/src/templates/docs/README.md +124 -0
  267. package/src/templates/docs/adr-template.md +118 -0
  268. package/src/templates/docs/hld-template.md +220 -0
  269. package/src/templates/docs/lld-template.md +580 -0
  270. package/src/templates/docs/prd-template.md +132 -0
  271. package/src/templates/docs/rfc-template.md +229 -0
  272. package/src/templates/docs/runbook-template.md +298 -0
  273. package/src/templates/environments/minimal/.env.production +16 -0
  274. package/src/templates/environments/minimal/README.md +54 -0
  275. package/src/templates/environments/minimal/deploy-production.yml +52 -0
  276. package/src/templates/environments/progressive/.env.qa +28 -0
  277. package/src/templates/environments/progressive/README.md +129 -0
  278. package/src/templates/environments/progressive/deploy-production.yml +93 -0
  279. package/src/templates/environments/progressive/deploy-qa.yml +62 -0
  280. package/src/templates/environments/progressive/deploy-staging.yml +67 -0
  281. package/src/templates/environments/standard/.env.development +20 -0
  282. package/src/templates/environments/standard/.env.production +30 -0
  283. package/src/templates/environments/standard/.env.staging +23 -0
  284. package/src/templates/environments/standard/README.md +97 -0
  285. package/src/templates/environments/standard/deploy-production.yml +68 -0
  286. package/src/templates/environments/standard/deploy-staging.yml +61 -0
  287. package/src/templates/environments/standard/docker-compose.yml +43 -0
  288. package/src/templates/increment-metadata-template.yaml +138 -0
@@ -0,0 +1,514 @@
1
+ /**
2
+ * Playwright Helper Utilities for SpecWeave
3
+ *
4
+ * Collection of helper functions for common Playwright testing patterns.
5
+ * SpecWeave-aware utilities for enhanced testing workflows.
6
+ */
7
+
8
+ const { execSync } = require('child_process');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * Detect running development servers on common ports
14
+ *
15
+ * @returns {Promise<Array<{port: number, url: string, name: string}>>}
16
+ */
17
+ async function detectServers() {
18
+ const commonPorts = [3000, 3001, 3002, 3010, 4000, 5000, 5173, 8000, 8080, 8888];
19
+ const servers = [];
20
+
21
+ for (const port of commonPorts) {
22
+ try {
23
+ // Use lsof to check if port is in use (macOS/Linux)
24
+ const result = execSync(`lsof -i :${port} -sTCP:LISTEN -t`, {
25
+ encoding: 'utf8',
26
+ stdio: ['pipe', 'pipe', 'ignore']
27
+ });
28
+
29
+ if (result.trim()) {
30
+ servers.push({
31
+ port,
32
+ url: `http://localhost:${port}`,
33
+ name: guessServerName(port)
34
+ });
35
+ }
36
+ } catch (e) {
37
+ // Port not in use, continue
38
+ }
39
+ }
40
+
41
+ return servers;
42
+ }
43
+
44
+ /**
45
+ * Guess server name based on common port conventions
46
+ */
47
+ function guessServerName(port) {
48
+ const nameMap = {
49
+ 3000: 'Next.js / React Dev',
50
+ 3001: 'Secondary Dev Server',
51
+ 5173: 'Vite',
52
+ 4000: 'Express / Backend',
53
+ 8000: 'Python / Django',
54
+ 8080: 'Java / Spring Boot'
55
+ };
56
+
57
+ return nameMap[port] || 'Dev Server';
58
+ }
59
+
60
+ /**
61
+ * Safe click with automatic wait and retry
62
+ *
63
+ * @param {Page} page - Playwright page object
64
+ * @param {string} selector - CSS selector
65
+ * @param {object} options - Click options
66
+ */
67
+ async function safeClick(page, selector, options = {}) {
68
+ const timeout = options.timeout || 10000;
69
+ const retries = options.retries || 3;
70
+
71
+ for (let i = 0; i < retries; i++) {
72
+ try {
73
+ await page.waitForSelector(selector, { timeout, state: 'visible' });
74
+ await page.click(selector, { timeout });
75
+ return;
76
+ } catch (error) {
77
+ if (i === retries - 1) {
78
+ throw new Error(`Failed to click "${selector}" after ${retries} attempts: ${error.message}`);
79
+ }
80
+ // Wait before retry
81
+ await page.waitForTimeout(1000);
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Safe type with automatic focus and validation
88
+ *
89
+ * @param {Page} page - Playwright page object
90
+ * @param {string} selector - CSS selector
91
+ * @param {string} text - Text to type
92
+ * @param {object} options - Type options
93
+ */
94
+ async function safeType(page, selector, text, options = {}) {
95
+ const timeout = options.timeout || 10000;
96
+ const clearFirst = options.clear !== false; // Default true
97
+
98
+ await page.waitForSelector(selector, { timeout, state: 'visible' });
99
+
100
+ // Focus the input
101
+ await page.focus(selector);
102
+
103
+ // Clear existing value if requested
104
+ if (clearFirst) {
105
+ await page.fill(selector, '');
106
+ }
107
+
108
+ // Type with slight delay for realism
109
+ await page.type(selector, text, { delay: options.delay || 50 });
110
+
111
+ // Verify text was entered (optional)
112
+ if (options.verify !== false) {
113
+ const value = await page.inputValue(selector);
114
+ if (value !== text && !options.partial) {
115
+ throw new Error(`Failed to type into "${selector}". Expected "${text}", got "${value}"`);
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Capture timestamped screenshot
122
+ *
123
+ * @param {Page} page - Playwright page object
124
+ * @param {string} name - Base name for screenshot
125
+ * @param {object} options - Screenshot options
126
+ * @returns {string} Path to screenshot
127
+ */
128
+ async function captureScreenshot(page, name, options = {}) {
129
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
130
+ const filename = `${name}-${timestamp}.png`;
131
+ const filepath = path.join(options.dir || '/tmp', filename);
132
+
133
+ await page.screenshot({
134
+ path: filepath,
135
+ fullPage: options.fullPage !== false, // Default true
136
+ ...options
137
+ });
138
+
139
+ console.log(`📸 Screenshot saved: ${filepath}`);
140
+ return filepath;
141
+ }
142
+
143
+ /**
144
+ * Handle common cookie consent banners
145
+ *
146
+ * @param {Page} page - Playwright page object
147
+ */
148
+ async function handleCookieBanner(page) {
149
+ const commonSelectors = [
150
+ 'button:has-text("Accept")',
151
+ 'button:has-text("Accept All")',
152
+ 'button:has-text("Agree")',
153
+ 'button:has-text("OK")',
154
+ 'button:has-text("I Agree")',
155
+ 'button[id*="accept"]',
156
+ 'button[class*="accept"]',
157
+ 'button[class*="cookie"]',
158
+ '[data-testid="cookie-accept"]'
159
+ ];
160
+
161
+ for (const selector of commonSelectors) {
162
+ try {
163
+ const element = await page.locator(selector).first();
164
+ if (await element.isVisible({ timeout: 2000 })) {
165
+ await element.click();
166
+ console.log('✅ Cookie banner dismissed');
167
+ return true;
168
+ }
169
+ } catch (e) {
170
+ // Continue to next selector
171
+ }
172
+ }
173
+
174
+ return false;
175
+ }
176
+
177
+ /**
178
+ * Extract data from HTML table
179
+ *
180
+ * @param {Page} page - Playwright page object
181
+ * @param {string} selector - Table selector
182
+ * @returns {Array<object>} Array of row objects
183
+ */
184
+ async function extractTableData(page, selector) {
185
+ return await page.evaluate((sel) => {
186
+ const table = document.querySelector(sel);
187
+ if (!table) return [];
188
+
189
+ const headers = Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim());
190
+ const rows = Array.from(table.querySelectorAll('tbody tr'));
191
+
192
+ return rows.map(row => {
193
+ const cells = Array.from(row.querySelectorAll('td'));
194
+ const rowData = {};
195
+ cells.forEach((cell, index) => {
196
+ rowData[headers[index] || `column${index}`] = cell.textContent.trim();
197
+ });
198
+ return rowData;
199
+ });
200
+ }, selector);
201
+ }
202
+
203
+ /**
204
+ * Wait for DOM to stabilize (no changes for specified duration)
205
+ *
206
+ * @param {Page} page - Playwright page object
207
+ * @param {number} stabilityTime - Time in ms to wait for stability
208
+ */
209
+ async function waitForStableDOM(page, stabilityTime = 1000) {
210
+ let lastMutationTime = Date.now();
211
+ let stabilityTimeout;
212
+
213
+ return new Promise((resolve) => {
214
+ const observer = page.evaluateHandle((ms) => {
215
+ return new Promise((res) => {
216
+ let lastChange = Date.now();
217
+ let timeout;
218
+
219
+ const checkStability = () => {
220
+ if (Date.now() - lastChange >= ms) {
221
+ observer.disconnect();
222
+ res();
223
+ }
224
+ };
225
+
226
+ const observer = new MutationObserver(() => {
227
+ lastChange = Date.now();
228
+ clearTimeout(timeout);
229
+ timeout = setTimeout(checkStability, ms);
230
+ });
231
+
232
+ observer.observe(document.body, {
233
+ childList: true,
234
+ subtree: true,
235
+ attributes: true
236
+ });
237
+
238
+ // Initial stability check
239
+ timeout = setTimeout(checkStability, ms);
240
+ });
241
+ }, stabilityTime);
242
+
243
+ observer.then(() => resolve());
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Basic accessibility checks
249
+ *
250
+ * @param {Page} page - Playwright page object
251
+ * @returns {Array<{type: string, element: string, message: string}>}
252
+ */
253
+ async function checkAccessibility(page) {
254
+ return await page.evaluate(() => {
255
+ const issues = [];
256
+
257
+ // Check for images without alt text
258
+ document.querySelectorAll('img').forEach(img => {
259
+ if (!img.alt) {
260
+ issues.push({
261
+ type: 'missing-alt',
262
+ element: img.outerHTML.substring(0, 100),
263
+ message: 'Image missing alt text'
264
+ });
265
+ }
266
+ });
267
+
268
+ // Check for form inputs without labels
269
+ document.querySelectorAll('input, textarea, select').forEach(input => {
270
+ if (input.type === 'hidden') return;
271
+
272
+ const hasLabel = !!input.closest('label') ||
273
+ !!document.querySelector(`label[for="${input.id}"]`) ||
274
+ !!input.getAttribute('aria-label') ||
275
+ !!input.getAttribute('aria-labelledby');
276
+
277
+ if (!hasLabel) {
278
+ issues.push({
279
+ type: 'missing-label',
280
+ element: input.outerHTML.substring(0, 100),
281
+ message: 'Form input missing label or ARIA label'
282
+ });
283
+ }
284
+ });
285
+
286
+ // Check for buttons without accessible text
287
+ document.querySelectorAll('button').forEach(button => {
288
+ const hasText = button.textContent.trim() ||
289
+ button.getAttribute('aria-label') ||
290
+ button.querySelector('img')?.alt;
291
+
292
+ if (!hasText) {
293
+ issues.push({
294
+ type: 'missing-button-text',
295
+ element: button.outerHTML.substring(0, 100),
296
+ message: 'Button missing accessible text'
297
+ });
298
+ }
299
+ });
300
+
301
+ // Check for links without text
302
+ document.querySelectorAll('a').forEach(link => {
303
+ const hasText = link.textContent.trim() ||
304
+ link.getAttribute('aria-label') ||
305
+ link.querySelector('img')?.alt;
306
+
307
+ if (!hasText) {
308
+ issues.push({
309
+ type: 'missing-link-text',
310
+ element: link.outerHTML.substring(0, 100),
311
+ message: 'Link missing accessible text'
312
+ });
313
+ }
314
+ });
315
+
316
+ return issues;
317
+ });
318
+ }
319
+
320
+ /**
321
+ * Generate SpecWeave test report
322
+ *
323
+ * @param {object} results - Test results object
324
+ * @param {string} incrementId - Increment ID (e.g., "0003-user-auth")
325
+ * @returns {string} Report content in Markdown
326
+ */
327
+ function generateTestReport(results, incrementId) {
328
+ const {
329
+ tests = [],
330
+ summary = {},
331
+ performance = {},
332
+ accessibility = [],
333
+ recommendations = []
334
+ } = results;
335
+
336
+ const date = new Date().toISOString().split('T')[0];
337
+ const status = summary.failed === 0 ? '✅ Passed' : '❌ Failed';
338
+
339
+ let report = `# E2E Test Report - Increment ${incrementId}\n\n`;
340
+ report += `**Date**: ${date}\n`;
341
+ report += `**Duration**: ${summary.duration || 'N/A'}\n`;
342
+ report += `**Status**: ${status}\n\n`;
343
+
344
+ report += `## Test Summary\n\n`;
345
+ report += `- Total Tests: ${summary.total || 0}\n`;
346
+ report += `- Passed: ${summary.passed || 0}\n`;
347
+ report += `- Failed: ${summary.failed || 0}\n`;
348
+ report += `- Skipped: ${summary.skipped || 0}\n\n`;
349
+
350
+ if (tests.length > 0) {
351
+ report += `## Test Results\n\n`;
352
+ tests.forEach(test => {
353
+ report += `### ${test.name}\n`;
354
+ report += `- **Status**: ${test.status}\n`;
355
+ report += `- **Duration**: ${test.duration}\n`;
356
+ if (test.screenshot) {
357
+ report += `- **Screenshot**: \`${test.screenshot}\`\n`;
358
+ }
359
+ if (test.error) {
360
+ report += `- **Error**: ${test.error}\n`;
361
+ }
362
+ report += `\n`;
363
+ });
364
+ }
365
+
366
+ if (Object.keys(performance).length > 0) {
367
+ report += `## Performance Metrics\n\n`;
368
+ Object.entries(performance).forEach(([key, value]) => {
369
+ report += `- ${key}: ${value}\n`;
370
+ });
371
+ report += `\n`;
372
+ }
373
+
374
+ if (accessibility.length > 0) {
375
+ report += `## Accessibility Issues\n\n`;
376
+ const grouped = accessibility.reduce((acc, issue) => {
377
+ acc[issue.type] = (acc[issue.type] || 0) + 1;
378
+ return acc;
379
+ }, {});
380
+
381
+ Object.entries(grouped).forEach(([type, count]) => {
382
+ report += `- ${type}: ${count} instance${count > 1 ? 's' : ''}\n`;
383
+ });
384
+ report += `\n`;
385
+ }
386
+
387
+ if (recommendations.length > 0) {
388
+ report += `## Recommendations\n\n`;
389
+ recommendations.forEach((rec, i) => {
390
+ report += `${i + 1}. ${rec}\n`;
391
+ });
392
+ report += `\n`;
393
+ }
394
+
395
+ report += `---\n`;
396
+ report += `*Generated by e2e-playwright skill*\n`;
397
+
398
+ return report;
399
+ }
400
+
401
+ /**
402
+ * Save test report to SpecWeave increment folder
403
+ *
404
+ * @param {string} reportContent - Markdown report content
405
+ * @param {string} incrementPath - Path to increment folder
406
+ * @param {string} filename - Report filename
407
+ */
408
+ function saveTestReport(reportContent, incrementPath, filename = 'e2e-test-report.md') {
409
+ const reportsDir = path.join(incrementPath, 'reports');
410
+
411
+ // Create reports directory if it doesn't exist
412
+ if (!fs.existsSync(reportsDir)) {
413
+ fs.mkdirSync(reportsDir, { recursive: true });
414
+ }
415
+
416
+ const reportPath = path.join(reportsDir, filename);
417
+ fs.writeFileSync(reportPath, reportContent, 'utf8');
418
+
419
+ console.log(`📄 Test report saved: ${reportPath}`);
420
+ return reportPath;
421
+ }
422
+
423
+ /**
424
+ * Wait for network to be idle
425
+ *
426
+ * @param {Page} page - Playwright page object
427
+ * @param {number} timeout - Timeout in ms
428
+ */
429
+ async function waitForNetworkIdle(page, timeout = 30000) {
430
+ try {
431
+ await page.waitForLoadState('networkidle', { timeout });
432
+ } catch (e) {
433
+ console.warn('⚠️ Network idle timeout - continuing anyway');
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scroll to element
439
+ *
440
+ * @param {Page} page - Playwright page object
441
+ * @param {string} selector - CSS selector
442
+ */
443
+ async function scrollToElement(page, selector) {
444
+ await page.locator(selector).scrollIntoViewIfNeeded();
445
+ }
446
+
447
+ /**
448
+ * Wait for element to be visible
449
+ *
450
+ * @param {Page} page - Playwright page object
451
+ * @param {string} selector - CSS selector
452
+ * @param {number} timeout - Timeout in ms
453
+ */
454
+ async function waitForVisible(page, selector, timeout = 10000) {
455
+ await page.waitForSelector(selector, { state: 'visible', timeout });
456
+ }
457
+
458
+ /**
459
+ * Check if element exists
460
+ *
461
+ * @param {Page} page - Playwright page object
462
+ * @param {string} selector - CSS selector
463
+ * @returns {boolean}
464
+ */
465
+ async function elementExists(page, selector) {
466
+ try {
467
+ await page.waitForSelector(selector, { timeout: 1000 });
468
+ return true;
469
+ } catch {
470
+ return false;
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Get text content of element
476
+ *
477
+ * @param {Page} page - Playwright page object
478
+ * @param {string} selector - CSS selector
479
+ * @returns {string}
480
+ */
481
+ async function getText(page, selector) {
482
+ return await page.locator(selector).textContent();
483
+ }
484
+
485
+ /**
486
+ * Get all matching elements count
487
+ *
488
+ * @param {Page} page - Playwright page object
489
+ * @param {string} selector - CSS selector
490
+ * @returns {number}
491
+ */
492
+ async function countElements(page, selector) {
493
+ return await page.locator(selector).count();
494
+ }
495
+
496
+ // Export all utilities
497
+ module.exports = {
498
+ detectServers,
499
+ safeClick,
500
+ safeType,
501
+ captureScreenshot,
502
+ handleCookieBanner,
503
+ extractTableData,
504
+ waitForStableDOM,
505
+ checkAccessibility,
506
+ generateTestReport,
507
+ saveTestReport,
508
+ waitForNetworkIdle,
509
+ scrollToElement,
510
+ waitForVisible,
511
+ elementExists,
512
+ getText,
513
+ countElements
514
+ };
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "e2e-playwright-skill",
3
+ "version": "1.0.0",
4
+ "description": "End-to-end browser automation and testing skill for SpecWeave using Playwright",
5
+ "main": "execute.js",
6
+ "scripts": {
7
+ "setup": "npm install && npx playwright install chromium",
8
+ "install-browsers": "npx playwright install",
9
+ "test": "node execute.js --version",
10
+ "clean": "rm -f /tmp/e2e-execution-*.js /tmp/e2e-test-*.js"
11
+ },
12
+ "keywords": [
13
+ "playwright",
14
+ "e2e",
15
+ "testing",
16
+ "browser-automation",
17
+ "specweave",
18
+ "claude-code",
19
+ "skill"
20
+ ],
21
+ "author": "SpecWeave",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "playwright": "^1.48.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=14.0.0"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/specweave/specweave"
32
+ }
33
+ }
@@ -0,0 +1,54 @@
1
+ id: TC-001
2
+ name: Basic Navigation Test
3
+ description: Verify that the skill can navigate to a webpage and capture a screenshot
4
+ priority: high
5
+ category: functional
6
+
7
+ preconditions:
8
+ - Playwright is installed
9
+ - Chromium browser is available
10
+ - Development server is running on localhost:3000
11
+
12
+ test_steps:
13
+ - step: 1
14
+ action: Create test script that navigates to localhost:3000
15
+ expected: Test script is created in /tmp/
16
+
17
+ - step: 2
18
+ action: Execute test script via execute.js
19
+ expected: Browser launches and navigates to homepage
20
+
21
+ - step: 3
22
+ action: Capture full-page screenshot
23
+ expected: Screenshot is saved to /tmp/ with timestamp
24
+
25
+ - step: 4
26
+ action: Verify page title
27
+ expected: Page title is captured and logged to console
28
+
29
+ - step: 5
30
+ action: Close browser
31
+ expected: Browser closes cleanly without errors
32
+
33
+ acceptance_criteria:
34
+ - Browser launches in visible mode (headless: false)
35
+ - Navigation completes successfully
36
+ - Screenshot file is created with correct naming pattern
37
+ - Console output shows execution steps
38
+ - No errors are thrown during execution
39
+
40
+ test_data:
41
+ url: http://localhost:3000
42
+ expected_title: SpecWeave
43
+ screenshot_pattern: /tmp/homepage-*.png
44
+
45
+ expected_results:
46
+ - Browser opens visibly
47
+ - Page loads within 10 seconds
48
+ - Screenshot file exists and is valid PNG
49
+ - Console shows "✅ Test passed" message
50
+ - Process exits with code 0
51
+
52
+ notes: |
53
+ This is the most basic test case to verify core functionality.
54
+ All other tests depend on this working correctly.
@@ -0,0 +1,64 @@
1
+ id: TC-002
2
+ name: Form Interaction Test
3
+ description: Verify that the skill can interact with form elements using helper utilities
4
+ priority: high
5
+ category: functional
6
+
7
+ preconditions:
8
+ - TC-001 passes
9
+ - Test page has a login form with email and password fields
10
+ - safeType and safeClick utilities are available
11
+
12
+ test_steps:
13
+ - step: 1
14
+ action: Navigate to login page
15
+ expected: Login form is visible
16
+
17
+ - step: 2
18
+ action: Use safeType() to enter email address
19
+ expected: Email field is filled with test email
20
+
21
+ - step: 3
22
+ action: Use safeType() to enter password
23
+ expected: Password field is filled (masked)
24
+
25
+ - step: 4
26
+ action: Use safeClick() to submit form
27
+ expected: Form submission is triggered
28
+
29
+ - step: 5
30
+ action: Wait for navigation or success message
31
+ expected: User is redirected or sees success state
32
+
33
+ - step: 6
34
+ action: Capture screenshot of result
35
+ expected: Screenshot shows post-login state
36
+
37
+ acceptance_criteria:
38
+ - safeType() handles focus and clearing automatically
39
+ - safeType() verifies text was entered correctly
40
+ - safeClick() waits for element to be visible
41
+ - Form submission completes without timeout
42
+ - Screenshots capture both pre and post submission states
43
+
44
+ test_data:
45
+ email: test@example.com
46
+ password: testpassword123
47
+ login_url: http://localhost:3000/login
48
+ expected_redirect: /dashboard
49
+
50
+ expected_results:
51
+ - Email field contains entered value
52
+ - Password field is masked
53
+ - Form submits successfully
54
+ - Navigation completes within 10 seconds
55
+ - Success state is reached
56
+
57
+ error_scenarios:
58
+ - If email field not found: Error message with selector details
59
+ - If click times out: Retry up to 3 times
60
+ - If verification fails: Clear error about mismatch
61
+
62
+ notes: |
63
+ Tests the core interaction utilities that make Playwright easier to use.
64
+ safeType() and safeClick() should handle common edge cases automatically.