@brunosps00/dev-workflow 0.0.3 → 0.0.6

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 (221) hide show
  1. package/README.md +42 -42
  2. package/bin/dev-workflow.js +6 -4
  3. package/lib/constants.js +42 -40
  4. package/lib/init.js +66 -19
  5. package/package.json +1 -1
  6. package/scaffold/en/commands/{analyze-project.md → dw-analyze-project.md} +69 -40
  7. package/scaffold/en/commands/{brainstorm.md → dw-brainstorm.md} +31 -4
  8. package/scaffold/en/commands/{bugfix.md → dw-bugfix.md} +63 -19
  9. package/scaffold/en/commands/{code-review.md → dw-code-review.md} +38 -15
  10. package/scaffold/en/commands/{commit.md → dw-commit.md} +25 -0
  11. package/scaffold/en/commands/{create-prd.md → dw-create-prd.md} +24 -10
  12. package/scaffold/en/commands/{create-tasks.md → dw-create-tasks.md} +11 -4
  13. package/scaffold/en/commands/{create-techspec.md → dw-create-techspec.md} +38 -11
  14. package/scaffold/en/commands/{deep-research.md → dw-deep-research.md} +18 -17
  15. package/scaffold/en/commands/{fix-qa.md → dw-fix-qa.md} +20 -3
  16. package/scaffold/en/commands/dw-functional-doc.md +276 -0
  17. package/scaffold/en/commands/{generate-pr.md → dw-generate-pr.md} +20 -5
  18. package/scaffold/en/commands/dw-help.md +309 -0
  19. package/scaffold/en/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +50 -26
  20. package/scaffold/en/commands/{review-implementation.md → dw-review-implementation.md} +25 -6
  21. package/scaffold/en/commands/{run-plan.md → dw-run-plan.md} +21 -6
  22. package/scaffold/en/commands/{run-qa.md → dw-run-qa.md} +32 -13
  23. package/scaffold/en/commands/{run-task.md → dw-run-task.md} +17 -7
  24. package/scaffold/en/references/playwright-patterns.md +136 -0
  25. package/scaffold/en/references/refactoring-catalog.md +167 -0
  26. package/scaffold/en/templates/brainstorm-matrix.md +44 -0
  27. package/scaffold/en/templates/functional-doc/case-matrix.md +5 -0
  28. package/scaffold/en/templates/functional-doc/e2e-runbook.md +3 -0
  29. package/scaffold/en/templates/functional-doc/features.md +3 -0
  30. package/scaffold/en/templates/functional-doc/overview.md +21 -0
  31. package/scaffold/en/templates/functional-doc/playwright.spec.ts.tpl +19 -0
  32. package/scaffold/en/templates/pr-bugfix-template.md +28 -0
  33. package/scaffold/en/templates/qa-test-credentials.md +37 -0
  34. package/scaffold/en/templates/tasks-template.md +1 -1
  35. package/scaffold/en/templates/techspec-template.md +1 -1
  36. package/scaffold/pt-br/commands/{analyze-project.md → dw-analyze-project.md} +94 -44
  37. package/scaffold/pt-br/commands/{brainstorm.md → dw-brainstorm.md} +32 -5
  38. package/scaffold/pt-br/commands/{bugfix.md → dw-bugfix.md} +73 -16
  39. package/scaffold/pt-br/commands/{code-review.md → dw-code-review.md} +80 -17
  40. package/scaffold/pt-br/commands/{commit.md → dw-commit.md} +45 -1
  41. package/scaffold/pt-br/commands/{create-prd.md → dw-create-prd.md} +25 -10
  42. package/scaffold/pt-br/commands/{create-tasks.md → dw-create-tasks.md} +24 -17
  43. package/scaffold/pt-br/commands/{create-techspec.md → dw-create-techspec.md} +40 -13
  44. package/scaffold/pt-br/commands/{deep-research.md → dw-deep-research.md} +19 -11
  45. package/scaffold/pt-br/commands/{fix-qa.md → dw-fix-qa.md} +30 -1
  46. package/scaffold/pt-br/commands/dw-functional-doc.md +276 -0
  47. package/scaffold/pt-br/commands/{generate-pr.md → dw-generate-pr.md} +61 -6
  48. package/scaffold/pt-br/commands/dw-help.md +248 -0
  49. package/scaffold/pt-br/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +49 -25
  50. package/scaffold/pt-br/commands/{review-implementation.md → dw-review-implementation.md} +53 -5
  51. package/scaffold/pt-br/commands/{run-plan.md → dw-run-plan.md} +100 -12
  52. package/scaffold/pt-br/commands/{run-qa.md → dw-run-qa.md} +93 -18
  53. package/scaffold/pt-br/commands/{run-task.md → dw-run-task.md} +35 -10
  54. package/scaffold/pt-br/references/playwright-patterns.md +133 -0
  55. package/scaffold/pt-br/references/refactoring-catalog.md +166 -0
  56. package/scaffold/pt-br/templates/brainstorm-matrix.md +44 -0
  57. package/scaffold/pt-br/templates/functional-doc/case-matrix.md +5 -0
  58. package/scaffold/pt-br/templates/functional-doc/e2e-runbook.md +3 -0
  59. package/scaffold/pt-br/templates/functional-doc/features.md +3 -0
  60. package/scaffold/pt-br/templates/functional-doc/overview.md +21 -0
  61. package/scaffold/pt-br/templates/functional-doc/playwright.spec.ts.tpl +19 -0
  62. package/scaffold/pt-br/templates/pr-bugfix-template.md +28 -0
  63. package/scaffold/pt-br/templates/qa-test-credentials.md +37 -0
  64. package/scaffold/pt-br/templates/tasks-template.md +2 -2
  65. package/scaffold/pt-br/templates/techspec-template.md +1 -1
  66. package/scaffold/rules-readme.md +3 -3
  67. package/scaffold/scripts/functional-doc/generate-dossier.mjs +821 -0
  68. package/scaffold/scripts/functional-doc/run-playwright-flow.mjs +275 -0
  69. package/scaffold/skills/agent-browser/SKILL.md +750 -0
  70. package/scaffold/skills/agent-browser/references/authentication.md +303 -0
  71. package/scaffold/skills/agent-browser/references/commands.md +295 -0
  72. package/scaffold/skills/agent-browser/references/profiling.md +120 -0
  73. package/scaffold/skills/agent-browser/references/proxy-support.md +194 -0
  74. package/scaffold/skills/agent-browser/references/session-management.md +193 -0
  75. package/scaffold/skills/agent-browser/references/snapshot-refs.md +219 -0
  76. package/scaffold/skills/agent-browser/references/video-recording.md +173 -0
  77. package/scaffold/skills/agent-browser/templates/authenticated-session.sh +105 -0
  78. package/scaffold/skills/agent-browser/templates/capture-workflow.sh +69 -0
  79. package/scaffold/skills/agent-browser/templates/form-automation.sh +62 -0
  80. package/scaffold/skills/humanizer/README.md +143 -0
  81. package/scaffold/skills/humanizer/SKILL.md +488 -0
  82. package/scaffold/skills/humanizer/WARP.md +53 -0
  83. package/scaffold/skills/remotion-best-practices/SKILL.md +61 -0
  84. package/scaffold/skills/remotion-best-practices/rules/3d.md +86 -0
  85. package/scaffold/skills/remotion-best-practices/rules/animations.md +27 -0
  86. package/scaffold/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  87. package/scaffold/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  88. package/scaffold/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +103 -0
  89. package/scaffold/skills/remotion-best-practices/rules/assets.md +78 -0
  90. package/scaffold/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
  91. package/scaffold/skills/remotion-best-practices/rules/audio.md +169 -0
  92. package/scaffold/skills/remotion-best-practices/rules/calculate-metadata.md +134 -0
  93. package/scaffold/skills/remotion-best-practices/rules/can-decode.md +75 -0
  94. package/scaffold/skills/remotion-best-practices/rules/charts.md +120 -0
  95. package/scaffold/skills/remotion-best-practices/rules/compositions.md +154 -0
  96. package/scaffold/skills/remotion-best-practices/rules/display-captions.md +184 -0
  97. package/scaffold/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  98. package/scaffold/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
  99. package/scaffold/skills/remotion-best-practices/rules/fonts.md +152 -0
  100. package/scaffold/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  101. package/scaffold/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  102. package/scaffold/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
  103. package/scaffold/skills/remotion-best-practices/rules/gifs.md +141 -0
  104. package/scaffold/skills/remotion-best-practices/rules/images.md +134 -0
  105. package/scaffold/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
  106. package/scaffold/skills/remotion-best-practices/rules/light-leaks.md +73 -0
  107. package/scaffold/skills/remotion-best-practices/rules/lottie.md +70 -0
  108. package/scaffold/skills/remotion-best-practices/rules/maps.md +412 -0
  109. package/scaffold/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
  110. package/scaffold/skills/remotion-best-practices/rules/measuring-text.md +140 -0
  111. package/scaffold/skills/remotion-best-practices/rules/parameters.md +109 -0
  112. package/scaffold/skills/remotion-best-practices/rules/sequencing.md +118 -0
  113. package/scaffold/skills/remotion-best-practices/rules/sfx.md +26 -0
  114. package/scaffold/skills/remotion-best-practices/rules/subtitles.md +36 -0
  115. package/scaffold/skills/remotion-best-practices/rules/tailwind.md +11 -0
  116. package/scaffold/skills/remotion-best-practices/rules/text-animations.md +20 -0
  117. package/scaffold/skills/remotion-best-practices/rules/timing.md +179 -0
  118. package/scaffold/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
  119. package/scaffold/skills/remotion-best-practices/rules/transitions.md +197 -0
  120. package/scaffold/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
  121. package/scaffold/skills/remotion-best-practices/rules/trimming.md +51 -0
  122. package/scaffold/skills/remotion-best-practices/rules/videos.md +171 -0
  123. package/scaffold/skills/remotion-best-practices/rules/voiceover.md +99 -0
  124. package/scaffold/skills/security-review/LICENSE +22 -0
  125. package/scaffold/skills/security-review/SKILL.md +312 -0
  126. package/scaffold/skills/security-review/infrastructure/docker.md +432 -0
  127. package/scaffold/skills/security-review/languages/javascript.md +388 -0
  128. package/scaffold/skills/security-review/languages/python.md +363 -0
  129. package/scaffold/skills/security-review/references/api-security.md +519 -0
  130. package/scaffold/skills/security-review/references/authentication.md +353 -0
  131. package/scaffold/skills/security-review/references/authorization.md +372 -0
  132. package/scaffold/skills/security-review/references/business-logic.md +443 -0
  133. package/scaffold/skills/security-review/references/cryptography.md +329 -0
  134. package/scaffold/skills/security-review/references/csrf.md +398 -0
  135. package/scaffold/skills/security-review/references/data-protection.md +378 -0
  136. package/scaffold/skills/security-review/references/deserialization.md +410 -0
  137. package/scaffold/skills/security-review/references/error-handling.md +436 -0
  138. package/scaffold/skills/security-review/references/file-security.md +457 -0
  139. package/scaffold/skills/security-review/references/injection.md +259 -0
  140. package/scaffold/skills/security-review/references/logging.md +433 -0
  141. package/scaffold/skills/security-review/references/misconfiguration.md +435 -0
  142. package/scaffold/skills/security-review/references/modern-threats.md +475 -0
  143. package/scaffold/skills/security-review/references/ssrf.md +415 -0
  144. package/scaffold/skills/security-review/references/supply-chain.md +405 -0
  145. package/scaffold/skills/security-review/references/xss.md +336 -0
  146. package/scaffold/skills/vercel-react-best-practices/AGENTS.md +3648 -0
  147. package/scaffold/skills/vercel-react-best-practices/README.md +123 -0
  148. package/scaffold/skills/vercel-react-best-practices/SKILL.md +146 -0
  149. package/scaffold/skills/vercel-react-best-practices/rules/_sections.md +46 -0
  150. package/scaffold/skills/vercel-react-best-practices/rules/_template.md +28 -0
  151. package/scaffold/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  152. package/scaffold/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  153. package/scaffold/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  154. package/scaffold/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  155. package/scaffold/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  156. package/scaffold/skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
  157. package/scaffold/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  158. package/scaffold/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  159. package/scaffold/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  160. package/scaffold/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
  161. package/scaffold/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  162. package/scaffold/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  163. package/scaffold/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  164. package/scaffold/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  165. package/scaffold/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  166. package/scaffold/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  167. package/scaffold/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  168. package/scaffold/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  169. package/scaffold/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  170. package/scaffold/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  171. package/scaffold/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  172. package/scaffold/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  173. package/scaffold/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  174. package/scaffold/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  175. package/scaffold/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
  176. package/scaffold/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  177. package/scaffold/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  178. package/scaffold/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  179. package/scaffold/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  180. package/scaffold/skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
  181. package/scaffold/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  182. package/scaffold/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  183. package/scaffold/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  184. package/scaffold/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  185. package/scaffold/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  186. package/scaffold/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  187. package/scaffold/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  188. package/scaffold/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  189. package/scaffold/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  190. package/scaffold/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
  191. package/scaffold/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
  192. package/scaffold/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  193. package/scaffold/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  194. package/scaffold/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  195. package/scaffold/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  196. package/scaffold/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  197. package/scaffold/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  198. package/scaffold/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  199. package/scaffold/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  200. package/scaffold/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  201. package/scaffold/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  202. package/scaffold/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  203. package/scaffold/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
  204. package/scaffold/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  205. package/scaffold/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  206. package/scaffold/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  207. package/scaffold/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  208. package/scaffold/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  209. package/scaffold/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  210. package/scaffold/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  211. package/scaffold/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  212. package/scaffold/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  213. package/scaffold/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  214. package/scaffold/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
  215. package/scaffold/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  216. package/scaffold/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  217. package/scaffold/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  218. package/scaffold/skills/webapp-testing/SKILL.md +133 -0
  219. package/scaffold/skills/webapp-testing/assets/test-helper.js +56 -0
  220. package/scaffold/en/commands/help.md +0 -289
  221. package/scaffold/pt-br/commands/help.md +0 -226
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+
7
+ function parseArgs(argv) {
8
+ const result = {};
9
+ for (let index = 0; index < argv.length; index += 1) {
10
+ const token = argv[index];
11
+ if (!token.startsWith("--")) {
12
+ continue;
13
+ }
14
+
15
+ const [rawKey, inlineValue] = token.slice(2).split("=", 2);
16
+ const key = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
17
+ if (inlineValue !== undefined) {
18
+ result[key] = inlineValue;
19
+ continue;
20
+ }
21
+
22
+ const next = argv[index + 1];
23
+ if (!next || next.startsWith("--")) {
24
+ result[key] = true;
25
+ continue;
26
+ }
27
+
28
+ result[key] = next;
29
+ index += 1;
30
+ }
31
+ return result;
32
+ }
33
+
34
+ function detectPackageManager(projectRoot) {
35
+ if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
36
+ if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) return "npm";
37
+ if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) return "yarn";
38
+ return "npm";
39
+ }
40
+
41
+ function updateManifest(manifestPath, patch) {
42
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
43
+ const next = {
44
+ ...manifest,
45
+ execution: {
46
+ ...(manifest.execution ?? {}),
47
+ ...patch,
48
+ },
49
+ };
50
+ fs.writeFileSync(manifestPath, `${JSON.stringify(next, null, 2)}\n`);
51
+ }
52
+
53
+ function detectTestDir(playwrightConfig) {
54
+ const content = fs.readFileSync(playwrightConfig, "utf8");
55
+ const match = content.match(/testDir:\s*"([^"]+)"/);
56
+ return match?.[1] ?? "e2e";
57
+ }
58
+
59
+ function ensureDir(dirPath) {
60
+ fs.mkdirSync(dirPath, { recursive: true });
61
+ }
62
+
63
+ function escapeForTemplate(value) {
64
+ return JSON.stringify(value);
65
+ }
66
+
67
+ function parseResolution(rawValue) {
68
+ const normalized = String(rawValue ?? "").trim().toLowerCase();
69
+ if (!normalized || normalized === "fullhd") {
70
+ return { width: 1920, height: 1080, label: "1920x1080" };
71
+ }
72
+
73
+ const match = normalized.match(/^(\d{3,5})x(\d{3,5})$/);
74
+ if (!match) {
75
+ return { width: 1920, height: 1080, label: "1920x1080" };
76
+ }
77
+
78
+ return {
79
+ width: Number(match[1]),
80
+ height: Number(match[2]),
81
+ label: `${Number(match[1])}x${Number(match[2])}`,
82
+ };
83
+ }
84
+
85
+ function createTemporaryPlaywrightConfig({
86
+ generatedDir,
87
+ flowDir,
88
+ baseUrl,
89
+ browserName,
90
+ resolution,
91
+ }) {
92
+ const rawArtifactsDir = path.join(flowDir, "evidence", "playwright-artifacts");
93
+ ensureDir(rawArtifactsDir);
94
+
95
+ const configPath = path.join(generatedDir, "playwright.functional-doc.config.mjs");
96
+ const content = `import { defineConfig } from "@playwright/test";
97
+
98
+ const rawOutputDir = ${escapeForTemplate(rawArtifactsDir)};
99
+ const baseURL = process.env.BASE_URL ?? ${escapeForTemplate(baseUrl)};
100
+ const generatedDir = ${escapeForTemplate(generatedDir)};
101
+
102
+ export default defineConfig({
103
+ testDir: generatedDir,
104
+ outputDir: rawOutputDir,
105
+ fullyParallel: false,
106
+ retries: 0,
107
+ workers: 1,
108
+ reporter: "line",
109
+ use: {
110
+ baseURL,
111
+ video: {
112
+ mode: "on",
113
+ size: { width: ${resolution.width}, height: ${resolution.height} },
114
+ },
115
+ screenshot: "on",
116
+ trace: "retain-on-failure",
117
+ viewport: { width: ${resolution.width}, height: ${resolution.height} },
118
+ },
119
+ projects: [
120
+ {
121
+ name: ${escapeForTemplate(browserName)},
122
+ use: { browserName: ${escapeForTemplate(browserName)} },
123
+ },
124
+ ],
125
+ });
126
+ `;
127
+ fs.writeFileSync(configPath, content);
128
+ return { configPath, rawArtifactsDir };
129
+ }
130
+
131
+ function collectFilesByExtension(rootDir, extension) {
132
+ if (!fs.existsSync(rootDir)) {
133
+ return [];
134
+ }
135
+
136
+ const found = [];
137
+ const queue = [rootDir];
138
+ while (queue.length > 0) {
139
+ const current = queue.pop();
140
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
141
+ const absolutePath = path.join(current, entry.name);
142
+ if (entry.isDirectory()) {
143
+ queue.push(absolutePath);
144
+ continue;
145
+ }
146
+ if (absolutePath.toLowerCase().endsWith(extension.toLowerCase())) {
147
+ found.push(absolutePath);
148
+ }
149
+ }
150
+ }
151
+ return found.sort();
152
+ }
153
+
154
+ function copyArtifacts(flowDir, slug, rawArtifactsDir) {
155
+ const videoDir = path.join(flowDir, "evidence", "videos");
156
+ const screenshotDir = path.join(flowDir, "evidence", "screenshots");
157
+ ensureDir(videoDir);
158
+ ensureDir(screenshotDir);
159
+
160
+ const videos = collectFilesByExtension(rawArtifactsDir, ".webm");
161
+ const screenshots = collectFilesByExtension(rawArtifactsDir, ".png");
162
+
163
+ const copiedVideos = videos.map((sourcePath, index) => {
164
+ const destinationPath = path.join(videoDir, `${slug}${videos.length > 1 ? `-${index + 1}` : ""}.webm`);
165
+ fs.copyFileSync(sourcePath, destinationPath);
166
+ return destinationPath;
167
+ });
168
+
169
+ const copiedScreenshots = screenshots.map((sourcePath, index) => {
170
+ const destinationPath = path.join(screenshotDir, `${slug}${screenshots.length > 1 ? `-${index + 1}` : ""}.png`);
171
+ fs.copyFileSync(sourcePath, destinationPath);
172
+ return destinationPath;
173
+ });
174
+
175
+ return {
176
+ videos: copiedVideos,
177
+ screenshots: copiedScreenshots,
178
+ };
179
+ }
180
+
181
+ function main() {
182
+ const args = parseArgs(process.argv.slice(2));
183
+ if (!args.flowDir) {
184
+ throw new Error("Missing required argument: --flow-dir");
185
+ }
186
+
187
+ const flowDir = path.resolve(args.flowDir);
188
+ const manifestPath = path.join(flowDir, "manifest.json");
189
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
190
+ const projectRoot = path.resolve(manifest.project);
191
+ const scriptPath = path.resolve(manifest.outputs.script);
192
+ const packageManager = detectPackageManager(projectRoot);
193
+ const browserName = args.browserName || "chromium";
194
+ const resolution = parseResolution(
195
+ args.videoResolution
196
+ ?? manifest.artifacts?.human_final?.resolution
197
+ ?? manifest.videoResolution
198
+ ?? "fullhd"
199
+ );
200
+ const playwrightConfig = ["playwright.config.ts", "playwright.config.js"]
201
+ .map((file) => path.join(projectRoot, file))
202
+ .find((file) => fs.existsSync(file));
203
+
204
+ if (!playwrightConfig) {
205
+ updateManifest(manifestPath, {
206
+ status: "blocked",
207
+ reason: "Playwright configuration not found",
208
+ });
209
+ process.stdout.write("Playwright configuration not found.\n");
210
+ process.exit(0);
211
+ }
212
+
213
+ const testDir = detectTestDir(playwrightConfig);
214
+ const generatedDir = path.join(projectRoot, testDir, ".functional-doc-generated");
215
+ ensureDir(generatedDir);
216
+ const materializedScriptPath = path.join(generatedDir, path.basename(scriptPath));
217
+ fs.copyFileSync(scriptPath, materializedScriptPath);
218
+ const slug = path.basename(scriptPath, path.extname(scriptPath));
219
+ const { configPath: temporaryConfigPath, rawArtifactsDir } = createTemporaryPlaywrightConfig({
220
+ generatedDir,
221
+ flowDir,
222
+ baseUrl: manifest.baseUrl,
223
+ browserName,
224
+ resolution,
225
+ });
226
+
227
+ const commandArgs = ["exec", "playwright", "test", materializedScriptPath, "--config", temporaryConfigPath];
228
+ if (args.listOnly) {
229
+ commandArgs.push("--list");
230
+ }
231
+
232
+ const result = spawnSync(
233
+ packageManager,
234
+ commandArgs,
235
+ {
236
+ cwd: projectRoot,
237
+ env: {
238
+ ...process.env,
239
+ BASE_URL: manifest.baseUrl,
240
+ },
241
+ encoding: "utf8",
242
+ },
243
+ );
244
+
245
+ const logDir = path.join(flowDir, "evidence", "logs");
246
+ fs.mkdirSync(logDir, { recursive: true });
247
+ fs.writeFileSync(path.join(logDir, "playwright.stdout.log"), result.stdout ?? "");
248
+ fs.writeFileSync(path.join(logDir, "playwright.stderr.log"), result.stderr ?? "");
249
+
250
+ const copiedArtifacts = args.listOnly
251
+ ? { videos: [], screenshots: [] }
252
+ : copyArtifacts(flowDir, slug, rawArtifactsDir);
253
+
254
+ updateManifest(manifestPath, {
255
+ status: args.listOnly ? (result.status === 0 ? "listed" : "list_failed") : (result.status === 0 ? "passed" : "failed"),
256
+ command: `${packageManager} ${commandArgs.join(" ")}`,
257
+ exitCode: result.status ?? 1,
258
+ videoResolution: resolution.label,
259
+ materializedScript: materializedScriptPath,
260
+ temporaryConfig: temporaryConfigPath,
261
+ video: copiedArtifacts.videos[0] ?? null,
262
+ videos: copiedArtifacts.videos,
263
+ screenshots: copiedArtifacts.screenshots,
264
+ logs: [
265
+ path.join(logDir, "playwright.stdout.log"),
266
+ path.join(logDir, "playwright.stderr.log"),
267
+ ],
268
+ });
269
+
270
+ process.stdout.write(result.stdout ?? "");
271
+ process.stderr.write(result.stderr ?? "");
272
+ process.exit(result.status ?? 1);
273
+ }
274
+
275
+ main();