playwright-intelligence-engine 0.0.0-stage → 2.0.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 (353) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +229 -3
  3. package/bin/pw-intel.js +37 -0
  4. package/dist/authoring/author.d.ts +83 -0
  5. package/dist/authoring/author.d.ts.map +1 -0
  6. package/dist/authoring/generation-contract.d.ts +90 -0
  7. package/dist/authoring/generation-contract.d.ts.map +1 -0
  8. package/dist/authoring/hybrid-author.d.ts +6 -0
  9. package/dist/authoring/hybrid-author.d.ts.map +1 -0
  10. package/dist/authoring/index.d.ts +7 -0
  11. package/dist/authoring/index.d.ts.map +1 -0
  12. package/dist/authoring/llm-author.d.ts +28 -0
  13. package/dist/authoring/llm-author.d.ts.map +1 -0
  14. package/dist/authoring/naming/index.d.ts +4 -0
  15. package/dist/authoring/naming/index.d.ts.map +1 -0
  16. package/dist/authoring/naming/name-plan.d.ts +45 -0
  17. package/dist/authoring/naming/name-plan.d.ts.map +1 -0
  18. package/dist/authoring/naming/name-planner.d.ts +25 -0
  19. package/dist/authoring/naming/name-planner.d.ts.map +1 -0
  20. package/dist/authoring/naming/naming-linter.d.ts +20 -0
  21. package/dist/authoring/naming/naming-linter.d.ts.map +1 -0
  22. package/dist/authoring/template-author.d.ts +6 -0
  23. package/dist/authoring/template-author.d.ts.map +1 -0
  24. package/dist/cli/index.d.ts +2 -0
  25. package/dist/cli/index.d.ts.map +1 -0
  26. package/dist/collectors/collector-interface.d.ts +7 -0
  27. package/dist/collectors/collector-interface.d.ts.map +1 -0
  28. package/dist/collectors/environment-collector.d.ts +12 -0
  29. package/dist/collectors/environment-collector.d.ts.map +1 -0
  30. package/dist/collectors/error-collector.d.ts +18 -0
  31. package/dist/collectors/error-collector.d.ts.map +1 -0
  32. package/dist/collectors/git-diff-collector.d.ts +20 -0
  33. package/dist/collectors/git-diff-collector.d.ts.map +1 -0
  34. package/dist/collectors/live-browser-collector.d.ts +45 -0
  35. package/dist/collectors/live-browser-collector.d.ts.map +1 -0
  36. package/dist/collectors/network-collector.d.ts +28 -0
  37. package/dist/collectors/network-collector.d.ts.map +1 -0
  38. package/dist/collectors/static-ast-collector.d.ts +32 -0
  39. package/dist/collectors/static-ast-collector.d.ts.map +1 -0
  40. package/dist/collectors/trace-collector.d.ts +12 -0
  41. package/dist/collectors/trace-collector.d.ts.map +1 -0
  42. package/dist/config/project-config.d.ts +33 -0
  43. package/dist/config/project-config.d.ts.map +1 -0
  44. package/dist/core/errors/engine-errors.d.ts +34 -0
  45. package/dist/core/errors/engine-errors.d.ts.map +1 -0
  46. package/dist/core/types/diagnostic-context.d.ts +151 -0
  47. package/dist/core/types/diagnostic-context.d.ts.map +1 -0
  48. package/dist/core/types/repair-evidence.d.ts +56 -0
  49. package/dist/core/types/repair-evidence.d.ts.map +1 -0
  50. package/dist/core/version.d.ts +13 -0
  51. package/dist/core/version.d.ts.map +1 -0
  52. package/dist/diagnostics/diagnostic-contract.d.ts +52 -0
  53. package/dist/diagnostics/diagnostic-contract.d.ts.map +1 -0
  54. package/dist/diagnostics/diagnostic-report.d.ts +11 -0
  55. package/dist/diagnostics/diagnostic-report.d.ts.map +1 -0
  56. package/dist/diagnostics/evidence-correlator.d.ts +22 -0
  57. package/dist/diagnostics/evidence-correlator.d.ts.map +1 -0
  58. package/dist/diagnostics/external-failure-evidence.d.ts +18 -0
  59. package/dist/diagnostics/external-failure-evidence.d.ts.map +1 -0
  60. package/dist/diagnostics/failure-category.d.ts +4 -0
  61. package/dist/diagnostics/failure-category.d.ts.map +1 -0
  62. package/dist/diagnostics/failure-classifier.d.ts +58 -0
  63. package/dist/diagnostics/failure-classifier.d.ts.map +1 -0
  64. package/dist/evaluation/evaluation-scorecard.d.ts +72 -0
  65. package/dist/evaluation/evaluation-scorecard.d.ts.map +1 -0
  66. package/dist/evaluation/repository-inspector.d.ts +21 -0
  67. package/dist/evaluation/repository-inspector.d.ts.map +1 -0
  68. package/dist/evaluation/repository-runner.d.ts +24 -0
  69. package/dist/evaluation/repository-runner.d.ts.map +1 -0
  70. package/dist/evidence/evidence-model.d.ts +88 -0
  71. package/dist/evidence/evidence-model.d.ts.map +1 -0
  72. package/dist/evidence/evidence-normalizer.d.ts +19 -0
  73. package/dist/evidence/evidence-normalizer.d.ts.map +1 -0
  74. package/dist/evidence/evidence-redactor.d.ts +37 -0
  75. package/dist/evidence/evidence-redactor.d.ts.map +1 -0
  76. package/dist/framework/config-adapter.d.ts +28 -0
  77. package/dist/framework/config-adapter.d.ts.map +1 -0
  78. package/dist/gate/index.d.ts +21 -0
  79. package/dist/gate/index.d.ts.map +1 -0
  80. package/dist/gate/oracle.d.ts +26 -0
  81. package/dist/gate/oracle.d.ts.map +1 -0
  82. package/dist/gate/quality.d.ts +27 -0
  83. package/dist/gate/quality.d.ts.map +1 -0
  84. package/dist/gate/safety.d.ts +28 -0
  85. package/dist/gate/safety.d.ts.map +1 -0
  86. package/dist/generator/architecture-generator.d.ts +55 -0
  87. package/dist/generator/architecture-generator.d.ts.map +1 -0
  88. package/dist/generator/assertion-quality-gate.d.ts +18 -0
  89. package/dist/generator/assertion-quality-gate.d.ts.map +1 -0
  90. package/dist/generator/candidate-writer.d.ts +34 -0
  91. package/dist/generator/candidate-writer.d.ts.map +1 -0
  92. package/dist/generator/test-generator.d.ts +26 -0
  93. package/dist/generator/test-generator.d.ts.map +1 -0
  94. package/dist/governance/audit-log.d.ts +23 -0
  95. package/dist/governance/audit-log.d.ts.map +1 -0
  96. package/dist/governance/governance-policy.d.ts +24 -0
  97. package/dist/governance/governance-policy.d.ts.map +1 -0
  98. package/dist/governance/team-dashboard.d.ts +51 -0
  99. package/dist/governance/team-dashboard.d.ts.map +1 -0
  100. package/dist/governance/workflow-audit.d.ts +13 -0
  101. package/dist/governance/workflow-audit.d.ts.map +1 -0
  102. package/dist/index.d.ts +90 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +20318 -0
  105. package/dist/integrations/ci-summary.d.ts +14 -0
  106. package/dist/integrations/ci-summary.d.ts.map +1 -0
  107. package/dist/integrations/playwright-report-adapter.d.ts +22 -0
  108. package/dist/integrations/playwright-report-adapter.d.ts.map +1 -0
  109. package/dist/integrations/quality-gate.d.ts +35 -0
  110. package/dist/integrations/quality-gate.d.ts.map +1 -0
  111. package/dist/integrations/sarif-report.d.ts +48 -0
  112. package/dist/integrations/sarif-report.d.ts.map +1 -0
  113. package/dist/intelligence/autonomous-repair-workflow.d.ts +31 -0
  114. package/dist/intelligence/autonomous-repair-workflow.d.ts.map +1 -0
  115. package/dist/intelligence/business-intent.d.ts +58 -0
  116. package/dist/intelligence/business-intent.d.ts.map +1 -0
  117. package/dist/intelligence/evidence-bundle.d.ts +364 -0
  118. package/dist/intelligence/evidence-bundle.d.ts.map +1 -0
  119. package/dist/intelligence/failure-clustering.d.ts +38 -0
  120. package/dist/intelligence/failure-clustering.d.ts.map +1 -0
  121. package/dist/intelligence/generation-agent.d.ts +120 -0
  122. package/dist/intelligence/generation-agent.d.ts.map +1 -0
  123. package/dist/intelligence/generation-verifier.d.ts +151 -0
  124. package/dist/intelligence/generation-verifier.d.ts.map +1 -0
  125. package/dist/intelligence/llm-proposal.d.ts +377 -0
  126. package/dist/intelligence/llm-proposal.d.ts.map +1 -0
  127. package/dist/intelligence/locator-disambiguation-engine.d.ts +38 -0
  128. package/dist/intelligence/locator-disambiguation-engine.d.ts.map +1 -0
  129. package/dist/intelligence/model-contract.d.ts +86 -0
  130. package/dist/intelligence/model-contract.d.ts.map +1 -0
  131. package/dist/intelligence/model-provider.d.ts +75 -0
  132. package/dist/intelligence/model-provider.d.ts.map +1 -0
  133. package/dist/intelligence/project-intelligence.d.ts +22 -0
  134. package/dist/intelligence/project-intelligence.d.ts.map +1 -0
  135. package/dist/intelligence/repair-agent.d.ts +46 -0
  136. package/dist/intelligence/repair-agent.d.ts.map +1 -0
  137. package/dist/intelligence/repository-profile.d.ts +131 -0
  138. package/dist/intelligence/repository-profile.d.ts.map +1 -0
  139. package/dist/intelligence/suite-intelligence.d.ts +59 -0
  140. package/dist/intelligence/suite-intelligence.d.ts.map +1 -0
  141. package/dist/intelligence/test-intelligence.d.ts +44 -0
  142. package/dist/intelligence/test-intelligence.d.ts.map +1 -0
  143. package/dist/intelligence/trace-proposal-workflow.d.ts +30 -0
  144. package/dist/intelligence/trace-proposal-workflow.d.ts.map +1 -0
  145. package/dist/intelligence/verified-llm-repair-workflow.d.ts +21 -0
  146. package/dist/intelligence/verified-llm-repair-workflow.d.ts.map +1 -0
  147. package/dist/intelligence/workflow-planner.d.ts +278 -0
  148. package/dist/intelligence/workflow-planner.d.ts.map +1 -0
  149. package/dist/mcp/server.d.ts +32 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/memory/stability-ledger.d.ts +46 -0
  152. package/dist/memory/stability-ledger.d.ts.map +1 -0
  153. package/dist/perception/app-model.d.ts +55 -0
  154. package/dist/perception/app-model.d.ts.map +1 -0
  155. package/dist/perception/crawler.d.ts +18 -0
  156. package/dist/perception/crawler.d.ts.map +1 -0
  157. package/dist/perception/index.d.ts +3 -0
  158. package/dist/perception/index.d.ts.map +1 -0
  159. package/dist/repair/approval-service.d.ts +139 -0
  160. package/dist/repair/approval-service.d.ts.map +1 -0
  161. package/dist/repair/assertion-hasher.d.ts +62 -0
  162. package/dist/repair/assertion-hasher.d.ts.map +1 -0
  163. package/dist/repair/ast-patcher.d.ts +36 -0
  164. package/dist/repair/ast-patcher.d.ts.map +1 -0
  165. package/dist/repair/browser-operation-executor.d.ts +27 -0
  166. package/dist/repair/browser-operation-executor.d.ts.map +1 -0
  167. package/dist/repair/diff-allowlist.d.ts +74 -0
  168. package/dist/repair/diff-allowlist.d.ts.map +1 -0
  169. package/dist/repair/intent-validator.d.ts +157 -0
  170. package/dist/repair/intent-validator.d.ts.map +1 -0
  171. package/dist/repair/repair-operations.d.ts +40 -0
  172. package/dist/repair/repair-operations.d.ts.map +1 -0
  173. package/dist/repair/repair-planner.d.ts +98 -0
  174. package/dist/repair/repair-planner.d.ts.map +1 -0
  175. package/dist/repair/repair-workflow.d.ts +42 -0
  176. package/dist/repair/repair-workflow.d.ts.map +1 -0
  177. package/dist/repair/safe-operation-compiler.d.ts +15 -0
  178. package/dist/repair/safe-operation-compiler.d.ts.map +1 -0
  179. package/dist/repair/source-mapper.d.ts +19 -0
  180. package/dist/repair/source-mapper.d.ts.map +1 -0
  181. package/dist/repair/statement-patcher.d.ts +75 -0
  182. package/dist/repair/statement-patcher.d.ts.map +1 -0
  183. package/dist/repair/verification-gates.d.ts +25 -0
  184. package/dist/repair/verification-gates.d.ts.map +1 -0
  185. package/dist/repair/workflow-plan.d.ts +35 -0
  186. package/dist/repair/workflow-plan.d.ts.map +1 -0
  187. package/dist/repair/workspace-promoter.d.ts +43 -0
  188. package/dist/repair/workspace-promoter.d.ts.map +1 -0
  189. package/dist/reproduce/diagnostic-rerunner.d.ts +40 -0
  190. package/dist/reproduce/diagnostic-rerunner.d.ts.map +1 -0
  191. package/dist/reproduce/direct-browser-investigator.d.ts +41 -0
  192. package/dist/reproduce/direct-browser-investigator.d.ts.map +1 -0
  193. package/dist/reproduce/interactive-workflow-runner.d.ts +43 -0
  194. package/dist/reproduce/interactive-workflow-runner.d.ts.map +1 -0
  195. package/dist/sandbox/dependency-strategy.d.ts +45 -0
  196. package/dist/sandbox/dependency-strategy.d.ts.map +1 -0
  197. package/dist/sandbox/execution-runner.d.ts +81 -0
  198. package/dist/sandbox/execution-runner.d.ts.map +1 -0
  199. package/dist/sandbox/worktree-manager.d.ts +51 -0
  200. package/dist/sandbox/worktree-manager.d.ts.map +1 -0
  201. package/dist/selector/abstention-policy.d.ts +34 -0
  202. package/dist/selector/abstention-policy.d.ts.map +1 -0
  203. package/dist/selector/candidate-extractor.d.ts +71 -0
  204. package/dist/selector/candidate-extractor.d.ts.map +1 -0
  205. package/dist/selector/live-locator-verifier.d.ts +28 -0
  206. package/dist/selector/live-locator-verifier.d.ts.map +1 -0
  207. package/dist/selector/repair-candidate-ranker.d.ts +10 -0
  208. package/dist/selector/repair-candidate-ranker.d.ts.map +1 -0
  209. package/dist/selector/selector-scorer.d.ts +43 -0
  210. package/dist/selector/selector-scorer.d.ts.map +1 -0
  211. package/dist/trace/adapters/adapter-registry.d.ts +18 -0
  212. package/dist/trace/adapters/adapter-registry.d.ts.map +1 -0
  213. package/dist/trace/adapters/trace-adapter.d.ts +21 -0
  214. package/dist/trace/adapters/trace-adapter.d.ts.map +1 -0
  215. package/dist/trace/adapters/unknown-adapter.d.ts +10 -0
  216. package/dist/trace/adapters/unknown-adapter.d.ts.map +1 -0
  217. package/dist/trace/adapters/v10-adapter.d.ts +10 -0
  218. package/dist/trace/adapters/v10-adapter.d.ts.map +1 -0
  219. package/dist/trace/adapters/v4-adapter.d.ts +11 -0
  220. package/dist/trace/adapters/v4-adapter.d.ts.map +1 -0
  221. package/dist/trace/adapters/v8-adapter.d.ts +10 -0
  222. package/dist/trace/adapters/v8-adapter.d.ts.map +1 -0
  223. package/dist/trace/adapters/v9-adapter.d.ts +10 -0
  224. package/dist/trace/adapters/v9-adapter.d.ts.map +1 -0
  225. package/dist/trace/archive-reader.d.ts +44 -0
  226. package/dist/trace/archive-reader.d.ts.map +1 -0
  227. package/dist/trace/snapshot-resolver.d.ts +55 -0
  228. package/dist/trace/snapshot-resolver.d.ts.map +1 -0
  229. package/dist/trace/trace-parser.d.ts +50 -0
  230. package/dist/trace/trace-parser.d.ts.map +1 -0
  231. package/dist/trace/version-detector.d.ts +20 -0
  232. package/dist/trace/version-detector.d.ts.map +1 -0
  233. package/package.json +76 -6
  234. package/skills/README.md +20 -0
  235. package/skills/pie-failure-investigation/SKILL.md +16 -0
  236. package/skills/pie-quality-engineer/SKILL.md +66 -0
  237. package/skills/pie-release-readiness/SKILL.md +22 -0
  238. package/skills/pie-repair-review/SKILL.md +21 -0
  239. package/skills/pie-trace-to-repair/SKILL.md +35 -0
  240. package/src/authoring/author.ts +81 -0
  241. package/src/authoring/generation-contract.ts +283 -0
  242. package/src/authoring/hybrid-author.ts +115 -0
  243. package/src/authoring/index.ts +7 -0
  244. package/src/authoring/llm-author.ts +282 -0
  245. package/src/authoring/naming/index.ts +4 -0
  246. package/src/authoring/naming/name-plan.ts +28 -0
  247. package/src/authoring/naming/name-planner.ts +339 -0
  248. package/src/authoring/naming/naming-linter.ts +292 -0
  249. package/src/authoring/template-author.ts +39 -0
  250. package/src/cli/index.ts +1172 -0
  251. package/src/collectors/collector-interface.ts +8 -0
  252. package/src/collectors/environment-collector.ts +103 -0
  253. package/src/collectors/error-collector.ts +177 -0
  254. package/src/collectors/git-diff-collector.ts +190 -0
  255. package/src/collectors/live-browser-collector.ts +412 -0
  256. package/src/collectors/network-collector.ts +90 -0
  257. package/src/collectors/static-ast-collector.ts +226 -0
  258. package/src/collectors/trace-collector.ts +66 -0
  259. package/src/config/project-config.ts +114 -0
  260. package/src/core/errors/engine-errors.ts +90 -0
  261. package/src/core/types/diagnostic-context.ts +94 -0
  262. package/src/core/types/repair-evidence.ts +45 -0
  263. package/src/core/version.ts +25 -0
  264. package/src/diagnostics/diagnostic-contract.ts +79 -0
  265. package/src/diagnostics/diagnostic-report.ts +204 -0
  266. package/src/diagnostics/evidence-correlator.ts +117 -0
  267. package/src/diagnostics/external-failure-evidence.ts +72 -0
  268. package/src/diagnostics/failure-category.ts +40 -0
  269. package/src/diagnostics/failure-classifier.ts +701 -0
  270. package/src/evaluation/evaluation-scorecard.ts +198 -0
  271. package/src/evaluation/repository-inspector.ts +143 -0
  272. package/src/evaluation/repository-runner.ts +197 -0
  273. package/src/evidence/evidence-model.ts +76 -0
  274. package/src/evidence/evidence-normalizer.ts +322 -0
  275. package/src/evidence/evidence-redactor.ts +218 -0
  276. package/src/framework/config-adapter.ts +131 -0
  277. package/src/gate/index.ts +43 -0
  278. package/src/gate/oracle.ts +69 -0
  279. package/src/gate/quality.ts +101 -0
  280. package/src/gate/safety.ts +94 -0
  281. package/src/generator/architecture-generator.ts +582 -0
  282. package/src/generator/assertion-quality-gate.ts +88 -0
  283. package/src/generator/candidate-writer.ts +288 -0
  284. package/src/generator/test-generator.ts +52 -0
  285. package/src/governance/audit-log.ts +115 -0
  286. package/src/governance/governance-policy.ts +72 -0
  287. package/src/governance/team-dashboard.ts +189 -0
  288. package/src/governance/workflow-audit.ts +72 -0
  289. package/src/index.ts +107 -0
  290. package/src/integrations/ci-summary.ts +37 -0
  291. package/src/integrations/playwright-report-adapter.ts +173 -0
  292. package/src/integrations/quality-gate.ts +95 -0
  293. package/src/integrations/sarif-report.ts +110 -0
  294. package/src/intelligence/autonomous-repair-workflow.ts +139 -0
  295. package/src/intelligence/business-intent.ts +163 -0
  296. package/src/intelligence/evidence-bundle.ts +300 -0
  297. package/src/intelligence/failure-clustering.ts +134 -0
  298. package/src/intelligence/generation-agent.ts +561 -0
  299. package/src/intelligence/generation-verifier.ts +1115 -0
  300. package/src/intelligence/llm-proposal.ts +519 -0
  301. package/src/intelligence/locator-disambiguation-engine.ts +331 -0
  302. package/src/intelligence/model-contract.ts +205 -0
  303. package/src/intelligence/model-provider.ts +348 -0
  304. package/src/intelligence/project-intelligence.ts +140 -0
  305. package/src/intelligence/repair-agent.ts +297 -0
  306. package/src/intelligence/repository-profile.ts +796 -0
  307. package/src/intelligence/suite-intelligence.ts +305 -0
  308. package/src/intelligence/test-intelligence.ts +136 -0
  309. package/src/intelligence/trace-proposal-workflow.ts +318 -0
  310. package/src/intelligence/verified-llm-repair-workflow.ts +48 -0
  311. package/src/intelligence/workflow-planner.ts +966 -0
  312. package/src/mcp/server.ts +970 -0
  313. package/src/memory/stability-ledger.ts +243 -0
  314. package/src/perception/app-model.ts +32 -0
  315. package/src/perception/crawler.ts +263 -0
  316. package/src/perception/index.ts +3 -0
  317. package/src/repair/approval-service.ts +541 -0
  318. package/src/repair/assertion-hasher.ts +386 -0
  319. package/src/repair/ast-patcher.ts +223 -0
  320. package/src/repair/browser-operation-executor.ts +263 -0
  321. package/src/repair/diff-allowlist.ts +612 -0
  322. package/src/repair/intent-validator.ts +761 -0
  323. package/src/repair/repair-operations.ts +65 -0
  324. package/src/repair/repair-planner.ts +467 -0
  325. package/src/repair/repair-workflow.ts +241 -0
  326. package/src/repair/safe-operation-compiler.ts +68 -0
  327. package/src/repair/source-mapper.ts +156 -0
  328. package/src/repair/statement-patcher.ts +544 -0
  329. package/src/repair/verification-gates.ts +46 -0
  330. package/src/repair/workflow-plan.ts +59 -0
  331. package/src/repair/workspace-promoter.ts +313 -0
  332. package/src/reproduce/diagnostic-rerunner.ts +287 -0
  333. package/src/reproduce/direct-browser-investigator.ts +208 -0
  334. package/src/reproduce/interactive-workflow-runner.ts +828 -0
  335. package/src/sandbox/dependency-strategy.ts +215 -0
  336. package/src/sandbox/execution-runner.ts +408 -0
  337. package/src/sandbox/worktree-manager.ts +287 -0
  338. package/src/selector/abstention-policy.ts +175 -0
  339. package/src/selector/candidate-extractor.ts +343 -0
  340. package/src/selector/live-locator-verifier.ts +139 -0
  341. package/src/selector/repair-candidate-ranker.ts +92 -0
  342. package/src/selector/selector-scorer.ts +344 -0
  343. package/src/trace/adapters/adapter-registry.ts +49 -0
  344. package/src/trace/adapters/trace-adapter.ts +28 -0
  345. package/src/trace/adapters/unknown-adapter.ts +50 -0
  346. package/src/trace/adapters/v10-adapter.ts +39 -0
  347. package/src/trace/adapters/v4-adapter.ts +31 -0
  348. package/src/trace/adapters/v8-adapter.ts +42 -0
  349. package/src/trace/adapters/v9-adapter.ts +39 -0
  350. package/src/trace/archive-reader.ts +246 -0
  351. package/src/trace/snapshot-resolver.ts +308 -0
  352. package/src/trace/trace-parser.ts +467 -0
  353. package/src/trace/version-detector.ts +81 -0
@@ -0,0 +1,1172 @@
1
+ import * as path from 'node:path';
2
+ import * as fs from 'node:fs';
3
+ import { spawn, execSync } from 'node:child_process';
4
+ import { ArchiveReader } from '../trace/archive-reader.js';
5
+ import { TraceParser } from '../trace/trace-parser.js';
6
+ import { EvidenceCorrelator } from '../diagnostics/evidence-correlator.js';
7
+ import { DiagnosticReportGenerator } from '../diagnostics/diagnostic-report.js';
8
+ import { McpServer } from '../mcp/server.js';
9
+ import { TestGenerator } from '../generator/test-generator.js';
10
+ import { DiagnosticRerunner } from '../reproduce/diagnostic-rerunner.js';
11
+ import { ApprovalService } from '../repair/approval-service.js';
12
+ import { WorkspacePromoter } from '../repair/workspace-promoter.js';
13
+ import { getRuntimeProvenance, PACKAGE_VERSION } from '../core/version.js';
14
+ import { inspectTestbed } from '../evaluation/repository-inspector.js';
15
+ import { RepositoryRunner } from '../evaluation/repository-runner.js';
16
+ import { initProjectConfig } from '../config/project-config.js';
17
+ import { buildRepositoryProfile, writeRepositoryProfile } from '../intelligence/repository-profile.js';
18
+ import { bootstrapProjectIntelligence, recordProjectFailure, recordProjectRepairAccepted, recordProjectRepairFeedback } from '../intelligence/project-intelligence.js';
19
+ import { clusterFailures, FailureObservation } from '../intelligence/failure-clustering.js';
20
+ import { analyzeTestStability, analyzeRepositoryImpact, TestRunObservation } from '../intelligence/suite-intelligence.js';
21
+ import { planArchitecture, writeArchitecturePlan, ArchitectureMode, ArchitectureGenerationInput } from '../generator/architecture-generator.js';
22
+ import { AppCrawler } from '../perception/crawler.js';
23
+ import type { AppModel } from '../perception/app-model.js';
24
+ import { ExecutionRunner } from '../sandbox/execution-runner.js';
25
+ import { buildCiEvidenceReport } from '../integrations/playwright-report-adapter.js';
26
+ import { buildSarifReport } from '../integrations/sarif-report.js';
27
+ import { recordArchitectureWorkflow, recordCiWorkflow } from '../governance/workflow-audit.js';
28
+ import { buildTeamDashboard, TeamEvidenceRun } from '../governance/team-dashboard.js';
29
+ import { parseExternalFailureEvidence, supplementDiagnosticContext } from '../diagnostics/external-failure-evidence.js';
30
+ import { runRepairWorkflow } from '../repair/repair-workflow.js';
31
+ import { runAutonomousRepairWorkflow } from '../intelligence/autonomous-repair-workflow.js';
32
+ import { normalizeFailureCategory } from '../diagnostics/failure-category.js';
33
+ import { buildEvaluationScorecard, EvaluationObservation } from '../evaluation/evaluation-scorecard.js';
34
+ import { buildTestIntelligencePlan, ExistingTestInput, RequirementInput } from '../intelligence/test-intelligence.js';
35
+ import { runGenerationAgent } from '../intelligence/generation-agent.js';
36
+ import { runEvidenceDrivenGeneration } from '../intelligence/generation-verifier.js';
37
+ import { GitDiffCollector } from '../collectors/git-diff-collector.js';
38
+ import type { AuthorKind } from '../authoring/index.js';
39
+
40
+ export async function runCli(argv: string[]): Promise<void> {
41
+ const command = argv[2];
42
+
43
+ if (!command || command === '--help' || command === '-h') {
44
+ printHelp();
45
+ return;
46
+ }
47
+
48
+ if (command === '--version' || command === '-v') {
49
+ console.log(PACKAGE_VERSION);
50
+ return;
51
+ }
52
+
53
+ if (command === 'mcp') {
54
+ const server = new McpServer();
55
+ server.start();
56
+ return;
57
+ }
58
+
59
+ if (command === 'inspect-testbed') {
60
+ const testbedPath = argv[3];
61
+ if (!testbedPath) {
62
+ console.error('Error: testbed directory required. Usage: pw-intel inspect-testbed <directory> [--json] [--out <manifest.json>]');
63
+ process.exit(1);
64
+ }
65
+
66
+ try {
67
+ const manifests = await inspectTestbed(testbedPath);
68
+ const outputPathIndex = argv.indexOf('--out');
69
+ const outputPath = outputPathIndex >= 0 ? argv[outputPathIndex + 1] : undefined;
70
+ const serialized = JSON.stringify({ generatedAt: new Date().toISOString(), testbedPath, repositories: manifests }, null, 2);
71
+
72
+ if (outputPath) {
73
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
74
+ await fs.promises.writeFile(path.resolve(outputPath), serialized, 'utf8');
75
+ }
76
+
77
+ if (argv.includes('--json') || outputPath) {
78
+ console.log(serialized);
79
+ } else {
80
+ console.log(`Testbed: ${testbedPath}`);
81
+ for (const manifest of manifests) {
82
+ console.log(`- ${manifest.name}: ${manifest.status}; ${manifest.testFiles} test files; ${manifest.pageObjectFiles} Page Object files; ${manifest.packageManager}`);
83
+ }
84
+ }
85
+ return;
86
+ } catch (err: any) {
87
+ console.error(`Testbed inspection failed: ${err?.message ?? String(err)}`);
88
+ process.exit(1);
89
+ }
90
+ }
91
+
92
+ if (command === 'run-testbed') {
93
+ const testbedPath = argv[3];
94
+ if (!testbedPath) {
95
+ console.error('Error: testbed directory required. Usage: pw-intel run-testbed <directory> [--script <name>]');
96
+ process.exit(1);
97
+ }
98
+
99
+ try {
100
+ const manifests = await inspectTestbed(testbedPath);
101
+ const scriptIndex = argv.indexOf('--script');
102
+ const script = scriptIndex >= 0 ? argv[scriptIndex + 1] : undefined;
103
+ const repositoryIndex = argv.indexOf('--repository');
104
+ const repository = repositoryIndex >= 0 ? argv[repositoryIndex + 1] : undefined;
105
+ const timeoutIndex = argv.indexOf('--timeout');
106
+ const timeoutValue = timeoutIndex >= 0 ? Number(argv[timeoutIndex + 1]) : undefined;
107
+ if (timeoutValue !== undefined && (!Number.isFinite(timeoutValue) || timeoutValue <= 0)) {
108
+ throw new Error('--timeout must be a positive number of milliseconds');
109
+ }
110
+ const outputPathIndex = argv.indexOf('--out');
111
+ const outputPath = outputPathIndex >= 0 ? argv[outputPathIndex + 1] : undefined;
112
+ const selectedManifests = repository ? manifests.filter((manifest) => manifest.name === repository) : manifests;
113
+ if (repository && selectedManifests.length === 0) {
114
+ throw new Error(`Repository '${repository}' was not found in the testbed manifest`);
115
+ }
116
+ const results = [];
117
+ for (const manifest of selectedManifests) {
118
+ results.push(await RepositoryRunner.runBaseline(manifest, { script, timeoutMs: timeoutValue }));
119
+ }
120
+ const serialized = JSON.stringify({ generatedAt: new Date().toISOString(), testbedPath, results }, null, 2);
121
+ if (outputPath) {
122
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
123
+ await fs.promises.writeFile(path.resolve(outputPath), serialized, 'utf8');
124
+ }
125
+ console.log(serialized);
126
+ return;
127
+ } catch (err: any) {
128
+ console.error(`Testbed execution failed: ${err?.message ?? String(err)}`);
129
+ process.exit(1);
130
+ }
131
+ }
132
+
133
+ if (command === 'diagnose') {
134
+ const tracePath = argv[3];
135
+ if (!tracePath) {
136
+ console.error('Error: trace.zip path required. Usage: pw-intel diagnose <path-to-trace.zip> [--error-context <error-context.md>]');
137
+ process.exit(1);
138
+ }
139
+
140
+ try {
141
+ const { traceEntryName, networkEntryName, traceEntryNames, networkEntryNames } = ArchiveReader.findTraceFileEntries(tracePath);
142
+ if (!traceEntryName) {
143
+ throw new Error(`No .trace event stream found in archive '${tracePath}'`);
144
+ }
145
+
146
+ const traceText = await ArchiveReader.readEntriesAsText(tracePath, traceEntryNames ?? [traceEntryName]);
147
+ const networkText = (networkEntryNames ?? (networkEntryName ? [networkEntryName] : [])).length > 0
148
+ ? await ArchiveReader.readEntriesAsText(tracePath, networkEntryNames ?? [networkEntryName!])
149
+ : '';
150
+
151
+ const traceLines = traceText.split('\n');
152
+ const networkLines = networkText.split('\n');
153
+ let context = TraceParser.assembleDiagnosticContext(traceLines, networkLines);
154
+ const errorContextIndex = argv.indexOf('--error-context');
155
+ const errorContextPath = errorContextIndex >= 0 ? argv[errorContextIndex + 1] : undefined;
156
+ if (errorContextPath) {
157
+ const errorContext = await fs.promises.readFile(path.resolve(errorContextPath), 'utf8');
158
+ context = supplementDiagnosticContext(context, parseExternalFailureEvidence(errorContext));
159
+ }
160
+ const correlated = EvidenceCorrelator.correlate(context);
161
+ const intelligence = await bootstrapProjectIntelligence(process.cwd());
162
+ await recordProjectFailure(process.cwd(), context.failedAction.selector, correlated.classification.category);
163
+
164
+ if (argv.includes('--json')) {
165
+ const proposedLocator = DiagnosticReportGenerator.extractProposedLocator(correlated, intelligence);
166
+ const shouldAbstain = correlated.classification.shouldAbstain || !proposedLocator;
167
+ const actionOutcome = shouldAbstain ? 'ABSTAIN' : 'REPAIR';
168
+ const canonicalCategory = normalizeFailureCategory(correlated.classification.category);
169
+ const abstentionReason =
170
+ correlated.classification.abstentionReason ??
171
+ (!proposedLocator ? 'No safe, unique replacement locator candidate discovered in DOM snapshot.' : undefined);
172
+
173
+ console.log(JSON.stringify({
174
+ provenance: getRuntimeProvenance(process.cwd()),
175
+ classification: canonicalCategory,
176
+ rawClassification: correlated.classification.category,
177
+ confidence: correlated.classification.confidence,
178
+ shouldAbstain,
179
+ actionOutcome,
180
+ reason: correlated.classification.reason,
181
+ abstentionReason,
182
+ failedAction: correlated.context.failedAction,
183
+ failureFingerprint: correlated.failureFingerprint,
184
+ proposedLocator,
185
+ }, null, 2));
186
+ return;
187
+ }
188
+
189
+ const report = DiagnosticReportGenerator.generateTerminalReport(correlated, undefined, intelligence);
190
+
191
+ console.log(report);
192
+ } catch (err: any) {
193
+ console.error(`Diagnostic execution failed: ${err.message}`);
194
+ process.exit(1);
195
+ }
196
+ return;
197
+ }
198
+
199
+ if (command === 'repair') {
200
+ const tracePath = argv[3];
201
+ if (!tracePath) {
202
+ console.error('Error: trace.zip path required. Usage: pw-intel repair <trace.zip> --test <spec> --pom <page-object> --class <Class> --member <member> [--execute]');
203
+ process.exit(1);
204
+ }
205
+ try {
206
+ const option = (name: string): string | undefined => {
207
+ const index = argv.indexOf(name);
208
+ return index >= 0 ? argv[index + 1] : undefined;
209
+ };
210
+ const operationText = option('--operation');
211
+ const operation = operationText ? JSON.parse(operationText) as unknown : undefined;
212
+ const result = await runRepairWorkflow({
213
+ tracePath,
214
+ workspaceRoot: option('--workspace') ?? process.cwd(),
215
+ errorContextPath: option('--error-context'),
216
+ testFilePath: option('--test'),
217
+ pomFilePath: option('--pom'),
218
+ className: option('--class'),
219
+ memberName: option('--member'),
220
+ configPath: option('--config'),
221
+ project: option('--project'),
222
+ execute: argv.includes('--execute'),
223
+ worktreeId: option('--worktree-id'),
224
+ operation,
225
+ });
226
+ const canonicalClassification = {
227
+ ...result.correlated.classification,
228
+ category: normalizeFailureCategory(result.correlated.classification.category),
229
+ };
230
+ console.log(JSON.stringify({
231
+ provenance: getRuntimeProvenance(process.cwd()),
232
+ status: result.status,
233
+ classification: canonicalClassification,
234
+ failureFingerprint: result.correlated.failureFingerprint,
235
+ proposedLocator: result.proposedLocator,
236
+ candidate: result.candidate,
237
+ proposalId: result.stagedProposal?.proposalId,
238
+ worktree: result.worktree,
239
+ verification: result.verification,
240
+ reason: result.reason,
241
+ }, null, 2));
242
+ if (result.status === 'REJECTED') process.exitCode = 1;
243
+ } catch (err: any) {
244
+ console.error(`Repair workflow failed: ${err?.message ?? String(err)}`);
245
+ process.exit(1);
246
+ }
247
+ return;
248
+ }
249
+
250
+ if (command === 'autonomous-repair') {
251
+ const tracePath = argv[3];
252
+ if (!tracePath) {
253
+ console.error('Error: trace.zip path required. Usage: pw-intel autonomous-repair <trace.zip> [--execute] [--test <spec>] [--pom <page-object>] [--class <Class>] [--member <member>]');
254
+ process.exit(1);
255
+ }
256
+ try {
257
+ const option = (name: string): string | undefined => {
258
+ const index = argv.indexOf(name);
259
+ return index >= 0 ? argv[index + 1] : undefined;
260
+ };
261
+ const result = await runAutonomousRepairWorkflow({
262
+ tracePath,
263
+ workspaceRoot: option('--workspace') ?? process.cwd(),
264
+ errorContextPath: option('--error-context'),
265
+ testFilePath: option('--test'),
266
+ pomFilePath: option('--pom'),
267
+ className: option('--class'),
268
+ memberName: option('--member'),
269
+ configPath: option('--config'),
270
+ worktreeId: option('--worktree-id'),
271
+ enableBrowserInvestigation: argv.includes('--browser'),
272
+ browserUrl: option('--browser-url'),
273
+ browserAllowedHosts: (option('--allow-host') ?? '').split(',').map((host) => host.trim()).filter(Boolean),
274
+ enableTestExecution: argv.includes('--run-tests'),
275
+ autoPromote: argv.includes('--promote'),
276
+ execute: argv.includes('--execute'),
277
+ });
278
+ console.log(JSON.stringify({
279
+ provenance: getRuntimeProvenance(process.cwd()),
280
+ status: result.status,
281
+ reason: result.reason,
282
+ agent: {
283
+ state: result.proposalRun.agent.state,
284
+ modelInvoked: result.proposalRun.agent.modelInvoked,
285
+ classification: result.proposalRun.agent.classification,
286
+ proposal: result.proposal,
287
+ errors: result.proposalRun.agent.errors,
288
+ steps: result.proposalRun.agent.steps,
289
+ },
290
+ verification: result.verification,
291
+ promotion: result.promotion,
292
+ }, null, 2));
293
+ if (result.status === 'PROPOSAL_REJECTED' || result.status === 'VERIFICATION_FAILED') process.exitCode = 1;
294
+ } catch (err: any) {
295
+ console.error(`Autonomous repair failed: ${err?.message ?? String(err)}`);
296
+ process.exit(1);
297
+ }
298
+ return;
299
+ }
300
+
301
+ if (command === 'reproduce') {
302
+ const specFile = argv[3];
303
+ if (!specFile) {
304
+ console.error('Error: spec file path required. Usage: pw-intel reproduce <specFilePath>');
305
+ process.exit(1);
306
+ }
307
+
308
+ try {
309
+ const option = (name: string): string | undefined => {
310
+ const index = argv.indexOf(name);
311
+ return index >= 0 ? argv[index + 1] : undefined;
312
+ };
313
+ console.log(`Running diagnostic reproduction for '${specFile}'...`);
314
+ const result = await DiagnosticRerunner.rerunAndDiagnose({
315
+ specFilePath: specFile,
316
+ workspaceRoot: option('--workspace') ?? process.cwd(),
317
+ });
318
+
319
+ console.log(result.report);
320
+ if (!result.passed) {
321
+ process.exit(1);
322
+ }
323
+ } catch (err: any) {
324
+ console.error(`Reproduction execution failed: ${err.message}`);
325
+ process.exit(1);
326
+ }
327
+ return;
328
+ }
329
+
330
+ if (command === 'benchmark') {
331
+ // Resolve benchmark script from package directory first, falling back to process.cwd()
332
+ const cliDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'));
333
+ const packageRootDir = path.resolve(cliDir, '..');
334
+ const packageBenchmarkScript = path.resolve(packageRootDir, 'benchmarks', 'runner', 'master-benchmark-runner.ts');
335
+ const localBenchmarkScript = path.resolve(process.cwd(), 'benchmarks', 'runner', 'master-benchmark-runner.ts');
336
+ const benchmarkScript = fs.existsSync(packageBenchmarkScript)
337
+ ? packageBenchmarkScript
338
+ : (fs.existsSync(localBenchmarkScript) ? localBenchmarkScript : undefined);
339
+
340
+ if (!benchmarkScript) {
341
+ console.log('Empirical benchmark harness is maintained in the research testbed repository.');
342
+ console.log('See docs/BENCHMARK.md for methodology, metrics, and empirical validation results.');
343
+ return;
344
+ }
345
+
346
+ // Locate tsx CLI entry point
347
+ const tsxCandidatePaths = [
348
+ path.resolve(packageRootDir, 'node_modules', 'tsx', 'dist', 'cli.mjs'),
349
+ path.resolve(process.cwd(), 'node_modules', 'tsx', 'dist', 'cli.mjs'),
350
+ ];
351
+ const tsxCli = tsxCandidatePaths.find((p) => fs.existsSync(p));
352
+
353
+ let executable: string;
354
+ let finalArgs: string[];
355
+
356
+ if (tsxCli) {
357
+ executable = process.execPath;
358
+ finalArgs = [tsxCli, benchmarkScript, ...argv.slice(3)];
359
+ } else {
360
+ executable = process.platform === 'win32' ? 'cmd.exe' : 'npx';
361
+ finalArgs = process.platform === 'win32'
362
+ ? ['/d', '/s', '/c', 'npx', 'tsx', benchmarkScript, ...argv.slice(3)]
363
+ : ['tsx', benchmarkScript, ...argv.slice(3)];
364
+ }
365
+
366
+ const child = spawn(executable, finalArgs, {
367
+ cwd: process.cwd(),
368
+ stdio: 'inherit',
369
+ });
370
+
371
+ child.on('close', (code) => {
372
+ process.exit(code ?? 0);
373
+ });
374
+ return;
375
+ }
376
+
377
+ if (command === 'doctor') {
378
+ await runDoctor();
379
+ return;
380
+ }
381
+
382
+ if (command === 'init') {
383
+ const workspaceRoot = argv[3] && !argv[3].startsWith('--') ? argv[3] : process.cwd();
384
+ try {
385
+ const option = (name: string): string | undefined => {
386
+ const index = argv.indexOf(name);
387
+ return index >= 0 ? argv[index + 1] : undefined;
388
+ };
389
+ const apiKeyArg = option('--api-key');
390
+ const modelArg = option('--model') ?? (apiKeyArg?.startsWith('sk-') ? 'gpt-4o' : 'deepseek-chat');
391
+ const endpointArg = option('--endpoint');
392
+
393
+ const result = await initProjectConfig(workspaceRoot, { force: argv.includes('--force') });
394
+
395
+ // Handle optional LLM configuration
396
+ if (apiKeyArg) {
397
+ const envPath = path.join(workspaceRoot, '.env');
398
+ const envLines = [
399
+ '# Playwright Intelligence Engine (PIE) Configuration',
400
+ `PIE_LLM_API_KEY=${apiKeyArg}`,
401
+ `PIE_LLM_MODEL=${modelArg}`,
402
+ ];
403
+ if (endpointArg) {
404
+ envLines.push(`PIE_LLM_ENDPOINT=${endpointArg}`);
405
+ } else if (apiKeyArg.startsWith('sk-proj-') || apiKeyArg.startsWith('sk-')) {
406
+ envLines.push('PIE_LLM_ENDPOINT=https://api.openai.com/v1/chat/completions');
407
+ }
408
+ await fs.promises.writeFile(envPath, `${envLines.join('\n')}\n`, 'utf8');
409
+
410
+ // Ensure .env is in .gitignore
411
+ const gitignorePath = path.join(workspaceRoot, '.gitignore');
412
+ try {
413
+ const gitignoreContent = await fs.promises.readFile(gitignorePath, 'utf8').catch(() => '');
414
+ if (!gitignoreContent.includes('.env')) {
415
+ await fs.promises.appendFile(gitignorePath, '\n.env\n', 'utf8');
416
+ }
417
+ } catch {}
418
+ }
419
+
420
+ const activeApiKey = apiKeyArg ?? process.env.PIE_LLM_API_KEY ?? process.env.OPENAI_API_KEY ?? process.env.DEEPSEEK_API_KEY;
421
+
422
+ if (argv.includes('--json')) {
423
+ console.log(JSON.stringify({ ...result, llmConfigured: Boolean(activeApiKey) }, null, 2));
424
+ } else {
425
+ console.log(`${result.created ? 'Created' : 'Found'} PIE project configuration: ${result.configPath}`);
426
+ console.log(` Package manager : ${result.config.test.packageManager}`);
427
+ console.log(` Test command : ${result.config.test.command ?? 'not detected'}`);
428
+ console.log(` Fail-closed : ${result.config.diagnostics.failClosed ? 'enabled' : 'disabled'}`);
429
+ console.log(` LLM Provider : ${activeApiKey ? `configured (${apiKeyArg ? 'written to .env' : 'detected in environment'})` : 'not configured (100% offline deterministic mode)'}`);
430
+ if (!activeApiKey) {
431
+ console.log(` Note : Core diagnosis and repair are 100% offline. To enable AI test generation, run: pw-intel init --api-key <key>`);
432
+ }
433
+ console.log('\nQuickstart Commands:');
434
+ console.log(' Diagnose a trace : pw-intel diagnose ./test-results/trace.zip');
435
+ console.log(' Repair a failure : pw-intel autonomous-repair ./test-results/trace.zip --execute');
436
+ console.log(' Generate tests : pw-intel generate-agent ./requirements.json --mode brownfield --write');
437
+ }
438
+ } catch (err: any) {
439
+ console.error(`PIE initialization failed: ${err?.message ?? String(err)}`);
440
+ process.exit(1);
441
+ }
442
+ return;
443
+ }
444
+
445
+ if (command === 'profile') {
446
+ const workspaceRoot = argv[3] && !argv[3].startsWith('--') ? argv[3] : process.cwd();
447
+ try {
448
+ const profile = await buildRepositoryProfile(workspaceRoot);
449
+ const outputIndex = argv.indexOf('--out');
450
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
451
+ if (outputPath) {
452
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
453
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(profile, null, 2)}\n`, 'utf8');
454
+ } else {
455
+ await writeRepositoryProfile(workspaceRoot, profile);
456
+ }
457
+ if (argv.includes('--json') || outputPath) console.log(JSON.stringify(profile, null, 2));
458
+ else {
459
+ console.log(`Repository profile generated for ${profile.repository.name}`);
460
+ console.log(` Tests : ${profile.structure.testFiles}`);
461
+ console.log(` Page Objects : ${profile.structure.pageObjectFiles}`);
462
+ console.log(` Fixtures : ${profile.structure.fixtureFiles}`);
463
+ console.log(` Preferred loc. : ${profile.locatorConventions.preferred}`);
464
+ console.log(` Domain terms : ${profile.domainTerms.slice(0, 10).join(', ') || 'none detected'}`);
465
+ }
466
+ } catch (err: any) {
467
+ console.error(`Repository profiling failed: ${err?.message ?? String(err)}`);
468
+ process.exit(1);
469
+ }
470
+ return;
471
+ }
472
+
473
+ if (command === 'cluster-failures') {
474
+ const inputPath = argv[3];
475
+ if (!inputPath) {
476
+ console.error('Error: input JSON path required. Usage: pw-intel cluster-failures <observations.json> [--out <report.json>]');
477
+ process.exit(1);
478
+ }
479
+ try {
480
+ const parsed = JSON.parse(await fs.promises.readFile(path.resolve(inputPath), 'utf8')) as { observations?: FailureObservation[] } | FailureObservation[];
481
+ const observations = Array.isArray(parsed) ? parsed : parsed.observations ?? [];
482
+ const clusters = clusterFailures(observations);
483
+ const report = {
484
+ generatedAt: new Date().toISOString(),
485
+ observationCount: observations.length,
486
+ clusterCount: clusters.length,
487
+ clusters,
488
+ };
489
+ const outputIndex = argv.indexOf('--out');
490
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
491
+ if (outputPath) {
492
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
493
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
494
+ }
495
+ console.log(JSON.stringify(report, null, 2));
496
+ } catch (err: any) {
497
+ console.error(`Failure clustering failed: ${err?.message ?? String(err)}`);
498
+ process.exit(1);
499
+ }
500
+ return;
501
+ }
502
+
503
+ if (command === 'evaluation-scorecard') {
504
+ const inputPath = argv[3];
505
+ if (!inputPath) {
506
+ console.error('Error: evaluation JSON path required. Usage: pw-intel evaluation-scorecard <results.json> [--out <scorecard.json>]');
507
+ process.exit(1);
508
+ }
509
+ try {
510
+ const parsed = JSON.parse(await fs.promises.readFile(path.resolve(inputPath), 'utf8')) as EvaluationObservation[] | { observations?: EvaluationObservation[]; diagnosisResults?: EvaluationObservation[] };
511
+ const observations = Array.isArray(parsed) ? parsed : parsed.observations ?? parsed.diagnosisResults ?? [];
512
+ const scorecard = buildEvaluationScorecard(observations);
513
+ const report = { generatedAt: new Date().toISOString(), inputPath: path.resolve(inputPath), ...scorecard };
514
+ const outputIndex = argv.indexOf('--out');
515
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
516
+ if (outputPath) {
517
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
518
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
519
+ }
520
+ console.log(JSON.stringify(report, null, 2));
521
+ } catch (err: any) {
522
+ console.error(`Evaluation scorecard failed: ${err?.message ?? String(err)}`);
523
+ process.exit(1);
524
+ }
525
+ return;
526
+ }
527
+
528
+ if (command === 'plan-test-intelligence') {
529
+ const requirementsPath = argv[3];
530
+ if (!requirementsPath) {
531
+ console.error('Error: requirements JSON path required. Usage: pw-intel plan-test-intelligence <requirements.json> [--tests <tests.json>] [--out <plan.json>]');
532
+ process.exit(1);
533
+ }
534
+ try {
535
+ const requirementsRaw = JSON.parse(await fs.promises.readFile(path.resolve(requirementsPath), 'utf8')) as RequirementInput[] | { requirements?: RequirementInput[] | string; title?: string; steps?: ArchitectureGenerationInput['steps']; baseUrl?: string; includeCi?: boolean };
536
+ const requirements = Array.isArray(requirementsRaw)
537
+ ? requirementsRaw
538
+ : Array.isArray(requirementsRaw.requirements)
539
+ ? requirementsRaw.requirements
540
+ : typeof requirementsRaw.requirements === 'string'
541
+ ? [{ id: 'requirement-1', title: requirementsRaw.title ?? requirementsRaw.requirements.slice(0, 100), description: requirementsRaw.requirements }]
542
+ : [];
543
+ if (requirements.length === 0) throw new Error('The requirements document contains no requirements');
544
+ const testsIndex = argv.indexOf('--tests');
545
+ const testsPath = testsIndex >= 0 ? argv[testsIndex + 1] : undefined;
546
+ let existingTests: ExistingTestInput[] = [];
547
+ if (testsPath) {
548
+ const testsRaw = JSON.parse(await fs.promises.readFile(path.resolve(testsPath), 'utf8')) as ExistingTestInput[] | { tests?: ExistingTestInput[] };
549
+ existingTests = Array.isArray(testsRaw) ? testsRaw : testsRaw.tests ?? [];
550
+ }
551
+ const plan = buildTestIntelligencePlan(requirements, existingTests);
552
+ const report = { generatedAt: new Date().toISOString(), requirementsPath: path.resolve(requirementsPath), testsPath: testsPath ? path.resolve(testsPath) : undefined, ...plan };
553
+ const outputIndex = argv.indexOf('--out');
554
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
555
+ if (outputPath) {
556
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
557
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
558
+ }
559
+ console.log(JSON.stringify(report, null, 2));
560
+ } catch (err: any) {
561
+ console.error(`Test intelligence planning failed: ${err?.message ?? String(err)}`);
562
+ process.exit(1);
563
+ }
564
+ return;
565
+ }
566
+
567
+ if (command === 'generate-agent') {
568
+ const requirementsPath = argv[3];
569
+ if (!requirementsPath) {
570
+ console.error('Error: requirements JSON path required. Usage: pw-intel generate-agent <requirements.json> [--mode greenfield|brownfield] [--tests <tests.json>] [--write]');
571
+ process.exit(1);
572
+ }
573
+ try {
574
+ const requirementsRaw = JSON.parse(await fs.promises.readFile(path.resolve(requirementsPath), 'utf8')) as RequirementInput[] | { requirements?: RequirementInput[] | string; title?: string; steps?: ArchitectureGenerationInput['steps']; baseUrl?: string; includeCi?: boolean };
575
+ const requirementsDocument = Array.isArray(requirementsRaw) ? undefined : requirementsRaw;
576
+ const requirements = Array.isArray(requirementsRaw)
577
+ ? requirementsRaw
578
+ : Array.isArray(requirementsRaw.requirements)
579
+ ? requirementsRaw.requirements
580
+ : typeof requirementsRaw.requirements === 'string'
581
+ ? [{ id: 'requirement-1', title: requirementsRaw.title ?? requirementsRaw.requirements.slice(0, 100), description: requirementsRaw.requirements }]
582
+ : [];
583
+ if (requirements.length === 0) throw new Error('The requirements document contains no requirements');
584
+ const testsIndex = argv.indexOf('--tests');
585
+ const testsPath = testsIndex >= 0 ? argv[testsIndex + 1] : undefined;
586
+ let existingTests: ExistingTestInput[] = [];
587
+ if (testsPath) {
588
+ const testsRaw = JSON.parse(await fs.promises.readFile(path.resolve(testsPath), 'utf8')) as ExistingTestInput[] | { tests?: ExistingTestInput[] };
589
+ existingTests = Array.isArray(testsRaw) ? testsRaw : testsRaw.tests ?? [];
590
+ }
591
+ const modeIndex = argv.indexOf('--mode');
592
+ const mode = (modeIndex >= 0 ? argv[modeIndex + 1] : 'brownfield') as 'greenfield' | 'brownfield';
593
+ if (mode !== 'greenfield' && mode !== 'brownfield') throw new Error("--mode must be 'greenfield' or 'brownfield'");
594
+ const baseUrlIndex = argv.indexOf('--base-url');
595
+ const baseUrl = baseUrlIndex >= 0 ? argv[baseUrlIndex + 1] : requirementsDocument?.baseUrl;
596
+ const authorArg = argv.find((arg) => arg.startsWith('--author='));
597
+ const authorIndex = argv.indexOf('--author');
598
+ const author = authorArg
599
+ ? (authorArg.slice('--author='.length) as 'template' | 'hybrid' | 'llm')
600
+ : authorIndex >= 0
601
+ ? (argv[authorIndex + 1] as 'template' | 'hybrid' | 'llm')
602
+ : 'hybrid';
603
+ const result = await runGenerationAgent({
604
+ requirements,
605
+ existingTests,
606
+ mode,
607
+ author,
608
+ workspaceRoot: (argv.indexOf('--workspace') >= 0 ? argv[argv.indexOf('--workspace') + 1] : undefined) ?? process.cwd(),
609
+ ...(baseUrl ? { baseUrl } : {}),
610
+ ...(requirementsDocument?.steps ? { suggestedSteps: requirementsDocument.steps } : {}),
611
+ ...(requirementsDocument?.includeCi !== undefined ? { includeCi: requirementsDocument.includeCi } : {}),
612
+ write: argv.includes('--write'),
613
+ verify: argv.includes('--verify'),
614
+ });
615
+ console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requirementsPath: path.resolve(requirementsPath), ...result }, null, 2));
616
+ if (result.status === 'ABSTAINED' || result.status === 'MODEL_NOT_CONFIGURED' || result.status === 'NEEDS_INPUT' || result.status === 'VERIFICATION_FAILED' || result.status === 'AGENT_NO_PROGRESS') process.exitCode = 1;
617
+ } catch (err: any) {
618
+ console.error(`Generation agent failed: ${err?.message ?? String(err)}`);
619
+ process.exit(1);
620
+ }
621
+ return;
622
+ }
623
+
624
+ if (command === 'stability-feedback') {
625
+ const selector = argv[3];
626
+ const outcomeIndex = argv.indexOf('--outcome');
627
+ const outcome = outcomeIndex >= 0 ? argv[outcomeIndex + 1] : undefined;
628
+ if (!selector || (outcome !== 'accepted' && outcome !== 'rejected')) {
629
+ console.error('Error: selector and outcome are required. Usage: pw-intel stability-feedback <selector> --outcome accepted|rejected');
630
+ process.exit(1);
631
+ }
632
+ try {
633
+ await recordProjectRepairFeedback(process.cwd(), selector, outcome);
634
+ console.log(JSON.stringify({ selector, outcome, recorded: true }));
635
+ } catch (err: any) {
636
+ console.error(`Stability feedback failed: ${err?.message ?? String(err)}`);
637
+ process.exit(1);
638
+ }
639
+ return;
640
+ }
641
+
642
+ if (command === 'suite-health') {
643
+ const inputPath = argv[3];
644
+ if (!inputPath) {
645
+ console.error('Error: run-record JSON path required. Usage: pw-intel suite-health <runs.json> [--out <report.json>]');
646
+ process.exit(1);
647
+ }
648
+ try {
649
+ const parsed = JSON.parse(await fs.promises.readFile(path.resolve(inputPath), 'utf8')) as { runs?: TestRunObservation[] } | TestRunObservation[];
650
+ const runs = Array.isArray(parsed) ? parsed : parsed.runs ?? [];
651
+ const summaries = analyzeTestStability(runs);
652
+ const report = { generatedAt: new Date().toISOString(), runCount: runs.length, testCount: summaries.length, flakyTestCount: summaries.filter((summary) => summary.flaky).length, tests: summaries };
653
+ const outputIndex = argv.indexOf('--out');
654
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
655
+ if (outputPath) {
656
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
657
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
658
+ }
659
+ console.log(JSON.stringify(report, null, 2));
660
+ } catch (err: any) {
661
+ console.error(`Suite health analysis failed: ${err?.message ?? String(err)}`);
662
+ process.exit(1);
663
+ }
664
+ return;
665
+ }
666
+
667
+ if (command === 'team-dashboard') {
668
+ const inputPath = argv[3];
669
+ if (!inputPath) {
670
+ console.error('Error: team evidence JSON path required. Usage: pw-intel team-dashboard <evidence.json> [--out <report.json>]');
671
+ process.exit(1);
672
+ }
673
+ try {
674
+ const parsed = JSON.parse(await fs.promises.readFile(path.resolve(inputPath), 'utf8')) as { runs?: TeamEvidenceRun[] } | TeamEvidenceRun[];
675
+ const runs = Array.isArray(parsed) ? parsed : parsed.runs ?? [];
676
+ const report = buildTeamDashboard(runs);
677
+ const outputIndex = argv.indexOf('--out');
678
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
679
+ if (outputPath) {
680
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
681
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
682
+ }
683
+ console.log(JSON.stringify(report, null, 2));
684
+ } catch (err: any) {
685
+ console.error(`Team dashboard generation failed: ${err?.message ?? String(err)}`);
686
+ process.exit(1);
687
+ }
688
+ return;
689
+ }
690
+
691
+ if (command === 'impact') {
692
+ const changedPath = argv[3];
693
+ if (!changedPath) {
694
+ console.error('Error: changed-files JSON path required. Usage: pw-intel impact <changed-files.json> [--root <directory>] [--out <report.json>]');
695
+ process.exit(1);
696
+ }
697
+ try {
698
+ const parsed = JSON.parse(await fs.promises.readFile(path.resolve(changedPath), 'utf8')) as { changedFiles?: string[] } | string[];
699
+ const changedFiles = Array.isArray(parsed) ? parsed : parsed.changedFiles ?? [];
700
+ const rootIndex = argv.indexOf('--root');
701
+ const root = (rootIndex >= 0 ? argv[rootIndex + 1] : undefined) ?? process.cwd();
702
+ const report = await analyzeRepositoryImpact(root, changedFiles);
703
+ const outputIndex = argv.indexOf('--out');
704
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
705
+ if (outputPath) {
706
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
707
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
708
+ }
709
+ console.log(JSON.stringify(report, null, 2));
710
+ } catch (err: any) {
711
+ console.error(`Impact analysis failed: ${err?.message ?? String(err)}`);
712
+ process.exit(1);
713
+ }
714
+ return;
715
+ }
716
+
717
+ if (command === 'architect') {
718
+ const featureName = argv[3];
719
+ if (!featureName || featureName.startsWith('--')) {
720
+ console.error('Error: feature name required. Usage: pw-intel architect <featureName> [--mode greenfield|brownfield] [--baseUrl <url>] [--crawl] [--app-model <path>] [--requirements <json>] [--write] [--verify]');
721
+ process.exit(1);
722
+ }
723
+ try {
724
+ const modeIndex = argv.indexOf('--mode');
725
+ const mode = (modeIndex >= 0 ? argv[modeIndex + 1] : 'greenfield') as ArchitectureMode;
726
+ if (mode !== 'greenfield' && mode !== 'brownfield') throw new Error("--mode must be 'greenfield' or 'brownfield'");
727
+ const requirementsIndex = argv.indexOf('--requirements');
728
+ const requirementsPath = requirementsIndex >= 0 ? argv[requirementsIndex + 1] : undefined;
729
+ const requirements = requirementsPath
730
+ ? JSON.parse(await fs.promises.readFile(path.resolve(requirementsPath), 'utf8')) as Partial<ArchitectureGenerationInput>
731
+ : {};
732
+ const baseUrlIndex = argv.indexOf('--baseUrl');
733
+ const baseUrlFromArg = baseUrlIndex >= 0 ? argv[baseUrlIndex + 1] : undefined;
734
+ const baseUrl = baseUrlFromArg ?? requirements.baseUrl;
735
+
736
+ let appModel = requirements.appModel;
737
+ const appModelIndex = argv.indexOf('--app-model');
738
+ if (appModelIndex >= 0 && argv[appModelIndex + 1]) {
739
+ const modelPath = path.resolve(argv[appModelIndex + 1]!);
740
+ appModel = JSON.parse(await fs.promises.readFile(modelPath, 'utf8')) as AppModel;
741
+ } else if (baseUrl && (argv.includes('--crawl') || (!requirements.steps || requirements.steps.length === 0))) {
742
+ try {
743
+ appModel = await AppCrawler.crawl({ baseUrl });
744
+ } catch {
745
+ // Graceful fallback if live crawl cannot connect
746
+ }
747
+ }
748
+
749
+ const profile = mode === 'brownfield' ? await buildRepositoryProfile(process.cwd()) : undefined;
750
+ const plan = planArchitecture({
751
+ featureName,
752
+ mode,
753
+ profile,
754
+ baseUrl,
755
+ requirements: requirements.requirements,
756
+ steps: requirements.steps,
757
+ includeCi: requirements.includeCi,
758
+ appModel,
759
+ });
760
+ let writeResult;
761
+ if (argv.includes('--write')) writeResult = await writeArchitecturePlan(process.cwd(), plan);
762
+ let verification;
763
+ if (argv.includes('--verify') && writeResult && writeResult.written.length > 0) {
764
+ const spec = plan.files.find((file) => file.relativePath.endsWith('.spec.ts'));
765
+ if (spec) {
766
+ verification = await ExecutionRunner.run('npx', {
767
+ cwd: process.cwd(),
768
+ args: ['playwright', 'test', spec.relativePath, '--reporter=line'],
769
+ timeoutMs: 60_000,
770
+ });
771
+
772
+ // Falsifiability Prover (Mutation Verification)
773
+ if (verification.exitCode === 0) {
774
+ try {
775
+ const specFullPath = path.resolve(process.cwd(), spec.relativePath);
776
+ const originalContent = await fs.promises.readFile(specFullPath, 'utf8');
777
+ const mutatedContent = originalContent.replace(
778
+ /expect\(page\.getByText\(['"][^'"]+['"]\)\)/,
779
+ `expect(page.getByText('__UNMATCHED_FALSIFICATION_TARGET__'))`
780
+ );
781
+ if (mutatedContent !== originalContent) {
782
+ await fs.promises.writeFile(specFullPath, mutatedContent, 'utf8');
783
+ const mutatedRun = await ExecutionRunner.run('npx', {
784
+ cwd: process.cwd(),
785
+ args: ['playwright', 'test', spec.relativePath, '--reporter=line'],
786
+ timeoutMs: 60_000,
787
+ });
788
+ await fs.promises.writeFile(specFullPath, originalContent, 'utf8');
789
+ (verification as any).falsifiable = mutatedRun.exitCode !== 0;
790
+ }
791
+ } catch {
792
+ // Non-fatal if mutation check cannot run
793
+ }
794
+ }
795
+ }
796
+ }
797
+ await recordArchitectureWorkflow(process.cwd(), plan, writeResult);
798
+ const report = { generatedAt: new Date().toISOString(), plan, writeResult, ...(verification ? { verification } : {}) };
799
+ const outputIndex = argv.indexOf('--out');
800
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
801
+ if (outputPath) {
802
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
803
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(report, null, 2)}\n`, 'utf8');
804
+ }
805
+ console.log(JSON.stringify(report, null, 2));
806
+ } catch (err: any) {
807
+ console.error(`Architecture generation failed: ${err?.message ?? String(err)}`);
808
+ process.exit(1);
809
+ }
810
+ return;
811
+ }
812
+
813
+ if (command === 'qa') {
814
+ const featureName = argv[3] && !argv[3].startsWith('--') ? argv[3] : undefined;
815
+ const readFlag = (flag: string): string | undefined => {
816
+ const idx = argv.indexOf(flag);
817
+ return idx >= 0 && argv[idx + 1] && !argv[idx + 1]!.startsWith('--') ? argv[idx + 1] : undefined;
818
+ };
819
+ const workspaceRoot = path.resolve(readFlag('--workspace') ?? process.cwd());
820
+ const baseUrl = readFlag('--url') ?? readFlag('--base-url');
821
+ const requirementsPath = readFlag('--requirements');
822
+ const markdownPath = readFlag('--markdown');
823
+ const gherkinPath = readFlag('--gherkin');
824
+ const promptFlag = readFlag('--prompt');
825
+ const outArtifactDir = readFlag('--out-dir');
826
+ const storageStatePath = readFlag('--storage-state');
827
+ const authorFlag = readFlag('--author') as AuthorKind | undefined;
828
+ const author: AuthorKind | undefined = authorFlag === 'template' || authorFlag === 'hybrid' || authorFlag === 'llm' ? authorFlag : undefined;
829
+ const useDiff = argv.includes('--diff');
830
+ const write = !argv.includes('--no-write');
831
+ const verify = !argv.includes('--no-verify');
832
+ const runMutations = !argv.includes('--no-mutate');
833
+ const allowMutatingActions = argv.includes('--allow-mutating');
834
+
835
+ try {
836
+ let markdown = markdownPath ? await fs.promises.readFile(path.resolve(markdownPath), 'utf8') : undefined;
837
+ let jsonRequirements: unknown;
838
+ let prompt = promptFlag;
839
+ let featureText = gherkinPath ? await fs.promises.readFile(path.resolve(gherkinPath), 'utf8') : undefined;
840
+
841
+ if (requirementsPath) {
842
+ const resolvedReqPath = path.resolve(requirementsPath);
843
+ const rawReq = await fs.promises.readFile(resolvedReqPath, 'utf8');
844
+ if (resolvedReqPath.toLowerCase().endsWith('.json')) {
845
+ jsonRequirements = JSON.parse(rawReq);
846
+ } else if (resolvedReqPath.toLowerCase().endsWith('.feature')) {
847
+ featureText = featureText ?? rawReq;
848
+ } else {
849
+ markdown = markdown ?? rawReq;
850
+ }
851
+ }
852
+
853
+ if (!featureName && !markdown && !jsonRequirements && !featureText && !prompt && !useDiff) {
854
+ console.error(
855
+ 'Usage: pw-intel qa <featureName> [--workspace <dir>] [--url <baseUrl>] [--requirements <path>] [--markdown <path>] [--gherkin <path>] [--prompt <text>] [--diff] [--author template|hybrid|llm] [--out-dir <dir>] [--storage-state <path>] [--allow-mutating] [--no-write] [--no-verify] [--no-mutate]',
856
+ );
857
+ process.exit(1);
858
+ }
859
+
860
+ let gitDiff: { changedFiles?: string[]; changedRoutes?: string[]; changedSymbols?: string[]; apiEndpoints?: string[]; validationHints?: string[] } | undefined;
861
+ if (useDiff) {
862
+ const collected = await new GitDiffCollector().collect({ workspaceRoot });
863
+ const payload = collected[0]?.payload as { changedFiles?: string[]; diffHints?: { changedRoutes?: string[]; changedSymbols?: string[]; apiEndpoints?: string[]; validationHints?: string[] } } | undefined;
864
+ if (payload?.changedFiles?.length) {
865
+ gitDiff = {
866
+ changedFiles: payload.changedFiles,
867
+ ...(payload.diffHints ?? {}),
868
+ };
869
+ }
870
+ }
871
+
872
+ const result = await runEvidenceDrivenGeneration({
873
+ workspaceRoot,
874
+ featureName,
875
+ baseUrl,
876
+ prompt,
877
+ markdown,
878
+ jsonRequirements,
879
+ featureText,
880
+ gitDiff,
881
+ author,
882
+ outArtifactDir,
883
+ storageStatePath,
884
+ allowMutatingActions,
885
+ write,
886
+ verify,
887
+ runMutations,
888
+ });
889
+
890
+ const outFlag = readFlag('--out');
891
+ if (outFlag) {
892
+ await fs.promises.mkdir(path.dirname(path.resolve(outFlag)), { recursive: true });
893
+ await fs.promises.writeFile(path.resolve(outFlag), `${JSON.stringify(result.qaReport, null, 2)}\n`, 'utf8');
894
+ }
895
+ console.log(JSON.stringify(result.qaReport, null, 2));
896
+ } catch (err: any) {
897
+ console.error(`QA pipeline failed: ${err?.message ?? String(err)}`);
898
+ process.exit(1);
899
+ }
900
+ return;
901
+ }
902
+
903
+ if (command === 'ci-report') {
904
+ const reportPath = argv[3];
905
+ if (!reportPath) {
906
+ console.error('Error: Playwright JSON report path required. Usage: pw-intel ci-report <playwright-report.json> [--repository <name>] [--out <report.json>]');
907
+ process.exit(1);
908
+ }
909
+ try {
910
+ const report = JSON.parse(await fs.promises.readFile(path.resolve(reportPath), 'utf8')) as unknown;
911
+ const repositoryIndex = argv.indexOf('--repository');
912
+ const repository = repositoryIndex >= 0 ? argv[repositoryIndex + 1] : undefined;
913
+ const evidence = buildCiEvidenceReport(report, { repository, runId: process.env.GITHUB_RUN_ID ?? process.env.CI_PIPELINE_ID });
914
+ await recordCiWorkflow(process.cwd(), evidence);
915
+ const outputIndex = argv.indexOf('--out');
916
+ const outputPath = outputIndex >= 0 ? argv[outputIndex + 1] : undefined;
917
+ if (outputPath) {
918
+ await fs.promises.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
919
+ await fs.promises.writeFile(path.resolve(outputPath), `${JSON.stringify(evidence, null, 2)}\n`, 'utf8');
920
+ }
921
+ const sarifIndex = argv.indexOf('--sarif-out');
922
+ const sarifPath = sarifIndex >= 0 ? argv[sarifIndex + 1] : undefined;
923
+ if (sarifPath) {
924
+ const sarif = buildSarifReport(evidence, PACKAGE_VERSION);
925
+ await fs.promises.mkdir(path.dirname(path.resolve(sarifPath)), { recursive: true });
926
+ await fs.promises.writeFile(path.resolve(sarifPath), `${JSON.stringify(sarif, null, 2)}\n`, 'utf8');
927
+ }
928
+ console.log(evidence.markdown);
929
+ for (const annotation of evidence.annotations) console.log(annotation);
930
+ if (evidence.qualityGate.status === 'FAIL') process.exitCode = 1;
931
+ } catch (err: any) {
932
+ console.error(`CI report ingestion failed: ${err?.message ?? String(err)}`);
933
+ process.exit(1);
934
+ }
935
+ return;
936
+ }
937
+
938
+ if (command === 'approve') {
939
+ const proposalId = argv[3];
940
+ if (!proposalId) {
941
+ console.error('Error: proposalId required. Usage: pw-intel approve <proposalId>');
942
+ process.exit(1);
943
+ }
944
+
945
+ const proposal = ApprovalService.getInstance().getProposal(proposalId);
946
+ if (!proposal) {
947
+ console.error(`Error: Proposal '${proposalId}' not found or expired.`);
948
+ process.exit(1);
949
+ }
950
+
951
+ console.log('========================================================================');
952
+ console.log(' PLAYWRIGHT INTELLIGENCE ENGINE — OPERATOR REPAIR APPROVAL');
953
+ console.log('========================================================================\n');
954
+ console.log(`Proposal ID : ${proposal.proposalId}`);
955
+ console.log(`Target File : ${proposal.targetFilePath}`);
956
+ console.log(`Target Class : ${proposal.targetClassName}`);
957
+ console.log(`Target Member : ${proposal.targetMemberName}`);
958
+ console.log(`Original Hash : ${proposal.originalSourceHash.slice(0, 16)}...`);
959
+ console.log(`Patched Hash : ${proposal.patchedSourceHash.slice(0, 16)}...`);
960
+ console.log(`Worktree Status : ${proposal.worktreeExecutionVerified ? 'VERIFIED (Target test passed in isolated worktree)' : 'UNVERIFIED'}`);
961
+ console.log('\n--- Proposed Unified Diff ---');
962
+ console.log(proposal.fullDiff || '(No diff recorded)');
963
+ console.log('-----------------------------\n');
964
+
965
+ if (!proposal.worktreeExecutionVerified) {
966
+ console.error('Security Violation: Cannot authorize proposal. Worktree test execution was not verified.');
967
+ process.exit(1);
968
+ }
969
+
970
+ try {
971
+ const tokenPayload = ApprovalService.getInstance().authorizeProposal({ proposalId });
972
+ console.log('PROPOSAL AUTHORIZED.');
973
+ console.log(`Single-Use Approval Token: ${tokenPayload.tokenId}\n`);
974
+ console.log(`To atomically apply this patch to your workspace, run:`);
975
+ console.log(` pw-intel promote --token ${tokenPayload.tokenId}\n`);
976
+ } catch (err: any) {
977
+ console.error(`Authorization failed: ${err.message}`);
978
+ process.exit(1);
979
+ }
980
+ return;
981
+ }
982
+
983
+ if (command === 'promote') {
984
+ const tokenIndex = argv.indexOf('--token');
985
+ const token = tokenIndex !== -1 ? argv[tokenIndex + 1] : undefined;
986
+ if (!token) {
987
+ console.error('Error: --token argument required. Usage: pw-intel promote --token <approvalToken>');
988
+ process.exit(1);
989
+ }
990
+
991
+ const tokenPayload = ApprovalService.getInstance().getTokenPayload(token);
992
+ if (!tokenPayload) {
993
+ console.error(`Error: Invalid or expired approval token '${token}'.`);
994
+ process.exit(1);
995
+ }
996
+
997
+ const proposal = ApprovalService.getInstance().getProposal(tokenPayload.proposalId);
998
+ if (!proposal) {
999
+ console.error(`Error: Staged proposal '${tokenPayload.proposalId}' no longer available.`);
1000
+ process.exit(1);
1001
+ }
1002
+
1003
+ console.log(`Applying authorized repair to '${tokenPayload.targetFilePath}'...`);
1004
+ const promoResult = await WorkspacePromoter.promote({
1005
+ targetFilePath: tokenPayload.targetFilePath,
1006
+ workspaceRoot: process.cwd(),
1007
+ patchedContent: proposal.patchedSource,
1008
+ approvalTokenPayload: tokenPayload,
1009
+ });
1010
+
1011
+ if (!promoResult.success) {
1012
+ console.error(`Promotion failed: ${promoResult.error}`);
1013
+ process.exit(1);
1014
+ }
1015
+
1016
+ await recordProjectRepairAccepted(process.cwd(), proposal.replacementSelector).catch(() => {});
1017
+
1018
+ console.log(`Successfully promoted verified repair!`);
1019
+ console.log(`Target File : ${promoResult.targetFilePath}`);
1020
+ console.log(`Bytes Written : ${promoResult.bytesWritten}`);
1021
+ console.log(`Final Hash : ${promoResult.finalHash.slice(0, 16)}...`);
1022
+ return;
1023
+ }
1024
+
1025
+ if (command === 'generate') {
1026
+ const featureName = argv[3] ?? 'Dashboard';
1027
+ const suite = TestGenerator.generateSuite(featureName, [
1028
+ { action: 'fill', name: 'Search', value: 'query' },
1029
+ { action: 'click', role: 'button', name: 'Submit' },
1030
+ { action: 'assert-visible', role: 'heading', name: 'Results' },
1031
+ ]);
1032
+
1033
+ console.log(`Generated Page Object (${suite.pageObjectName}.ts):`);
1034
+ console.log(suite.pageObjectCode);
1035
+ console.log('─────────────────────────────────────────────────────────────────');
1036
+ console.log(`Generated Spec:`);
1037
+ console.log(suite.specCode);
1038
+ return;
1039
+ }
1040
+
1041
+ console.error(`Unknown command: ${command}`);
1042
+ printHelp();
1043
+ process.exit(1);
1044
+ }
1045
+
1046
+ async function runDoctor(): Promise<void> {
1047
+ console.log('Running Playwright Intelligence Engine Diagnostic Healthcheck (pw-intel doctor)...\n');
1048
+ let healthy = true;
1049
+
1050
+ // 1. Node.js version
1051
+ const nodeVer = process.versions.node;
1052
+ const major = parseInt(nodeVer.split('.')[0] || '0', 10);
1053
+ if (major >= 20) {
1054
+ console.log(` ✓ Node.js Runtime : v${nodeVer} (Supported >= 20.0.0)`);
1055
+ } else {
1056
+ console.log(` ✗ Node.js Runtime : v${nodeVer} (Unsupported, requires Node >= 20.0.0)`);
1057
+ healthy = false;
1058
+ }
1059
+
1060
+ // 2. Git CLI availability
1061
+ try {
1062
+ const gitVer = execSync('git --version', { encoding: 'utf-8' }).trim();
1063
+ console.log(` ✓ Git Version Control : ${gitVer}`);
1064
+ } catch {
1065
+ console.log(` ✗ Git Version Control : Not found in PATH (Required for isolated worktrees)`);
1066
+ healthy = false;
1067
+ }
1068
+
1069
+ // 3. Playwright availability
1070
+ let pwFound = false;
1071
+ try {
1072
+ const cwdPkg = path.resolve(process.cwd(), 'package.json');
1073
+ if (fs.existsSync(cwdPkg)) {
1074
+ const pkg = JSON.parse(fs.readFileSync(cwdPkg, 'utf-8'));
1075
+ const pwVer = pkg.devDependencies?.['@playwright/test'] || pkg.dependencies?.['@playwright/test'];
1076
+ if (pwVer) {
1077
+ console.log(` ✓ Playwright Test : ${pwVer} (Found in package.json)`);
1078
+ pwFound = true;
1079
+ }
1080
+ }
1081
+ } catch {}
1082
+ if (!pwFound) {
1083
+ try {
1084
+ const pwCliVer = execSync('npx --no-install playwright --version', {
1085
+ encoding: 'utf-8',
1086
+ timeout: 4000,
1087
+ stdio: ['ignore', 'pipe', 'ignore'],
1088
+ }).trim();
1089
+ console.log(` ✓ Playwright CLI : ${pwCliVer}`);
1090
+ pwFound = true;
1091
+ } catch {
1092
+ console.log(` ! Playwright Test : Not detected in current working directory (Optional for offline trace analysis)`);
1093
+ }
1094
+ }
1095
+
1096
+ // 4. Workspace write access
1097
+ try {
1098
+ fs.accessSync(process.cwd(), fs.constants.W_OK);
1099
+ console.log(` ✓ Workspace Access : Read/Write verified for '${process.cwd()}'`);
1100
+ } catch {
1101
+ console.log(` ✗ Workspace Access : Permission denied writing to '${process.cwd()}'`);
1102
+ healthy = false;
1103
+ }
1104
+
1105
+ // 5. Host platform info
1106
+ console.log(` ✓ Host Platform : ${process.platform} (${process.arch})`);
1107
+
1108
+ // 6. Optional LLM status
1109
+ const apiKey = process.env.PIE_LLM_API_KEY ?? process.env.OPENAI_API_KEY ?? process.env.DEEPSEEK_API_KEY;
1110
+ if (apiKey) {
1111
+ console.log(` ✓ LLM Provider : Configured (AI test generation enabled)`);
1112
+ } else {
1113
+ console.log(` ℹ LLM Provider : None configured (100% offline deterministic mode active; run 'pw-intel init --api-key <key>' to enable AI generation)`);
1114
+ }
1115
+
1116
+ console.log('\nHealthcheck Verdict: ' + (healthy ? 'HEALTHY (Ready for diagnostic and repair workflows)' : 'DEGRADED (Check failed items above)'));
1117
+ if (!healthy) {
1118
+ process.exit(1);
1119
+ }
1120
+ }
1121
+
1122
+ function printHelp(): void {
1123
+ console.log(`
1124
+ Playwright Intelligence Engine CLI (pw-intel)
1125
+
1126
+ Usage:
1127
+ pw-intel init [dir] [--api-key <k>] [--model <m>] Initializes PIE config & optional LLM credentials
1128
+ pw-intel profile [directory] Builds the project repository convention profile
1129
+ pw-intel cluster-failures <json> Groups failure observations by deterministic fingerprints
1130
+ pw-intel evaluation-scorecard <json> Reconciles expected/predicted categories, repairs, abstentions, and latency
1131
+ pw-intel plan-test-intelligence <requirements.json> Plans coverage, missing scenarios, and duplicate tests
1132
+ pw-intel generate-agent <requirements.json> Uses structured LLM planning, then deterministic Playwright generation
1133
+ --mode greenfield|brownfield --tests <tests.json> --base-url <url> --write --verify
1134
+ pw-intel stability-feedback <selector> --outcome accepted|rejected Records operator repair feedback
1135
+ pw-intel suite-health <json> Calculates flaky-test and duration summaries
1136
+ pw-intel team-dashboard <json> Aggregates repository CI evidence into a team dashboard
1137
+ pw-intel impact <json> Selects impacted tests from changed files and repository imports
1138
+ pw-intel architect <featureName> Plans greenfield/brownfield Playwright architecture; add --write to create missing files
1139
+ pw-intel qa <featureName> Runs 4-phase evidence-driven QA workflow (plan, explore, generate, verify & falsify)
1140
+ --workspace <dir> --url <baseUrl> --requirements <path> --gherkin <path> --diff --author template|hybrid|llm --out-dir <dir>
1141
+ pw-intel ci-report <playwright-report.json> Ingests Playwright JSON and emits clusters, stability, gate, and CI annotations
1142
+ --sarif-out <file> writes GitHub/code-scanning SARIF output
1143
+ pw-intel doctor Inspects host environment, Node, Git, Playwright & workspace readiness
1144
+ pw-intel diagnose <trace.zip> Diagnoses a Playwright trace archive and outputs root cause
1145
+ --error-context <file> supplements traces with Playwright error-context.md
1146
+ pw-intel repair <trace.zip> Creates a proposal; add --execute for isolated verification
1147
+ --test <spec> --pom <page-object> --class <Class> --member <member>
1148
+ --config <playwright.config> --error-context <file> --worktree-id <id>
1149
+ --operation <JSON> for an allowlisted frame/shadow/locator operation
1150
+ pw-intel autonomous-repair <trace.zip> Runs the bounded LLM agent and deterministic isolated verifier
1151
+ --execute applies the proposal only in a disposable worktree
1152
+ --browser --browser-url <url> --allow-host <hostname[,hostname]>
1153
+ --run-tests allows the agent to run an explicitly requested target test
1154
+ --promote promotes only an independently verified repair; implies execution
1155
+ pw-intel reproduce <spec.ts> Reruns a failing test with live multi-source diagnostic instrumentation
1156
+ pw-intel inspect-testbed <dir> Inspects external Playwright repositories and emits a reproducible manifest
1157
+ pw-intel run-testbed <dir> Runs bounded baseline commands for discovered repositories
1158
+ --repository <name> --script <name> --timeout <ms> --out <file>
1159
+ pw-intel approve <proposalId> Human review and cryptographic authorization of staged repair proposal
1160
+ pw-intel promote --token <token> Atomically applies authorized repair proposal to active workspace
1161
+ pw-intel benchmark [options] Executes empirical real browser benchmark suite against Controlled App Server
1162
+ pw-intel mcp Starts the Model Context Protocol (MCP) JSON-RPC server on stdio
1163
+ pw-intel generate <featureName> Generates verified Page Object and Spec scaffolding
1164
+ pw-intel --help Shows this help message
1165
+
1166
+ Benchmark Options:
1167
+ --family <name> Filter mutation scenarios by family name (e.g. "Dynamic IDs")
1168
+ --scenario <id> Filter negative control scenarios by category or name
1169
+ --browser <browser> Target browser (default: chromium)
1170
+ `);
1171
+ }
1172
+