playwright-intelligence-engine 0.0.0-stage → 2.0.1

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,287 @@
1
+ import fs from 'node:fs/promises';
2
+ import fsSync from 'node:fs';
3
+ import path from 'node:path';
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { WorktreeOperationError } from '../core/errors/engine-errors.js';
7
+ import { DependencyStrategyResolver, IsolationLevel } from './dependency-strategy.js';
8
+
9
+ const execFileAsync = promisify(execFile);
10
+
11
+ export interface WorktreeInfo {
12
+ id: string;
13
+ worktreePath: string;
14
+ branch: string;
15
+ createdAt: number;
16
+ ownerPid: number;
17
+ isolationLevel: IsolationLevel;
18
+ strategy: string;
19
+ }
20
+
21
+ export interface WorktreeRegistry {
22
+ worktrees: Record<string, WorktreeInfo>;
23
+ }
24
+
25
+ export class WorktreeManager {
26
+ private readonly rootDir: string;
27
+ private readonly baseWorktreeDir: string;
28
+ private readonly registryPath: string;
29
+
30
+ constructor(rootDir: string) {
31
+ this.rootDir = path.resolve(rootDir);
32
+ this.baseWorktreeDir = path.resolve(this.rootDir, '.pw-intelligence', 'worktrees');
33
+ this.registryPath = path.resolve(this.baseWorktreeDir, 'registry.json');
34
+ }
35
+
36
+ private static readonly VALID_ID_REGEX = /^[a-zA-Z0-9_-]{1,64}$/;
37
+
38
+ /**
39
+ * Checks whether a given process ID is currently alive on the host system.
40
+ */
41
+ public static isProcessAlive(pid: number): boolean {
42
+ try {
43
+ process.kill(pid, 0);
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Asserts that a worktree ID is valid and that the resolved path is strictly contained
52
+ * within the base worktree directory using canonical real paths (AUDIT-05 / AUDIT-06).
53
+ * Prevents symlink/junction escape, directory traversal, and recursive host deletion.
54
+ */
55
+ private assertWorktreeContainment(id: string): string {
56
+ if (!WorktreeManager.VALID_ID_REGEX.test(id)) {
57
+ throw new WorktreeOperationError(
58
+ 'validate-id',
59
+ `Security Violation: Invalid worktree ID '${id}'. Must match /^[a-zA-Z0-9_-]{1,64}$/`
60
+ );
61
+ }
62
+
63
+ fsSync.mkdirSync(this.baseWorktreeDir, { recursive: true });
64
+ let canonicalBase: string;
65
+ try {
66
+ canonicalBase = fsSync.realpathSync(this.baseWorktreeDir);
67
+ } catch (err: any) {
68
+ throw new WorktreeOperationError(
69
+ 'containment',
70
+ `Security Violation: Failed to resolve canonical realpath of base worktree directory '${this.baseWorktreeDir}': ${err.message}`
71
+ );
72
+ }
73
+
74
+ const worktreePath = path.resolve(canonicalBase, id);
75
+ const rel = path.relative(canonicalBase, worktreePath);
76
+
77
+ if (rel.startsWith('..') || path.isAbsolute(rel) || rel === '') {
78
+ throw new WorktreeOperationError(
79
+ 'containment',
80
+ `Security Violation: Worktree path '${worktreePath}' escapes base worktree directory '${canonicalBase}'.`
81
+ );
82
+ }
83
+
84
+ if (fsSync.existsSync(worktreePath)) {
85
+ try {
86
+ const canonicalWorktree = fsSync.realpathSync(worktreePath);
87
+ const relWorktree = path.relative(canonicalBase, canonicalWorktree);
88
+ if (relWorktree.startsWith('..') || path.isAbsolute(relWorktree)) {
89
+ throw new WorktreeOperationError(
90
+ 'containment',
91
+ `Security Violation: Canonical worktree '${canonicalWorktree}' escapes base worktree directory '${canonicalBase}'.`
92
+ );
93
+ }
94
+ return canonicalWorktree;
95
+ } catch (err: any) {
96
+ if (err instanceof WorktreeOperationError) throw err;
97
+ throw new WorktreeOperationError(
98
+ 'containment',
99
+ `Security Violation: Failed to resolve canonical realpath of worktree '${worktreePath}': ${err.message}`
100
+ );
101
+ }
102
+ }
103
+
104
+ return worktreePath;
105
+ }
106
+
107
+ /**
108
+ * Creates an isolated detached Git worktree using the appropriate DependencyStrategy.
109
+ * Enforces fail-closed dependency validation (AUDIT-16).
110
+ */
111
+ public async createWorktree(worktreeId?: string): Promise<WorktreeInfo> {
112
+ const id = worktreeId ?? `repair-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 7)}`;
113
+ const worktreePath = this.assertWorktreeContainment(id);
114
+ const branch = `pw-repair/${id}`;
115
+
116
+ await fs.mkdir(this.baseWorktreeDir, { recursive: true });
117
+
118
+ // Step 1: Detect dirty workspace state and create snapshot if uncommitted changes exist
119
+ let baseRef = 'HEAD';
120
+ let untrackedFiles: string[] = [];
121
+
122
+ try {
123
+ const { stdout: statusOut } = await execFileAsync('git', ['status', '--porcelain'], { cwd: this.rootDir });
124
+ const isDirty = statusOut.trim().length > 0;
125
+
126
+ if (isDirty) {
127
+ // Create an in-memory commit representing the exact staged and unstaged state without touching workspace
128
+ const { stdout: stashSha } = await execFileAsync('git', ['stash', 'create'], { cwd: this.rootDir });
129
+ const trimmedSha = stashSha.trim();
130
+ if (trimmedSha) {
131
+ baseRef = trimmedSha;
132
+ }
133
+
134
+ // Identify untracked, non-ignored files to mirror
135
+ const { stdout: untrackedOut } = await execFileAsync('git', ['ls-files', '--others', '--exclude-standard'], { cwd: this.rootDir });
136
+ untrackedFiles = untrackedOut.split('\n').map((f) => f.trim()).filter(Boolean);
137
+ }
138
+ } catch {
139
+ // Fall back to HEAD if status check encounters a non-git environment
140
+ baseRef = 'HEAD';
141
+ }
142
+
143
+ // Step 2: Create Git worktree
144
+ try {
145
+ await execFileAsync('git', ['worktree', 'add', '-b', branch, worktreePath, baseRef], {
146
+ cwd: this.rootDir,
147
+ });
148
+ } catch (err: any) {
149
+ throw new WorktreeOperationError('create', `Git worktree add failed: ${err.message}`);
150
+ }
151
+
152
+ // Step 3: Mirror untracked files into worktree
153
+ if (untrackedFiles.length > 0) {
154
+ for (const relFile of untrackedFiles) {
155
+ try {
156
+ const src = path.resolve(this.rootDir, relFile);
157
+ const dest = path.resolve(worktreePath, relFile);
158
+ await fs.mkdir(path.dirname(dest), { recursive: true });
159
+ await fs.copyFile(src, dest);
160
+ } catch {
161
+ // Non-critical file copy failure
162
+ }
163
+ }
164
+ }
165
+
166
+ // Step 2: Prepare dependencies via DependencyStrategy and enforce validation (AUDIT-16)
167
+ const strategy = await DependencyStrategyResolver.resolve(this.rootDir);
168
+ let prepResult;
169
+ try {
170
+ prepResult = await strategy.prepare(this.rootDir, worktreePath);
171
+ // Validate dependency strategy isolation
172
+ const validation = await strategy.validate(worktreePath);
173
+ if (!validation.isValid) {
174
+ throw new Error(
175
+ `Dependency validation failed for '${prepResult.strategy}' (isolation: ${prepResult.isolationLevel}): ${validation.error ?? 'Unmet dependency requirements'}`
176
+ );
177
+ }
178
+ } catch (err: any) {
179
+ await this.destroyWorktree(id).catch(() => {});
180
+ throw new WorktreeOperationError('prepare-dependencies', err.message);
181
+ }
182
+
183
+ const info: WorktreeInfo = {
184
+ id,
185
+ worktreePath,
186
+ branch,
187
+ createdAt: Date.now(),
188
+ ownerPid: process.pid,
189
+ isolationLevel: prepResult.isolationLevel,
190
+ strategy: prepResult.strategy,
191
+ };
192
+
193
+ await this.registerWorktree(info);
194
+ return info;
195
+ }
196
+
197
+ /**
198
+ * Destroys an isolated worktree safely:
199
+ * 1. Unlinks the dependency junction/symlink FIRST (CRITICAL: prevents wiping root dependencies)
200
+ * 2. Runs git worktree remove
201
+ * 3. Deletes leftover branch
202
+ * 4. Cleans up registry
203
+ */
204
+ public async destroyWorktree(id: string): Promise<void> {
205
+ const worktreePath = this.assertWorktreeContainment(id);
206
+ const branch = `pw-repair/${id}`;
207
+
208
+ // Step 1: Cleanup dependency links via strategy
209
+ const strategy = await DependencyStrategyResolver.resolve(this.rootDir);
210
+ await strategy.cleanup(worktreePath).catch(() => {});
211
+
212
+ // Step 2: Git worktree remove
213
+ try {
214
+ await execFileAsync('git', ['worktree', 'remove', '--force', worktreePath], {
215
+ cwd: this.rootDir,
216
+ });
217
+ } catch {
218
+ // Fallback: prune if removal failed
219
+ await execFileAsync('git', ['worktree', 'prune'], { cwd: this.rootDir }).catch(() => {});
220
+ }
221
+
222
+ // Step 3: Resilient directory cleanup with backoff for Windows file locks
223
+ await this.resilientRemove(worktreePath).catch(() => {});
224
+
225
+ // Step 4: Delete temporary branch
226
+ try {
227
+ await execFileAsync('git', ['branch', '-D', branch], { cwd: this.rootDir });
228
+ } catch {
229
+ // Ignore if branch was already deleted
230
+ }
231
+
232
+ await this.unregisterWorktree(id);
233
+ }
234
+
235
+ /**
236
+ * Robust directory removal with exponential backoff to handle temporary
237
+ * Windows anti-virus or search indexer file locks.
238
+ */
239
+ private async resilientRemove(targetPath: string, maxRetries = 5): Promise<void> {
240
+ let delayMs = 100;
241
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
242
+ try {
243
+ await fs.rm(targetPath, { recursive: true, force: true });
244
+ return;
245
+ } catch (err: any) {
246
+ if (err.code === 'ENOENT') {
247
+ return;
248
+ }
249
+ if (attempt === maxRetries - 1) {
250
+ // Fallback: attempt sync removal or log warning
251
+ try {
252
+ fsSync.rmSync(targetPath, { recursive: true, force: true });
253
+ return;
254
+ } catch {
255
+ return;
256
+ }
257
+ }
258
+ await new Promise((res) => setTimeout(res, delayMs));
259
+ delayMs *= 2;
260
+ }
261
+ }
262
+ }
263
+
264
+ private async registerWorktree(info: WorktreeInfo): Promise<void> {
265
+ let reg: WorktreeRegistry = { worktrees: {} };
266
+ try {
267
+ const data = await fs.readFile(this.registryPath, 'utf-8');
268
+ reg = JSON.parse(data);
269
+ } catch {
270
+ // Default to empty registry if unreadable
271
+ }
272
+
273
+ reg.worktrees[info.id] = info;
274
+ await fs.writeFile(this.registryPath, JSON.stringify(reg, null, 2), 'utf-8');
275
+ }
276
+
277
+ private async unregisterWorktree(id: string): Promise<void> {
278
+ try {
279
+ const data = await fs.readFile(this.registryPath, 'utf-8');
280
+ const reg: WorktreeRegistry = JSON.parse(data);
281
+ delete reg.worktrees[id];
282
+ await fs.writeFile(this.registryPath, JSON.stringify(reg, null, 2), 'utf-8');
283
+ } catch {
284
+ // Ignore registry read errors
285
+ }
286
+ }
287
+ }
@@ -0,0 +1,175 @@
1
+ import { ScoreBreakdown } from './selector-scorer.js';
2
+ import { ResolvedElement } from '../trace/snapshot-resolver.js';
3
+
4
+ export interface ScoredCandidate {
5
+ element: ResolvedElement;
6
+ breakdown: ScoreBreakdown;
7
+ locatorExpression?: string | undefined;
8
+ originalSelector?: string | undefined;
9
+ intendedAction?: string | undefined; // e.g. "click", "fill"
10
+ parentScopeVerified?: boolean | undefined;
11
+ disambiguationStrategy?: string | undefined;
12
+ disambiguationExplanation?: string | undefined;
13
+ }
14
+
15
+ export type DecisionStage =
16
+ | 'SUPPORTED_ELEMENT'
17
+ | 'TARGET_IDENTITY'
18
+ | 'STRICT_UNIQUENESS'
19
+ | 'ACTION_SEMANTICS'
20
+ | 'SCORE_CONFIDENCE'
21
+ | 'AMBIGUITY_DELTA'
22
+ | 'AUTHORIZED';
23
+
24
+ export interface AbstentionDecision {
25
+ shouldAbstain: boolean;
26
+ stage: DecisionStage;
27
+ selectedCandidate?: ScoredCandidate | undefined;
28
+ reason?: string | undefined;
29
+ }
30
+
31
+ export class AbstentionPolicy {
32
+ public static readonly MINIMUM_CONFIDENCE_THRESHOLD = 0.70;
33
+ public static readonly AMBIGUITY_DELTA_THRESHOLD = 0.08;
34
+
35
+ /**
36
+ * Applies the formal Multi-Stage Decision Pipeline to candidate locators:
37
+ * 1. Supported Element? (Must be recognized interactive element)
38
+ * 2. Target Identity Verified? (Affinity to intended business entity)
39
+ * 3. Exactly One Scoped Match? (Strict Uniqueness: matchCount === 1, unless parentScopeVerified)
40
+ * 4. Action Semantics Preserved? (e.g. click -> clickable element, fill -> input/textarea)
41
+ * 5. Score & Calibrated Threshold Pass? (rawScore >= 0.70)
42
+ * 6. Ambiguity Delta Pass? (top.score - runnerUp.score >= 0.08)
43
+ */
44
+ public static evaluate(candidates: ScoredCandidate[]): AbstentionDecision {
45
+ if (candidates.length === 0) {
46
+ return {
47
+ shouldAbstain: true,
48
+ stage: 'SUPPORTED_ELEMENT',
49
+ reason: 'Zero matching candidate elements found in DOM snapshot',
50
+ };
51
+ }
52
+
53
+ // Sort descending by score
54
+ const sorted = [...candidates].sort((a, b) => b.breakdown.score - a.breakdown.score);
55
+ const top = sorted[0]!;
56
+
57
+ // Stage 1: Supported Element Check
58
+ const supportedTags = new Set([
59
+ 'button',
60
+ 'a',
61
+ 'input',
62
+ 'select',
63
+ 'textarea',
64
+ 'option',
65
+ 'div',
66
+ 'span',
67
+ 'li',
68
+ 'td',
69
+ 'th',
70
+ 'p',
71
+ 'h1',
72
+ 'h2',
73
+ 'h3',
74
+ 'h4',
75
+ 'label',
76
+ 'img',
77
+ 'form',
78
+ ]);
79
+ if (!supportedTags.has(top.element.tag) && !top.element.role) {
80
+ return {
81
+ shouldAbstain: true,
82
+ stage: 'SUPPORTED_ELEMENT',
83
+ reason: `Unsupported DOM tag '<${top.element.tag}>' without ARIA role cannot be safely automated.`,
84
+ };
85
+ }
86
+
87
+ // Stage 2: Target Identity Verification
88
+ if (top.breakdown.identityMatch < 0.4) {
89
+ return {
90
+ shouldAbstain: true,
91
+ stage: 'TARGET_IDENTITY',
92
+ reason: `Target identity mismatch: candidate accessible name '${top.element.accessibleName ?? 'unnamed'}' does not align with original selector intent.`,
93
+ };
94
+ }
95
+
96
+ // Stage 3: Strict Uniqueness Gate (CRITICAL: matchCount !== 1 triggers ABSTAIN)
97
+ // Exactly 2 or more matches fail the uniqueness requirement unless parentScopeVerified is true
98
+ if (top.breakdown.matchCount !== 1 && !top.parentScopeVerified) {
99
+ return {
100
+ shouldAbstain: true,
101
+ stage: 'STRICT_UNIQUENESS',
102
+ reason: `NON_UNIQUE_CANDIDATE: Candidate element matches ${top.breakdown.matchCount} elements in DOM. Strict uniqueness requires exactly 1 match (U = 1.0) unless scoped by a verified parent container.`,
103
+ };
104
+ }
105
+
106
+ // Stage 4: Action Semantics Check
107
+ if (top.intendedAction) {
108
+ const action = top.intendedAction.toLowerCase();
109
+ if (action.includes('click')) {
110
+ const isClickable =
111
+ top.element.tag === 'button' ||
112
+ top.element.tag === 'a' ||
113
+ top.element.role === 'button' ||
114
+ top.element.role === 'link' ||
115
+ top.element.role === 'tab' ||
116
+ top.element.role === 'menuitem' ||
117
+ Boolean(top.element.attributes?.['onclick']) ||
118
+ Boolean(top.element.attributes?.['href']);
119
+
120
+ if (!isClickable && top.element.tag !== 'div' && top.element.tag !== 'span' && top.element.tag !== 'td') {
121
+ return {
122
+ shouldAbstain: true,
123
+ stage: 'ACTION_SEMANTICS',
124
+ reason: `Action semantics violation: action '${top.intendedAction}' cannot be safely dispatched to non-clickable element <${top.element.tag}>.`,
125
+ };
126
+ }
127
+ } else if (action.includes('fill') || action.includes('type')) {
128
+ const isInput =
129
+ top.element.tag === 'input' ||
130
+ top.element.tag === 'textarea' ||
131
+ top.element.role === 'textbox' ||
132
+ top.element.attributes['contenteditable'] === 'true';
133
+
134
+ if (!isInput) {
135
+ return {
136
+ shouldAbstain: true,
137
+ stage: 'ACTION_SEMANTICS',
138
+ reason: `Action semantics violation: fill/type action requires input or textbox element, but found <${top.element.tag}>.`,
139
+ };
140
+ }
141
+ }
142
+ }
143
+
144
+ // Stage 5: Confidence Threshold Gate
145
+ if (top.breakdown.score < AbstentionPolicy.MINIMUM_CONFIDENCE_THRESHOLD) {
146
+ return {
147
+ shouldAbstain: true,
148
+ stage: 'SCORE_CONFIDENCE',
149
+ reason: `Highest candidate confidence (${(top.breakdown.score * 100).toFixed(1)}%) is below minimum safety threshold (${(AbstentionPolicy.MINIMUM_CONFIDENCE_THRESHOLD * 100).toFixed(1)}%)`,
150
+ };
151
+ }
152
+
153
+ // Stage 6: Ambiguity Delta Gate (if top two candidates are neck and neck, guess is unsafe)
154
+ if (sorted.length > 1) {
155
+ const runnerUp = sorted[1]!;
156
+ const delta = parseFloat((top.breakdown.score - runnerUp.breakdown.score).toFixed(3));
157
+ const identitySeparation = top.breakdown.identityMatch - runnerUp.breakdown.identityMatch;
158
+ const hasStrongIdentitySeparation = top.breakdown.identityMatch >= 0.9 && identitySeparation >= 0.12;
159
+ if (delta < AbstentionPolicy.AMBIGUITY_DELTA_THRESHOLD && !hasStrongIdentitySeparation) {
160
+ return {
161
+ shouldAbstain: true,
162
+ stage: 'AMBIGUITY_DELTA',
163
+ reason: `Ambiguous match: top two candidate elements have nearly identical scores (${top.breakdown.score} vs ${runnerUp.breakdown.score}, delta: ${delta.toFixed(3)} < threshold ${AbstentionPolicy.AMBIGUITY_DELTA_THRESHOLD}). Abstaining to prevent incorrect action dispatch.`,
164
+ };
165
+ }
166
+ }
167
+
168
+ return {
169
+ shouldAbstain: false,
170
+ stage: 'AUTHORIZED',
171
+ selectedCandidate: top,
172
+ };
173
+ }
174
+ }
175
+