@herbertgao/pi-extensions 2026.10.1 → 2026.10.3

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 (934) hide show
  1. package/README.md +14 -17
  2. package/THIRD_PARTY_NOTICES.md +2 -2
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  9. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  10. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  11. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  12. package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
  13. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
  14. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  15. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  16. package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
  17. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  18. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
  19. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  20. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
  21. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
  22. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
  23. package/node_modules/{remote-pi → @herbertgao/pi-agency}/LICENSE +1 -1
  24. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  25. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  26. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  34. package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
  35. package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
  38. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
  39. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
  40. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
  41. package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
  43. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
  44. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
  45. package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
  46. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
  47. package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
  48. package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
  49. package/node_modules/@tifan/pi-recap/package.json +1 -1
  50. package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
  51. package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
  52. package/node_modules/pi-antigravity/README.md +23 -2
  53. package/node_modules/pi-antigravity/package.json +5 -3
  54. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
  55. package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
  56. package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +86 -47
  58. package/node_modules/pi-antigravity/src/image/native.ts +69 -0
  59. package/node_modules/pi-antigravity/src/index.ts +52 -137
  60. package/node_modules/pi-antigravity/src/search/search.ts +130 -31
  61. package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
  62. package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
  63. package/node_modules/pi-antigravity/src/types/types.ts +1 -1
  64. package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
  65. package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
  66. package/node_modules/pi-lens/CHANGELOG.md +1159 -0
  67. package/node_modules/pi-lens/README.md +116 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  70. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  71. package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
  72. package/node_modules/pi-lens/dist/chunk-2UMYMRI3.js +118 -0
  73. package/node_modules/pi-lens/dist/chunk-34OCDWOS.js +48 -0
  74. package/node_modules/pi-lens/dist/chunk-3FERWMCH.js +144 -0
  75. package/node_modules/pi-lens/dist/chunk-3JL6IFD7.js +97 -0
  76. package/node_modules/pi-lens/dist/chunk-3JSEWUWI.js +327 -0
  77. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  78. package/node_modules/pi-lens/dist/chunk-3MRXIJ6I.js +79 -0
  79. package/node_modules/pi-lens/dist/chunk-3P4PF2BX.js +2250 -0
  80. package/node_modules/pi-lens/dist/chunk-3PNFWVHT.js +201 -0
  81. package/node_modules/pi-lens/dist/chunk-3TTPKMEF.js +162 -0
  82. package/node_modules/pi-lens/dist/chunk-5JHXUETW.js +326 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-64LU2OVH.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6B5I6ICN.js +44 -0
  86. package/node_modules/pi-lens/dist/chunk-6P54TMWL.js +274 -0
  87. package/node_modules/pi-lens/dist/chunk-6UYA642J.js +145 -0
  88. package/node_modules/pi-lens/dist/chunk-6XX5HCLK.js +398 -0
  89. package/node_modules/pi-lens/dist/chunk-7AXO3ZDF.js +30 -0
  90. package/node_modules/pi-lens/dist/chunk-7BX5DWSA.js +1907 -0
  91. package/node_modules/pi-lens/dist/chunk-7ELY2CSM.js +902 -0
  92. package/node_modules/pi-lens/dist/chunk-7M4T5N3V.js +38 -0
  93. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  94. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  95. package/node_modules/pi-lens/dist/chunk-AFZYMAIV.js +756 -0
  96. package/node_modules/pi-lens/dist/chunk-ALQRYH6O.js +112 -0
  97. package/node_modules/pi-lens/dist/chunk-AXKPQ6ZI.js +1610 -0
  98. package/node_modules/pi-lens/dist/chunk-BHWHXMQ2.js +62 -0
  99. package/node_modules/pi-lens/dist/chunk-BJF25SUG.js +22 -0
  100. package/node_modules/pi-lens/dist/chunk-BNKLYOOO.js +18 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-CBD2B5CP.js +599 -0
  103. package/node_modules/pi-lens/dist/chunk-CFYISHUW.js +42 -0
  104. package/node_modules/pi-lens/dist/chunk-CMU4RMJB.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-CULQYFLU.js +14 -0
  106. package/node_modules/pi-lens/dist/chunk-D65GOCJB.js +289 -0
  107. package/node_modules/pi-lens/dist/chunk-D6YF5HKX.js +59 -0
  108. package/node_modules/pi-lens/dist/chunk-DMS52BEP.js +342 -0
  109. package/node_modules/pi-lens/dist/chunk-ELVWJYRV.js +211 -0
  110. package/node_modules/pi-lens/dist/chunk-EUT3B22Y.js +27 -0
  111. package/node_modules/pi-lens/dist/chunk-F5VLZHUW.js +146 -0
  112. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  113. package/node_modules/pi-lens/dist/chunk-FGALODG3.js +55 -0
  114. package/node_modules/pi-lens/dist/chunk-G3RLJYA2.js +2358 -0
  115. package/node_modules/pi-lens/dist/chunk-GAYGGICG.js +32 -0
  116. package/node_modules/pi-lens/dist/chunk-H2W2AXD3.js +419 -0
  117. package/node_modules/pi-lens/dist/chunk-H5EV3VMF.js +114 -0
  118. package/node_modules/pi-lens/dist/chunk-HREZQCT3.js +106 -0
  119. package/node_modules/pi-lens/dist/chunk-HSIRMK2D.js +517 -0
  120. package/node_modules/pi-lens/dist/chunk-IMSQR4WO.js +184 -0
  121. package/node_modules/pi-lens/dist/chunk-IQZXOZ7S.js +4816 -0
  122. package/node_modules/pi-lens/dist/chunk-IR6XFA5P.js +37 -0
  123. package/node_modules/pi-lens/dist/chunk-IUKPRZER.js +78 -0
  124. package/node_modules/pi-lens/dist/chunk-IUR76K6A.js +262 -0
  125. package/node_modules/pi-lens/dist/chunk-J3FZR3YY.js +98 -0
  126. package/node_modules/pi-lens/dist/chunk-J6DFZDFA.js +562 -0
  127. package/node_modules/pi-lens/dist/chunk-J7WJZ6W7.js +50 -0
  128. package/node_modules/pi-lens/dist/chunk-JONGANYH.js +202 -0
  129. package/node_modules/pi-lens/dist/chunk-KJCWXUUS.js +425 -0
  130. package/node_modules/pi-lens/dist/chunk-KJN4IOEJ.js +17 -0
  131. package/node_modules/pi-lens/dist/chunk-KMGPUKFD.js +25 -0
  132. package/node_modules/pi-lens/dist/chunk-LD7WFTRX.js +4533 -0
  133. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  134. package/node_modules/pi-lens/dist/chunk-LNOMA3MP.js +169 -0
  135. package/node_modules/pi-lens/dist/chunk-MGIWSROG.js +2334 -0
  136. package/node_modules/pi-lens/dist/chunk-MQA2YBAH.js +308 -0
  137. package/node_modules/pi-lens/dist/chunk-MTDRLSI7.js +21720 -0
  138. package/node_modules/pi-lens/dist/chunk-N2IRDBZI.js +2355 -0
  139. package/node_modules/pi-lens/dist/chunk-N77WFUL7.js +50 -0
  140. package/node_modules/pi-lens/dist/chunk-N7YNJE5R.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  142. package/node_modules/pi-lens/dist/chunk-NFXP47RV.js +999 -0
  143. package/node_modules/pi-lens/dist/chunk-NJF2X5XF.js +45 -0
  144. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  145. package/node_modules/pi-lens/dist/chunk-O7HZX7CA.js +309 -0
  146. package/node_modules/pi-lens/dist/chunk-OC7LYAU5.js +708 -0
  147. package/node_modules/pi-lens/dist/chunk-OEX24ECI.js +348 -0
  148. package/node_modules/pi-lens/dist/chunk-OYOB3ZFG.js +78 -0
  149. package/node_modules/pi-lens/dist/chunk-OYTMZXRF.js +41 -0
  150. package/node_modules/pi-lens/dist/chunk-OZJINN4O.js +84 -0
  151. package/node_modules/pi-lens/dist/chunk-PCHDGUKT.js +23 -0
  152. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  153. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  154. package/node_modules/pi-lens/dist/chunk-RJFED73B.js +79 -0
  155. package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
  156. package/node_modules/pi-lens/dist/chunk-S6AMWKPC.js +189 -0
  157. package/node_modules/pi-lens/dist/chunk-T4QZ6FEH.js +308 -0
  158. package/node_modules/pi-lens/dist/chunk-T54VJRAD.js +5456 -0
  159. package/node_modules/pi-lens/dist/chunk-TA5L5KWQ.js +167 -0
  160. package/node_modules/pi-lens/dist/chunk-TQFBZVCG.js +27 -0
  161. package/node_modules/pi-lens/dist/chunk-U52SAGRM.js +62 -0
  162. package/node_modules/pi-lens/dist/chunk-UEAHAMEI.js +5814 -0
  163. package/node_modules/pi-lens/dist/chunk-UGSMCQ4D.js +327 -0
  164. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  165. package/node_modules/pi-lens/dist/chunk-USHG4DLH.js +459 -0
  166. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  167. package/node_modules/pi-lens/dist/chunk-V5MEBST3.js +4139 -0
  168. package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
  169. package/node_modules/pi-lens/dist/chunk-VPWKF3GN.js +87 -0
  170. package/node_modules/pi-lens/dist/chunk-VSMJK3ZJ.js +1250 -0
  171. package/node_modules/pi-lens/dist/chunk-VYKKMYVT.js +560 -0
  172. package/node_modules/pi-lens/dist/chunk-VZHJS6DR.js +442 -0
  173. package/node_modules/pi-lens/dist/chunk-WATC3IQR.js +213 -0
  174. package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
  175. package/node_modules/pi-lens/dist/chunk-WSEWKCXU.js +188 -0
  176. package/node_modules/pi-lens/dist/chunk-WW5BLB74.js +659 -0
  177. package/node_modules/pi-lens/dist/chunk-XGUL4SLQ.js +406 -0
  178. package/node_modules/pi-lens/dist/chunk-XHSCVSOL.js +170 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-YLN4K6WD.js +385 -0
  181. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  182. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  183. package/node_modules/pi-lens/dist/chunk-Z6BB4JLQ.js +478 -0
  184. package/node_modules/pi-lens/dist/chunk-Z7QQA2KT.js +27 -0
  185. package/node_modules/pi-lens/dist/chunk-ZETYB3XG.js +2626 -0
  186. package/node_modules/pi-lens/dist/chunk-ZMOMPX2Z.js +87 -0
  187. package/node_modules/pi-lens/dist/chunk-ZOEYVXSV.js +149 -0
  188. package/node_modules/pi-lens/dist/chunk-ZZUFZUQZ.js +12120 -0
  189. package/node_modules/pi-lens/dist/index.js +39712 -29876
  190. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
  191. package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
  192. package/node_modules/pi-lens/dist/mcp/server.js +22688 -1573
  193. package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
  194. package/node_modules/pi-lens/dist/probes/installer.js +135 -0
  195. package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
  196. package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
  197. package/node_modules/pi-lens/docs/agent-guide.md +13 -4
  198. package/node_modules/pi-lens/docs/agent-tools.md +42 -0
  199. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -4
  200. package/node_modules/pi-lens/docs/configuration.md +28 -2
  201. package/node_modules/pi-lens/docs/custom-rules.md +25 -2
  202. package/node_modules/pi-lens/docs/environment-variables.md +86 -8
  203. package/node_modules/pi-lens/docs/features.md +45 -5
  204. package/node_modules/pi-lens/docs/globalconfig.md +2 -1
  205. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  206. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +37 -4
  207. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  208. package/node_modules/pi-lens/docs/mcp.md +21 -5
  209. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  210. package/node_modules/pi-lens/docs/pi-lens-fixer.md +160 -88
  211. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  212. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +736 -0
  213. package/node_modules/pi-lens/docs/pi-lens-monitor.md +75 -7
  214. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  215. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +189 -48
  216. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  217. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  218. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  219. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  220. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  221. package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
  222. package/node_modules/pi-lens/docs/servercapabilities.md +56 -49
  223. package/node_modules/pi-lens/docs/settings.md +1 -0
  224. package/node_modules/pi-lens/docs/usage.md +5 -1
  225. package/node_modules/pi-lens/docs/word-index.md +6 -0
  226. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  227. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  228. package/node_modules/pi-lens/package.json +18 -6
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  237. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  238. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  239. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  240. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  241. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  242. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  243. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  244. package/node_modules/pi-web-access/README.md +10 -6
  245. package/node_modules/pi-web-access/ceramic.ts +219 -0
  246. package/node_modules/pi-web-access/curator-page.ts +3 -1
  247. package/node_modules/pi-web-access/curator-server.ts +1 -0
  248. package/node_modules/pi-web-access/dist/index.js +846 -588
  249. package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
  250. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  251. package/node_modules/pi-web-access/github-extract.ts +34 -2
  252. package/node_modules/pi-web-access/index.ts +72 -30
  253. package/node_modules/pi-web-access/openai-search.ts +3 -3
  254. package/node_modules/pi-web-access/package.json +2 -2
  255. package/node_modules/pi-web-access/page-query.ts +6 -9
  256. package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
  257. package/node_modules/pi-web-access/query-rewrite.ts +2 -2
  258. package/node_modules/pi-web-access/storage.ts +49 -0
  259. package/node_modules/pi-web-access/summary-review.ts +7 -6
  260. package/node_modules/pi-web-access/utils.ts +21 -6
  261. package/node_modules/pi-web-access/web-tool-core.ts +1 -0
  262. package/package.json +19 -19
  263. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
  264. package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
  265. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
  266. package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
  267. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  268. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  269. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  270. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  271. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  272. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  273. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  274. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  275. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  276. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  277. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  278. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  279. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  280. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  281. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  282. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  283. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  284. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  285. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  286. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  287. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  288. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  289. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  290. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  291. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  292. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  293. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  294. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  295. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  296. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  297. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  298. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  299. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  300. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  301. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  302. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  303. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  304. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  305. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  306. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  307. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  308. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  309. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  310. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  311. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  312. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  313. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  314. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  315. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  316. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  317. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  318. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  319. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  320. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  321. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  322. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  323. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  324. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  325. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  326. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  327. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  328. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  329. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  330. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  331. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  332. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  333. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  334. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  335. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  336. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  337. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  338. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  339. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  340. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  341. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  342. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  343. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  344. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  345. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  346. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  347. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  348. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  349. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  350. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  351. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  352. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  353. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  354. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  355. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  356. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  357. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  358. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  359. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  360. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  361. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  362. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  363. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  364. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  365. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  366. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  367. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  368. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  369. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  370. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  371. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  372. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  373. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  374. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  375. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  376. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  377. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  378. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  379. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  380. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  381. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  382. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  383. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  384. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  385. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  386. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  387. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  388. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  389. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  390. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  391. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  392. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  393. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  394. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  395. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  396. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  397. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  398. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  399. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  400. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  401. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  402. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  403. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  404. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  405. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  406. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  407. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  447. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  448. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  449. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  450. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  451. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  452. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  453. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  454. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  455. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  456. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  457. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  458. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  459. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  460. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  461. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  462. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  463. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  464. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  465. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  466. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  467. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  468. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  469. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  470. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  471. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  472. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  473. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  474. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  475. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  476. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  477. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  478. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  479. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  480. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  481. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  482. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  483. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  484. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  485. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  486. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  487. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  488. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  489. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  490. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  491. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  492. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  493. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  494. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  495. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  496. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  497. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  498. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  499. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  500. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  501. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  502. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  503. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  504. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  505. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  506. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  507. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  508. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  509. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  510. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  511. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  512. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  513. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  514. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  515. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  516. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  517. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  518. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  519. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  520. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  521. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  522. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  523. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  524. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  525. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  526. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  527. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  528. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  529. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  530. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  531. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  532. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  533. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  534. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  535. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  536. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  537. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  538. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  539. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  540. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  541. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  542. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  543. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  544. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  545. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  546. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  547. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  548. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  549. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  550. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  551. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  552. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  553. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  554. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  555. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  556. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  557. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  558. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  559. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  560. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  561. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  562. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  563. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  564. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  565. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  566. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  567. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  568. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  569. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  570. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  571. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  572. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  573. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  574. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  575. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  576. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  577. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  578. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  579. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  580. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  581. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  582. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  583. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  584. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  585. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  586. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  587. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  588. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  589. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  590. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  591. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  592. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  593. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  594. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  595. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  596. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  597. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  598. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  599. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  600. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  601. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  602. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  603. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  604. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  605. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  606. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  607. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  608. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  609. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  610. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  611. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  612. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  613. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  614. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  615. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  616. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  617. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  618. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  619. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  620. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  621. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  623. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  624. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  625. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  626. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  627. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  628. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  629. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  630. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  631. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  632. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  633. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  634. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  635. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  636. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  637. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  638. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  639. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  640. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  641. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  642. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  643. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  644. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  645. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  646. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  647. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  648. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  649. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  650. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  651. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  652. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  653. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  654. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  655. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  656. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  657. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  658. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  659. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  660. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  661. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  662. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  663. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  664. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  665. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  666. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  667. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  668. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  669. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  670. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  671. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  672. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  673. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  674. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  675. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  676. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  677. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  678. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  679. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  680. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  681. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  682. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  683. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  684. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  685. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  686. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  687. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  688. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  689. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  690. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  691. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  692. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  693. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  694. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  695. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  696. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  697. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  698. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  699. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  700. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  701. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  702. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  703. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  704. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  705. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  706. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  707. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  708. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  709. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  710. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  711. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  712. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  713. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  714. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  715. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  716. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  717. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  718. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  719. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  720. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  721. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  722. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  723. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  724. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  725. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  726. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  727. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  728. package/node_modules/pi-lens/dist/i18n.js +0 -69
  729. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  730. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  731. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  732. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  733. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  734. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  735. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  736. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  737. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  738. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  739. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  740. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  741. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  742. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  743. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  744. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  745. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  746. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  747. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  748. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
  749. package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
  750. package/node_modules/remote-pi/README.md +0 -675
  751. package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
  752. package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
  753. package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
  754. package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
  755. package/node_modules/remote-pi/dist/actions/registry.js +0 -58
  756. package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
  757. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
  758. package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
  759. package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
  760. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
  761. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
  762. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
  763. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
  764. package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
  765. package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
  766. package/node_modules/remote-pi/dist/config.d.ts +0 -62
  767. package/node_modules/remote-pi/dist/config.js +0 -115
  768. package/node_modules/remote-pi/dist/config.js.map +0 -1
  769. package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
  770. package/node_modules/remote-pi/dist/daemon/client.js +0 -131
  771. package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
  772. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
  773. package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
  774. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
  775. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
  776. package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
  777. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
  778. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
  779. package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
  780. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
  781. package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
  782. package/node_modules/remote-pi/dist/daemon/id.js +0 -36
  783. package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
  784. package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
  785. package/node_modules/remote-pi/dist/daemon/install.js +0 -547
  786. package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
  787. package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
  788. package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
  789. package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
  790. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
  791. package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
  792. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
  793. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
  794. package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
  795. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
  796. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
  797. package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
  798. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
  799. package/node_modules/remote-pi/dist/index.d.ts +0 -213
  800. package/node_modules/remote-pi/dist/index.js +0 -4996
  801. package/node_modules/remote-pi/dist/index.js.map +0 -1
  802. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
  803. package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
  804. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
  805. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
  806. package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
  807. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
  808. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
  809. package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
  810. package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
  811. package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
  812. package/node_modules/remote-pi/dist/mesh/client.js +0 -125
  813. package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
  814. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
  815. package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
  816. package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
  817. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
  818. package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
  819. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
  820. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
  821. package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
  822. package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
  823. package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
  824. package/node_modules/remote-pi/dist/mesh/types.js +0 -11
  825. package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
  826. package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
  827. package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
  828. package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
  829. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
  830. package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
  831. package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
  832. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
  833. package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
  834. package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
  835. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
  836. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
  837. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
  838. package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
  839. package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
  840. package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
  841. package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
  842. package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
  843. package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
  844. package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
  845. package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
  846. package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
  847. package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
  848. package/node_modules/remote-pi/dist/protocol/types.js +0 -2
  849. package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
  850. package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
  851. package/node_modules/remote-pi/dist/rooms.js +0 -57
  852. package/node_modules/remote-pi/dist/rooms.js.map +0 -1
  853. package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
  854. package/node_modules/remote-pi/dist/session/address.js +0 -58
  855. package/node_modules/remote-pi/dist/session/address.js.map +0 -1
  856. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
  857. package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
  858. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
  859. package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
  860. package/node_modules/remote-pi/dist/session/bridge.js +0 -130
  861. package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
  862. package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
  863. package/node_modules/remote-pi/dist/session/broker.js +0 -507
  864. package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
  865. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
  866. package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
  867. package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
  868. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
  869. package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
  870. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
  871. package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
  872. package/node_modules/remote-pi/dist/session/envelope.js +0 -117
  873. package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
  874. package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
  875. package/node_modules/remote-pi/dist/session/global_config.js +0 -73
  876. package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
  877. package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
  878. package/node_modules/remote-pi/dist/session/ipc.js +0 -22
  879. package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
  880. package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
  881. package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
  882. package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
  883. package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
  884. package/node_modules/remote-pi/dist/session/local_config.js +0 -157
  885. package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
  886. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
  887. package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
  888. package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
  889. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
  890. package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
  891. package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
  892. package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
  893. package/node_modules/remote-pi/dist/session/peer.js +0 -451
  894. package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
  895. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
  896. package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
  897. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
  898. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
  899. package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
  900. package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
  901. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
  902. package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
  903. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
  904. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
  905. package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
  906. package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
  907. package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
  908. package/node_modules/remote-pi/dist/session/tools.js +0 -252
  909. package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
  910. package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
  911. package/node_modules/remote-pi/dist/session/wizard.js +0 -20
  912. package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
  913. package/node_modules/remote-pi/dist/settings.d.ts +0 -15
  914. package/node_modules/remote-pi/dist/settings.js +0 -52
  915. package/node_modules/remote-pi/dist/settings.js.map +0 -1
  916. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
  917. package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
  918. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
  919. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
  920. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
  921. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
  922. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
  923. package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
  924. package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
  925. package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
  926. package/node_modules/remote-pi/dist/ui/footer.js +0 -34
  927. package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
  928. package/node_modules/remote-pi/docs/daemon.md +0 -289
  929. package/node_modules/remote-pi/package.json +0 -92
  930. package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
  931. package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
  932. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
  933. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
  934. package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
@@ -0,0 +1,2250 @@
1
+ import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
+ import {
3
+ stripAnsi
4
+ } from "./chunk-FC4RP4I2.js";
5
+ import {
6
+ resolveGitCheckout
7
+ } from "./chunk-IMSQR4WO.js";
8
+ import {
9
+ emitBounded
10
+ } from "./chunk-OZJINN4O.js";
11
+ import {
12
+ augmentPythonEnvironment,
13
+ detectPythonEnvironment
14
+ } from "./chunk-LNOMA3MP.js";
15
+ import {
16
+ PathKeyedMap
17
+ } from "./chunk-Z3CCVDUB.js";
18
+ import {
19
+ resolveToolCwd
20
+ } from "./chunk-IUR76K6A.js";
21
+ import {
22
+ findGlobalBinary
23
+ } from "./chunk-T4QZ6FEH.js";
24
+ import {
25
+ resolveLanguageRootForFile
26
+ } from "./chunk-VYKKMYVT.js";
27
+ import {
28
+ safeSpawn,
29
+ safeSpawnAsync
30
+ } from "./chunk-T54VJRAD.js";
31
+ import {
32
+ detectFileKind
33
+ } from "./chunk-64LU2OVH.js";
34
+ import {
35
+ detectFileRole
36
+ } from "./chunk-N2IRDBZI.js";
37
+ import {
38
+ findNearestDirWithAnyBasename
39
+ } from "./chunk-Z6BB4JLQ.js";
40
+ import {
41
+ LEDGER_FIELD_MAX,
42
+ recordDegradationOnce
43
+ } from "./chunk-7ELY2CSM.js";
44
+ import {
45
+ createSubsystemLogger
46
+ } from "./chunk-34OCDWOS.js";
47
+ import {
48
+ BoundedFifoMap
49
+ } from "./chunk-ABBOA7UD.js";
50
+ import {
51
+ findNearestMarkerRootDetailed,
52
+ isRealGitMarker,
53
+ isUnderDir,
54
+ minimatch,
55
+ normalizeEphemeralMapKey,
56
+ normalizeMapKey,
57
+ pathsEqual,
58
+ toPosix
59
+ } from "./chunk-G3RLJYA2.js";
60
+
61
+ // dist/clients/test-runner-client.js
62
+ import { createHash } from "node:crypto";
63
+ import * as fs from "node:fs";
64
+ import * as path from "node:path";
65
+
66
+ // dist/clients/run-duration.js
67
+ function isMeasuredDuration(duration) {
68
+ return typeof duration === "number" && Number.isFinite(duration) && duration >= 0;
69
+ }
70
+ function toMeasuredDurationMs(duration) {
71
+ return isMeasuredDuration(duration) ? Math.round(duration) : void 0;
72
+ }
73
+ function formatRunDurationMs(duration) {
74
+ return isMeasuredDuration(duration) ? `${Math.round(duration)}ms` : "unmeasured";
75
+ }
76
+
77
+ // dist/clients/test-runner-client.js
78
+ function isRunnerErrorResult(result) {
79
+ return result.failed === 0 && !!result.error;
80
+ }
81
+ var SOURCE_TO_TEST_PATTERNS = [
82
+ {
83
+ ext: ".ts",
84
+ testExts: [".test.ts", ".spec.ts"],
85
+ dirs: ["__tests__", "tests", ".", "__tests__"]
86
+ },
87
+ {
88
+ ext: ".tsx",
89
+ testExts: [".test.tsx", ".spec.tsx"],
90
+ dirs: ["__tests__", "tests", ".", "__tests__"]
91
+ },
92
+ {
93
+ ext: ".js",
94
+ testExts: [".test.js", ".spec.js"],
95
+ dirs: ["__tests__", "tests", ".", "__tests__"]
96
+ },
97
+ {
98
+ ext: ".jsx",
99
+ testExts: [".test.jsx", ".spec.jsx"],
100
+ dirs: ["__tests__", "tests", ".", "__tests__"]
101
+ },
102
+ {
103
+ ext: ".py",
104
+ testExts: ["test_*.py", "*_test.py"],
105
+ dirs: ["tests", "test", ".", "."]
106
+ },
107
+ { ext: ".go", testExts: ["_test.go"], dirs: [".", ".", ".", "."] },
108
+ // Go tests are co-located
109
+ { ext: ".rs", testExts: [".rs"], dirs: ["tests", "tests", "src", "."] },
110
+ // Rust: tests/ or #[test] in src
111
+ // PHPUnit convention: tests/ mirrors src/ with ClassNameTest.php naming
112
+ // (e.g. src/Foo/Bar.php -> tests/Foo/BarTest.php). Basename is already the
113
+ // class name (PHP files are named after their class), so no case transform
114
+ // is needed — the mirrored-directory search below handles the tests/ root.
115
+ { ext: ".php", testExts: ["Test.php"], dirs: ["tests"] },
116
+ // ExUnit convention: test/ mirrors lib/ with a _test.exs suffix on the same
117
+ // basename (e.g. lib/accounts/user.ex -> test/accounts/user_test.exs).
118
+ { ext: ".ex", testExts: ["_test.exs"], dirs: ["test"] }
119
+ ];
120
+ function findHoistedNodeModulesPackage(cwd, packageName) {
121
+ return findNearestDirWithAnyBasename(path.resolve(cwd), [
122
+ `node_modules/${packageName}`
123
+ ]);
124
+ }
125
+ var MAX_PYTEST_RECURSE_DEPTH = 3;
126
+ var TURN_END_EXCLUDED_TEST_GLOBS = [
127
+ "**/integration/**",
128
+ "**/e2e/**",
129
+ "**/*.integration.*",
130
+ "**/*.e2e.*"
131
+ ];
132
+ function recordCheckoutIdentityGap(metadata) {
133
+ recordDegradationOnce({
134
+ kind: "test-checkout-identity-unavailable",
135
+ subject: createHash("sha256").update(JSON.stringify(metadata)).digest("hex"),
136
+ reason: "cannot establish automatic test checkout identity",
137
+ metadata
138
+ });
139
+ }
140
+ function foreignGitRoot(testFilePath, cwd) {
141
+ let realCwd;
142
+ let realTarget;
143
+ try {
144
+ realCwd = fs.realpathSync.native(path.resolve(cwd));
145
+ realTarget = fs.realpathSync.native(path.resolve(cwd, toPosix(testFilePath)));
146
+ } catch (error_) {
147
+ const errorCode = filesystemErrorCode(error_);
148
+ if (errorCode === "ENOENT" || errorCode === "ENOTDIR")
149
+ return null;
150
+ recordCheckoutIdentityGap({
151
+ cwd,
152
+ candidate: testFilePath,
153
+ lookup: "filesystem",
154
+ detail: "realpath",
155
+ errorCode
156
+ });
157
+ return null;
158
+ }
159
+ const targetWalk = findNearestMarkerRootDetailed(path.dirname(realTarget), [".git"], {
160
+ markerPredicate: (marker) => isRealGitMarker(marker, true)
161
+ });
162
+ if (targetWalk.kind !== "found") {
163
+ if (targetWalk.kind === "unavailable") {
164
+ recordCheckoutIdentityGap({
165
+ cwd,
166
+ candidate: testFilePath,
167
+ lookup: "target-root",
168
+ detail: "marker-error",
169
+ markerPath: targetWalk.markerPath,
170
+ errorCode: filesystemErrorCode(targetWalk.cause)
171
+ });
172
+ } else if (targetWalk.reason === "depth-limit") {
173
+ recordCheckoutIdentityGap({
174
+ cwd,
175
+ candidate: testFilePath,
176
+ lookup: "target-root",
177
+ detail: targetWalk.reason
178
+ });
179
+ }
180
+ return null;
181
+ }
182
+ const root = targetWalk.root;
183
+ if (!isUnderDir(realCwd, root))
184
+ return root;
185
+ const dispatchWalk = findNearestMarkerRootDetailed(realCwd, [".git"], {
186
+ markerPredicate: (marker) => isRealGitMarker(marker, true)
187
+ });
188
+ if (dispatchWalk.kind !== "found") {
189
+ recordCheckoutIdentityGap({
190
+ cwd,
191
+ candidate: testFilePath,
192
+ lookup: "dispatch-root",
193
+ ...dispatchWalk.kind === "unavailable" ? {
194
+ detail: "marker-error",
195
+ markerPath: dispatchWalk.markerPath,
196
+ errorCode: filesystemErrorCode(dispatchWalk.cause)
197
+ } : { detail: dispatchWalk.reason }
198
+ });
199
+ return null;
200
+ }
201
+ return !pathsEqual(root, dispatchWalk.root) ? root : null;
202
+ }
203
+ function sharesCommonDir(cwd, checkoutRoot) {
204
+ const dispatch = resolveGitCheckout(cwd);
205
+ const excluded = resolveGitCheckout(checkoutRoot);
206
+ return dispatch !== null && excluded !== null && pathsEqual(dispatch.commonDir, excluded.commonDir);
207
+ }
208
+ function isExcludedTestTarget(testFilePath, cwd) {
209
+ const absolute = path.resolve(cwd, toPosix(testFilePath));
210
+ let rel = toPosix(path.relative(cwd, absolute));
211
+ if (rel === "" || rel === ".." || rel.startsWith("../") || path.isAbsolute(rel)) {
212
+ const realTarget = canonicalFailedPath(absolute);
213
+ const realCwd = canonicalFailedPath(cwd);
214
+ if (pathsEqual(realTarget, realCwd) || !isUnderDir(realTarget, realCwd))
215
+ return true;
216
+ rel = toPosix(path.relative(realCwd, realTarget));
217
+ }
218
+ const checkoutRoot = foreignGitRoot(testFilePath, cwd);
219
+ if (checkoutRoot) {
220
+ recordDegradationOnce({
221
+ kind: "test-target-foreign-checkout",
222
+ subject: createHash("sha256").update(JSON.stringify([cwd, testFilePath, checkoutRoot])).digest("hex"),
223
+ reason: "automatic test target belongs to another Git checkout",
224
+ metadata: {
225
+ cwd,
226
+ candidate: testFilePath,
227
+ checkoutRoot,
228
+ sameCommonDir: sharesCommonDir(cwd, checkoutRoot)
229
+ }
230
+ });
231
+ return true;
232
+ }
233
+ return TURN_END_EXCLUDED_TEST_GLOBS.some((glob) => minimatch(rel, glob, { dot: true, nocase: true }));
234
+ }
235
+ var RUNNERS = {
236
+ vitest: {
237
+ kinds: ["jsts"],
238
+ configFiles: ["vitest.config.ts", "vitest.config.js", "vitest.config.mjs"],
239
+ command: "npx",
240
+ binName: "vitest",
241
+ args: (testFile, _cwd) => [
242
+ "vitest",
243
+ "run",
244
+ testFile,
245
+ "--reporter=json",
246
+ "--passWithNoTests"
247
+ ],
248
+ parseJson: true
249
+ },
250
+ jest: {
251
+ kinds: ["jsts"],
252
+ configFiles: [
253
+ "jest.config.ts",
254
+ "jest.config.js",
255
+ "jest.config.json",
256
+ ".jestrc.js"
257
+ ],
258
+ command: "npx",
259
+ binName: "jest",
260
+ args: (testFile, _cwd) => [
261
+ "jest",
262
+ testFile,
263
+ "--json",
264
+ "--passWithNoTests",
265
+ "--forceExit"
266
+ ],
267
+ parseJson: true
268
+ },
269
+ pytest: {
270
+ kinds: ["python"],
271
+ configFiles: ["pytest.ini", "pyproject.toml", "setup.cfg", "tox.ini"],
272
+ // #2879 review round 3, F7: `pyproject.toml` is CONTENT-conditional in
273
+ // detection — the Priority-1 loop accepts it only when it carries
274
+ // `[tool.pytest.ini_options]` — but the cwd seam walks basenames, so
275
+ // handing `configFiles` over verbatim anchored the child on the very
276
+ // file detection had refused. Omitting it here makes the spawn walk
277
+ // stop only on evidence the detector itself would accept; a project
278
+ // whose ONLY pytest config is a real `[tool.pytest.ini_options]`
279
+ // section therefore falls back to the dispatch root, which is exactly
280
+ // where master ran it. (The proper fix is to carry detection's
281
+ // accepted evidence path into the spawn resolution instead of
282
+ // re-deriving it from a basename table — filed as a follow-up.)
283
+ spawnCwdMarkers: ["pytest.ini", "tox.ini", "setup.cfg"],
284
+ command: "python",
285
+ args: (testFile, _cwd) => ["-m", "pytest", testFile, "--tb=short", "-q"],
286
+ parseJson: false
287
+ // pytest JSON requires plugin, use text parsing
288
+ },
289
+ go: {
290
+ kinds: ["go"],
291
+ configFiles: ["go.mod"],
292
+ command: "go",
293
+ args: (testFile, cwd) => {
294
+ const relPath = path.relative(cwd, testFile);
295
+ const pkgDir = path.dirname(relPath);
296
+ return ["test", `-run`, ".", `./${pkgDir === "." ? "." : pkgDir}`];
297
+ },
298
+ parseJson: false
299
+ // Go test output is text-based
300
+ },
301
+ cargo: {
302
+ kinds: ["rust"],
303
+ configFiles: ["Cargo.toml"],
304
+ command: "cargo",
305
+ args: (_testFile, _cwd) => ["test", "--no-fail-fast"],
306
+ parseJson: false
307
+ // cargo test output is text-based
308
+ },
309
+ dotnet: {
310
+ kinds: ["csharp", "fsharp"],
311
+ configFiles: ["*.csproj", "*.sln"],
312
+ command: "dotnet",
313
+ args: (_testFile, _cwd) => ["test", "--no-build"],
314
+ parseJson: false
315
+ },
316
+ gradle: {
317
+ kinds: ["java", "kotlin"],
318
+ // #2870: `settings.gradle.kts` alongside its Groovy sibling — a
319
+ // Kotlin-DSL build whose root carries only the settings script was
320
+ // invisible to detection, the same one-spelling gap the java root
321
+ // markers had.
322
+ configFiles: [
323
+ "build.gradle",
324
+ "build.gradle.kts",
325
+ "settings.gradle",
326
+ "settings.gradle.kts"
327
+ ],
328
+ // #3956: a cwd-relative launcher must carry a separator, or the Windows
329
+ // resolver treats it as a PATH-only name and never looks in the child cwd
330
+ // where the wrapper actually lives. Forward slash is the platform-neutral
331
+ // spelling the resolver accepts on win32 (same #2870 lesson as ./gradlew).
332
+ command: process.platform === "win32" ? "./gradlew.bat" : "./gradlew",
333
+ // The child's cwd must be a directory the wrapper actually lives in.
334
+ spawnCwdMarkers: ["gradlew", "gradlew.bat"],
335
+ args: (_testFile, _cwd) => ["test", "--no-daemon"],
336
+ parseJson: false
337
+ },
338
+ maven: {
339
+ kinds: ["java", "kotlin"],
340
+ configFiles: ["pom.xml"],
341
+ command: "mvn",
342
+ args: (_testFile, _cwd) => ["test", "-q"],
343
+ parseJson: false
344
+ },
345
+ rspec: {
346
+ kinds: ["ruby"],
347
+ configFiles: [".rspec", "spec/spec_helper.rb"],
348
+ command: "bundle",
349
+ // The real binary is "bundle" (the command runs `bundle exec rspec
350
+ // <file>`), NOT "rspec" — without this, binName defaulted to the
351
+ // runner key "rspec" and local/global resolution looked for the wrong
352
+ // binary name (#1098).
353
+ binName: "bundle",
354
+ args: (testFile, _cwd) => ["exec", "rspec", testFile],
355
+ parseJson: false
356
+ },
357
+ minitest: {
358
+ kinds: ["ruby"],
359
+ configFiles: ["Gemfile"],
360
+ command: "ruby",
361
+ args: (testFile, _cwd) => ["-Itest", testFile],
362
+ parseJson: false
363
+ },
364
+ phpunit: {
365
+ kinds: ["php"],
366
+ // phpunit.xml(.dist) is the strong signal; composer.json is checked for
367
+ // a require-dev dependency on phpunit/phpunit (see the special case in
368
+ // detectRunner's Priority-1 loop, mirroring the pytest/pyproject.toml
369
+ // handling above).
370
+ configFiles: ["phpunit.xml", "phpunit.xml.dist", "composer.json"],
371
+ // #2879 review round 3, F7: same shape as pytest's `pyproject.toml`,
372
+ // and this one is load-bearing — phpunit reads `phpunit.xml` from its
373
+ // CWD only, so a child launched in a directory whose `composer.json`
374
+ // carries no `phpunit/phpunit` dependency runs with no bootstrap and
375
+ // no autoloader, and the resulting fatal error reaches the agent as a
376
+ // test failure (measured end to end with a fake phpunit recording its
377
+ // own cwd). `composer.json` is an anchor, never phpunit evidence.
378
+ spawnCwdMarkers: ["phpunit.xml", "phpunit.xml.dist"],
379
+ command: "phpunit",
380
+ args: (testFile, _cwd) => [testFile],
381
+ parseJson: false
382
+ // PHPUnit's default CLI output is text-based
383
+ },
384
+ mix: {
385
+ kinds: ["elixir"],
386
+ configFiles: ["mix.exs"],
387
+ command: "mix",
388
+ args: (testFile, _cwd) => ["test", testFile],
389
+ parseJson: false
390
+ // mix test's default output is text-based
391
+ }
392
+ };
393
+ function stripWrapperArgs(binName, args) {
394
+ if (args[0] === binName)
395
+ return args.slice(1);
396
+ if (args[0] === "-m" && args[1] === binName)
397
+ return args.slice(2);
398
+ return args;
399
+ }
400
+ var MAX_FAILED_TARGETS_PER_RUNNER = 32;
401
+ var MAX_FAILED_TARGET_CHECKS_PER_SELECTION = 8;
402
+ var FAILED_TARGET_DETAIL_CAP_PER_TURN = 8;
403
+ function canonicalFailedPath(filePath) {
404
+ const absolute = path.resolve(filePath);
405
+ try {
406
+ return normalizeMapKey(fs.realpathSync.native(absolute));
407
+ } catch {
408
+ return normalizeMapKey(absolute);
409
+ }
410
+ }
411
+ function canonicalProjectRoot(cwd) {
412
+ const absolute = path.resolve(cwd);
413
+ try {
414
+ return {
415
+ key: normalizeEphemeralMapKey(fs.realpathSync.native(absolute)),
416
+ resolved: true
417
+ };
418
+ } catch {
419
+ return { key: normalizeEphemeralMapKey(absolute), resolved: false };
420
+ }
421
+ }
422
+ var MAX_CANONICAL_ROOT_MEMO_ENTRIES = 512;
423
+ function filesystemErrorCode(error) {
424
+ if (error !== null && typeof error === "object" && "code" in error && typeof error.code === "string") {
425
+ return error.code;
426
+ }
427
+ return void 0;
428
+ }
429
+ var PYTEST_SUMMARY_OUTCOME = "(?:passed|failed|skipped|reruns?|errors?|warnings?|deselected|xfailed|xpassed)";
430
+ var PYTEST_SUMMARY_LINE = new RegExp(`^\\s*(?:=+\\s*)?\\d+\\s+${PYTEST_SUMMARY_OUTCOME}(?:\\s*,\\s*\\d+\\s+${PYTEST_SUMMARY_OUTCOME})*\\s+in\\s+[\\d.]+s(?:\\s*=+)?\\s*$`, "i");
431
+ function parsePytestSummary(output) {
432
+ const normalizedOutput = stripAnsi(output);
433
+ const summaryLine = normalizedOutput.split(/\r?\n/).reverse().find((line) => PYTEST_SUMMARY_LINE.test(line));
434
+ let passed = 0;
435
+ let failed = 0;
436
+ let skipped = 0;
437
+ if (!summaryLine)
438
+ return { passed, failed, skipped };
439
+ const summaryBody = summaryLine.replace(/^=+\s*|\s*=+\s*$/g, "");
440
+ for (const field of summaryBody.split(",")) {
441
+ const [countText, outcome] = field.trim().split(/\s+/, 2);
442
+ const count = Number.parseInt(countText, 10);
443
+ if (!Number.isFinite(count))
444
+ continue;
445
+ switch (outcome) {
446
+ case "passed":
447
+ passed = count;
448
+ break;
449
+ case "failed":
450
+ failed = count;
451
+ break;
452
+ case "skipped":
453
+ skipped = count;
454
+ break;
455
+ }
456
+ }
457
+ const durationMatch = /in\s+([\d.]+)s/.exec(summaryLine);
458
+ const duration = durationMatch ? Math.round(Number.parseFloat(durationMatch[1]) * 1e3) : void 0;
459
+ return { passed, failed, skipped, duration };
460
+ }
461
+ var TestRunnerClient = class {
462
+ log;
463
+ // This is an instance-lifetime memo of RESOLVED spellings only, which leaves
464
+ // two temporal edges. A symlink retargeted mid-session keeps its old
465
+ // resolution until a new client instance — acceptable because round 2's
466
+ // evidence re-validation already handles verdict-level staleness (positive
467
+ // verdicts re-stat their config file). A spelling that did NOT resolve is
468
+ // never memoized (#2077): the fallback key is a guess about a path that does
469
+ // not exist yet, so memoizing it would pin an alias probed before its
470
+ // symlink was created to a stale verdict for the instance's life. Re-probing
471
+ // costs one failing realpath per call, and only for a cwd that does not
472
+ // resolve — a state where `detectRunner` already walks node_modules on every
473
+ // call. Keep the memo bounded so pathological spelling churn cannot grow it
474
+ // without limit.
475
+ canonicalRootMemo = new BoundedFifoMap(MAX_CANONICAL_ROOT_MEMO_ENTRIES);
476
+ availableRunners = new PathKeyedMap(normalizeEphemeralMapKey);
477
+ failedTestsByRunner = /* @__PURE__ */ new Map();
478
+ failedTargetSequence = 0;
479
+ statFailedTarget;
480
+ // Best-effort vitest config `test.include`/`test.exclude` globs, scraped as
481
+ // plain text (never executed) and cached per cwd so the config file is
482
+ // only read/parsed once, not on every edit. `null` means "no config found
483
+ // or it couldn't be parsed in the simple shape we look for" — callers
484
+ // treat that as "no additional signal" and fall back to naming-convention
485
+ // detection only.
486
+ vitestTestGlobsCache = new PathKeyedMap(normalizeEphemeralMapKey);
487
+ constructor(verbose = false, options = {}) {
488
+ this.log = verbose ? createSubsystemLogger("test-runner") : () => {
489
+ };
490
+ this.statFailedTarget = options.statFailedTarget ?? ((filePath) => void fs.statSync(filePath));
491
+ }
492
+ getCanonicalProjectRoot(cwd) {
493
+ const cached = this.canonicalRootMemo.get(cwd);
494
+ if (cached !== void 0)
495
+ return cached;
496
+ const { key, resolved } = canonicalProjectRoot(cwd);
497
+ if (!resolved)
498
+ return key;
499
+ this.canonicalRootMemo.set(cwd, key);
500
+ return key;
501
+ }
502
+ /** #2442 test-only: exercise canonicalRootMemo's bounded eviction directly. */
503
+ _getCanonicalProjectRootForTests(cwd) {
504
+ return this.getCanonicalProjectRoot(cwd);
505
+ }
506
+ getRunnerAvailability(byRunner, runner) {
507
+ const cached = byRunner?.get(runner);
508
+ if (!cached)
509
+ return void 0;
510
+ if (cached.available && cached.evidencePath !== void 0 && !fs.existsSync(cached.evidencePath)) {
511
+ byRunner?.delete(runner);
512
+ return void 0;
513
+ }
514
+ return cached.available;
515
+ }
516
+ /**
517
+ * #2252: only a POSITIVE verdict is memoized. A negative one has no
518
+ * `evidencePath` to re-stat, so `getRunnerAvailability` had no way to tell
519
+ * "still absent" from "a config file just appeared" and served the first
520
+ * miss for the client's whole process lifetime — measured live: probe an
521
+ * empty directory, add `vitest.config.ts`, and the SAME client kept
522
+ * answering "no runner". Same precedent as #2242's alias-canonicalization
523
+ * fix (`clients/test-runner-client.ts`'s `getCanonicalProjectRoot`): drop
524
+ * the memo write on the failure branch rather than adding a TTL or a
525
+ * re-arm signal. The cost is bounded and already paid today — a cache miss
526
+ * re-runs the exact same `configFiles.some(fs.existsSync)` walk this
527
+ * method's caller already does on every FIRST probe of a runner.
528
+ */
529
+ setRunnerAvailability(byRunner, runner, available, evidencePath) {
530
+ if (!available)
531
+ return;
532
+ byRunner.set(runner, { available, evidencePath });
533
+ }
534
+ /**
535
+ * Which runners may be handed `sourceFilePath`, and the directory their
536
+ * config files are probed in (#2870).
537
+ *
538
+ * `eligible === null` means "no file was named" — the session-level
539
+ * caller (`runtime-session.ts`'s summary) asks "what runner does this
540
+ * project have" and has no file to scope to, so every runner stays a
541
+ * candidate and the probe root is the dispatch root, exactly as before.
542
+ *
543
+ * A named file resolves TWO independent facts, and the order matters:
544
+ * - its KIND decides which runners may claim it at all (the gate), and
545
+ * - `resolveLanguageRootForFile` decides WHERE that kind's config files
546
+ * are looked for (the anchor) — the same per-kind, cached,
547
+ * workspace-clamped walk the dispatch runners already resolve their
548
+ * roots with, not a private walker.
549
+ *
550
+ * Returning `null` means no runner can own this file (a kind no entry
551
+ * claims: markdown, yaml, an unknown extension), so no directory is
552
+ * probed at all.
553
+ */
554
+ resolveDetectionScope(cwd, sourceFilePath) {
555
+ if (sourceFilePath === void 0)
556
+ return { root: cwd, eligible: null };
557
+ const absoluteFile = path.resolve(sourceFilePath);
558
+ if (!isUnderDir(absoluteFile, path.resolve(cwd))) {
559
+ return { root: cwd, eligible: null };
560
+ }
561
+ const kind = detectFileKind(absoluteFile);
562
+ if (!kind)
563
+ return null;
564
+ const eligible = new Set(Object.entries(RUNNERS).filter(([, config]) => config.kinds.includes(kind)).map(([name]) => name));
565
+ if (eligible.size === 0)
566
+ return null;
567
+ return {
568
+ root: resolveLanguageRootForFile(absoluteFile, cwd),
569
+ eligible
570
+ };
571
+ }
572
+ /**
573
+ * Check if a test runner is available in the project
574
+ * Detection order:
575
+ * 1. Config files (vitest.config.ts, jest.config.js, etc.)
576
+ * 2. package.json dependencies
577
+ * 3. node_modules presence
578
+ *
579
+ * #2870: when `sourceFilePath` is given, the answer is scoped to that
580
+ * file's kind and resolved language root — see `resolveDetectionScope`.
581
+ */
582
+ detectRunner(dispatchRoot, sourceFilePath) {
583
+ const scope = this.resolveDetectionScope(dispatchRoot, sourceFilePath);
584
+ if (!scope)
585
+ return null;
586
+ const { root, eligible } = scope;
587
+ const anchored = this.probeRunnersAt(root, eligible);
588
+ if (anchored)
589
+ return anchored;
590
+ const dispatch = path.resolve(dispatchRoot);
591
+ if (path.resolve(root) !== dispatch) {
592
+ const atDispatch = this.probeRunnersAt(dispatch, eligible);
593
+ if (atDispatch)
594
+ return atDispatch;
595
+ }
596
+ const isPythonSource = typeof sourceFilePath === "string" && sourceFilePath.endsWith(".py");
597
+ if (!isPythonSource)
598
+ return null;
599
+ try {
600
+ const whichCmd = process.platform === "win32" ? "where" : "which";
601
+ const result = safeSpawn(whichCmd, ["pytest"], {
602
+ timeout: 2e3
603
+ });
604
+ if (result.status === 0) {
605
+ this.log("Detected pytest globally");
606
+ return { runner: "pytest", config: RUNNERS.pytest };
607
+ }
608
+ } catch (err) {
609
+ void err;
610
+ }
611
+ return null;
612
+ }
613
+ /**
614
+ * Priorities 1-4 of the detection ladder, against ONE directory: config
615
+ * files, `package.json` dependencies, a hoisted `node_modules`, and the
616
+ * glob-capable whole-project runners. `eligible` is the kind gate
617
+ * (#2870); `null` means no file was named and every runner is a
618
+ * candidate. Returns `null` when this directory configures no eligible
619
+ * runner, which is what lets `detectRunner` try the dispatch root next.
620
+ */
621
+ probeRunnersAt(cwd, eligible) {
622
+ const rootKey = this.getCanonicalProjectRoot(cwd);
623
+ let byRunner = this.availableRunners.get(rootKey);
624
+ if (!byRunner) {
625
+ byRunner = /* @__PURE__ */ new Map();
626
+ this.availableRunners.set(rootKey, byRunner);
627
+ }
628
+ for (const [name, config] of Object.entries(RUNNERS)) {
629
+ if (eligible && !eligible.has(name))
630
+ continue;
631
+ const cached = this.getRunnerAvailability(byRunner, name);
632
+ if (cached !== void 0) {
633
+ if (cached) {
634
+ return { runner: name, config };
635
+ }
636
+ continue;
637
+ }
638
+ let configEvidencePath;
639
+ const found = config.configFiles.some((cf) => {
640
+ if (name === "pytest" && cf === "pyproject.toml") {
641
+ const pyprojectPath = path.join(cwd, cf);
642
+ if (!fs.existsSync(pyprojectPath))
643
+ return false;
644
+ try {
645
+ const pyproject = fs.readFileSync(pyprojectPath, "utf-8");
646
+ const matches2 = pyproject.includes("[tool.pytest.ini_options]");
647
+ if (matches2)
648
+ configEvidencePath = pyprojectPath;
649
+ return matches2;
650
+ } catch {
651
+ return false;
652
+ }
653
+ }
654
+ if (name === "phpunit" && cf === "composer.json") {
655
+ const composerPath = path.join(cwd, cf);
656
+ if (!fs.existsSync(composerPath))
657
+ return false;
658
+ try {
659
+ const composer = JSON.parse(fs.readFileSync(composerPath, "utf-8"));
660
+ const allDeps = {
661
+ ...composer.require,
662
+ ...composer["require-dev"]
663
+ };
664
+ const matches2 = Boolean(allDeps["phpunit/phpunit"]);
665
+ if (matches2)
666
+ configEvidencePath = composerPath;
667
+ return matches2;
668
+ } catch {
669
+ return false;
670
+ }
671
+ }
672
+ const candidate = path.join(cwd, cf);
673
+ const matches = fs.existsSync(candidate);
674
+ if (matches)
675
+ configEvidencePath = candidate;
676
+ return matches;
677
+ });
678
+ this.setRunnerAvailability(byRunner, name, found, configEvidencePath);
679
+ if (found) {
680
+ this.log(`Detected runner via config: ${name}`);
681
+ return { runner: name, config };
682
+ }
683
+ }
684
+ const packageJsonPath = path.join(cwd, "package.json");
685
+ try {
686
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
687
+ const allDeps = {
688
+ ...pkg.dependencies,
689
+ ...pkg.devDependencies
690
+ };
691
+ if (allDeps.vitest && (!eligible || eligible.has("vitest"))) {
692
+ this.log("Detected vitest in package.json");
693
+ this.setRunnerAvailability(byRunner, "vitest", true, packageJsonPath);
694
+ return { runner: "vitest", config: RUNNERS.vitest };
695
+ }
696
+ if (allDeps.jest && (!eligible || eligible.has("jest"))) {
697
+ this.log("Detected jest in package.json");
698
+ this.setRunnerAvailability(byRunner, "jest", true, packageJsonPath);
699
+ return { runner: "jest", config: RUNNERS.jest };
700
+ }
701
+ if ((allDeps.pytest || allDeps["pytest-cov"]) && (!eligible || eligible.has("pytest"))) {
702
+ this.log("Detected pytest in package.json (unusual)");
703
+ this.setRunnerAvailability(byRunner, "pytest", true, packageJsonPath);
704
+ return { runner: "pytest", config: RUNNERS.pytest };
705
+ }
706
+ } catch (err) {
707
+ void err;
708
+ }
709
+ for (const name of ["vitest", "jest"]) {
710
+ if (eligible && !eligible.has(name))
711
+ continue;
712
+ const hoisted = findHoistedNodeModulesPackage(cwd, name);
713
+ if (hoisted) {
714
+ this.log(`Detected ${name} in node_modules (${hoisted})`);
715
+ return { runner: name, config: RUNNERS[name] };
716
+ }
717
+ }
718
+ for (const name of ["go", "cargo", "dotnet", "gradle", "maven"]) {
719
+ if (eligible && !eligible.has(name))
720
+ continue;
721
+ const config = RUNNERS[name];
722
+ const found = config.configFiles.some((cf) => {
723
+ if (cf.includes("*")) {
724
+ try {
725
+ const files = fs.readdirSync(cwd);
726
+ return files.some((f) => new RegExp(cf.replace(/\*/g, ".*")).test(f));
727
+ } catch {
728
+ return false;
729
+ }
730
+ }
731
+ return fs.existsSync(path.join(cwd, cf));
732
+ });
733
+ if (found) {
734
+ this.log(`Detected ${name} from config file`);
735
+ return { runner: name, config };
736
+ }
737
+ }
738
+ return null;
739
+ }
740
+ /**
741
+ * Depth-bounded breadth-first search under `rootDir` for a pytest-style
742
+ * test file matching `pattern` (exact, e.g. `test_foo.py`) or the
743
+ * looser `test_*<basename>*.py` convention. Used as a last-resort
744
+ * fallback when a Python test suite groups tests by kind
745
+ * (`tests/unit/`, `tests/integration/`) instead of mirroring the
746
+ * source directory layout, so the exact-mirror candidates in
747
+ * `findTestFile` don't match. Bounded by `maxDepth` levels below
748
+ * `rootDir` and skips hidden directories and `__pycache__` — never an
749
+ * unbounded walk of the whole tests tree.
750
+ */
751
+ findPytestMatchRecursive(rootDir, pattern, basename2, maxDepth, acceptCandidate) {
752
+ const queue = [
753
+ { dir: rootDir, depth: 0 }
754
+ ];
755
+ while (queue.length > 0) {
756
+ const next = queue.shift();
757
+ if (!next)
758
+ break;
759
+ const { dir, depth } = next;
760
+ let entries;
761
+ try {
762
+ entries = fs.readdirSync(dir, { withFileTypes: true });
763
+ } catch {
764
+ continue;
765
+ }
766
+ for (const entry of entries) {
767
+ const fullPath = path.join(dir, entry.name);
768
+ if (entry.isFile()) {
769
+ if ((entry.name === pattern || entry.name.startsWith("test_") && entry.name.endsWith(".py") && entry.name.includes(basename2)) && acceptCandidate(fullPath)) {
770
+ return fullPath;
771
+ }
772
+ } else if (entry.isDirectory() && depth < maxDepth) {
773
+ if (entry.name === "__pycache__" || entry.name.startsWith("."))
774
+ continue;
775
+ queue.push({ dir: fullPath, depth: depth + 1 });
776
+ }
777
+ }
778
+ }
779
+ return null;
780
+ }
781
+ /**
782
+ * Path of `dir` relative to `cwd`, using forward slashes, or null if `dir`
783
+ * is not inside `cwd` (e.g. resolves to `..` or an absolute path).
784
+ * Used to compute a mirrored test-tree subdirectory (e.g. `clients` for
785
+ * `clients/knip-client.ts`, so `tests/clients/knip-client.test.ts` is
786
+ * checked alongside the flat `tests/knip-client.test.ts` candidate).
787
+ */
788
+ relativeSourceDir(sourceFilePath, cwd) {
789
+ const dir = path.dirname(sourceFilePath);
790
+ const relDir = path.relative(cwd, path.resolve(cwd, dir));
791
+ if (!relDir || relDir === "." || relDir.startsWith("..") || path.isAbsolute(relDir)) {
792
+ return null;
793
+ }
794
+ return relDir;
795
+ }
796
+ /**
797
+ * Best-effort, text-only scrape of a vitest config's `test.include` /
798
+ * `test.exclude` arrays. This deliberately does NOT execute the config
799
+ * file (that would mean loading arbitrary ESM/TS via Vite's config
800
+ * loader — too heavy for a per-edit hot path). It just looks for a
801
+ * simple `include: [ ... ]` / `exclude: [ ... ]` shape with string
802
+ * literals inside and pulls those out with a regex.
803
+ *
804
+ * Returns `null` (never throws) when there's no vitest config file, it
805
+ * can't be read, or the include/exclude shape isn't a plain array of
806
+ * string literals (e.g. it's built from a function call, spread, or
807
+ * template expression) — anything more dynamic than that is out of
808
+ * scope for this heuristic.
809
+ *
810
+ * Cached per `cwd`, including a `null` result — #2252 F2: a project with
811
+ * no vitest config, or one this heuristic can't scrape, is a COMMON shape
812
+ * (every non-vitest project pays this on every edit otherwise; measured
813
+ * ~1500x — 0.4µs cached vs. 598.7µs re-reading the candidate list every
814
+ * call). The cache entry is revalidated on every read, the same shape
815
+ * `getRunnerAvailability` uses for a positive verdict: bounded
816
+ * `fs.existsSync` checks against the config file the result was derived
817
+ * from (or, when none existed, against the candidate list itself), never
818
+ * a full re-read/re-parse on a cache hit. So a config file appearing (or
819
+ * a broken one being fixed) still converges — only the FULL parse is
820
+ * paid once, not the existence check.
821
+ */
822
+ parseVitestTestGlobs(cwd) {
823
+ const rootKey = this.getCanonicalProjectRoot(cwd);
824
+ const candidates = [...RUNNERS.vitest.configFiles, "vitest.config.mts"];
825
+ const cached = this.vitestTestGlobsCache.get(rootKey);
826
+ if (cached !== void 0) {
827
+ const stillValid = cached.evidencePath !== void 0 ? fs.existsSync(cached.evidencePath) : !candidates.some((cf) => fs.existsSync(path.join(cwd, cf)));
828
+ if (stillValid)
829
+ return cached.result;
830
+ this.vitestTestGlobsCache.delete(rootKey);
831
+ }
832
+ let content = null;
833
+ let foundPath;
834
+ for (const cf of candidates) {
835
+ const candidatePath = path.join(cwd, cf);
836
+ try {
837
+ content = fs.readFileSync(candidatePath, "utf-8");
838
+ foundPath = candidatePath;
839
+ break;
840
+ } catch {
841
+ continue;
842
+ }
843
+ }
844
+ let result = null;
845
+ if (content !== null) {
846
+ const include = this.extractGlobArrayLiteral(content, "include");
847
+ const exclude = this.extractGlobArrayLiteral(content, "exclude");
848
+ if (include || exclude) {
849
+ result = {};
850
+ if (include)
851
+ result.include = include;
852
+ if (exclude)
853
+ result.exclude = exclude;
854
+ }
855
+ }
856
+ this.vitestTestGlobsCache.set(rootKey, { result, evidencePath: foundPath });
857
+ return result;
858
+ }
859
+ /**
860
+ * Extract `<key>: [ 'a', "b", `c` ]` as a plain string array from raw
861
+ * config text. Returns undefined if the key isn't present, or if the
862
+ * array body contains anything besides string literals and commas/
863
+ * whitespace (a function call, spread, variable reference, etc.) —
864
+ * that's a sign the value is dynamic and this best-effort scrape can't
865
+ * safely interpret it.
866
+ */
867
+ extractGlobArrayLiteral(content, key) {
868
+ const arrayMatch = content.match(new RegExp(`\\b${key}\\s*:\\s*\\[([^\\]]*)\\]`));
869
+ if (!arrayMatch)
870
+ return void 0;
871
+ const body = arrayMatch[1];
872
+ const literalPattern = /'([^'\\]*)'|"([^"\\]*)"|`([^`\\]*)`/g;
873
+ const literals = [];
874
+ let lastEnd = 0;
875
+ let match;
876
+ while ((match = literalPattern.exec(body)) !== null) {
877
+ const between = body.slice(lastEnd, match.index).trim();
878
+ if (between !== "" && !/^,$/.test(between))
879
+ return void 0;
880
+ literals.push(match[1] ?? match[2] ?? match[3] ?? "");
881
+ lastEnd = literalPattern.lastIndex;
882
+ }
883
+ const trailing = body.slice(lastEnd).trim();
884
+ if (trailing !== "" && trailing !== ",")
885
+ return void 0;
886
+ return literals.length > 0 ? literals : void 0;
887
+ }
888
+ /**
889
+ * Whether `sourceFilePath` is itself a test file (as opposed to a source
890
+ * file whose *related* test file needs to be discovered).
891
+ *
892
+ * Primary signal: `detectFileRole` (naming convention: `.test.`/`.spec.`
893
+ * basenames, `_test.`/`_spec.` suffix infixes, `*Test(s).<ext>` CamelCase
894
+ * suffixes, `test_`/`spec_` prefixes, `__tests__/`/`tests/`/`spec/`
895
+ * directories — shared with the rest of the codebase, not a second
896
+ * parallel detector).
897
+ *
898
+ * Secondary signal (vitest only): the project's own `test.include` /
899
+ * `test.exclude` globs, best-effort scraped by `parseVitestTestGlobs`.
900
+ * This can correct the naming-convention answer in both directions —
901
+ * an `exclude` glob can rule out a path that looks like a test by name,
902
+ * and an `include` glob can catch a project that puts tests somewhere
903
+ * unconventional. When no config is found or it can't be parsed, this
904
+ * is a no-op and behavior is unchanged.
905
+ *
906
+ * #628: a positive `include` override is only trusted when the glob is a
907
+ * *narrow* test signal (see `isNarrowTestGlob`) — a bare "any file with
908
+ * this extension" include (e.g. `src/**\/*.ts`) is common in real vitest
909
+ * configs and matches ordinary source files, so treating any match as
910
+ * "this is a test" produced vacuous `0p/0f` self-runs on plain source
911
+ * files (background-review.ts, index.ts, …). The `exclude` direction is
912
+ * left as a plain match: over-excluding only causes discovery to run on a
913
+ * file that's actually a test (falls back to `findTestFile`, not a false
914
+ * "self" positive), which is the safe failure mode.
915
+ */
916
+ isTestFile(sourceFilePath, cwd, runner) {
917
+ let result = detectFileRole(sourceFilePath) === "test";
918
+ if (runner === "vitest") {
919
+ const globs = this.parseVitestTestGlobs(cwd);
920
+ if (globs) {
921
+ const rel = path.relative(cwd, path.resolve(cwd, sourceFilePath)).replace(/\\/g, "/");
922
+ const matches = (globs_, filter) => !!globs_?.some((g) => (!filter || filter(g)) && minimatch(rel, g, { dot: true }));
923
+ if (matches(globs.exclude)) {
924
+ result = false;
925
+ } else if (!result && matches(globs.include, (g) => this.isNarrowTestGlob(g))) {
926
+ result = true;
927
+ }
928
+ }
929
+ }
930
+ return result;
931
+ }
932
+ /**
933
+ * Whether an `include` glob is a specific enough signal to override a
934
+ * plain "this is source, not a test" naming-convention verdict (#628).
935
+ *
936
+ * Trusted when either:
937
+ * - a literal (non-wildcard) path segment before the first wildcard
938
+ * names a conventional test location (`tests/`, `test/`, `spec/`,
939
+ * `specs/`, `__tests__/`) — the real case this override exists for:
940
+ * a project whose test files live in such a directory without a
941
+ * `.test.`/`.spec.` name (e.g. `tests/**\/*.ts`).
942
+ * - the static suffix after the last wildcard encodes more than the
943
+ * bare language extension (e.g. `.check.ts`, `.flow.ts`) — an explicit
944
+ * project-specific naming convention, not "any file with this
945
+ * extension" (e.g. `**\/*.check.ts`).
946
+ *
947
+ * Rejected for a bare extension glob with no test-ish directory (e.g.
948
+ * `src/**\/*.ts`, `**\/*.ts`) — that shape matches every source file in
949
+ * the tree and is exactly what produced vacuous self-runs in practice.
950
+ */
951
+ isNarrowTestGlob(glob) {
952
+ const testDirPattern = /^(tests?|specs?|__tests__)$/i;
953
+ for (const segment of glob.split("/")) {
954
+ if (segment.includes("*") || segment.includes("?"))
955
+ break;
956
+ if (testDirPattern.test(segment))
957
+ return true;
958
+ }
959
+ const lastWildcard = Math.max(glob.lastIndexOf("*"), glob.lastIndexOf("?"));
960
+ const suffix = lastWildcard >= 0 ? glob.slice(lastWildcard + 1) : glob;
961
+ const dotSegments = suffix.split(".").filter(Boolean);
962
+ return dotSegments.length >= 2;
963
+ }
964
+ /**
965
+ * Find test file for a given source file
966
+ * Returns the test file path if it exists, null otherwise.
967
+ * Automatic selection supplies eligibility before each first-match return;
968
+ * ordinary discovery, existence checks, and suggestions remain unfiltered.
969
+ */
970
+ findTestFile(sourceFilePath, cwd, runnerOverride, acceptCandidate = () => true) {
971
+ const ext = path.extname(sourceFilePath);
972
+ const basename2 = path.basename(sourceFilePath, ext);
973
+ const dir = path.dirname(sourceFilePath);
974
+ const patterns = SOURCE_TO_TEST_PATTERNS.find((p) => p.ext === ext);
975
+ if (!patterns)
976
+ return null;
977
+ const detected = runnerOverride ? { runner: runnerOverride, config: RUNNERS[runnerOverride] } : this.detectRunner(cwd, sourceFilePath);
978
+ if (!detected)
979
+ return null;
980
+ const relDir = this.relativeSourceDir(sourceFilePath, cwd);
981
+ for (let i = 0; i < patterns.testExts.length; i++) {
982
+ const testExt = patterns.testExts[i];
983
+ const testDir = patterns.dirs[i];
984
+ if (testExt.includes("*")) {
985
+ const pattern = testExt.replace(/\*/g, basename2);
986
+ const searchDirs = testDir === "." ? [dir] : relDir ? [path.join(cwd, testDir, relDir), path.join(cwd, testDir)] : [path.join(cwd, testDir)];
987
+ for (const searchDir of searchDirs) {
988
+ let files;
989
+ try {
990
+ files = fs.readdirSync(searchDir);
991
+ } catch (err) {
992
+ void err;
993
+ continue;
994
+ }
995
+ const match = files.find((f) => (f === pattern || f.startsWith("test_") && f.endsWith(".py") && f.includes(basename2)) && acceptCandidate(path.join(searchDir, f)));
996
+ if (match) {
997
+ const testPath = path.join(searchDir, match);
998
+ this.log(`Found test file: ${testPath}`);
999
+ return { testFile: testPath, runner: detected.runner };
1000
+ }
1001
+ }
1002
+ if (testDir !== ".") {
1003
+ const recursiveMatch = this.findPytestMatchRecursive(path.join(cwd, testDir), pattern, basename2, MAX_PYTEST_RECURSE_DEPTH, acceptCandidate);
1004
+ if (recursiveMatch) {
1005
+ this.log(`Found test file (recursive): ${recursiveMatch}`);
1006
+ return { testFile: recursiveMatch, runner: detected.runner };
1007
+ }
1008
+ }
1009
+ } else {
1010
+ const testFilename = basename2 + testExt;
1011
+ const searchPaths = [
1012
+ path.join(dir, testFilename),
1013
+ // same directory
1014
+ path.join(dir, "__tests__", testFilename),
1015
+ // __tests__ subdirectory
1016
+ ...relDir ? [
1017
+ path.join(cwd, "tests", relDir, testFilename),
1018
+ // mirrored tests/<subdir>/
1019
+ path.join(cwd, "__tests__", relDir, testFilename)
1020
+ // mirrored __tests__/<subdir>/
1021
+ ] : [],
1022
+ path.join(cwd, "tests", testFilename),
1023
+ // top-level tests/
1024
+ path.join(cwd, "__tests__", testFilename),
1025
+ // top-level __tests__/
1026
+ // PHP/Elixir-style source-root mirroring (e.g. src/Foo/Bar.php ->
1027
+ // tests/Foo/BarTest.php, lib/accounts/user.ex ->
1028
+ // test/accounts/user_test.exs): strips a conventional source-root
1029
+ // segment and mirrors under this pattern's OWN configured test
1030
+ // root (testDir), not the hardcoded "tests"/"__tests__" above —
1031
+ // ExUnit's root is "test" (singular), which those don't cover.
1032
+ ...this.sourceRootMirroredCandidates(dir, cwd, testDir, testFilename)
1033
+ ];
1034
+ for (const testPath of searchPaths) {
1035
+ if (fs.existsSync(testPath) && acceptCandidate(testPath)) {
1036
+ this.log(`Found test file: ${testPath}`);
1037
+ return { testFile: testPath, runner: detected.runner };
1038
+ }
1039
+ }
1040
+ }
1041
+ }
1042
+ const importMatch = this.findTestFileByImport(sourceFilePath, cwd, acceptCandidate);
1043
+ if (importMatch) {
1044
+ return { testFile: importMatch, runner: detected.runner };
1045
+ }
1046
+ return null;
1047
+ }
1048
+ /**
1049
+ * Select the most useful test target for this edit.
1050
+ *
1051
+ * Strategy:
1052
+ * 1) If there are known failing tests, rerun those first (fast feedback loop).
1053
+ * 2) Otherwise run related tests for the edited file.
1054
+ */
1055
+ getTestRunTarget(sourceFilePath, cwd, turnIndex) {
1056
+ const detected = this.detectRunner(cwd, sourceFilePath);
1057
+ if (!detected)
1058
+ return null;
1059
+ const failedSet = this.getFailedTargets(cwd, detected.runner);
1060
+ const selfIsTest = this.isTestFile(sourceFilePath, cwd, detected.runner);
1061
+ const related = selfIsTest ? null : this.findTestFile(sourceFilePath, cwd, detected.runner, (candidate) => {
1062
+ const checkoutRoot = foreignGitRoot(candidate, cwd);
1063
+ if (!checkoutRoot)
1064
+ return true;
1065
+ recordDegradationOnce({
1066
+ kind: "test-discovery-foreign-checkout",
1067
+ subject: createHash("sha256").update(JSON.stringify([cwd, candidate, checkoutRoot])).digest("hex"),
1068
+ reason: "automatic test candidate belongs to another Git checkout",
1069
+ metadata: { cwd, candidate, checkoutRoot, runner: detected.runner }
1070
+ });
1071
+ return false;
1072
+ });
1073
+ if (failedSet && failedSet.size > 0) {
1074
+ const failedFirst = this.retireIneligibleFailedTargets({
1075
+ cwd,
1076
+ runner: detected.runner,
1077
+ failedTargets: failedSet,
1078
+ relatedAbs: related ? path.resolve(related.testFile) : void 0,
1079
+ selfAbs: selfIsTest ? path.resolve(sourceFilePath) : void 0,
1080
+ turnIndex
1081
+ });
1082
+ if (failedFirst) {
1083
+ return {
1084
+ testFile: failedFirst,
1085
+ runner: detected.runner,
1086
+ config: detected.config,
1087
+ strategy: "failed-first"
1088
+ };
1089
+ }
1090
+ }
1091
+ if (selfIsTest) {
1092
+ return {
1093
+ testFile: path.resolve(sourceFilePath),
1094
+ runner: detected.runner,
1095
+ config: detected.config,
1096
+ strategy: "self"
1097
+ };
1098
+ }
1099
+ if (!related)
1100
+ return null;
1101
+ return {
1102
+ testFile: path.resolve(related.testFile),
1103
+ runner: detected.runner,
1104
+ config: detected.config,
1105
+ strategy: "related"
1106
+ };
1107
+ }
1108
+ async runTestFileAsync(testFile, cwd, runnerOrRequest, legacyConfig) {
1109
+ const absoluteTestFile = path.resolve(testFile);
1110
+ let request;
1111
+ if (typeof runnerOrRequest === "string") {
1112
+ if (!legacyConfig) {
1113
+ return this.emptyResult(absoluteTestFile, "", runnerOrRequest, "Runner configuration missing");
1114
+ }
1115
+ request = { runner: runnerOrRequest, config: legacyConfig };
1116
+ } else {
1117
+ request = runnerOrRequest;
1118
+ }
1119
+ const { runner, config, turnIndex, signal, displayRoot, requireOwnInstall } = request;
1120
+ if (!fs.existsSync(absoluteTestFile)) {
1121
+ return this.emptyResult(absoluteTestFile, "", runner, "Test file not found");
1122
+ }
1123
+ try {
1124
+ const spawnCwd = resolveToolCwd("runner", runner, absoluteTestFile, {
1125
+ cwd: path.resolve(cwd),
1126
+ rootMarkers: config.spawnCwdMarkers ?? config.configFiles
1127
+ }).cwd;
1128
+ const { command, args, env, ownInstall } = await this.resolveExec(runner, config, absoluteTestFile, cwd, spawnCwd, requireOwnInstall);
1129
+ if (requireOwnInstall && !ownInstall) {
1130
+ return {
1131
+ ...this.emptyResult(absoluteTestFile, "", runner),
1132
+ notRun: "no-runner-install"
1133
+ };
1134
+ }
1135
+ this.log(`Running (async): ${command} ${args.join(" ")} (cwd ${spawnCwd})`);
1136
+ const result = await safeSpawnAsync(command, args, {
1137
+ cwd: spawnCwd,
1138
+ timeout: 6e4,
1139
+ env,
1140
+ // #2522 R2 F1. `safeSpawnAsync` resolves `options.signal ?? ambient`,
1141
+ // so an absent batch signal keeps the pre-#2522 ambient behaviour.
1142
+ signal
1143
+ });
1144
+ const stdout = result.stdout || "";
1145
+ const stderr = result.stderr || "";
1146
+ if (result.error) {
1147
+ this.log(`Runner error: ${result.error.message}`);
1148
+ return this.emptyResult(absoluteTestFile, "", runner, `Runner error: ${result.error.message}`);
1149
+ }
1150
+ let parsed;
1151
+ switch (runner) {
1152
+ case "vitest":
1153
+ parsed = this.parseVitestOutput(stdout, stderr, absoluteTestFile, displayRoot ?? cwd, runner);
1154
+ break;
1155
+ case "jest":
1156
+ parsed = this.parseJestOutput(stdout, stderr, absoluteTestFile, displayRoot ?? cwd, runner);
1157
+ break;
1158
+ case "pytest":
1159
+ parsed = this.parsePytestOutput(stdout, stderr, result.status ?? 0, absoluteTestFile, cwd, runner, displayRoot ?? cwd, spawnCwd);
1160
+ break;
1161
+ case "phpunit":
1162
+ parsed = this.parsePhpunitOutput(stdout, stderr, result.status ?? 0, absoluteTestFile, runner, displayRoot ?? cwd, cwd, spawnCwd);
1163
+ break;
1164
+ case "mix":
1165
+ parsed = this.parseMixTestOutput(stdout, stderr, result.status ?? 0, absoluteTestFile, runner, displayRoot ?? cwd, cwd, spawnCwd);
1166
+ break;
1167
+ default:
1168
+ parsed = this.parseGenericRunnerOutput(stdout, stderr, result.status ?? 0, absoluteTestFile, runner, displayRoot ?? cwd, cwd, spawnCwd);
1169
+ break;
1170
+ }
1171
+ this.recordResult({
1172
+ cwd,
1173
+ runner,
1174
+ testFile: absoluteTestFile,
1175
+ result: parsed,
1176
+ turnIndex
1177
+ });
1178
+ return parsed;
1179
+ } catch (err) {
1180
+ this.log(`Run error: ${err.message}`);
1181
+ return this.emptyResult(absoluteTestFile, "", runner, err.message);
1182
+ }
1183
+ }
1184
+ getFailedTargets(cwd, runner, create = false) {
1185
+ let roots = this.failedTestsByRunner.get(runner);
1186
+ if (!roots && create) {
1187
+ roots = new PathKeyedMap(canonicalFailedPath);
1188
+ this.failedTestsByRunner.set(runner, roots);
1189
+ }
1190
+ if (!roots)
1191
+ return void 0;
1192
+ const root = canonicalFailedPath(cwd);
1193
+ let targets = roots.get(root);
1194
+ if (!targets && create) {
1195
+ targets = new PathKeyedMap(canonicalFailedPath);
1196
+ roots.set(root, targets);
1197
+ }
1198
+ return targets;
1199
+ }
1200
+ deleteFailedRoot(cwd, runner) {
1201
+ const roots = this.failedTestsByRunner.get(runner);
1202
+ if (!roots)
1203
+ return;
1204
+ roots.delete(canonicalFailedPath(cwd));
1205
+ if (roots.size === 0)
1206
+ this.failedTestsByRunner.delete(runner);
1207
+ }
1208
+ failedTargetCount(runner) {
1209
+ const roots = this.failedTestsByRunner.get(runner);
1210
+ if (!roots)
1211
+ return 0;
1212
+ let count = 0;
1213
+ for (const targets of roots.values())
1214
+ count += targets.size;
1215
+ return count;
1216
+ }
1217
+ evictOldestFailedTarget(runner) {
1218
+ const roots = this.failedTestsByRunner.get(runner);
1219
+ if (!roots)
1220
+ return void 0;
1221
+ let oldest;
1222
+ for (const [root, targets2] of roots) {
1223
+ for (const [identity, entry] of targets2) {
1224
+ if (!oldest || entry.sequence < oldest.entry.sequence) {
1225
+ oldest = { root, identity, entry };
1226
+ }
1227
+ }
1228
+ }
1229
+ if (!oldest)
1230
+ return void 0;
1231
+ const targets = roots.get(oldest.root);
1232
+ targets?.delete(oldest.identity);
1233
+ if (targets?.size === 0)
1234
+ roots.delete(oldest.root);
1235
+ if (roots.size === 0)
1236
+ this.failedTestsByRunner.delete(runner);
1237
+ return oldest.entry.displayPath;
1238
+ }
1239
+ classifyFailedTarget(candidate) {
1240
+ try {
1241
+ this.statFailedTarget(candidate);
1242
+ return { status: "present" };
1243
+ } catch (error) {
1244
+ const errorCode = filesystemErrorCode(error);
1245
+ if (errorCode === "ENOENT" || errorCode === "ENOTDIR") {
1246
+ return { status: "missing", errorCode };
1247
+ }
1248
+ return { status: "indeterminate", errorCode };
1249
+ }
1250
+ }
1251
+ recordFailedTargetState(record) {
1252
+ const { outcome, runner, candidate, errorCode, turnIndex } = record;
1253
+ const boundedTarget = candidate.length <= LEDGER_FIELD_MAX ? candidate : `\u2026${candidate.slice(1 - LEDGER_FIELD_MAX)}`;
1254
+ const targetIdentity = createHash("sha256").update(runner).update("\0").update(candidate).digest("hex");
1255
+ const identity = `${outcome}:${targetIdentity}`;
1256
+ const payload = {
1257
+ durationMs: 0,
1258
+ filePath: boundedTarget,
1259
+ metadata: {
1260
+ outcome,
1261
+ runner,
1262
+ errorCode: errorCode ?? "unknown"
1263
+ }
1264
+ };
1265
+ const options = {
1266
+ ledgerKind: "test-runner-failed-target-state",
1267
+ risingEdgePer: "identity",
1268
+ reason: `${outcome}: ${boundedTarget}`
1269
+ };
1270
+ emitBounded("test_runner_failed_target_state", identity, payload, turnIndex === void 0 ? options : {
1271
+ ...options,
1272
+ capPerTurn: {
1273
+ limit: FAILED_TARGET_DETAIL_CAP_PER_TURN,
1274
+ turnIndex
1275
+ }
1276
+ });
1277
+ }
1278
+ /**
1279
+ * Retire confirmed-missing or foreign-checkout failed-first entries, then
1280
+ * return one usable target. A bounded prefix is checked; remaining candidates
1281
+ * carry over to the next selection instead of adding unbounded synchronous
1282
+ * filesystem work to turn_end. Related/self targets keep priority.
1283
+ */
1284
+ retireIneligibleFailedTargets(selection) {
1285
+ const { cwd, runner, failedTargets, relatedAbs, selfAbs, turnIndex } = selection;
1286
+ let checked = 0;
1287
+ const inspected = /* @__PURE__ */ new Set();
1288
+ const inspect = (identity, entry) => {
1289
+ if (checked >= MAX_FAILED_TARGET_CHECKS_PER_SELECTION)
1290
+ return void 0;
1291
+ checked += 1;
1292
+ inspected.add(identity);
1293
+ const candidate = entry.displayPath;
1294
+ if (foreignGitRoot(candidate, cwd)) {
1295
+ failedTargets.delete(identity);
1296
+ this.recordFailedTargetState({
1297
+ outcome: "retired-nested-checkout",
1298
+ runner,
1299
+ candidate,
1300
+ turnIndex
1301
+ });
1302
+ return void 0;
1303
+ }
1304
+ const verdict = this.classifyFailedTarget(candidate);
1305
+ if (verdict.status === "missing") {
1306
+ failedTargets.delete(identity);
1307
+ this.recordFailedTargetState({
1308
+ outcome: "retired-missing",
1309
+ runner,
1310
+ candidate,
1311
+ errorCode: verdict.errorCode,
1312
+ turnIndex
1313
+ });
1314
+ return void 0;
1315
+ }
1316
+ if (verdict.status === "indeterminate") {
1317
+ this.recordFailedTargetState({
1318
+ outcome: "retained-indeterminate",
1319
+ runner,
1320
+ candidate,
1321
+ errorCode: verdict.errorCode,
1322
+ turnIndex
1323
+ });
1324
+ }
1325
+ return candidate;
1326
+ };
1327
+ for (const preferred of [relatedAbs, selfAbs]) {
1328
+ if (!preferred)
1329
+ continue;
1330
+ const identity = canonicalFailedPath(preferred);
1331
+ const entry = failedTargets.get(identity);
1332
+ if (!entry)
1333
+ continue;
1334
+ const selected = inspect(identity, entry);
1335
+ if (selected)
1336
+ return selected;
1337
+ }
1338
+ for (const [identity, entry] of failedTargets) {
1339
+ if (inspected.has(identity))
1340
+ continue;
1341
+ if (checked >= MAX_FAILED_TARGET_CHECKS_PER_SELECTION)
1342
+ break;
1343
+ const selected = inspect(identity, entry);
1344
+ if (selected)
1345
+ return selected;
1346
+ }
1347
+ if (failedTargets.size === 0)
1348
+ this.deleteFailedRoot(cwd, runner);
1349
+ return void 0;
1350
+ }
1351
+ /**
1352
+ * Check if a source file has corresponding tests (without running them)
1353
+ */
1354
+ hasTestFile(sourceFilePath, cwd) {
1355
+ return this.findTestFile(sourceFilePath, cwd) !== null;
1356
+ }
1357
+ /**
1358
+ * Suggest test files for a list of source files.
1359
+ * Returns deduplicated test file paths with their corresponding source file.
1360
+ */
1361
+ suggestTestFiles(sourceFiles, cwd) {
1362
+ const seen = /* @__PURE__ */ new Set();
1363
+ const results = [];
1364
+ for (const sourceFile of sourceFiles) {
1365
+ const found = this.findTestFile(sourceFile, cwd);
1366
+ if (!found)
1367
+ continue;
1368
+ const abs = path.resolve(found.testFile);
1369
+ if (seen.has(abs))
1370
+ continue;
1371
+ seen.add(abs);
1372
+ results.push({ testFile: abs, sourceFile, runner: found.runner });
1373
+ }
1374
+ return results;
1375
+ }
1376
+ // --- Shared JSON test output parser (Vitest + Jest share the same structure) ---
1377
+ parseJsonTestOutput(stdout, stderr, testFile, cwd, runner) {
1378
+ try {
1379
+ const json = JSON.parse(stdout);
1380
+ const failures = [];
1381
+ for (const suite of json.testResults || []) {
1382
+ if (suite.status === "failed" && suite.assertionResults) {
1383
+ for (const test of suite.assertionResults) {
1384
+ if (test.status === "failed") {
1385
+ failures.push({
1386
+ name: test.title,
1387
+ message: test.failureMessages?.[0] || suite.message || "Test failed",
1388
+ location: test.location ? `${path.relative(cwd, testFile)}:${test.location.line}` : void 0,
1389
+ stack: this.truncateStack(test.failureMessages?.join("\n"))
1390
+ });
1391
+ }
1392
+ }
1393
+ }
1394
+ }
1395
+ return {
1396
+ file: testFile,
1397
+ sourceFile: "",
1398
+ runner,
1399
+ passed: json.numPassedTests || 0,
1400
+ failed: json.numFailedTests || 0,
1401
+ // #1452: `numSkippedTests` is absent from both reporters' JSON, so
1402
+ // this read was always 0. `numPendingTests` is where a `test.skip`
1403
+ // actually lands; `numTodoTests` is counted with it because the
1404
+ // text parsers (pytest `N skipped`, mix `N excluded` + `N skipped`)
1405
+ // also fold every not-run test into one `skipped` figure.
1406
+ // `??` would accept a present 0, so a reporter that emits
1407
+ // `numSkippedTests: 0` beside a real `numPendingTests` would
1408
+ // reproduce the very defect this removes. Take the larger reading.
1409
+ skipped: Math.max(json.numSkippedTests ?? 0, (json.numPendingTests || 0) + (json.numTodoTests || 0)),
1410
+ failures,
1411
+ duration: this.jsonRunDurationMs(json.testResults)
1412
+ };
1413
+ } catch (err) {
1414
+ void err;
1415
+ const failed = stdout.includes("FAIL") || stderr.includes("FAIL");
1416
+ return this.emptyResult(testFile, "", runner, failed ? "Tests failed (could not parse output)" : void 0);
1417
+ }
1418
+ }
1419
+ /**
1420
+ * #1452: real run duration in ms from a vitest/jest `--json` payload.
1421
+ *
1422
+ * NOT `testResults[].perfStats`. That field exists on jest's INTERNAL
1423
+ * `TestResult`, but the JSON reporter's `formatTestResults` projects it to
1424
+ * per-suite `startTime`/`endTime` and drops it — measured absent from both
1425
+ * vitest 4.1.10 and jest 30.4.2 output, so reading it would have left this
1426
+ * at 0. The per-suite epoch pair is what both reporters actually emit.
1427
+ *
1428
+ * Wall-clock SPAN across suites (max end - min start), not a sum: suites in
1429
+ * one payload may have run in parallel workers, and summing would report
1430
+ * more elapsed time than the run took. With the single suite pi-lens
1431
+ * actually produces (one test file per invocation) the two agree.
1432
+ *
1433
+ * The span excludes the runner's own startup: the top-level `startTime` is
1434
+ * ~330ms earlier than the first suite's on this repo. What the per-suite
1435
+ * pair then measures is NOT the same quantity across runners. On vitest it
1436
+ * tracks test time closely (135ms span against 134ms of summed assertions),
1437
+ * but jest stamps a suite's `startTime` before transform and module load,
1438
+ * so the same fields give 5595ms against 128ms of assertions. Both are
1439
+ * honest suite wall clock; neither is comparable to the other, and only the
1440
+ * vitest figure is close to what pytest's `in 0.05s` or ExUnit's
1441
+ * `Finished in 0.05 seconds` report.
1442
+ *
1443
+ * Falls back to the summed per-assertion `duration` when a reporter omits
1444
+ * the suite pair. Never returns a negative or non-finite value — a garbled
1445
+ * payload must degrade to "unmeasured", not to a wrong number.
1446
+ *
1447
+ * #1479: that degradation is now literal. This used to return 0 for a
1448
+ * payload it could not read, which is the figure a sub-millisecond suite
1449
+ * also produces, so the caller could not tell them apart. It returns
1450
+ * `undefined` instead. A readable pair whose span is 0 still returns 0,
1451
+ * because that is a measurement.
1452
+ */
1453
+ jsonRunDurationMs(suites) {
1454
+ let minStart = Number.POSITIVE_INFINITY;
1455
+ let maxEnd = Number.NEGATIVE_INFINITY;
1456
+ let assertionTotal = 0;
1457
+ for (const suite of suites || []) {
1458
+ if (typeof suite.startTime === "number" && Number.isFinite(suite.startTime) && typeof suite.endTime === "number" && Number.isFinite(suite.endTime)) {
1459
+ minStart = Math.min(minStart, suite.startTime);
1460
+ maxEnd = Math.max(maxEnd, suite.endTime);
1461
+ }
1462
+ for (const assertion of suite.assertionResults || []) {
1463
+ if (typeof assertion.duration === "number" && Number.isFinite(assertion.duration) && assertion.duration > 0) {
1464
+ assertionTotal += assertion.duration;
1465
+ }
1466
+ }
1467
+ }
1468
+ const span = maxEnd - minStart;
1469
+ if (Number.isFinite(span) && span > 0)
1470
+ return Math.round(span);
1471
+ if (assertionTotal > 0)
1472
+ return Math.round(assertionTotal);
1473
+ if (Number.isFinite(span) && span === 0)
1474
+ return 0;
1475
+ return void 0;
1476
+ }
1477
+ // --- Vitest Parser ---
1478
+ parseVitestOutput(stdout, stderr, testFile, cwd, runner) {
1479
+ return this.parseJsonTestOutput(stdout, stderr, testFile, cwd, runner);
1480
+ }
1481
+ // --- Jest Parser ---
1482
+ parseJestOutput(stdout, stderr, testFile, cwd, runner) {
1483
+ return this.parseJsonTestOutput(stdout, stderr, testFile, cwd, runner);
1484
+ }
1485
+ // --- Pytest Parser (text-based, no JSON dependency) ---
1486
+ /**
1487
+ * #3871: a text runner prints a path relative to the directory it ran in
1488
+ * (`spawnCwd`, first in `bases`) or to the dispatch root. The first base
1489
+ * that holds the file wins; a path that resolves nowhere stays as printed,
1490
+ * and an absent capture is no location.
1491
+ */
1492
+ renderTextLocation(displayRoot, bases, file, line) {
1493
+ if (file === void 0 || line === void 0)
1494
+ return void 0;
1495
+ for (const base of bases) {
1496
+ const resolved = path.resolve(base, file);
1497
+ if (fs.existsSync(resolved)) {
1498
+ return `${toPosix(path.relative(displayRoot, resolved))}:${line}`;
1499
+ }
1500
+ }
1501
+ return `${file}:${line}`;
1502
+ }
1503
+ parsePytestOutput(stdout, stderr, exitCode, testFile, cwd, runner, displayRoot = cwd, spawnCwd = cwd) {
1504
+ const failures = [];
1505
+ const output = `${stdout}
1506
+ ${stderr}`;
1507
+ const { passed, failed, skipped, duration } = parsePytestSummary(output);
1508
+ const failureRegex = /FAILED\s+(\S+::\S+)\s*-\s*(.+?)(?:\n|$)/g;
1509
+ let match;
1510
+ while ((match = failureRegex.exec(output)) !== null) {
1511
+ const [file, ...testParts] = match[1].split("::");
1512
+ const location = this.renderTextLocation(displayRoot, [spawnCwd, cwd], file, testParts.join("::"));
1513
+ failures.push({
1514
+ name: match[1],
1515
+ message: match[2].trim().slice(0, 500),
1516
+ ...location === void 0 ? {} : { location }
1517
+ });
1518
+ }
1519
+ const tracebackRegex = /(?<!_)_{10,}[^\S\n]*\n\s*(\w+Error:\s*.+?)(?:\n|$)/gs;
1520
+ while ((match = tracebackRegex.exec(output)) !== null) {
1521
+ if (failures.length > 0 && !failures[failures.length - 1].stack) {
1522
+ failures[failures.length - 1].stack = match[1].trim().slice(0, 1e3);
1523
+ }
1524
+ }
1525
+ return {
1526
+ file: testFile,
1527
+ sourceFile: "",
1528
+ runner,
1529
+ passed,
1530
+ failed,
1531
+ skipped,
1532
+ failures,
1533
+ duration,
1534
+ error: exitCode === 4 ? "Pytest configuration error" : exitCode === 2 ? "Pytest interrupted" : void 0
1535
+ };
1536
+ }
1537
+ // --- PHPUnit Parser (text-based, default CLI output) ---
1538
+ parsePhpunitOutput(stdout, stderr, exitCode, testFile, runner, displayRoot = path.dirname(testFile), cwd = path.dirname(testFile), spawnCwd = cwd) {
1539
+ const output = `${stdout}
1540
+ ${stderr}`;
1541
+ let passed = 0;
1542
+ let failed = 0;
1543
+ let skipped = 0;
1544
+ const okMatch = output.match(/OK\s*\((\d+)\s+tests?,\s*\d+\s+assertions?\)/i);
1545
+ if (okMatch) {
1546
+ passed = Number.parseInt(okMatch[1], 10);
1547
+ } else {
1548
+ const testsMatch = output.match(/Tests:\s*(\d+)/i);
1549
+ const failuresMatch = output.match(/Failures:\s*(\d+)/i);
1550
+ const errorsMatch = output.match(/Errors:\s*(\d+)/i);
1551
+ const skippedMatch = output.match(/Skipped:\s*(\d+)/i);
1552
+ const total = testsMatch ? Number.parseInt(testsMatch[1], 10) : 0;
1553
+ const failures2 = failuresMatch ? Number.parseInt(failuresMatch[1], 10) : 0;
1554
+ const errors = errorsMatch ? Number.parseInt(errorsMatch[1], 10) : 0;
1555
+ skipped = skippedMatch ? Number.parseInt(skippedMatch[1], 10) : 0;
1556
+ failed = failures2 + errors;
1557
+ passed = Math.max(0, total - failed - skipped);
1558
+ }
1559
+ const failures = [];
1560
+ const failureRegex = /^\d+\)\s+(\S+)/gm;
1561
+ let match;
1562
+ while ((match = failureRegex.exec(output)) !== null) {
1563
+ const afterFailure = output.slice(match.index + match[0].length);
1564
+ const nextFailure = afterFailure.search(/(?:^|\n)[^\S\n]*\d+\)\s/);
1565
+ const failureBlock = nextFailure === -1 ? afterFailure : afterFailure.slice(0, nextFailure);
1566
+ const locationMatch = /(?:^|\n)[^\S\n]*([^\s:]+\.php):(\d+)/.exec(failureBlock);
1567
+ const location = this.renderTextLocation(displayRoot, [spawnCwd, cwd], locationMatch?.[1], locationMatch?.[2]);
1568
+ failures.push({
1569
+ name: match[1],
1570
+ message: match[1],
1571
+ ...location === void 0 ? {} : { location }
1572
+ });
1573
+ }
1574
+ let duration;
1575
+ const clockMatch = output.match(/^Time:\s*(?:(\d+):)?(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?/im);
1576
+ if (clockMatch) {
1577
+ const hours = clockMatch[1] ? Number.parseInt(clockMatch[1], 10) : 0;
1578
+ const minutes = Number.parseInt(clockMatch[2], 10);
1579
+ const seconds = Number.parseInt(clockMatch[3], 10);
1580
+ const millis = clockMatch[4] ? Number.parseInt(clockMatch[4].padEnd(3, "0"), 10) : 0;
1581
+ duration = ((hours * 60 + minutes) * 60 + seconds) * 1e3 + millis;
1582
+ } else {
1583
+ const legacyMatch = output.match(/^Time:\s*([\d.]+)\s*(seconds?|s|ms|milliseconds?|minutes?)\b/im);
1584
+ if (legacyMatch) {
1585
+ const value = Number.parseFloat(legacyMatch[1]);
1586
+ const unit = legacyMatch[2].toLowerCase();
1587
+ const scale = unit.startsWith("ms") || unit.startsWith("milli") ? 1 : unit.startsWith("min") ? 6e4 : 1e3;
1588
+ if (Number.isFinite(value) && value > 0) {
1589
+ duration = Math.round(value * scale);
1590
+ }
1591
+ }
1592
+ }
1593
+ return {
1594
+ file: testFile,
1595
+ sourceFile: "",
1596
+ runner,
1597
+ passed,
1598
+ failed,
1599
+ skipped,
1600
+ failures,
1601
+ duration,
1602
+ error: exitCode !== 0 && passed === 0 && failed === 0 ? "PHPUnit runner error" : void 0
1603
+ };
1604
+ }
1605
+ // --- mix test Parser (ExUnit, text-based, default CLI output) ---
1606
+ parseMixTestOutput(stdout, stderr, exitCode, testFile, runner, displayRoot = path.dirname(testFile), cwd = path.dirname(testFile), spawnCwd = cwd) {
1607
+ const output = `${stdout}
1608
+ ${stderr}`;
1609
+ let passed = 0;
1610
+ let failed = 0;
1611
+ let skipped = 0;
1612
+ let duration;
1613
+ const summaryMatch = output.match(/(\d+)\s+tests?,\s*(\d+)\s+failures?(?:,\s*(\d+)\s+excluded)?(?:,\s*(\d+)\s+skipped)?/i);
1614
+ if (summaryMatch) {
1615
+ const total = Number.parseInt(summaryMatch[1], 10);
1616
+ failed = Number.parseInt(summaryMatch[2], 10);
1617
+ const excluded = summaryMatch[3] ? Number.parseInt(summaryMatch[3], 10) : 0;
1618
+ const skippedCount = summaryMatch[4] ? Number.parseInt(summaryMatch[4], 10) : 0;
1619
+ skipped = excluded + skippedCount;
1620
+ passed = Math.max(0, total - failed - skipped);
1621
+ }
1622
+ const durationMatch = output.match(/Finished in\s+([\d.]+)\s+seconds?/i);
1623
+ if (durationMatch) {
1624
+ duration = Math.round(Number.parseFloat(durationMatch[1]) * 1e3);
1625
+ }
1626
+ const failures = [];
1627
+ const failureRegex = /^[^\S\n]*\d+\)\s+(.+?)\s*\(([^)]+)\)\s*$/gm;
1628
+ let match;
1629
+ while ((match = failureRegex.exec(output)) !== null) {
1630
+ const afterFailure = output.slice(match.index + match[0].length);
1631
+ const nextFailure = afterFailure.search(/(?:^|\n)[^\S\n]*\d+\)\s/);
1632
+ const failureBlock = nextFailure === -1 ? afterFailure : afterFailure.slice(0, nextFailure);
1633
+ const locationMatch = /(?:^|\n)[^\S\n]*([^\s:]+\.exs):(\d+)/.exec(failureBlock);
1634
+ failures.push({
1635
+ name: match[1].trim(),
1636
+ message: match[1].trim(),
1637
+ location: this.renderTextLocation(displayRoot, [spawnCwd, cwd], locationMatch?.[1], locationMatch?.[2]) ?? match[2].trim()
1638
+ });
1639
+ }
1640
+ return {
1641
+ file: testFile,
1642
+ sourceFile: "",
1643
+ runner,
1644
+ passed,
1645
+ failed,
1646
+ skipped,
1647
+ failures,
1648
+ duration,
1649
+ error: exitCode !== 0 && passed === 0 && failed === 0 ? "mix test runner error" : void 0
1650
+ };
1651
+ }
1652
+ // --- Generic text parser for non-JSON runners ---
1653
+ /**
1654
+ * #1480: elapsed time for the runners `parseGenericRunnerOutput` handles.
1655
+ *
1656
+ * Before this, only go's `ok pkg 0.25s` was read and every other runner
1657
+ * reported a hardcoded 0. #1479 made the log tell "measured" from
1658
+ * "unmeasured", but this parser is the `default:` arm behind cargo, dotnet,
1659
+ * maven, gradle, rspec, minitest and every unrecognised runner, so all of
1660
+ * them still reported a number nobody measured. Each runner below prints
1661
+ * its elapsed time in the same summary block this parser already regexes
1662
+ * for pass/fail counts.
1663
+ *
1664
+ * Absent, not 0, is the answer when nothing is found — see
1665
+ * `TestResult.duration` and `run-duration.ts`. A probe that returned 0 here
1666
+ * would be claiming a measurement.
1667
+ *
1668
+ * One parser serves all runners, so the probe is selected BY RUNNER NAME.
1669
+ * Running every probe over every runner's output was the original shape of
1670
+ * this code, and it let gradle borrow a number: `BUILD SUCCESSFUL in 3s`
1671
+ * plus a preceding `... ok` line satisfied go's `ok <pkg> <n>s` probe, so
1672
+ * the whole-build wall clock got reported as test time — the exact wrong
1673
+ * number this function refuses to print. Gating on the runner makes that
1674
+ * structurally impossible rather than merely unlikely, and it matters most
1675
+ * for the `default:` arm of the switch, which is where an unrecognised or
1676
+ * custom runner's arbitrary output lands.
1677
+ *
1678
+ * Within a runner the patterns are still anchored where an anchor helps,
1679
+ * for the same reason #1452's PHPUnit `Time:` pattern is anchored: an
1680
+ * unanchored /m match takes the FIRST hit over stdout+stderr, and a failure
1681
+ * diff quoting "Finished in ..." would beat the real summary. Note what the
1682
+ * `^` in `^Finished in` does and does not buy. It rejects a decoy that is
1683
+ * INDENTED, which is what a quoted expectation or an assertion diff is; it
1684
+ * does NOT rank two column-0 matches, so an unindented decoy printed by the
1685
+ * suite itself would still win. It is a cheap filter for the common shape,
1686
+ * not a proof of uniqueness. And it is not an anchor to the counts line for
1687
+ * rspec or minitest: both print their elapsed time on a `Finished in ...`
1688
+ * line and their counts (`3 examples, 0 failures`, `1 runs, 1 assertions,
1689
+ * ...`) on a different line.
1690
+ *
1691
+ * KNOWN LIMIT — first summary only. cargo across multiple crates, `dotnet
1692
+ * test` across multiple assemblies, and `go test ./...` across multiple
1693
+ * packages each print one summary per unit, and these probes take the
1694
+ * first. A multi-unit run therefore UNDER-REPORTS its duration. That is
1695
+ * left as-is deliberately: the count parsers below have the same first-match
1696
+ * shape for those runners, so duration and counts describe the same scope.
1697
+ * Fixing one without the other would trade an under-report for an
1698
+ * inconsistency. Pinned by test so it stays a known limit, not an accident.
1699
+ *
1700
+ * Formats and how each was verified:
1701
+ *
1702
+ * - go — `ok example.com/pkg 0.253s`. Pre-existing pattern, unchanged
1703
+ * apart from the shared finite/non-negative guard.
1704
+ *
1705
+ * - cargo — `test result: ok. 3 passed; 0 failed; 1 ignored; 0 measured;
1706
+ * 0 filtered out; finished in 0.253s`. NOT VERIFIED AGAINST A LIVE CARGO
1707
+ * RUN — this box has no MSVC linker, so `cargo test` cannot link. Format
1708
+ * read out of the libtest printer shipped with the local rustc 1.94.1:
1709
+ * `library/test/src/formatters/pretty.rs` builds `"; finished in
1710
+ * {exec_time}"` and `library/test/src/time.rs` renders `TestSuiteExecTime`
1711
+ * as `{:.2}s`. Older rustc omits the suffix entirely; that degrades to
1712
+ * unmeasured.
1713
+ *
1714
+ * - dotnet/vstest — `Failed: 1, Passed: 2, Skipped: 0, Total: 3, Duration:
1715
+ * 1 m 30 s - t.dll (net8.0)`. NOT VERIFIED AGAINST A LIVE `dotnet test` —
1716
+ * NuGet restore has no network here. Format read out of the
1717
+ * vstest.console.dll shipped with the local .NET SDK 8.0.423, which holds
1718
+ * the literal `{0} - Failed: {1}, Passed: {2}, Skipped: {3}, Total: {4},
1719
+ * Duration: {5}` next to the unit literals `" h"`, `" m"`, `" s"`,
1720
+ * `" ms"`, `"< 1 ms"`. The duration is a space-joined token list, so it
1721
+ * is summed rather than read as one number.
1722
+ *
1723
+ * - maven/surefire — `Tests run: 4, Failures: 0, Errors: 0, Skipped: 0,
1724
+ * Time elapsed: 0.05 s -- in com.example.AppTest`. NOT VERIFIED AGAINST A
1725
+ * LIVE MAVEN — no mvn on this box. Summed across the per-class lines,
1726
+ * because surefire prints `Time elapsed` per test class and its final
1727
+ * `Results:` total carries no time. `[INFO] Total time: 3.4 s` is
1728
+ * deliberately NOT used: that is whole-build wall clock including compile,
1729
+ * which would report a wrong number rather than none. Surefire 2.x wrote
1730
+ * `sec` where 3.x writes `s`; both are accepted.
1731
+ *
1732
+ * EXPECT THIS TO BE ABSENT IN PRACTICE. pi-lens invokes `mvn test -q`
1733
+ * (see RUNNERS.maven above), and surefire logs its per-class `Tests run:
1734
+ * ..., Time elapsed: ...` lines at INFO, which `-q` suppresses. Only the
1735
+ * ERROR-level lines of a FAILING class survive, so a green maven run
1736
+ * typically reports unmeasured and a red one reports the failing classes'
1737
+ * time alone. REASONED, NOT RUN — there is no mvn on this box to confirm
1738
+ * it. Left in rather than dropped: it costs nothing, it is correct when
1739
+ * the output does carry the lines (a repo that sets `-Dsurefire.useFile`
1740
+ * or drops `-q` via `.mvn/maven.config`), and `unmeasured` is an honest
1741
+ * report of the quiet case.
1742
+ *
1743
+ * - rspec — `Finished in 0.32394 seconds (files took 0.49427 seconds to
1744
+ * load)`. VERIFIED against a live rspec-core 3.13.6 run on ruby 3.4.10.
1745
+ * The minutes form (`Finished in 2 minutes 15.14 seconds`) comes from
1746
+ * `RSpec::Core::Formatters::Helpers.format_duration` in the same
1747
+ * installed gem; rspec never prints hours. Load time trails the run time
1748
+ * on the same line and must not be read instead of it.
1749
+ *
1750
+ * - minitest — `Finished in 0.254594s, 7.8557 runs/s, 7.8557 assertions/s.`
1751
+ * VERIFIED against a live minitest 5.25.4 run on ruby 3.4.10. The format
1752
+ * string is `"Finished in %.6fs, ..."` in minitest.rb, always seconds.
1753
+ *
1754
+ * - gradle — deliberately left unmeasured, and now UNREACHABLE by any other
1755
+ * runner's probe rather than merely unmatched by it. Gradle's console
1756
+ * summary (`4 tests completed, 1 failed`) carries no elapsed time, and
1757
+ * `BUILD SUCCESSFUL in 3s` is whole-build wall clock including compile
1758
+ * and dependency resolution. Reporting that as test time would be a wrong
1759
+ * number; #1479 makes the absence legible in the log instead.
1760
+ */
1761
+ parseGenericRunnerDuration(output, runner) {
1762
+ switch (runner) {
1763
+ case "go":
1764
+ return this.parseGoDuration(output);
1765
+ case "cargo":
1766
+ return this.parseCargoDuration(output);
1767
+ case "dotnet":
1768
+ return this.parseDotnetDuration(output);
1769
+ case "maven":
1770
+ return this.parseMavenDuration(output);
1771
+ case "rspec":
1772
+ return this.parseRspecDuration(output);
1773
+ case "minitest":
1774
+ return this.parseMinitestDuration(output);
1775
+ default:
1776
+ return void 0;
1777
+ }
1778
+ }
1779
+ /** go: `ok example.com/pkg 0.253s`. First package summary only. */
1780
+ parseGoDuration(output) {
1781
+ const goSummary = output.match(/ok\s+\S+\s+([\d.]+)s/m);
1782
+ if (!goSummary)
1783
+ return void 0;
1784
+ return toMeasuredDurationMs(Number.parseFloat(goSummary[1]) * 1e3);
1785
+ }
1786
+ /** cargo: `...; 0 filtered out; finished in 0.25s`. First crate only. */
1787
+ parseCargoDuration(output) {
1788
+ const cargoTime = output.match(/^test result:.*?;\s*finished in\s+([\d.]+)\s*s\b/im);
1789
+ if (!cargoTime)
1790
+ return void 0;
1791
+ return toMeasuredDurationMs(Number.parseFloat(cargoTime[1]) * 1e3);
1792
+ }
1793
+ /**
1794
+ * dotnet/vstest: `..., Total: 3, Duration: 1 m 30 s - t.dll (net8.0)`.
1795
+ *
1796
+ * Anchored to the counts line, and the tail stops at the ` - <dll>`
1797
+ * separator: without that stop an assembly name is scanned for unit tokens,
1798
+ * and a name like `Timeouts.30s.Tests.dll` adds 30 seconds of nothing.
1799
+ * First assembly only.
1800
+ */
1801
+ parseDotnetDuration(output) {
1802
+ const dotnetTime = output.match(/Failed:\s*\d+,\s*Passed:\s*\d+,\s*Skipped:\s*\d+,\s*Total:\s*\d+,\s*Duration:\s*([^\r\n-]+)/i);
1803
+ if (!dotnetTime)
1804
+ return void 0;
1805
+ if (/^\s*</.test(dotnetTime[1]))
1806
+ return 0;
1807
+ let total = 0;
1808
+ let tokens = 0;
1809
+ const units = {
1810
+ ms: 1,
1811
+ s: 1e3,
1812
+ m: 6e4,
1813
+ h: 36e5
1814
+ };
1815
+ for (const token of dotnetTime[1].matchAll(/([\d.]+)\s*(ms|h|m|s)\b/gi)) {
1816
+ total += Number.parseFloat(token[1]) * units[token[2].toLowerCase()];
1817
+ tokens++;
1818
+ }
1819
+ if (tokens === 0)
1820
+ return void 0;
1821
+ return toMeasuredDurationMs(total);
1822
+ }
1823
+ /**
1824
+ * maven/surefire: summed across per-class `Time elapsed` lines.
1825
+ *
1826
+ * The guard is "did any line match", NOT "is the sum positive". Surefire
1827
+ * prints `Time elapsed: 0.00 s` for a trivial test class, and that is a
1828
+ * measurement of zero, not a failure to measure.
1829
+ */
1830
+ parseMavenDuration(output) {
1831
+ let surefireTotal = 0;
1832
+ let matched = false;
1833
+ for (const line of output.matchAll(/^.*Tests run:\s*\d+,.*?Time elapsed:\s*([\d.]+)\s*(?:s|sec|secs|seconds)\b.*$/gim)) {
1834
+ const seconds = Number.parseFloat(line[1]);
1835
+ if (!Number.isFinite(seconds) || seconds < 0)
1836
+ continue;
1837
+ surefireTotal += seconds;
1838
+ matched = true;
1839
+ }
1840
+ if (!matched)
1841
+ return void 0;
1842
+ return toMeasuredDurationMs(surefireTotal * 1e3);
1843
+ }
1844
+ /** rspec: `Finished in 2 minutes 15.14 seconds (files took 0.5 ...)`. */
1845
+ parseRspecDuration(output) {
1846
+ const rspecTime = output.match(/^Finished in\s+(?:([\d.]+)\s+minutes?\s+)?([\d.]+)\s+seconds?/im);
1847
+ if (!rspecTime)
1848
+ return void 0;
1849
+ const minutes = rspecTime[1] ? Number.parseFloat(rspecTime[1]) : 0;
1850
+ return toMeasuredDurationMs(minutes * 6e4 + Number.parseFloat(rspecTime[2]) * 1e3);
1851
+ }
1852
+ /**
1853
+ * minitest: `Finished in 0.254594s, 7.8557 runs/s, ...`.
1854
+ *
1855
+ * The trailing `,` is load-bearing, not decoration: it is what separates
1856
+ * minitest's own line from a bare `Finished in 99s` the suite under test
1857
+ * printed at column 0, which the `^` alone does not rank.
1858
+ */
1859
+ parseMinitestDuration(output) {
1860
+ const minitestTime = output.match(/^Finished in\s+([\d.]+)s\s*,/im);
1861
+ if (!minitestTime)
1862
+ return void 0;
1863
+ return toMeasuredDurationMs(Number.parseFloat(minitestTime[1]) * 1e3);
1864
+ }
1865
+ parseGenericRunnerOutput(stdout, stderr, exitCode, testFile, runner, displayRoot = path.dirname(testFile), cwd = path.dirname(testFile), spawnCwd = cwd) {
1866
+ const output = `${stdout}
1867
+ ${stderr}`;
1868
+ const lower = output.toLowerCase();
1869
+ let passed = 0;
1870
+ let failed = 0;
1871
+ let skipped = 0;
1872
+ let matched = false;
1873
+ const duration = this.parseGenericRunnerDuration(output, runner);
1874
+ const cargoSummary = output.match(/test result:\s+\w+\.\s+(\d+)\s+passed;\s+(\d+)\s+failed;\s+(\d+)\s+ignored;/i);
1875
+ if (cargoSummary) {
1876
+ passed = Number.parseInt(cargoSummary[1], 10);
1877
+ failed = Number.parseInt(cargoSummary[2], 10);
1878
+ skipped = Number.parseInt(cargoSummary[3], 10);
1879
+ matched = true;
1880
+ }
1881
+ const dotnetSummary = output.match(/Failed:\s*(\d+),\s*Passed:\s*(\d+),\s*Skipped:\s*(\d+)/i);
1882
+ if (dotnetSummary) {
1883
+ failed = Number.parseInt(dotnetSummary[1], 10);
1884
+ passed = Number.parseInt(dotnetSummary[2], 10);
1885
+ skipped = Number.parseInt(dotnetSummary[3], 10);
1886
+ matched = true;
1887
+ }
1888
+ const mavenLines = [
1889
+ ...output.matchAll(/^.*?Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+).*$/gim)
1890
+ ];
1891
+ const mavenAggregates = mavenLines.filter((line) => !/Time elapsed:/i.test(line[0]));
1892
+ const mavenScored = mavenAggregates.length > 0 ? mavenAggregates : mavenLines;
1893
+ if (mavenScored.length > 0) {
1894
+ let total = 0;
1895
+ let mavenFailed = 0;
1896
+ let mavenSkipped = 0;
1897
+ for (const line of mavenScored) {
1898
+ total += Number.parseInt(line[1], 10);
1899
+ mavenFailed += Number.parseInt(line[2], 10) + Number.parseInt(line[3], 10);
1900
+ mavenSkipped += Number.parseInt(line[4], 10);
1901
+ }
1902
+ failed = mavenFailed;
1903
+ skipped = mavenSkipped;
1904
+ passed = Math.max(0, total - failed - skipped);
1905
+ matched = true;
1906
+ }
1907
+ const rspecSummary = output.match(/(\d+)\s+examples?,\s+(\d+)\s+failures?/i);
1908
+ if (rspecSummary) {
1909
+ const total = Number.parseInt(rspecSummary[1], 10);
1910
+ failed = Number.parseInt(rspecSummary[2], 10);
1911
+ passed = Math.max(0, total - failed);
1912
+ matched = true;
1913
+ }
1914
+ const minitestSummary = output.match(/(\d+)\s+runs?,\s+\d+\s+assertions?,\s+(\d+)\s+failures?,\s+(\d+)\s+errors?/i);
1915
+ if (minitestSummary) {
1916
+ const total = Number.parseInt(minitestSummary[1], 10);
1917
+ const failures2 = Number.parseInt(minitestSummary[2], 10);
1918
+ const errors = Number.parseInt(minitestSummary[3], 10);
1919
+ failed = failures2 + errors;
1920
+ passed = Math.max(0, total - failed);
1921
+ matched = true;
1922
+ }
1923
+ const gradleSummary = output.match(/(\d+)\s+tests? completed,\s+(\d+)\s+failed/i);
1924
+ if (gradleSummary) {
1925
+ const total = Number.parseInt(gradleSummary[1], 10);
1926
+ failed = Number.parseInt(gradleSummary[2], 10);
1927
+ passed = Math.max(0, total - failed);
1928
+ matched = true;
1929
+ }
1930
+ const goFailNames = [...output.matchAll(/--- FAIL:[^\S\n]+([^\s(]+)/g)];
1931
+ const goFailNameSet = new Set(goFailNames.map((m) => m[1].trim()));
1932
+ const goTopLevelFailCount = goFailNames.filter((m) => {
1933
+ const name = m[1].trim();
1934
+ const slash = name.indexOf("/");
1935
+ return slash === -1 || !goFailNameSet.has(name.slice(0, slash));
1936
+ }).length;
1937
+ let goInfraVerdict = false;
1938
+ if (runner === "go") {
1939
+ goInfraVerdict = /^FAIL[^\S\n]+\S+[^\S\n]+\[setup failed\]/m.test(output);
1940
+ const goFailPackage = /^FAIL(?![^\n]*\[(?:build|setup) failed\])[^\S\n]+\S+/m.test(output);
1941
+ const goOkPackages = [
1942
+ ...output.matchAll(/^ok[^\S\n]+\S+[^\S\n]+[\d.]+s/gm)
1943
+ ];
1944
+ if (goFailNames.length > 0 || goFailPackage) {
1945
+ failed = Math.max(failed, goTopLevelFailCount || 1);
1946
+ matched = true;
1947
+ }
1948
+ if (goOkPackages.length > 0) {
1949
+ passed = Math.max(passed, goOkPackages.length);
1950
+ matched = true;
1951
+ }
1952
+ }
1953
+ const failures = [];
1954
+ for (const m of goFailNames.slice(0, 5)) {
1955
+ failures.push({ name: m[1].trim(), message: m[1].trim() });
1956
+ }
1957
+ const otherNames = [
1958
+ ...output.matchAll(/^[^\S\n]*FAILED[^\S\n]+(\S.*)$/gm),
1959
+ ...output.matchAll(/^[^\S\n]*Failure:[^\S\n]+(\S.*)$/gm)
1960
+ ];
1961
+ for (const m of otherNames) {
1962
+ if (failures.length >= 5)
1963
+ break;
1964
+ const name = m[1].trim();
1965
+ const locationMatch = /(?<!\S)([^\s:]+\.(?:go|rs|rb|java|kt|cs|fs|py|php|exs)):(\d+)/.exec(name);
1966
+ const location = this.renderTextLocation(displayRoot, [spawnCwd, cwd], locationMatch?.[1], locationMatch?.[2]);
1967
+ failures.push({
1968
+ name,
1969
+ message: name,
1970
+ ...location === void 0 ? {} : { location }
1971
+ });
1972
+ }
1973
+ const runnerError = exitCode !== 0 && !matched && goFailNames.length === 0 && (lower.includes("error") || goInfraVerdict) ? `Runner ${runner} exited with ${exitCode}` : void 0;
1974
+ if (runnerError) {
1975
+ failures.length = 0;
1976
+ }
1977
+ if (exitCode !== 0 && failed === 0 && !runnerError) {
1978
+ failed = 1;
1979
+ }
1980
+ if (passed === 0 && failed === 0 && skipped === 0 && exitCode === 0) {
1981
+ passed = 1;
1982
+ }
1983
+ if (failures.length === 0 && failed > 0) {
1984
+ const firstLine = output.split("\n").find((l) => /fail|error|exception/i.test(l))?.trim().slice(0, 300) || `Tests failed for runner ${runner}`;
1985
+ failures.push({ name: `${runner} failure`, message: firstLine });
1986
+ }
1987
+ this.log(runnerError ? `Generic runner ${runner}: never started (${runnerError})` : `Generic runner ${runner}: ran (matched=${matched}, passed=${passed}, failed=${failed}, failures=${failures.length})`);
1988
+ return {
1989
+ file: testFile,
1990
+ sourceFile: "",
1991
+ runner,
1992
+ passed,
1993
+ failed,
1994
+ skipped,
1995
+ failures,
1996
+ duration,
1997
+ error: runnerError
1998
+ };
1999
+ }
2000
+ // --- Formatting ---
2001
+ /**
2002
+ * Format test result for LLM consumption
2003
+ */
2004
+ formatResult(result) {
2005
+ if (isRunnerErrorResult(result)) {
2006
+ return result.passed > 0 ? `[Tests] \u26A0 Could not complete tests: ${result.error} (${result.passed} passed before)` : `[Tests] \u26A0 Could not run tests: ${result.error}`;
2007
+ }
2008
+ const total = result.passed + result.failed + result.skipped;
2009
+ if (total === 0) {
2010
+ return "";
2011
+ }
2012
+ const durationStr = isMeasuredDuration(result.duration) && result.duration > 0 ? ` (${(result.duration / 1e3).toFixed(2)}s)` : "";
2013
+ if (result.failed === 0) {
2014
+ return `[Tests] \u2713 ${result.passed}/${total} passed${durationStr} \u2014 ${result.runner}`;
2015
+ }
2016
+ let output = `[Tests] \u2717 ${result.failed}/${total} failed, ${result.passed} passed${durationStr} \u2014 ${result.runner}
2017
+ `;
2018
+ for (const failure of result.failures.slice(0, 5)) {
2019
+ output += ` \u2717 ${failure.name}
2020
+ `;
2021
+ const msg = failure.message.split("\n")[0].slice(0, 200);
2022
+ output += ` ${msg}
2023
+ `;
2024
+ if (failure.location) {
2025
+ output += ` at ${failure.location}
2026
+ `;
2027
+ }
2028
+ }
2029
+ if (result.failures.length > 5) {
2030
+ output += ` ... and ${result.failures.length - 5} more failure(s)
2031
+ `;
2032
+ }
2033
+ output += ` \u2192 Fix failing tests before proceeding
2034
+ `;
2035
+ return output.trimEnd();
2036
+ }
2037
+ // --- Helpers ---
2038
+ /**
2039
+ * Additional mirrored-directory candidate for source trees whose test
2040
+ * tree mirrors the source tree under a *different*, conventional
2041
+ * source-root segment rather than the source file's full relative
2042
+ * directory — e.g. PHPUnit's `src/Foo/Bar.php` -> `tests/Foo/BarTest.php`
2043
+ * (strips `src`) or ExUnit's `lib/accounts/user.ex` ->
2044
+ * `test/accounts/user_test.exs` (strips `lib`).
2045
+ *
2046
+ * Unlike the `relDir`-based candidates above (which mirror under the
2047
+ * hardcoded "tests"/"__tests__" roots), this uses `testDir` — the
2048
+ * pattern's own configured test root from `SOURCE_TO_TEST_PATTERNS`
2049
+ * (e.g. "tests" for PHP, "test" for Elixir) — since ExUnit's root is
2050
+ * singular and wouldn't otherwise be checked.
2051
+ *
2052
+ * Returns an empty array when the source directory doesn't start with a
2053
+ * known source-root segment (src/lib/app) followed by at least one more
2054
+ * path segment — i.e. this is a no-op for languages/layouts that don't
2055
+ * use this convention.
2056
+ */
2057
+ sourceRootMirroredCandidates(dir, cwd, testDir, testFilename) {
2058
+ const knownSourceRoots = /* @__PURE__ */ new Set(["src", "lib", "app"]);
2059
+ const relDir = path.relative(cwd, dir);
2060
+ const segments = relDir.split(path.sep).filter(Boolean);
2061
+ if (segments.length > 1 && knownSourceRoots.has(segments[0])) {
2062
+ return [path.join(cwd, testDir, ...segments.slice(1), testFilename)];
2063
+ }
2064
+ return [];
2065
+ }
2066
+ /**
2067
+ * Fallback discovery: scan known test directories for a file that imports
2068
+ * the source module. Catches cases where the test file name doesn't match
2069
+ * the source basename (e.g. cline.test.ts testing cline-auth.ts).
2070
+ *
2071
+ * Checks for the basename appearing in a quoted import/require path:
2072
+ * from "../providers/cline/cline-auth" → /cline-auth" ✓
2073
+ * from "./cline-auth.js" → /cline-auth. ✓
2074
+ * import("cline-auth") → "cline-auth" ✓
2075
+ */
2076
+ findTestFileByImport(sourceFilePath, cwd, acceptCandidate) {
2077
+ const ext = path.extname(sourceFilePath);
2078
+ const basename2 = path.basename(sourceFilePath, ext);
2079
+ const testPattern = /\.(test|spec)\.(ts|tsx|js|jsx|mjs)$/;
2080
+ const searchDirs = [
2081
+ path.join(cwd, "tests"),
2082
+ path.join(cwd, "__tests__"),
2083
+ path.dirname(sourceFilePath)
2084
+ ];
2085
+ for (const dir of searchDirs) {
2086
+ let entries;
2087
+ try {
2088
+ entries = fs.readdirSync(dir);
2089
+ } catch {
2090
+ continue;
2091
+ }
2092
+ for (const entry of entries) {
2093
+ const testPath = path.join(dir, entry);
2094
+ if (!testPattern.test(entry) || !acceptCandidate(testPath))
2095
+ continue;
2096
+ let content;
2097
+ try {
2098
+ content = fs.readFileSync(testPath, "utf-8");
2099
+ } catch {
2100
+ continue;
2101
+ }
2102
+ if (content.includes(`/${basename2}"`) || content.includes(`/${basename2}'`) || content.includes(`/${basename2}.`) || content.includes(`"${basename2}"`) || content.includes(`'${basename2}'`)) {
2103
+ this.log(`Found test file via import scan: ${testPath}`);
2104
+ return testPath;
2105
+ }
2106
+ }
2107
+ }
2108
+ return null;
2109
+ }
2110
+ /**
2111
+ * Resolve the executable and args for a runner, preferring a local
2112
+ * node_modules/.bin binary over npx to avoid the ~150ms npx startup cost.
2113
+ *
2114
+ * When a resolved binary becomes the command itself, `stripWrapperArgs`
2115
+ * drops ONLY the leading arg(s) that named the wrapped binary — never a
2116
+ * real subcommand (#1098: `cargo test --no-fail-fast` unconditionally lost
2117
+ * `test` here because the old code assumed every runner's args() started
2118
+ * with an npx-style runner-name arg, which only holds for wrapper-style
2119
+ * runners like vitest/jest/pytest).
2120
+ */
2121
+ async resolveExec(runner, config, testFile, cwd, spawnCwd = cwd, requireOwnInstall = false) {
2122
+ const needsInstall = config.command === "npx" || runner === "pytest" || runner === "phpunit";
2123
+ if (runner === "pytest") {
2124
+ const pythonEnvironment = await detectPythonEnvironment(cwd, void 0, {
2125
+ allowAmbient: !requireOwnInstall
2126
+ });
2127
+ if (pythonEnvironment) {
2128
+ return {
2129
+ command: pythonEnvironment.pythonPath,
2130
+ args: config.args(testFile, spawnCwd),
2131
+ env: augmentPythonEnvironment(process.env, pythonEnvironment),
2132
+ ownInstall: true
2133
+ };
2134
+ }
2135
+ }
2136
+ if (runner === "phpunit") {
2137
+ const suffix2 = process.platform === "win32" ? ".bat" : "";
2138
+ const vendorBin = path.join(cwd, "vendor", "bin", `phpunit${suffix2}`);
2139
+ if (fs.existsSync(vendorBin)) {
2140
+ return {
2141
+ command: vendorBin,
2142
+ args: config.args(testFile, spawnCwd),
2143
+ ownInstall: true
2144
+ };
2145
+ }
2146
+ return {
2147
+ command: "phpunit",
2148
+ args: config.args(testFile, spawnCwd),
2149
+ ownInstall: false
2150
+ };
2151
+ }
2152
+ const binName = config.binName ?? runner;
2153
+ const suffix = process.platform === "win32" ? ".cmd" : "";
2154
+ const localBin = path.join(cwd, "node_modules", ".bin", binName + suffix);
2155
+ if (fs.existsSync(localBin)) {
2156
+ return {
2157
+ command: localBin,
2158
+ args: stripWrapperArgs(binName, config.args(testFile, spawnCwd)),
2159
+ ownInstall: true
2160
+ };
2161
+ }
2162
+ const globalBin = await findGlobalBinary(binName);
2163
+ if (globalBin) {
2164
+ return {
2165
+ command: globalBin,
2166
+ args: stripWrapperArgs(binName, config.args(testFile, spawnCwd)),
2167
+ ownInstall: !needsInstall
2168
+ };
2169
+ }
2170
+ return {
2171
+ command: config.command,
2172
+ args: config.args(testFile, spawnCwd),
2173
+ ownInstall: !needsInstall
2174
+ };
2175
+ }
2176
+ emptyResult(testFile, sourceFile, runner, error) {
2177
+ return {
2178
+ file: testFile,
2179
+ sourceFile,
2180
+ runner,
2181
+ passed: 0,
2182
+ failed: 0,
2183
+ skipped: 0,
2184
+ failures: [],
2185
+ // #1479: no duration key at all. Nothing ran, so there is nothing
2186
+ // to report — this used to say 0, which reads as "ran, instantly".
2187
+ error
2188
+ };
2189
+ }
2190
+ truncateStack(stack) {
2191
+ if (!stack)
2192
+ return void 0;
2193
+ const lines = stack.split("\n").slice(0, 3);
2194
+ return lines.join("\n").slice(0, 500);
2195
+ }
2196
+ recordResult(record) {
2197
+ const { cwd, runner, testFile, result, turnIndex } = record;
2198
+ const targetPath = path.resolve(testFile);
2199
+ if (result.failed > 0 && foreignGitRoot(targetPath, cwd)) {
2200
+ this.recordFailedTargetState({
2201
+ outcome: "rejected-nested-checkout",
2202
+ runner,
2203
+ candidate: targetPath,
2204
+ turnIndex
2205
+ });
2206
+ return;
2207
+ }
2208
+ const target = canonicalFailedPath(targetPath);
2209
+ let failedTargets = this.getFailedTargets(cwd, runner, result.failed > 0);
2210
+ if (!failedTargets)
2211
+ return;
2212
+ if (result.failed > 0) {
2213
+ const alreadyRecorded = failedTargets.has(target);
2214
+ if (!alreadyRecorded && this.failedTargetCount(runner) >= MAX_FAILED_TARGETS_PER_RUNNER) {
2215
+ const evicted = this.evictOldestFailedTarget(runner);
2216
+ if (evicted) {
2217
+ this.recordFailedTargetState({
2218
+ outcome: "capacity-evicted",
2219
+ runner,
2220
+ candidate: evicted,
2221
+ turnIndex
2222
+ });
2223
+ }
2224
+ failedTargets = this.getFailedTargets(cwd, runner, true);
2225
+ if (!failedTargets)
2226
+ return;
2227
+ }
2228
+ failedTargets.set(target, {
2229
+ displayPath: targetPath,
2230
+ sequence: ++this.failedTargetSequence
2231
+ });
2232
+ return;
2233
+ }
2234
+ if (failedTargets.delete(target) && failedTargets.size === 0) {
2235
+ this.deleteFailedRoot(cwd, runner);
2236
+ }
2237
+ }
2238
+ };
2239
+
2240
+ export {
2241
+ formatRunDurationMs,
2242
+ isRunnerErrorResult,
2243
+ SOURCE_TO_TEST_PATTERNS,
2244
+ TURN_END_EXCLUDED_TEST_GLOBS,
2245
+ isExcludedTestTarget,
2246
+ RUNNERS,
2247
+ stripWrapperArgs,
2248
+ MAX_CANONICAL_ROOT_MEMO_ENTRIES,
2249
+ TestRunnerClient
2250
+ };