agentic-qe 3.15.16 → 3.15.17

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 (415) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +78 -0
  3. package/assets/skills/skills-manifest.json +1 -1
  4. package/dist/cli/bundle.js +6 -6
  5. package/dist/cli/chunks/adapter-OMILP6KH.js +2 -0
  6. package/dist/cli/chunks/{agent-booster-wasm-45NWEKVS.js → agent-booster-wasm-6BUOFQZA.js} +2 -2
  7. package/dist/cli/chunks/{agent-handler-LVHMYFEF.js → agent-handler-HPDK3JI3.js} +2 -2
  8. package/dist/cli/chunks/{agent-memory-branch-YDWOJFEQ.js → agent-memory-branch-5QKUTWK5.js} +2 -2
  9. package/dist/cli/chunks/aqe-learning-engine-OXH5CCOS.js +2 -0
  10. package/dist/cli/chunks/{arena-CZKHXDDS.js → arena-7OKPMNHD.js} +2 -2
  11. package/dist/cli/chunks/{arena-ELDZH3CZ.js → arena-HZ35LESR.js} +4 -4
  12. package/dist/cli/chunks/{audit-5MT4T47Z.js → audit-XI575SVK.js} +3 -3
  13. package/dist/cli/chunks/base-MAK2BLTG.js +2 -0
  14. package/dist/cli/chunks/{better-sqlite3-6TGYPC2B.js → better-sqlite3-YO5X7ZJS.js} +2 -2
  15. package/dist/cli/chunks/{brain-handler-HQWZFO52.js → brain-handler-GCQZFZGQ.js} +3 -3
  16. package/dist/cli/chunks/{branch-enumerator-WVILM4SO.js → branch-enumerator-N6G44UU2.js} +2 -2
  17. package/dist/cli/chunks/{browser-RX5ALU3H.js → browser-EVKPTLOI.js} +2 -2
  18. package/dist/cli/chunks/browser-workflow-IR3VF2TL.js +2 -0
  19. package/dist/cli/chunks/{chunk-F742SZAX.js → chunk-2EQK7B5L.js} +2 -2
  20. package/dist/cli/chunks/{chunk-5NNNEWLS.js → chunk-2PCEMDIX.js} +67 -65
  21. package/dist/cli/chunks/{chunk-FBDVGRSC.js → chunk-2UUZCQSD.js} +7 -7
  22. package/dist/cli/chunks/{chunk-AG2EWGXS.js → chunk-2YWYW7NU.js} +2 -2
  23. package/dist/cli/chunks/{chunk-4LQFBY5L.js → chunk-45223KK6.js} +3 -3
  24. package/dist/cli/chunks/chunk-4EJLHNER.js +12 -0
  25. package/dist/cli/chunks/{chunk-KQYJ4VQ5.js → chunk-4L6WX5KC.js} +1 -1
  26. package/dist/cli/chunks/{chunk-KVF4NXGQ.js → chunk-4NKZU2JZ.js} +2 -2
  27. package/dist/cli/chunks/{chunk-YRAX7SFJ.js → chunk-4UKTIQDT.js} +2 -2
  28. package/dist/cli/chunks/{chunk-YTHTQPWR.js → chunk-4YM45Z5B.js} +1 -1
  29. package/dist/cli/chunks/{chunk-XIIQ5QTT.js → chunk-5FSPELOG.js} +2 -2
  30. package/dist/cli/chunks/{chunk-LKTTUHLF.js → chunk-5WATBVU6.js} +2 -2
  31. package/dist/cli/chunks/{chunk-SR52XP5F.js → chunk-5WYZVSL2.js} +1 -1
  32. package/dist/cli/chunks/{chunk-GQ6TUIKY.js → chunk-6LSZCEW2.js} +1 -1
  33. package/dist/cli/chunks/{chunk-NDJ7P2EP.js → chunk-6V6BD6PH.js} +1 -1
  34. package/dist/cli/chunks/{chunk-63GMRQ25.js → chunk-74MDUIAZ.js} +1 -1
  35. package/dist/cli/chunks/{chunk-4D4DENG6.js → chunk-776K4ZLE.js} +2 -2
  36. package/dist/cli/chunks/{chunk-ZJH5MU2I.js → chunk-7BSOO3CY.js} +2 -2
  37. package/dist/cli/chunks/{chunk-IOQ6ZQ5W.js → chunk-7SNSI4VY.js} +1 -1
  38. package/dist/cli/chunks/{chunk-LRSX2FJM.js → chunk-A4BVDYIL.js} +1 -1
  39. package/dist/cli/chunks/{chunk-SASBO5Y5.js → chunk-AGCS5GSO.js} +2 -2
  40. package/dist/cli/chunks/{chunk-GAHNO6JJ.js → chunk-AVHYJ3XP.js} +2 -2
  41. package/dist/cli/chunks/{chunk-CGGUM2YJ.js → chunk-BE7ZHM34.js} +2 -2
  42. package/dist/cli/chunks/{chunk-7TE5ONQQ.js → chunk-BHQ2DLJU.js} +1 -1
  43. package/dist/cli/chunks/{chunk-IESR7TNB.js → chunk-BKHEQIRZ.js} +2 -2
  44. package/dist/cli/chunks/{chunk-J4ELZNLY.js → chunk-BSIWI4ES.js} +2 -2
  45. package/dist/cli/chunks/{chunk-NVEWF4JV.js → chunk-C7QLLMYI.js} +1 -1
  46. package/dist/cli/chunks/{chunk-V5NHZV7S.js → chunk-CMN7PU2H.js} +2 -2
  47. package/dist/cli/chunks/{chunk-PPUZWYUG.js → chunk-COTQ44YO.js} +2 -2
  48. package/dist/cli/chunks/{chunk-5V3TI2LG.js → chunk-CQER6IJ5.js} +1 -1
  49. package/dist/cli/chunks/{chunk-SI6DPU5D.js → chunk-CVPXHDQU.js} +1 -1
  50. package/dist/cli/chunks/{chunk-6ZBXSOX6.js → chunk-CVYPMGUM.js} +2 -2
  51. package/dist/cli/chunks/{chunk-DXD6K2ZP.js → chunk-CXCMATZ6.js} +2 -2
  52. package/dist/cli/chunks/{chunk-PF7RBAYL.js → chunk-D75GLKEL.js} +2 -2
  53. package/dist/cli/chunks/chunk-DCDSN67A.js +2 -0
  54. package/dist/cli/chunks/{chunk-MAFQV4U4.js → chunk-DHKSPOUQ.js} +2 -2
  55. package/dist/cli/chunks/{chunk-RJIEG3NA.js → chunk-DJDVD6RM.js} +2 -2
  56. package/dist/cli/chunks/{chunk-GFWHUQSF.js → chunk-DNVRTJKB.js} +2 -2
  57. package/dist/cli/chunks/{chunk-G5RHFBAY.js → chunk-DS5IFRY6.js} +2 -2
  58. package/dist/cli/chunks/{chunk-BEKDKVQT.js → chunk-E7XIMOKW.js} +2 -2
  59. package/dist/cli/chunks/{chunk-5JZRZI4S.js → chunk-ECKHDHR2.js} +2 -2
  60. package/dist/cli/chunks/chunk-EPVPXHL7.js +2 -0
  61. package/dist/cli/chunks/{chunk-HVRRLYX6.js → chunk-ERYADKMZ.js} +2 -2
  62. package/dist/cli/chunks/{chunk-SUDMM5SO.js → chunk-EUWG2JG4.js} +2 -2
  63. package/dist/cli/chunks/{chunk-VSIKHWZ4.js → chunk-FAQ24UHB.js} +3 -3
  64. package/dist/cli/chunks/{chunk-YNMJEDN5.js → chunk-FBBPRVNF.js} +2 -2
  65. package/dist/cli/chunks/{chunk-72XSEQMW.js → chunk-FBSBXCZS.js} +2 -2
  66. package/dist/cli/chunks/{chunk-J7I6PABJ.js → chunk-G2X3OLMN.js} +2 -2
  67. package/dist/cli/chunks/{chunk-YU2BDRSD.js → chunk-GG7PMTHD.js} +1 -1
  68. package/dist/cli/chunks/{chunk-HLUXXCHF.js → chunk-GN6OE3JT.js} +1 -1
  69. package/dist/cli/chunks/{chunk-AGRV5B3T.js → chunk-GPPTRKEM.js} +2 -2
  70. package/dist/cli/chunks/{chunk-ZQ2XW3IP.js → chunk-H2LHWZRE.js} +2 -2
  71. package/dist/cli/chunks/{chunk-6DPYNAOK.js → chunk-HAQJFUAQ.js} +2 -2
  72. package/dist/cli/chunks/{chunk-SSUAAX3F.js → chunk-HD6W6PHV.js} +2 -2
  73. package/dist/cli/chunks/{chunk-L2DWPVNJ.js → chunk-HFZD5KLR.js} +1 -1
  74. package/dist/cli/chunks/{chunk-QVDCVD5D.js → chunk-HRUHHHVR.js} +2 -2
  75. package/dist/cli/chunks/{chunk-PINSJCM3.js → chunk-HVT7RMGV.js} +1 -1
  76. package/dist/cli/chunks/chunk-HWQPMUSN.js +2 -0
  77. package/dist/cli/chunks/{chunk-4NFQLP6R.js → chunk-HZ5O7G2P.js} +1 -1
  78. package/dist/cli/chunks/{chunk-C6D5AHBO.js → chunk-IHQGNDMX.js} +2 -2
  79. package/dist/cli/chunks/{chunk-QQWBWEJS.js → chunk-IJSROBM6.js} +3 -3
  80. package/dist/cli/chunks/{chunk-OCV2XEWQ.js → chunk-IJZFJPHS.js} +2 -2
  81. package/dist/cli/chunks/{chunk-N7HL5PHH.js → chunk-IKB3M7OF.js} +1 -1
  82. package/dist/cli/chunks/{chunk-24OX5TQJ.js → chunk-INI3Q4CN.js} +1 -1
  83. package/dist/cli/chunks/{chunk-SJ6Y4725.js → chunk-IOWEBPXJ.js} +2 -2
  84. package/dist/cli/chunks/{chunk-VXFLJNCA.js → chunk-IQL6XRF5.js} +2 -2
  85. package/dist/cli/chunks/{chunk-WP4RETKG.js → chunk-IRHXGCQR.js} +2 -2
  86. package/dist/cli/chunks/{chunk-63ZKI4PX.js → chunk-J3QFJI2Y.js} +2 -2
  87. package/dist/cli/chunks/{chunk-UHEOGZLX.js → chunk-J6QRNQJF.js} +5 -5
  88. package/dist/cli/chunks/{chunk-KNSRL6NO.js → chunk-J7Q5TXFZ.js} +2 -2
  89. package/dist/cli/chunks/{chunk-L6QSTQME.js → chunk-JHKKIJT2.js} +2 -2
  90. package/dist/cli/chunks/{chunk-VMDFFTCK.js → chunk-JI33PMPZ.js} +1 -1
  91. package/dist/cli/chunks/{chunk-E3D4GPLP.js → chunk-JRIJM4LE.js} +3 -3
  92. package/dist/cli/chunks/{chunk-XIQW7EFL.js → chunk-JVWZAE2H.js} +2 -2
  93. package/dist/cli/chunks/{chunk-YPBSUGMI.js → chunk-K7WAI662.js} +1 -1
  94. package/dist/cli/chunks/{chunk-RV74I3YO.js → chunk-KB34MTFZ.js} +2 -2
  95. package/dist/cli/chunks/{chunk-MI5MFHRA.js → chunk-KPIPVSIP.js} +2 -2
  96. package/dist/cli/chunks/{chunk-Y4PFUMOU.js → chunk-KR2TAAF6.js} +1 -1
  97. package/dist/cli/chunks/{chunk-W5HQUIML.js → chunk-KT2QCXO6.js} +2 -2
  98. package/dist/cli/chunks/{chunk-SSYAQZXA.js → chunk-KTORRIX3.js} +2 -2
  99. package/dist/cli/chunks/{chunk-CRPIYUTJ.js → chunk-LCWEQRZI.js} +2 -2
  100. package/dist/cli/chunks/{chunk-NO3IV5Q5.js → chunk-LG5PPJT7.js} +1 -1
  101. package/dist/cli/chunks/{chunk-5VZL4UVN.js → chunk-LIF6UWHV.js} +2 -2
  102. package/dist/cli/chunks/{chunk-32BPDLRZ.js → chunk-LN7NKE3N.js} +2 -2
  103. package/dist/cli/chunks/{chunk-ZB3LEFYG.js → chunk-LOLSZG5J.js} +2 -2
  104. package/dist/cli/chunks/{chunk-6XXZDV2C.js → chunk-LOMT4UC2.js} +2 -2
  105. package/dist/cli/chunks/{chunk-JDIYMZCQ.js → chunk-LRKLATSH.js} +2 -2
  106. package/dist/cli/chunks/{chunk-5SPCGMBB.js → chunk-LRY6UIRI.js} +2 -2
  107. package/dist/cli/chunks/{chunk-5PBLPQ5T.js → chunk-LV7UIK4Q.js} +2 -2
  108. package/dist/cli/chunks/{chunk-FDDC72IA.js → chunk-M2YI5YUR.js} +2 -2
  109. package/dist/cli/chunks/{chunk-D6COXLYE.js → chunk-MCXW3DKE.js} +2 -2
  110. package/dist/cli/chunks/{chunk-J442IZLE.js → chunk-MGZ6XZHU.js} +1 -1
  111. package/dist/cli/chunks/{chunk-TIUKLYAI.js → chunk-MLMD3I75.js} +1 -1
  112. package/dist/cli/chunks/{chunk-PHT5N7SC.js → chunk-MVVVGQJ6.js} +2 -2
  113. package/dist/cli/chunks/{chunk-525CFDUE.js → chunk-ND72UDVH.js} +2 -2
  114. package/dist/cli/chunks/{chunk-XWUWX7BV.js → chunk-NE3LRUNC.js} +4 -4
  115. package/dist/cli/chunks/{chunk-DTMAS2GB.js → chunk-NKUHMA4T.js} +2 -2
  116. package/dist/cli/chunks/{chunk-QPEOLAAS.js → chunk-NQFDJ6BT.js} +1 -1
  117. package/dist/cli/chunks/{chunk-MO55CVMT.js → chunk-NZJVDOUN.js} +14 -14
  118. package/dist/cli/chunks/{chunk-NZU4ACBZ.js → chunk-O7VJ3VKD.js} +2 -2
  119. package/dist/cli/chunks/{chunk-32GN3JKR.js → chunk-OJIUIEI4.js} +1 -1
  120. package/dist/cli/chunks/{chunk-UNGBRZDK.js → chunk-OO2M2QUV.js} +3 -3
  121. package/dist/cli/chunks/{chunk-CJSHWQXP.js → chunk-ORQ2YBRR.js} +2 -2
  122. package/dist/cli/chunks/chunk-OWIQR6N2.js +4 -0
  123. package/dist/cli/chunks/{chunk-NAEWCFQT.js → chunk-OWIZBZLN.js} +2 -2
  124. package/dist/cli/chunks/{chunk-24QHCVX6.js → chunk-P3D7SEHW.js} +1 -1
  125. package/dist/cli/chunks/{chunk-YLMNVLDK.js → chunk-P4OB6WDJ.js} +1 -1
  126. package/dist/cli/chunks/{chunk-PEQDY4BO.js → chunk-PCL5JRKN.js} +2 -2
  127. package/dist/cli/chunks/{chunk-IUBJVHNB.js → chunk-PIWGKGWP.js} +2 -2
  128. package/dist/cli/chunks/{chunk-GJYHWYXR.js → chunk-PK34KJH7.js} +2 -2
  129. package/dist/cli/chunks/{chunk-5B5QPCNU.js → chunk-POPSZDI2.js} +4 -4
  130. package/dist/cli/chunks/{chunk-HCH4Y6XX.js → chunk-Q6F3DYZN.js} +2 -2
  131. package/dist/cli/chunks/{chunk-SHKOCGNK.js → chunk-QJSSUBO3.js} +2 -2
  132. package/dist/cli/chunks/{chunk-DVYHMDFR.js → chunk-QN7HP2UW.js} +421 -418
  133. package/dist/cli/chunks/{chunk-ZFL436FN.js → chunk-QSQNXBD2.js} +2 -2
  134. package/dist/cli/chunks/{chunk-TVGI32MB.js → chunk-QTQDX4TZ.js} +3 -3
  135. package/dist/cli/chunks/{chunk-EK7OLYOZ.js → chunk-R3KBOQ3O.js} +2 -2
  136. package/dist/cli/chunks/{chunk-LE4QJREF.js → chunk-REY52O7J.js} +1 -1
  137. package/dist/cli/chunks/{chunk-LQLPB2T6.js → chunk-RKCN4B26.js} +2 -2
  138. package/dist/cli/chunks/{chunk-ZR2TKVXQ.js → chunk-S3DT5TPR.js} +2 -2
  139. package/dist/cli/chunks/{chunk-6ICX3FEG.js → chunk-S3TBOKAZ.js} +22 -22
  140. package/dist/cli/chunks/{chunk-JHQCRRJI.js → chunk-SAEECT23.js} +2 -2
  141. package/dist/cli/chunks/{chunk-VWPM27GU.js → chunk-SAZ4PJBR.js} +2 -2
  142. package/dist/cli/chunks/{chunk-KPRCB5LT.js → chunk-SFITYZLV.js} +2 -2
  143. package/dist/cli/chunks/{chunk-5LAVKGLH.js → chunk-SRBCBVSI.js} +2 -2
  144. package/dist/cli/chunks/{chunk-O5W3FIO6.js → chunk-SUAZ5Z47.js} +2 -2
  145. package/dist/cli/chunks/{chunk-KU2AUFGX.js → chunk-TDZ6N43D.js} +2 -2
  146. package/dist/cli/chunks/{chunk-YAPLJR7K.js → chunk-TIVKDIF7.js} +2 -2
  147. package/dist/cli/chunks/{chunk-U2A7NHZA.js → chunk-TM75GAXG.js} +2 -2
  148. package/dist/cli/chunks/{chunk-A5GBXCP5.js → chunk-TWBZONKT.js} +2 -2
  149. package/dist/cli/chunks/{chunk-I7MQIM2U.js → chunk-U4S74IIZ.js} +1 -1
  150. package/dist/cli/chunks/{chunk-3HD7E6HN.js → chunk-U5SMBJ6F.js} +2 -2
  151. package/dist/cli/chunks/{chunk-JQ3DYUMS.js → chunk-UHJVZEQA.js} +2 -2
  152. package/dist/cli/chunks/{chunk-C5FP5GW3.js → chunk-UOVCTK5L.js} +2 -2
  153. package/dist/cli/chunks/{chunk-HJ6ZGVTB.js → chunk-VDEKVIP5.js} +2 -2
  154. package/dist/cli/chunks/{chunk-KXCLACFG.js → chunk-VITV7AEC.js} +2 -2
  155. package/dist/cli/chunks/{chunk-P3EYRILV.js → chunk-VKREKVFI.js} +2 -2
  156. package/dist/cli/chunks/{chunk-2H4XH6FT.js → chunk-VLTDW7XJ.js} +2 -2
  157. package/dist/cli/chunks/{chunk-MD43XRX5.js → chunk-VQFV2IPV.js} +1 -1
  158. package/dist/cli/chunks/{chunk-67Z5R7RJ.js → chunk-VYPVU3JF.js} +2 -2
  159. package/dist/cli/chunks/{chunk-SDLWKRVL.js → chunk-WAOEQA3W.js} +2 -2
  160. package/dist/cli/chunks/{chunk-IOYG4VFL.js → chunk-WBDWHHKP.js} +2 -2
  161. package/dist/cli/chunks/{chunk-ASEOFRIG.js → chunk-WX4PO65Q.js} +2 -2
  162. package/dist/cli/chunks/chunk-X2DWHGCC.js +6 -0
  163. package/dist/cli/chunks/{chunk-CEYSYSK5.js → chunk-X5QD76IU.js} +2 -2
  164. package/dist/cli/chunks/{chunk-GSKMOPL7.js → chunk-XBTGEYED.js} +2 -2
  165. package/dist/cli/chunks/{chunk-FL64OUBA.js → chunk-XEMCHGAJ.js} +2 -2
  166. package/dist/cli/chunks/{chunk-2YZ4G6NW.js → chunk-XEYFZLIT.js} +1 -1
  167. package/dist/cli/chunks/{chunk-EA4YQVKS.js → chunk-XFTX3KLE.js} +1 -1
  168. package/dist/cli/chunks/{chunk-HGWXF45W.js → chunk-XPTCQKVE.js} +1 -1
  169. package/dist/cli/chunks/{chunk-CAKMOVZ6.js → chunk-YAJOBZE5.js} +2 -2
  170. package/dist/cli/chunks/{chunk-NQXXI77K.js → chunk-YCVLKU6H.js} +2 -2
  171. package/dist/cli/chunks/{chunk-AR67RJPY.js → chunk-YD2C6EPN.js} +2 -2
  172. package/dist/cli/chunks/{chunk-S75SOOZL.js → chunk-YKRLUTK7.js} +2 -2
  173. package/dist/cli/chunks/{chunk-IOKYCMTJ.js → chunk-YMUY3TES.js} +2 -2
  174. package/dist/cli/chunks/{chunk-5QO3R3TU.js → chunk-YR5QBUHZ.js} +2 -2
  175. package/dist/cli/chunks/{chunk-AJMJNQMP.js → chunk-Z7SKUC6P.js} +1 -1
  176. package/dist/cli/chunks/{chunk-KITOPKRG.js → chunk-ZAQMRFBB.js} +2 -2
  177. package/dist/cli/chunks/{chunk-AAMLQ2LF.js → chunk-ZC3KDNJW.js} +3 -3
  178. package/dist/cli/chunks/{chunk-VJCIEHEF.js → chunk-ZGV7L4MW.js} +1 -1
  179. package/dist/cli/chunks/{chunk-BY5OF7OH.js → chunk-ZN35XSKF.js} +2 -2
  180. package/dist/cli/chunks/{chunk-KKU2QGSJ.js → chunk-ZNXM6WVL.js} +2 -2
  181. package/dist/cli/chunks/{chunk-FLNAW7C4.js → chunk-ZP5AWQRO.js} +2 -2
  182. package/dist/cli/chunks/{chunk-IHRXZZQM.js → chunk-ZTP3DQ5M.js} +1 -1
  183. package/dist/cli/chunks/{chunk-OAEE6JWQ.js → chunk-ZVMSSCTH.js} +2 -2
  184. package/dist/cli/chunks/{chunk-M7WJCTG3.js → chunk-ZWUF27P4.js} +2 -2
  185. package/dist/cli/chunks/{ci-MFUR4CWV.js → ci-3RTTTS2D.js} +2 -2
  186. package/dist/cli/chunks/{ci-config-PNDLQ6XV.js → ci-config-XRUR3I4K.js} +2 -2
  187. package/dist/cli/chunks/{ci-output-PFZ4M7NC.js → ci-output-E5IAMU2U.js} +2 -2
  188. package/dist/cli/chunks/{circuit-breaker-5HH6NLZN.js → circuit-breaker-SHR2P44P.js} +2 -2
  189. package/dist/cli/chunks/{claude-flow-setup-QCKYLGLC.js → claude-flow-setup-ZDDILFMF.js} +2 -2
  190. package/dist/cli/chunks/client-NO5FOWT4.js +2 -0
  191. package/dist/cli/chunks/{cline-installer-EASVPFXR.js → cline-installer-BGYVUM6G.js} +2 -2
  192. package/dist/cli/chunks/{code-UJXDVYRK.js → code-PJ4MC3MC.js} +3 -3
  193. package/dist/cli/chunks/{code-index-extractor-BZJ3UDP4.js → code-index-extractor-IW2P22YG.js} +2 -2
  194. package/dist/cli/chunks/{codex-installer-XSQZUVOZ.js → codex-installer-A7WZ6OGL.js} +2 -2
  195. package/dist/cli/chunks/{completions-OWOOJFI2.js → completions-KS23WD5J.js} +2 -2
  196. package/dist/cli/chunks/{complexity-analyzer-KP5TOHDT.js → complexity-analyzer-Q7GLSR2E.js} +2 -2
  197. package/dist/cli/chunks/{continuedev-installer-IFKQXLHM.js → continuedev-installer-SK55BFW2.js} +2 -2
  198. package/dist/cli/chunks/copilot-installer-3N5J3WTM.js +3 -0
  199. package/dist/cli/chunks/core-handlers-JNAZPXE2.js +2 -0
  200. package/dist/cli/chunks/{cost-tracker-MMDDNKZB.js → cost-tracker-NENVSO4A.js} +2 -2
  201. package/dist/cli/chunks/{coverage-LUH2B7NT.js → coverage-5L7UQWDH.js} +3 -3
  202. package/dist/cli/chunks/cross-domain-router-HEBM767Z.js +2 -0
  203. package/dist/cli/chunks/{cursor-installer-4YLM5GWM.js → cursor-installer-XXBO2ZE6.js} +2 -2
  204. package/dist/cli/chunks/{daemon-MZKVM4CJ.js → daemon-CDCQU3LR.js} +3 -3
  205. package/dist/cli/chunks/{daemon-CAVZ7OFV.js → daemon-JEBJGEUK.js} +4 -4
  206. package/dist/cli/chunks/{dag-attention-scheduler-X4CYW665.js → dag-attention-scheduler-HOMMF7FA.js} +2 -2
  207. package/dist/cli/chunks/{detect-7BM2XINO.js → detect-FXCOUG6X.js} +2 -2
  208. package/dist/cli/chunks/{dist-node-Y5APLEYB.js → dist-node-GXQ7FMTY.js} +2 -2
  209. package/dist/cli/chunks/{domain-handler-X3ZRJNAH.js → domain-handler-TI3JBAPC.js} +2 -2
  210. package/dist/cli/chunks/{domain-plugins-2UW5KQJB.js → domain-plugins-R7LTUM32.js} +2 -2
  211. package/dist/cli/chunks/{domain-transfer-35W4CIBV.js → domain-transfer-LBIAFHIQ.js} +2 -2
  212. package/dist/cli/chunks/dream-GQDCEJMS.js +2 -0
  213. package/dist/cli/chunks/embed-and-insert-pattern-33KLHDD5.js +3 -0
  214. package/dist/cli/chunks/{embedding-space-SBM2MZD2.js → embedding-space-HHLWTBF5.js} +2 -2
  215. package/dist/cli/chunks/{eval-4QA2SROK.js → eval-KDOZMAXJ.js} +2 -2
  216. package/dist/cli/chunks/{experience-capture-middleware-Z6BF6KYM.js → experience-capture-middleware-X45PECQN.js} +3 -3
  217. package/dist/cli/chunks/{fast-paths-FIORX6GN.js → fast-paths-462V6UUB.js} +2 -2
  218. package/dist/cli/chunks/{feature-flags-ZOO7LMIC.js → feature-flags-G66OT3AS.js} +2 -2
  219. package/dist/cli/chunks/{feature-flags-6LYI2T4G.js → feature-flags-NIRQ4YDM.js} +2 -2
  220. package/dist/cli/chunks/file-discovery-33USPLB5.js +2 -0
  221. package/dist/cli/chunks/{fleet-7WF53H4F.js → fleet-PXWGB7CU.js} +3 -3
  222. package/dist/cli/chunks/{gnn-wrapper-KJ6B7BRM.js → gnn-wrapper-7MUW3M5M.js} +2 -2
  223. package/dist/cli/chunks/{heartbeat-handler-DEKKMOVW.js → heartbeat-handler-5237PB35.js} +4 -4
  224. package/dist/cli/chunks/heartbeat-scheduler-LIXLC5IH.js +2 -0
  225. package/dist/cli/chunks/hnsw-adapter-62D5UH27.js +2 -0
  226. package/dist/cli/chunks/hnsw-index-GSGUMQQC.js +2 -0
  227. package/dist/cli/chunks/{hnsw-legacy-bridge-L27WS27N.js → hnsw-legacy-bridge-OSQPW7JP.js} +2 -2
  228. package/dist/cli/chunks/{hnswlib-node-4YOQC6L4.js → hnswlib-node-GX6WG2GB.js} +2 -2
  229. package/dist/cli/chunks/{hooks-TAOKV4EJ.js → hooks-GUWHL7VC.js} +10 -10
  230. package/dist/cli/chunks/hybrid-backend-FRWP4BOY.js +2 -0
  231. package/dist/cli/chunks/{hypergraph-engine-BODRJLHL.js → hypergraph-engine-SRWLLCU3.js} +2 -2
  232. package/dist/cli/chunks/{hypergraph-handler-RKMYJNTU.js → hypergraph-handler-AVOPRHHO.js} +3 -3
  233. package/dist/cli/chunks/impact-analyzer-7KF3AQXV.js +2 -0
  234. package/dist/cli/chunks/{init-guard-MXHSLSB2.js → init-guard-P4N6EVZS.js} +2 -2
  235. package/dist/cli/chunks/{init-handler-HUE57IFW.js → init-handler-LRGWOJMD.js} +6 -6
  236. package/dist/cli/chunks/init-wizard-XWWAXY25.js +2 -0
  237. package/dist/cli/chunks/kernel-ZEJPNDBW.js +2 -0
  238. package/dist/cli/chunks/{kilocode-installer-S333IKYQ.js → kilocode-installer-7R2EPOQ6.js} +2 -2
  239. package/dist/cli/chunks/{kiro-installer-WM5DLQWC.js → kiro-installer-QNM7DPOB.js} +2 -2
  240. package/dist/cli/chunks/knowledge-graph-RRZQS7KY.js +2 -0
  241. package/dist/cli/chunks/{learning-LNHMQLH5.js → learning-7OIZBPXZ.js} +3 -3
  242. package/dist/cli/chunks/{llm-router-EWFXYZZR.js → llm-router-SIDXES23.js} +4 -4
  243. package/dist/cli/chunks/llm-router-service-3OSW67LC.js +2 -0
  244. package/dist/cli/chunks/{load-FGYGWVDK.js → load-EI2E2C3S.js} +2 -2
  245. package/dist/cli/chunks/load-test-7TRIH2WJ.js +2 -0
  246. package/dist/cli/chunks/{mcp-2FHNAAOM.js → mcp-IOE5WOCS.js} +2 -2
  247. package/dist/cli/chunks/{memory-DZMQ73AB.js → memory-DLYGJAP7.js} +5 -5
  248. package/dist/cli/chunks/memory-backend-G7ZW6Q2B.js +2 -0
  249. package/dist/cli/chunks/memory-handlers-TPF3KCO5.js +2 -0
  250. package/dist/cli/chunks/{multi-model-executor-6F2UAEUW.js → multi-model-executor-WHH4YCHM.js} +2 -2
  251. package/dist/cli/chunks/{opencode-installer-XY3BGOTZ.js → opencode-installer-63FDZAMS.js} +2 -2
  252. package/dist/cli/chunks/{orchestrator-B4CNKUOS.js → orchestrator-3LA776KK.js} +5 -5
  253. package/dist/cli/chunks/pattern-null-store-OU3ZC3NR.js +2 -0
  254. package/dist/cli/chunks/{pattern-usage-recorder-R4GUFNMD.js → pattern-usage-recorder-NW54QUGI.js} +2 -2
  255. package/dist/cli/chunks/{pipeline-FG5RKVRO.js → pipeline-P3TYX6J6.js} +2 -2
  256. package/dist/cli/chunks/{platform-3BT4KEVJ.js → platform-FNA2WLI6.js} +2 -2
  257. package/dist/cli/chunks/{plugin-WI4RIN7O.js → plugin-NJS7BC2S.js} +2 -2
  258. package/dist/cli/chunks/{prime-radiant-advanced-wasm-7ECDNQFS.js → prime-radiant-advanced-wasm-NIAVL2OT.js} +2 -2
  259. package/dist/cli/chunks/project-root-GMOFJEKD.js +2 -0
  260. package/dist/cli/chunks/protocol-executor-AD345XRO.js +2 -0
  261. package/dist/cli/chunks/{protocol-handler-WNODKPGW.js → protocol-handler-JU5C4JHY.js} +2 -2
  262. package/dist/cli/chunks/{prove-5RHAEPYZ.js → prove-35VOSLYP.js} +2 -2
  263. package/dist/cli/chunks/qe-reasoning-bank-NLTP26RC.js +2 -0
  264. package/dist/cli/chunks/{quality-WPHR7BGQ.js → quality-ET74X7HN.js} +2 -2
  265. package/dist/cli/chunks/{quality-gate-4RKTEZMX.js → quality-gate-FEDQISNB.js} +3 -3
  266. package/dist/cli/chunks/queen-coordinator-2TB4FWS6.js +2 -0
  267. package/dist/cli/chunks/real-embeddings-II6CXTCI.js +2 -0
  268. package/dist/cli/chunks/{ref-validation-TPAWHOHH.js → ref-validation-DTBPYCEB.js} +2 -2
  269. package/dist/cli/chunks/{roocode-installer-7AVHFNDI.js → roocode-installer-LYQEMR4J.js} +2 -2
  270. package/dist/cli/chunks/router-PLM3MWME.js +2 -0
  271. package/dist/cli/chunks/routing-feedback-I7VOX2HU.js +2 -0
  272. package/dist/cli/chunks/{routing-handler-YKEDVJSH.js → routing-handler-DJOJFPII.js} +2 -2
  273. package/dist/cli/chunks/{ruvector-commands-JUAMM354.js → ruvector-commands-VXYGJDPW.js} +2 -2
  274. package/dist/cli/chunks/rvf-dual-writer-GQZHFWO2.js +2 -0
  275. package/dist/cli/chunks/rvf-migration-adapter-3TVKBCAM.js +2 -0
  276. package/dist/cli/chunks/rvf-migration-coordinator-XUHKLMT6.js +2 -0
  277. package/dist/cli/chunks/rvf-native-adapter-5GUTYOWS.js +2 -0
  278. package/dist/cli/chunks/{rvf-store-integrity-VJDUIRTX.js → rvf-store-integrity-GA2ZYOEG.js} +2 -2
  279. package/dist/cli/chunks/safe-db-RZPQGLGQ.js +2 -0
  280. package/dist/cli/chunks/schedule-G7JMQDUX.js +2 -0
  281. package/dist/cli/chunks/scheduler-4SEGPCKD.js +2 -0
  282. package/dist/cli/chunks/{security-ORDN3RD2.js → security-MM6YOECX.js} +3 -3
  283. package/dist/cli/chunks/{shared-rvf-adapter-CF4BCIO7.js → shared-rvf-adapter-6AHPA3V5.js} +2 -2
  284. package/dist/cli/chunks/{shared-rvf-dual-writer-C32UBR6D.js → shared-rvf-dual-writer-EYAVY2GZ.js} +2 -2
  285. package/dist/cli/chunks/sqlite-persistence-IVE4SQXR.js +2 -0
  286. package/dist/cli/chunks/{standalone-memory-OXRJYZXN.js → standalone-memory-A3PLB4CZ.js} +2 -2
  287. package/dist/cli/chunks/{status-handler-BCX5D4HY.js → status-handler-OSSFAP4Z.js} +2 -2
  288. package/dist/cli/chunks/{structural-health-LFG7RUU6.js → structural-health-PGY5XUNO.js} +2 -2
  289. package/dist/cli/chunks/{sync-YJEE2K2L.js → sync-CWEHYGNN.js} +2 -2
  290. package/dist/cli/chunks/sync-PM6D7EQF.js +2 -0
  291. package/dist/cli/chunks/{task-handler-6LXKPTXJ.js → task-handler-LHMQ75W4.js} +2 -2
  292. package/dist/cli/chunks/{task-handlers-CILP36I7.js → task-handlers-S2TX5FTR.js} +3 -3
  293. package/dist/cli/chunks/test-4EGZZ6NJ.js +34 -0
  294. package/dist/cli/chunks/test-runner-process-72YACW6M.js +2 -0
  295. package/dist/cli/chunks/{test-scheduling-ISI2O4Q5.js → test-scheduling-YJQHH3XE.js} +3 -3
  296. package/dist/cli/chunks/{token-bootstrap-VUELSBOS.js → token-bootstrap-DJNETMVW.js} +2 -2
  297. package/dist/cli/chunks/{token-usage-XOC5KG7S.js → token-usage-WPNOJR6H.js} +2 -2
  298. package/dist/cli/chunks/{transformers-LSLQAARK.js → transformers-4MRPXCPU.js} +2 -2
  299. package/dist/cli/chunks/tree-sitter-wasm-parser-UVWSDXFR.js +2 -0
  300. package/dist/cli/chunks/{types-5BLCGLCR.js → types-6P5FB4EB.js} +2 -2
  301. package/dist/cli/chunks/unified-memory-2VMGSGQL.js +2 -0
  302. package/dist/cli/chunks/unified-memory-hnsw-3KULVD3N.js +2 -0
  303. package/dist/cli/chunks/unified-persistence-QCSLU2SV.js +2 -0
  304. package/dist/cli/chunks/{upgrade-RQJYLK3J.js → upgrade-4ICTN63Y.js} +2 -2
  305. package/dist/cli/chunks/{validate-QBHWXXCK.js → validate-HQF5GZ7L.js} +2 -2
  306. package/dist/cli/chunks/{validate-swarm-IKXBKFB5.js → validate-swarm-45JYG7VW.js} +2 -2
  307. package/dist/cli/chunks/visual-security-ZYP2KPH7.js +2 -0
  308. package/dist/cli/chunks/{web-tree-sitter-ZF6FIE5R.js → web-tree-sitter-UOTWZLNH.js} +2 -2
  309. package/dist/cli/chunks/{windsurf-installer-HQGDZT5D.js → windsurf-installer-WDWSBXGN.js} +2 -2
  310. package/dist/cli/chunks/witness-chain-5QRT7EMX.js +2 -0
  311. package/dist/cli/chunks/{witness-chain-DCHCAMSQ.js → witness-chain-DOYMNPC7.js} +2 -2
  312. package/dist/cli/chunks/{witness-chain-repair-PUX2NFDL.js → witness-chain-repair-CUF2AFQB.js} +2 -2
  313. package/dist/cli/chunks/{witness-chain-verifier-PX2OQ6XT.js → witness-chain-verifier-JB42X62J.js} +2 -2
  314. package/dist/cli/chunks/{witness-key-manager-RXYQNFJB.js → witness-key-manager-LXXO6L2G.js} +2 -2
  315. package/dist/cli/chunks/{workflow-FBKCA4H6.js → workflow-6ARK47NF.js} +4 -4
  316. package/dist/cli/chunks/workflow-orchestrator-RYDDWIXN.js +2 -0
  317. package/dist/cli/chunks/{wrappers-TWSKBTWD.js → wrappers-CYQBOMPL.js} +2 -2
  318. package/dist/cli/commands/test.js +26 -4
  319. package/dist/coordination/handlers/test-execution-handlers.js +64 -20
  320. package/dist/domains/code-intelligence/services/semantic-analyzer.d.ts +8 -3
  321. package/dist/domains/code-intelligence/services/semantic-analyzer.js +39 -25
  322. package/dist/domains/code-intelligence/services/text-relevance.d.ts +32 -0
  323. package/dist/domains/code-intelligence/services/text-relevance.js +63 -0
  324. package/dist/domains/test-execution/coordinator.d.ts +3 -1
  325. package/dist/domains/test-execution/coordinator.js +13 -4
  326. package/dist/domains/test-execution/interfaces.d.ts +5 -0
  327. package/dist/domains/test-execution/plugin.js +2 -0
  328. package/dist/domains/test-execution/services/flaky-detector.d.ts +9 -1
  329. package/dist/domains/test-execution/services/flaky-detector.js +26 -8
  330. package/dist/domains/test-execution/services/retry-handler.d.ts +7 -1
  331. package/dist/domains/test-execution/services/retry-handler.js +44 -5
  332. package/dist/domains/test-execution/services/runner-test-cases.d.ts +2 -0
  333. package/dist/domains/test-execution/services/runner-test-cases.js +7 -3
  334. package/dist/domains/test-execution/services/test-executor.d.ts +5 -1
  335. package/dist/domains/test-execution/services/test-executor.js +79 -15
  336. package/dist/init/copilot-installer.d.ts +6 -0
  337. package/dist/init/copilot-installer.js +37 -17
  338. package/dist/init/platform-config-generator.d.ts +10 -0
  339. package/dist/init/platform-config-generator.js +26 -1
  340. package/dist/kernel/unified-memory.d.ts +8 -0
  341. package/dist/kernel/unified-memory.js +13 -0
  342. package/dist/learning/embedder-endpoint-client.d.ts +45 -0
  343. package/dist/learning/embedder-endpoint-client.js +98 -3
  344. package/dist/learning/embedding-space-registry.d.ts +75 -0
  345. package/dist/learning/embedding-space-registry.js +142 -0
  346. package/dist/learning/real-embeddings.d.ts +3 -0
  347. package/dist/learning/real-embeddings.js +49 -11
  348. package/dist/mcp/bundle.js +564 -555
  349. package/dist/mcp/protocol-server.js +1 -1
  350. package/dist/shared/embeddings/index.d.ts +2 -2
  351. package/dist/shared/embeddings/index.js +1 -1
  352. package/dist/shared/embeddings/nomic-embedder.d.ts +8 -2
  353. package/dist/shared/embeddings/nomic-embedder.js +19 -11
  354. package/dist/shared/embeddings/ollama-client.d.ts +28 -2
  355. package/dist/shared/embeddings/ollama-client.js +84 -22
  356. package/dist/shared/embeddings/types.d.ts +15 -0
  357. package/dist/shared/embeddings/types.js +4 -0
  358. package/dist/shared/pytest-junit-report.d.ts +93 -0
  359. package/dist/shared/pytest-junit-report.js +241 -0
  360. package/dist/shared/test-runner-project.d.ts +51 -1
  361. package/dist/shared/test-runner-project.js +140 -25
  362. package/package.json +1 -1
  363. package/dist/cli/chunks/adapter-6CQ2PIP5.js +0 -2
  364. package/dist/cli/chunks/aqe-learning-engine-73RR52VS.js +0 -2
  365. package/dist/cli/chunks/base-SMCMMVGP.js +0 -2
  366. package/dist/cli/chunks/browser-workflow-A2MEZ66K.js +0 -2
  367. package/dist/cli/chunks/chunk-EGYE5UPQ.js +0 -2
  368. package/dist/cli/chunks/chunk-KL7OKSU2.js +0 -4
  369. package/dist/cli/chunks/chunk-M4FRQ4TE.js +0 -12
  370. package/dist/cli/chunks/chunk-UE5ZOZR2.js +0 -2
  371. package/dist/cli/chunks/client-MHQRTZM4.js +0 -2
  372. package/dist/cli/chunks/copilot-installer-IGJ6KGQY.js +0 -3
  373. package/dist/cli/chunks/core-handlers-UAK3LLJE.js +0 -2
  374. package/dist/cli/chunks/cross-domain-router-WTRZQPNQ.js +0 -2
  375. package/dist/cli/chunks/dream-EJY24JN6.js +0 -2
  376. package/dist/cli/chunks/embed-and-insert-pattern-COG4C7ZI.js +0 -3
  377. package/dist/cli/chunks/file-discovery-UZSFSS4V.js +0 -2
  378. package/dist/cli/chunks/heartbeat-scheduler-NVR356I7.js +0 -2
  379. package/dist/cli/chunks/hnsw-adapter-LFMPLUJU.js +0 -2
  380. package/dist/cli/chunks/hnsw-index-POCPMVJH.js +0 -2
  381. package/dist/cli/chunks/hybrid-backend-4STAO7CV.js +0 -2
  382. package/dist/cli/chunks/impact-analyzer-3VIIISBQ.js +0 -2
  383. package/dist/cli/chunks/init-wizard-PL6QN6RQ.js +0 -2
  384. package/dist/cli/chunks/kernel-CCDWT7KS.js +0 -2
  385. package/dist/cli/chunks/knowledge-graph-KFOMEN3H.js +0 -2
  386. package/dist/cli/chunks/llm-router-service-7NBE2EX6.js +0 -2
  387. package/dist/cli/chunks/load-test-UNZUWY3F.js +0 -2
  388. package/dist/cli/chunks/memory-backend-DFGKWA2Q.js +0 -2
  389. package/dist/cli/chunks/memory-handlers-MP426XRJ.js +0 -2
  390. package/dist/cli/chunks/pattern-null-store-SSVF5TE5.js +0 -2
  391. package/dist/cli/chunks/project-root-BEANNYWU.js +0 -2
  392. package/dist/cli/chunks/protocol-executor-FQVIHXZ7.js +0 -2
  393. package/dist/cli/chunks/qe-reasoning-bank-5TFYSE24.js +0 -2
  394. package/dist/cli/chunks/queen-coordinator-Y2OUL7IN.js +0 -2
  395. package/dist/cli/chunks/real-embeddings-OXLYU7JW.js +0 -2
  396. package/dist/cli/chunks/router-EP34O3KB.js +0 -2
  397. package/dist/cli/chunks/routing-feedback-FJCRKXNG.js +0 -2
  398. package/dist/cli/chunks/rvf-dual-writer-ILITJSFG.js +0 -2
  399. package/dist/cli/chunks/rvf-migration-adapter-XF6W4C2N.js +0 -2
  400. package/dist/cli/chunks/rvf-migration-coordinator-CMKG56AR.js +0 -2
  401. package/dist/cli/chunks/rvf-native-adapter-7YFW6S4Y.js +0 -2
  402. package/dist/cli/chunks/safe-db-TCAU4FUT.js +0 -2
  403. package/dist/cli/chunks/schedule-NB5KV435.js +0 -2
  404. package/dist/cli/chunks/scheduler-JKXGKVK6.js +0 -2
  405. package/dist/cli/chunks/sqlite-persistence-IXCNZRTW.js +0 -2
  406. package/dist/cli/chunks/sync-OSF7C2NL.js +0 -2
  407. package/dist/cli/chunks/test-SD457QJ6.js +0 -34
  408. package/dist/cli/chunks/test-runner-process-VKD65QQU.js +0 -2
  409. package/dist/cli/chunks/tree-sitter-wasm-parser-YAIPHRIL.js +0 -2
  410. package/dist/cli/chunks/unified-memory-GI76TXFV.js +0 -2
  411. package/dist/cli/chunks/unified-memory-hnsw-GXJ5BTZO.js +0 -2
  412. package/dist/cli/chunks/unified-persistence-OP7FDQPD.js +0 -2
  413. package/dist/cli/chunks/visual-security-GZ2MOKWB.js +0 -2
  414. package/dist/cli/chunks/witness-chain-P3XXXA62.js +0 -2
  415. package/dist/cli/chunks/workflow-orchestrator-P2APAJAA.js +0 -2
@@ -11,10 +11,11 @@ import { toError } from '../../shared/error-utils.js';
11
11
  import { capDiagnostics, getTestRunnerExecutionError, TestRunnerExecutionError } from '../../shared/test-runner-verdict.js';
12
12
  import { createVitestJsonReport } from '../../shared/vitest-json-report.js';
13
13
  import { jestExactPathPattern, runnerFilePath } from '../../shared/test-runner-args.js';
14
- import { testCasesFromRunnerReport } from '../../domains/test-execution/services/runner-test-cases.js';
14
+ import { testCaseFailureMessage, testCasesFromRunnerReport } from '../../domains/test-execution/services/runner-test-cases.js';
15
+ import { createPytestJunitReport, isPythonTestFile, pytestCommand, pytestRunFromReport, PYTEST_ENV } from '../../shared/pytest-junit-report.js';
15
16
  import { runTestRunnerProcess } from '../../shared/test-runner-process.js';
16
17
  import { MAX_PAYLOAD_FILE_BYTES, requirePayloadFiles } from '../../shared/io/payload-path-guard.js';
17
- import { groupByRunnerProject, requireTestRunTargets } from '../../shared/test-runner-project.js';
18
+ import { groupByRunnerProject, requireTestRunTargets, runnerFrameworkFor } from '../../shared/test-runner-project.js';
18
19
  function escapeRegExp(s) {
19
20
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
21
  }
@@ -58,36 +59,76 @@ export function rewriteTempPathsInGeneratedTest(testCode, sourceFile, tempPath,
58
59
  const newRef = sourceFile === tempPath ? (originalFilePath || sourceFile) : sourceFile;
59
60
  return { testCode: newCode, sourceFile: newRef };
60
61
  }
62
+ /** #1248: run Python test files with pytest, reading its JUnit report. */
63
+ async function runPytestTests(testFiles, cwd) {
64
+ const report = createPytestJunitReport();
65
+ try {
66
+ const { command, args } = pytestCommand(testFiles.map(file => runnerFilePath(file)), report.path);
67
+ const execution = await runTestRunnerProcess(command, args, { cwd, timeout: 120000, env: { ...process.env, ...PYTEST_ENV } });
68
+ const output = [execution.stderr, execution.stdout].filter(Boolean).join('\n');
69
+ if (execution.error || execution.signal) {
70
+ const reason = execution.error?.message ?? `Terminated by ${execution.signal}`;
71
+ return err(new TestRunnerExecutionError(`pytest could not complete: ${capDiagnostics(`${reason}\n${output}`)}`));
72
+ }
73
+ const run = pytestRunFromReport(report.read(), testFiles, execution.status, output, command);
74
+ if (!run.success)
75
+ return run;
76
+ const testCases = run.value.tests.map(test => {
77
+ const failureMessage = test.failureMessage && testCaseFailureMessage(test.failureMessage);
78
+ return {
79
+ name: test.name, file: test.file, status: test.status,
80
+ ...(test.duration !== undefined ? { duration: test.duration } : {}),
81
+ ...(failureMessage ? { failureMessage } : {}),
82
+ };
83
+ });
84
+ const { total, passed, failed, skipped } = run.value;
85
+ return ok({ total, passed, failed, skipped, testCases });
86
+ }
87
+ finally {
88
+ report.cleanup();
89
+ }
90
+ }
61
91
  /**
62
92
  * Run `testFiles` (paths relative to the process cwd, or absolute) with the
63
93
  * runner started in `cwd`.
64
94
  */
65
95
  async function runProjectTests(testFiles, cwd) {
96
+ if (testFiles.some(isPythonTestFile))
97
+ return runPytestTests(testFiles, cwd);
66
98
  // #1074: run the runner asynchronously so the MCP server keeps serving
67
99
  // other requests (task_status, task_cancel, ...) during the test run.
68
100
  const options = { cwd, timeout: 120000 };
101
+ const runJest = () => runTestRunnerProcess('npx', ['jest', ...testFiles.map(file => jestExactPathPattern(file)), '--json'], options);
69
102
  let runner = 'vitest';
70
- // Vitest 5 writes the JSON report to a file rather than stdout; an explicit
71
- // --outputFile gives Vitest 4 and 5 the same contract.
72
- const report = createVitestJsonReport();
73
103
  let execution;
74
- let output;
75
- try {
76
- // #909: absolute paths cannot be read as flags (e.g. a "--watch" entry).
77
- execution = await runTestRunnerProcess('npx', ['vitest', 'run', ...testFiles.map(file => runnerFilePath(file)), '--reporter=json', ...report.args], options);
78
- output = execution.error || execution.signal
79
- ? ''
80
- : report.read(execution.stdout || '') ?? '';
81
- }
82
- finally {
83
- report.cleanup();
84
- }
85
- // Preserve the existing Jest fallback when Vitest cannot produce a report.
86
- if (!output.includes('{') && execution.status !== 0 && !execution.error && !execution.signal) {
104
+ let output = '';
105
+ if (runnerFrameworkFor(cwd) === 'jest') {
106
+ // #1272: a project configured for Jest runs with Jest, never Vitest first.
87
107
  runner = 'jest';
88
- execution = await runTestRunnerProcess('npx', ['jest', ...testFiles.map(file => jestExactPathPattern(file)), '--json'], options);
108
+ execution = await runJest();
89
109
  output = execution.stdout || '';
90
110
  }
111
+ else {
112
+ // Vitest 5 writes the JSON report to a file rather than stdout; an explicit
113
+ // --outputFile gives Vitest 4 and 5 the same contract.
114
+ const report = createVitestJsonReport();
115
+ try {
116
+ // #909: absolute paths cannot be read as flags (e.g. a "--watch" entry).
117
+ execution = await runTestRunnerProcess('npx', ['vitest', 'run', ...testFiles.map(file => runnerFilePath(file)), '--reporter=json', ...report.args], options);
118
+ output = execution.error || execution.signal
119
+ ? ''
120
+ : report.read(execution.stdout || '') ?? '';
121
+ }
122
+ finally {
123
+ report.cleanup();
124
+ }
125
+ // Preserve the existing Jest fallback when Vitest cannot produce a report.
126
+ if (!output.includes('{') && execution.status !== 0 && !execution.error && !execution.signal) {
127
+ runner = 'jest';
128
+ execution = await runJest();
129
+ output = execution.stdout || '';
130
+ }
131
+ }
91
132
  const diagnostics = [execution.error?.message, execution.signal && `Terminated by ${execution.signal}`, execution.stderr, output].filter(Boolean).join('\n');
92
133
  if (execution.error) {
93
134
  return err(new TestRunnerExecutionError(`${runner} could not complete: ${capDiagnostics(diagnostics)}`));
@@ -286,8 +327,11 @@ export function registerTestExecutionHandlers(ctx) {
286
327
  }
287
328
  // #1247: a runner loads the config of the directory it starts in, so
288
329
  // each runner project's files run from that project (or projectRoot).
330
+ // #1248: Python files run apart from the others, with pytest.
289
331
  const groups = projectRoot
290
- ? [{ cwd: projectRoot, files: testFiles }]
332
+ ? [testFiles.filter(isPythonTestFile), testFiles.filter(file => !isPythonTestFile(file))]
333
+ .filter(files => files.length > 0)
334
+ .map(files => ({ cwd: projectRoot, files }))
291
335
  : groupByRunnerProject(testFiles);
292
336
  const totals = { total: 0, passed: 0, failed: 0, skipped: 0 };
293
337
  const testCases = [];
@@ -164,15 +164,20 @@ export declare class SemanticAnalyzerService implements ISemanticAnalyzerService
164
164
  * vector, so vector search cannot rank them. Match those by text, label the
165
165
  * hits 'lexical', and report the result as 'partial-vector' with the counts.
166
166
  * Cosine and text-match scores are not comparable, so vector hits come
167
- * first (by cosine), then lexical hits (by share of query words found); the
168
- * reason says how many text matches the limit left out.
167
+ * first (by cosine), then lexical hits (by share of query words found, ties
168
+ * by text relevance, #1287); the reason says how many text matches the
169
+ * limit left out.
169
170
  */
170
171
  private withVectorlessFiles;
171
172
  /** Word-overlap matching for a semantic query when no vectors can be used. */
172
173
  private lexicalSearch;
173
174
  private exactSearch;
174
175
  private fuzzySearch;
175
- /** Token-overlap match of one indexed file, or null when it is filtered out or scores too low. */
176
+ /**
177
+ * Token-overlap match of one indexed file, or null when it is filtered out or
178
+ * scores too low. The score is the share of query words found; `relevance`
179
+ * only orders equal scores (#1287).
180
+ */
176
181
  private fuzzyMatch;
177
182
  private extractConcepts;
178
183
  private detectPatterns;
@@ -6,12 +6,15 @@ import { createHash } from 'node:crypto';
6
6
  import { ok, err } from '../../../shared/types/index.js';
7
7
  import { NomicEmbedder, EMBEDDING_CONFIG, } from '../../../shared/embeddings/index.js';
8
8
  import { toError } from '../../../shared/error-utils.js';
9
+ import { compareTextRelevance, textRelevance } from './text-relevance.js';
9
10
  /**
10
11
  * #1224: the message for a search over an empty index. "No results" is only
11
12
  * reported after searching an index that holds code.
12
13
  */
13
14
  export const NOTHING_INDEXED_FOR_SEARCH = 'No code is indexed for search yet, so the search was not run. ' +
14
15
  'Index the project first (MCP code_index, or `aqe code index <path>`), then search again.';
16
+ /** Highest score first; equal scores by text relevance (#1287). */
17
+ const byScoreThenRelevance = (a, b) => b.hit.score - a.hit.score || compareTextRelevance(a.relevance, b.relevance);
15
18
  /** Upper bound on indexed files one exact/fuzzy search reads. */
16
19
  const MAX_INDEXED_FILES_SCANNED = 100_000;
17
20
  const DEFAULT_CONFIG = {
@@ -60,17 +63,17 @@ export class SemanticAnalyzerService {
60
63
  ...this.config.nomicConfig,
61
64
  enableFallback: false,
62
65
  });
63
- // Check if Ollama is available
64
- const isAvailable = await nomicEmbedder.healthCheck();
65
- if (isAvailable) {
66
+ // #1229: Ollama lists the model under its own name (all-minilm:22m);
67
+ // the reason says whether Ollama is down or lacks the model.
68
+ const check = await nomicEmbedder.checkModel();
69
+ if (check.status === 'available') {
66
70
  this.embedder = nomicEmbedder;
67
71
  // Update dimension to match Nomic's output
68
72
  this.config.embeddingDimension =
69
73
  EMBEDDING_CONFIG.DIMENSIONS;
70
74
  }
71
75
  else {
72
- this.embedderUnavailableReason =
73
- `no embedding model available (Ollama model '${EMBEDDING_CONFIG.MODEL}' is not reachable)`;
76
+ this.embedderUnavailableReason = `no embedding model available (${check.reason})`;
74
77
  }
75
78
  }
76
79
  else {
@@ -276,7 +279,7 @@ export class SemanticAnalyzerService {
276
279
  }
277
280
  /** Identifies the model behind stored vectors, so vectors from another model are not compared. */
278
281
  embeddingModelId() {
279
- return this.embedder instanceof NomicEmbedder ? EMBEDDING_CONFIG.MODEL : 'injected-provider';
282
+ return this.embedder instanceof NomicEmbedder ? this.embedder.getModelId() : 'injected-provider';
280
283
  }
281
284
  isCurrentModelVector(metadata) {
282
285
  return metadata.embeddingModel === this.embeddingModelId();
@@ -366,8 +369,9 @@ export class SemanticAnalyzerService {
366
369
  * vector, so vector search cannot rank them. Match those by text, label the
367
370
  * hits 'lexical', and report the result as 'partial-vector' with the counts.
368
371
  * Cosine and text-match scores are not comparable, so vector hits come
369
- * first (by cosine), then lexical hits (by share of query words found); the
370
- * reason says how many text matches the limit left out.
372
+ * first (by cosine), then lexical hits (by share of query words found, ties
373
+ * by text relevance, #1287); the reason says how many text matches the
374
+ * limit left out.
371
375
  */
372
376
  async withVectorlessFiles(query, vectorHits, limit, scope, filters) {
373
377
  const model = this.embeddingModelId();
@@ -383,16 +387,19 @@ export class SemanticAnalyzerService {
383
387
  withoutVector++;
384
388
  if (vectorFiles.has(stored.file))
385
389
  return;
386
- const hit = this.fuzzyMatch(queryTokens, stored, scope, filters);
387
- if (hit)
388
- lexicalHits.push({ ...hit, matchType: 'lexical' });
390
+ const match = this.fuzzyMatch(query, queryTokens, stored, scope, filters);
391
+ if (match)
392
+ lexicalHits.push({ ...match, hit: { ...match.hit, matchType: 'lexical' } });
389
393
  });
390
394
  const coverage = { indexedFiles, filesWithVector: indexedFiles - withoutVector };
391
395
  if (withoutVector === 0) {
392
396
  return { results: vectorHits, mode: 'vector', coverage };
393
397
  }
394
398
  const byScore = (a, b) => b.score - a.score;
395
- const results = [...[...vectorHits].sort(byScore), ...lexicalHits.sort(byScore)].slice(0, limit);
399
+ const results = [
400
+ ...[...vectorHits].sort(byScore),
401
+ ...lexicalHits.sort(byScoreThenRelevance).map((match) => match.hit),
402
+ ].slice(0, limit);
396
403
  const droppedLexical = vectorHits.length + lexicalHits.length - results.length;
397
404
  return {
398
405
  results,
@@ -449,18 +456,22 @@ export class SemanticAnalyzerService {
449
456
  return results;
450
457
  }
451
458
  async fuzzySearch(query, limit, scope, filters) {
452
- const results = [];
459
+ const matches = [];
453
460
  const queryTokens = this.tokenize(query.toLowerCase());
454
461
  await this.forEachIndexed((stored) => {
455
- const hit = this.fuzzyMatch(queryTokens, stored, scope, filters);
456
- if (hit)
457
- results.push(hit);
462
+ const match = this.fuzzyMatch(query, queryTokens, stored, scope, filters);
463
+ if (match)
464
+ matches.push(match);
458
465
  });
459
- // Sort by score and limit
460
- return results.sort((a, b) => b.score - a.score).slice(0, limit);
466
+ // Sort by score, ties by relevance (#1287), and limit
467
+ return matches.sort(byScoreThenRelevance).slice(0, limit).map((match) => match.hit);
461
468
  }
462
- /** Token-overlap match of one indexed file, or null when it is filtered out or scores too low. */
463
- fuzzyMatch(queryTokens, stored, scope, filters) {
469
+ /**
470
+ * Token-overlap match of one indexed file, or null when it is filtered out or
471
+ * scores too low. The score is the share of query words found; `relevance`
472
+ * only orders equal scores (#1287).
473
+ */
474
+ fuzzyMatch(query, queryTokens, stored, scope, filters) {
464
475
  // Apply scope filter
465
476
  if (scope && scope.length > 0 && !scope.some((s) => stored.file.includes(s))) {
466
477
  return null;
@@ -475,11 +486,14 @@ export class SemanticAnalyzerService {
475
486
  if (score < this.config.minScore)
476
487
  return null;
477
488
  return {
478
- file: stored.file,
479
- snippet: this.extractSnippet(stored.content, 200),
480
- score,
481
- highlights: queryTokens.filter((t) => contentTokens.some((ct) => this.tokensMatch(t, ct))),
482
- metadata: stored.metadata,
489
+ hit: {
490
+ file: stored.file,
491
+ snippet: this.extractSnippet(stored.content, 200),
492
+ score,
493
+ highlights: queryTokens.filter((t) => contentTokens.some((ct) => this.tokensMatch(t, ct))),
494
+ metadata: stored.metadata,
495
+ },
496
+ relevance: textRelevance(query, stored.content),
483
497
  };
484
498
  }
485
499
  // ============================================================================
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Agentic QE v3 - Text match relevance (#1287)
3
+ *
4
+ * Text matching scores a file by the share of query words it contains, so
5
+ * every file containing all of them scores 1.0. This ranks those ties by how
6
+ * well the file matches the query, without changing the score itself.
7
+ */
8
+ /** How well one file's text matches a query. Higher fields rank first, in this order. */
9
+ export interface TextRelevance {
10
+ /**
11
+ * 2: an identifier is exactly the query words (`processOrder`, `process_order`
12
+ * for "process order"); 1: the query words appear next to each other
13
+ * (`process the order` does not, `process order` and `processOrderItems` do);
14
+ * 0: scattered, or the query is a single word.
15
+ */
16
+ adjacency: number;
17
+ /** Share of query words found as a whole word (`order`), not only inside one (`orders`, `reorder`). */
18
+ exactWords: number;
19
+ /** Number of words in the file that contain a query word. */
20
+ occurrences: number;
21
+ }
22
+ /** Splits an identifier into lowercase words: `processOrder`, `process_order`, `PROCESS_ORDER` → process, order. */
23
+ export declare function identifierWords(identifier: string): string[];
24
+ /**
25
+ * Relevance of `content` to `query`. Words are compared case-insensitively,
26
+ * and identifiers are split into words, so the query `process order` matches
27
+ * the identifier `processOrder` as adjacent whole words.
28
+ */
29
+ export declare function textRelevance(query: string, content: string): TextRelevance;
30
+ /** Sort comparator: more relevant first. */
31
+ export declare function compareTextRelevance(a: TextRelevance, b: TextRelevance): number;
32
+ //# sourceMappingURL=text-relevance.d.ts.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Agentic QE v3 - Text match relevance (#1287)
3
+ *
4
+ * Text matching scores a file by the share of query words it contains, so
5
+ * every file containing all of them scores 1.0. This ranks those ties by how
6
+ * well the file matches the query, without changing the score itself.
7
+ */
8
+ /** Splits an identifier into lowercase words: `processOrder`, `process_order`, `PROCESS_ORDER` → process, order. */
9
+ export function identifierWords(identifier) {
10
+ return identifier
11
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
12
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
13
+ .split(/[^A-Za-z0-9]+/)
14
+ .filter((word) => word.length > 0)
15
+ .map((word) => word.toLowerCase());
16
+ }
17
+ function identifiers(text) {
18
+ return text.split(/[^\w]+/).filter((token) => token.length > 0);
19
+ }
20
+ /** Whether `words` appears in `stream` as consecutive entries. */
21
+ function containsRun(stream, words) {
22
+ outer: for (let i = 0; i + words.length <= stream.length; i++) {
23
+ for (let j = 0; j < words.length; j++) {
24
+ if (stream[i + j] !== words[j])
25
+ continue outer;
26
+ }
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+ /**
32
+ * Relevance of `content` to `query`. Words are compared case-insensitively,
33
+ * and identifiers are split into words, so the query `process order` matches
34
+ * the identifier `processOrder` as adjacent whole words.
35
+ */
36
+ export function textRelevance(query, content) {
37
+ const queryWords = identifiers(query).flatMap(identifierWords);
38
+ const contentIdentifiers = identifiers(content);
39
+ const lowerIdentifiers = contentIdentifiers.map((token) => token.toLowerCase());
40
+ const splitIdentifiers = contentIdentifiers.map(identifierWords);
41
+ const contentWords = new Set([...lowerIdentifiers, ...splitIdentifiers.flat()]);
42
+ const queryTokens = identifiers(query.toLowerCase());
43
+ let adjacency = 0;
44
+ if (queryWords.length > 1) {
45
+ const phrase = queryWords.join(' ');
46
+ if (splitIdentifiers.some((words) => words.join(' ') === phrase))
47
+ adjacency = 2;
48
+ else if (containsRun(splitIdentifiers.flat(), queryWords))
49
+ adjacency = 1;
50
+ }
51
+ return {
52
+ adjacency,
53
+ exactWords: queryWords.length === 0
54
+ ? 0
55
+ : queryWords.filter((word) => contentWords.has(word)).length / queryWords.length,
56
+ occurrences: lowerIdentifiers.filter((token) => queryTokens.some((qt) => token.includes(qt))).length,
57
+ };
58
+ }
59
+ /** Sort comparator: more relevant first. */
60
+ export function compareTextRelevance(a, b) {
61
+ return b.adjacency - a.adjacency || b.exactWords - a.exactWords || b.occurrences - a.occurrences;
62
+ }
63
+ //# sourceMappingURL=text-relevance.js.map
@@ -131,7 +131,9 @@ export declare class TestExecutionCoordinator extends BaseDomainCoordinator<Test
131
131
  */
132
132
  private retryFailures;
133
133
  /**
134
- * Detect test framework from file patterns and project configuration
134
+ * Fallback framework from file names. With no explicit framework, each
135
+ * runner project's own config still decides between jest and vitest (#1272),
136
+ * and Python files always run with pytest (#1248).
135
137
  */
136
138
  private detectFramework;
137
139
  /**
@@ -16,6 +16,7 @@ import { createDomainFinding, } from '../../coordination/consensus/domain-findin
16
16
  // CQ-002: Base domain coordinator
17
17
  import { BaseDomainCoordinator, } from '../base-domain-coordinator.js';
18
18
  import { toError } from '../../shared/error-utils.js';
19
+ import { isPythonTestFile } from '../../shared/pytest-junit-report.js';
19
20
  import { createMinCutTestOptimizer, } from './services/mincut-test-optimizer.js';
20
21
  // ============================================================================
21
22
  // Coordinator Configuration
@@ -146,6 +147,8 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
146
147
  });
147
148
  }
148
149
  const framework = request.framework ?? this.detectFramework(testsToRun);
150
+ // #1272: without an explicit framework, each runner project's config decides.
151
+ const frameworkFromConfig = request.framework === undefined;
149
152
  const workers = request.workers ?? Math.min(4, Math.max(1, testsToRun.length));
150
153
  // Batched runners own internal parallelism, so the timeout applies to the
151
154
  // whole discovered suite rather than one file.
@@ -157,6 +160,7 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
157
160
  return this.executeParallel({
158
161
  testFiles: testsToRun,
159
162
  framework,
163
+ frameworkFromConfig,
160
164
  workers,
161
165
  timeout,
162
166
  retryCount: request.retryCount,
@@ -167,6 +171,7 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
167
171
  return this.execute({
168
172
  testFiles: testsToRun,
169
173
  framework,
174
+ frameworkFromConfig,
170
175
  timeout,
171
176
  retryCount: request.retryCount,
172
177
  projectRoot: request.projectRoot,
@@ -198,9 +203,13 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
198
203
  await this.executor.recordFinalOutcome(run, testFiles);
199
204
  }
200
205
  /**
201
- * Detect test framework from file patterns and project configuration
206
+ * Fallback framework from file names. With no explicit framework, each
207
+ * runner project's own config still decides between jest and vitest (#1272),
208
+ * and Python files always run with pytest (#1248).
202
209
  */
203
210
  detectFramework(testFiles) {
211
+ if (testFiles.every(isPythonTestFile))
212
+ return 'pytest';
204
213
  // Check file patterns for common framework indicators
205
214
  const hasVitestPattern = testFiles.some(f => f.includes('.test.') || f.includes('.spec.'));
206
215
  const hasMochaPattern = testFiles.some(f => f.includes('_test.') || f.includes('.mocha.'));
@@ -209,7 +218,6 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
209
218
  return 'vitest';
210
219
  if (hasMochaPattern)
211
220
  return 'mocha';
212
- // Could add project config detection here (vitest.config.ts, jest.config.js, etc.)
213
221
  return 'vitest';
214
222
  }
215
223
  /**
@@ -299,7 +307,7 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
299
307
  if (this.config.infraHealing && result.value.failedTests.length > 0) {
300
308
  this.feedInfraHealingFromFailures(result.value.failedTests);
301
309
  // ADR-057: Attempt automatic recovery and re-run affected tests
302
- const rerunResult = await this.attemptAutoRecoveryAndRerun(result.value.failedTests, request.framework, { timeout: request.timeout, env: request.env, retryCount: request.retryCount, projectRoot: request.projectRoot });
310
+ const rerunResult = await this.attemptAutoRecoveryAndRerun(result.value.failedTests, request.framework, { timeout: request.timeout, env: request.env, retryCount: request.retryCount, projectRoot: request.projectRoot, frameworkFromConfig: request.frameworkFromConfig });
303
311
  if (rerunResult)
304
312
  return rerunResult;
305
313
  }
@@ -410,7 +418,7 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
410
418
  if (this.config.infraHealing && result.value.failedTests.length > 0) {
411
419
  this.feedInfraHealingFromFailures(result.value.failedTests);
412
420
  // ADR-057: Attempt automatic recovery and re-run affected tests
413
- const rerunResult = await this.attemptAutoRecoveryAndRerun(result.value.failedTests, request.framework, { timeout: request.timeout, env: request.env, retryCount: request.retryCount, projectRoot: request.projectRoot });
421
+ const rerunResult = await this.attemptAutoRecoveryAndRerun(result.value.failedTests, request.framework, { timeout: request.timeout, env: request.env, retryCount: request.retryCount, projectRoot: request.projectRoot, frameworkFromConfig: request.frameworkFromConfig });
414
422
  if (rerunResult)
415
423
  return rerunResult;
416
424
  }
@@ -631,6 +639,7 @@ export class TestExecutionCoordinator extends BaseDomainCoordinator {
631
639
  return this.execute({
632
640
  testFiles: [...filesToRerun],
633
641
  framework,
642
+ frameworkFromConfig: originalRequest.frameworkFromConfig,
634
643
  timeout: originalRequest.timeout,
635
644
  env: originalRequest.env,
636
645
  retryCount: originalRequest.retryCount,
@@ -56,6 +56,11 @@ export interface ISimpleTestRequest {
56
56
  export interface IExecuteTestsRequest {
57
57
  testFiles: string[];
58
58
  framework: string;
59
+ /**
60
+ * #1272: the caller named no framework, so each runner project's own config
61
+ * chooses jest or vitest; `framework` is used where it names neither.
62
+ */
63
+ frameworkFromConfig?: boolean;
59
64
  timeout?: number;
60
65
  env?: Record<string, string>;
61
66
  reporters?: string[];
@@ -80,6 +80,8 @@ export class TestExecutionPlugin extends BaseDomainPlugin {
80
80
  // Simple request format
81
81
  const request = {
82
82
  testFiles,
83
+ // #1272: an explicit framework wins over the project's config.
84
+ framework: typeof payload.framework === 'string' ? payload.framework : undefined,
83
85
  parallel: payload.parallel,
84
86
  retryCount: payload.retryCount,
85
87
  timeout: payload.timeout,
@@ -39,7 +39,8 @@ export interface FlakyDetectorConfig {
39
39
  */
40
40
  testRunnerArgs: string[];
41
41
  /**
42
- * Working directory for test execution.
42
+ * Working directory for test execution. When omitted, each file runs from
43
+ * its nearest runner project, as test_execute_parallel does (#1247, #1272).
43
44
  */
44
45
  cwd?: string;
45
46
  /**
@@ -112,6 +113,13 @@ export declare class FlakyDetectorService implements IFlakyTestDetector {
112
113
  /** Cache TTL for analysis results (1 hour) */
113
114
  private readonly ANALYSIS_CACHE_TTL_MS;
114
115
  constructor(memory: MemoryBackend, config?: Partial<FlakyDetectorConfig>);
116
+ /** True when the caller chose the runner command; else the project's config picks it. */
117
+ private readonly runnerConfigured;
118
+ /**
119
+ * #1272: the runner command for a run started in `cwd`. A configured command
120
+ * is used as given; the default runs Jest in a project configured for Jest.
121
+ */
122
+ private runnerCommandIn;
115
123
  /**
116
124
  * Detect flaky tests by running them multiple times
117
125
  */
@@ -14,6 +14,7 @@ import { safeJsonParse } from '../../../shared/safe-json.js';
14
14
  import { getTestRunnerExecutionError } from '../../../shared/test-runner-verdict.js';
15
15
  import { createVitestJsonReport, needsVitestJsonReportFile } from '../../../shared/vitest-json-report.js';
16
16
  import { isJestCommand, jestExactPathPattern, runnerFilePath } from '../../../shared/test-runner-args.js';
17
+ import { runnerCwdFor, runnerFrameworkFor } from '../../../shared/test-runner-project.js';
17
18
  import { secureRandom } from '../../../shared/utils/crypto-random.js';
18
19
  import { redactFailureText } from './runner-test-cases.js';
19
20
  const DEFAULT_FLAKY_CONFIG = {
@@ -41,6 +42,19 @@ export class FlakyDetectorService {
41
42
  constructor(memory, config = {}) {
42
43
  this.memory = memory;
43
44
  this.config = { ...DEFAULT_FLAKY_CONFIG, ...config };
45
+ this.runnerConfigured = config.testRunner !== undefined || config.testRunnerArgs !== undefined;
46
+ }
47
+ /** True when the caller chose the runner command; else the project's config picks it. */
48
+ runnerConfigured;
49
+ /**
50
+ * #1272: the runner command for a run started in `cwd`. A configured command
51
+ * is used as given; the default runs Jest in a project configured for Jest.
52
+ */
53
+ runnerCommandIn(cwd) {
54
+ if (!this.runnerConfigured && runnerFrameworkFor(cwd) === 'jest') {
55
+ return { runner: 'npx', runnerArgs: ['jest', '--json'] };
56
+ }
57
+ return { runner: this.config.testRunner, runnerArgs: this.config.testRunnerArgs };
44
58
  }
45
59
  /**
46
60
  * Detect flaky tests by running them multiple times
@@ -259,20 +273,24 @@ export class FlakyDetectorService {
259
273
  const runId = uuidv4();
260
274
  const startTime = Date.now();
261
275
  return new Promise((resolve, reject) => {
262
- const cwd = this.config.cwd ?? process.cwd();
276
+ const fileBase = this.config.cwd ?? process.cwd();
277
+ // #1272: a runner loads the config of the directory it starts in, so the
278
+ // file runs from its own runner project (the file path stays absolute).
279
+ const cwd = this.config.cwd ?? runnerCwdFor(file);
280
+ const { runner, runnerArgs } = this.runnerCommandIn(cwd);
263
281
  // #909: pass an absolute path (never read as a flag); Jest reads it as a
264
282
  // regex, so escape and anchor it to select exactly this file.
265
- const fileArg = isJestCommand([this.config.testRunner, ...this.config.testRunnerArgs])
266
- ? jestExactPathPattern(file, cwd)
267
- : runnerFilePath(file, cwd);
268
- const baseArgs = [...this.config.testRunnerArgs, fileArg];
283
+ const fileArg = isJestCommand([runner, ...runnerArgs])
284
+ ? jestExactPathPattern(file, fileBase)
285
+ : runnerFilePath(file, fileBase);
286
+ const baseArgs = [...runnerArgs, fileArg];
269
287
  // Vitest 5 writes --reporter=json output to a file, not stdout. Detect
270
288
  // Vitest from the runner command too (testRunner: 'vitest', args: ['run', ...]).
271
- const report = needsVitestJsonReportFile([this.config.testRunner, ...baseArgs])
289
+ const report = needsVitestJsonReportFile([runner, ...baseArgs])
272
290
  ? createVitestJsonReport()
273
291
  : undefined;
274
292
  const args = report ? [...baseArgs, ...report.args] : baseArgs;
275
- const child = spawn(this.config.testRunner, args, {
293
+ const child = spawn(runner, args, {
276
294
  cwd,
277
295
  env: { ...process.env, ...this.config.env },
278
296
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -295,7 +313,7 @@ export class FlakyDetectorService {
295
313
  clearTimeout(timeout);
296
314
  report?.cleanup();
297
315
  reject(new Error(`Failed to execute test runner: ${error.message}. ` +
298
- `Ensure ${this.config.testRunner} is installed and accessible.`));
316
+ `Ensure ${[runner, ...runnerArgs.slice(0, 1)].join(' ')} is installed and accessible.`));
299
317
  });
300
318
  child.on('close', (code) => {
301
319
  clearTimeout(timeout);
@@ -8,7 +8,7 @@ import { MemoryBackend } from '../../../kernel/interfaces';
8
8
  /**
9
9
  * Supported test runners for retry execution
10
10
  */
11
- export type TestRunner = 'vitest' | 'jest' | 'mocha' | 'auto';
11
+ export type TestRunner = 'vitest' | 'jest' | 'mocha' | 'pytest' | 'auto';
12
12
  /**
13
13
  * Configuration for the retry handler service
14
14
  */
@@ -159,6 +159,12 @@ export declare class RetryHandlerService implements IRetryHandler {
159
159
  * Spawn a test process and parse the result
160
160
  */
161
161
  private spawnTestProcess;
162
+ /**
163
+ * #1248: a pytest retry passes only when its JUnit report shows the test
164
+ * passed. A node id pytest cannot find (exit code 4) or no collected test
165
+ * (exit code 5) reran nothing.
166
+ */
167
+ private parsePytestRetry;
162
168
  /**
163
169
  * Parse test runner output to determine pass/fail status
164
170
  */