@poetic-ai/poetic 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/SECURITY.md +47 -0
- package/.nvmrc +1 -0
- package/.poetic/README.md +37 -0
- package/.poetic/providers/catalog.json +12051 -0
- package/.poetic/providers/pricing.json +1272 -0
- package/.poetic/providers/registry.json +3369 -0
- package/CHANGELOG.md +552 -0
- package/CODE_OF_CONDUCT.md +40 -0
- package/CONTRIBUTING.md +23 -0
- package/INSTALL.md +454 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/BasicOptimizationCompetitionRunner-5H5TBYO4.js +153 -0
- package/dist/ExecutionTracker-UVONC4C6.js +16 -0
- package/dist/OptimizationConfig-7DFY2TST.js +17 -0
- package/dist/OptimizationEngine-JIMGP3LY.js +1597 -0
- package/dist/PromptStore-U6ZD3ZMQ.js +18 -0
- package/dist/actions-LUJPINLJ.js +322 -0
- package/dist/agent-ingest-3DJUJ7FU.js +53 -0
- package/dist/aggregator-DNCBINFO.js +12 -0
- package/dist/ai-judge-5QT5QJVU.js +156 -0
- package/dist/allowlist-grounding-RO6HKUFI.js +16 -0
- package/dist/allowlist-utils-23V7TJ3G.js +28 -0
- package/dist/anchored-turn-service-JULUCUIK.js +489 -0
- package/dist/api-transport-4TYTNLTJ.js +1087 -0
- package/dist/apply-completion-mode-AW2MOS3R.js +89 -0
- package/dist/artifact-migrator-23L45ISD.js +267 -0
- package/dist/ask-HWVV6QFR.js +135 -0
- package/dist/auth-5KEDKAPP.js +67 -0
- package/dist/auth-7TT3JINV.js +78 -0
- package/dist/auth-E6XUNZ5M.js +68 -0
- package/dist/auth-H3XDZE22.js +134 -0
- package/dist/auth-OC4E6633.js +71 -0
- package/dist/auth-S4CTX4GQ.js +44 -0
- package/dist/auth-YLH3S7EY.js +69 -0
- package/dist/auth-liveness-TDDEZY3T.js +46 -0
- package/dist/auto-optimizer-VPRWOWGU.js +80 -0
- package/dist/autoloop-cleanup-JOGKTLWA.js +109 -0
- package/dist/autoloop-evidence-gates-PCDALGVJ.js +38 -0
- package/dist/autoloop-gate-policy-Z7FVJVXV.js +175 -0
- package/dist/autoloop-helpers-2KTK43GL.js +22 -0
- package/dist/autoloop-ledger-W7QO62PX.js +96 -0
- package/dist/autoloop-ledger-subscriber-Z5RW5EIE.js +162 -0
- package/dist/autoloop-run-defaults-FERGRZGP.js +19 -0
- package/dist/autoloop-run-lock-CT2NWSV3.js +197 -0
- package/dist/autoloop-success-J2VKKBXQ.js +85 -0
- package/dist/autoloop-termination-summary-RN75F422.js +58 -0
- package/dist/autoloop-trajectory-XFIEXP5Z.js +282 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/autonomous-loop-controller-PW4XVFGQ.js +4120 -0
- package/dist/backend-P3DAYRP7.js +29 -0
- package/dist/background-executor-CBN552V4.js +257 -0
- package/dist/backlog-execution-intent-U4XYPTV6.js +15 -0
- package/dist/backup-active-store-5WXG6SXT.js +17 -0
- package/dist/backup-telemetry-db-XGLXAA3N.js +254 -0
- package/dist/basic-competition-master-VCSBJVRN.js +344 -0
- package/dist/branch-archive-manager-RP6FP3ZC.js +254 -0
- package/dist/branch-cleanup-manager-RSHAI7YR.js +20 -0
- package/dist/build-gate-YHGRB4QB.js +71 -0
- package/dist/change-summary-WY7J5TOX.js +33 -0
- package/dist/chunk-23A26CSN.js +123 -0
- package/dist/chunk-27UTJD3M.js +265 -0
- package/dist/chunk-2A5CILN3.js +310 -0
- package/dist/chunk-2AQUWJPW.js +225 -0
- package/dist/chunk-2BZ53J6J.js +2344 -0
- package/dist/chunk-2DY5KNVM.js +276 -0
- package/dist/chunk-2EIA4RBE.js +659 -0
- package/dist/chunk-2IUXIGQD.js +1064 -0
- package/dist/chunk-2N42MS2Z.js +2193 -0
- package/dist/chunk-2QJ3H3L7.js +42 -0
- package/dist/chunk-2QLYTVYX.js +791 -0
- package/dist/chunk-2VSQMRCB.js +18 -0
- package/dist/chunk-2XQXYBM2.js +130 -0
- package/dist/chunk-2YFUIER7.js +426 -0
- package/dist/chunk-34CNP2HB.js +148 -0
- package/dist/chunk-35P3W3JX.js +456 -0
- package/dist/chunk-36PEBZAF.js +18 -0
- package/dist/chunk-3AOKYKA7.js +22 -0
- package/dist/chunk-3BCUNHKF.js +475 -0
- package/dist/chunk-3FLXLLB7.js +26 -0
- package/dist/chunk-3FOZ2VHV.js +100 -0
- package/dist/chunk-3G6FZCRF.js +106 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3S62LLWJ.js +543 -0
- package/dist/chunk-3ZPQLGV7.js +2904 -0
- package/dist/chunk-44N5WZZH.js +45 -0
- package/dist/chunk-45FPSA3B.js +125 -0
- package/dist/chunk-4BJ3O4RQ.js +929 -0
- package/dist/chunk-4BOP7T6L.js +2321 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4CSS7WVI.js +143 -0
- package/dist/chunk-4CUY7AFY.js +247 -0
- package/dist/chunk-4GSN5O5J.js +62 -0
- package/dist/chunk-4HN6DF7V.js +1027 -0
- package/dist/chunk-4IAPOG5V.js +415 -0
- package/dist/chunk-4IYIZQWV.js +48 -0
- package/dist/chunk-4NXPXE62.js +84 -0
- package/dist/chunk-4RCCF5BR.js +712 -0
- package/dist/chunk-4V7NAQ4R.js +19527 -0
- package/dist/chunk-4VJITV4P.js +445 -0
- package/dist/chunk-4YX7J6QL.js +1859 -0
- package/dist/chunk-527OBWQB.js +338 -0
- package/dist/chunk-5D4A7E3D.js +448 -0
- package/dist/chunk-5DLKYTQX.js +4003 -0
- package/dist/chunk-5EERLVZB.js +139 -0
- package/dist/chunk-5H72XE4H.js +256 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5LAMRH4X.js +969 -0
- package/dist/chunk-5LTN43AS.js +1972 -0
- package/dist/chunk-5RBHQYDB.js +2206 -0
- package/dist/chunk-5WCQRDRB.js +588 -0
- package/dist/chunk-5WRLK5KW.js +700 -0
- package/dist/chunk-5ZCXC23G.js +595 -0
- package/dist/chunk-6BM72ZMG.js +31 -0
- package/dist/chunk-6EDAJH2I.js +68 -0
- package/dist/chunk-6EQKFDBH.js +457 -0
- package/dist/chunk-6MFRHTRI.js +115 -0
- package/dist/chunk-6TZJRKNW.js +321 -0
- package/dist/chunk-6V7HGQFZ.js +20 -0
- package/dist/chunk-6VWVO2TU.js +3746 -0
- package/dist/chunk-6Y5TWI7H.js +146 -0
- package/dist/chunk-6Y5U7UFY.js +91 -0
- package/dist/chunk-6ZLAK3XG.js +14 -0
- package/dist/chunk-72XCRQED.js +34 -0
- package/dist/chunk-72YTZMAM.js +589 -0
- package/dist/chunk-73BBJOKA.js +263 -0
- package/dist/chunk-73NCTWKL.js +188 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DHRDWFS.js +3104 -0
- package/dist/chunk-7DNSNKJV.js +78 -0
- package/dist/chunk-7FMJVYBS.js +307 -0
- package/dist/chunk-7GDRQBQC.js +419 -0
- package/dist/chunk-7GPCEFUV.js +270 -0
- package/dist/chunk-7IL4A2PP.js +948 -0
- package/dist/chunk-7MMLMXO6.js +3679 -0
- package/dist/chunk-7QCZSLBH.js +1 -0
- package/dist/chunk-7R2WLDOS.js +62 -0
- package/dist/chunk-7T7RUJI7.js +949 -0
- package/dist/chunk-A2WKSVX7.js +43 -0
- package/dist/chunk-A3YGID55.js +133 -0
- package/dist/chunk-A722DEVA.js +14 -0
- package/dist/chunk-ACDIT7OP.js +1376 -0
- package/dist/chunk-ACKIPAOJ.js +302 -0
- package/dist/chunk-APPNGGN7.js +365 -0
- package/dist/chunk-APV6MK5E.js +137 -0
- package/dist/chunk-ASEP3M2W.js +968 -0
- package/dist/chunk-AT4TNPWW.js +2931 -0
- package/dist/chunk-ATVKCSGT.js +250 -0
- package/dist/chunk-AURFMVCD.js +899 -0
- package/dist/chunk-AX6GWE7V.js +47 -0
- package/dist/chunk-AY5EWJPF.js +385 -0
- package/dist/chunk-BAV2HJXS.js +194 -0
- package/dist/chunk-BBHRN366.js +89 -0
- package/dist/chunk-BBPSTTPA.js +1004 -0
- package/dist/chunk-BDATUDDJ.js +992 -0
- package/dist/chunk-BKE5PG5L.js +20414 -0
- package/dist/chunk-BLPHWMDK.js +797 -0
- package/dist/chunk-BSE4R6XD.js +322 -0
- package/dist/chunk-BXGMAU4A.js +396 -0
- package/dist/chunk-BXI4NXL3.js +507 -0
- package/dist/chunk-BZU2O43F.js +1125 -0
- package/dist/chunk-C3L6YQ7P.js +147 -0
- package/dist/chunk-C57KJOUQ.js +121 -0
- package/dist/chunk-CCV7BPSY.js +118 -0
- package/dist/chunk-CD7ISXA4.js +826 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFBIG37O.js +154 -0
- package/dist/chunk-CJC6GZ46.js +457 -0
- package/dist/chunk-CKCDGXIV.js +408 -0
- package/dist/chunk-CKNX3TTP.js +10338 -0
- package/dist/chunk-CLKSETWE.js +10 -0
- package/dist/chunk-CP7H3HSP.js +1552 -0
- package/dist/chunk-CPLEWXNZ.js +66 -0
- package/dist/chunk-CQM3A35X.js +844 -0
- package/dist/chunk-CS6P76D3.js +103 -0
- package/dist/chunk-CTLUBNCW.js +622 -0
- package/dist/chunk-CUSRN6RJ.js +632 -0
- package/dist/chunk-D5EP5D2W.js +26 -0
- package/dist/chunk-D5RYINO7.js +412 -0
- package/dist/chunk-DBFS4HMH.js +751 -0
- package/dist/chunk-DC4ZYMXJ.js +115 -0
- package/dist/chunk-DCBZP442.js +9243 -0
- package/dist/chunk-DCYF7EKG.js +26 -0
- package/dist/chunk-DDMAFNUK.js +5351 -0
- package/dist/chunk-DF5SLDE4.js +966 -0
- package/dist/chunk-DJFW7SMX.js +348 -0
- package/dist/chunk-DQFLLBJW.js +121 -0
- package/dist/chunk-DSFM56OA.js +1203 -0
- package/dist/chunk-DTY4APYV.js +218 -0
- package/dist/chunk-DXH5JBRS.js +437 -0
- package/dist/chunk-E3NTPHEO.js +194 -0
- package/dist/chunk-E74LUIID.js +302 -0
- package/dist/chunk-EI57M5BI.js +194 -0
- package/dist/chunk-EJ4ZAWLV.js +305 -0
- package/dist/chunk-EJGCZGUF.js +1483 -0
- package/dist/chunk-EK2U3YEG.js +486 -0
- package/dist/chunk-ELMFYNV7.js +109 -0
- package/dist/chunk-ENRGKNR5.js +25 -0
- package/dist/chunk-ETYDXCK7.js +679 -0
- package/dist/chunk-EYRH3W74.js +239 -0
- package/dist/chunk-F23DIWNN.js +1024 -0
- package/dist/chunk-F5JO7HAS.js +94 -0
- package/dist/chunk-F7ER6ANO.js +174 -0
- package/dist/chunk-FATUVIIN.js +374 -0
- package/dist/chunk-FC23CTXV.js +322 -0
- package/dist/chunk-FD4ERYEG.js +22 -0
- package/dist/chunk-FH7WOJAE.js +79 -0
- package/dist/chunk-FHOWVDF5.js +1342 -0
- package/dist/chunk-FKAUPHMR.js +96 -0
- package/dist/chunk-FONNFJXA.js +137 -0
- package/dist/chunk-FQ7FSZRW.js +75 -0
- package/dist/chunk-FRG57FPM.js +240 -0
- package/dist/chunk-FUVP64ER.js +31 -0
- package/dist/chunk-FYOILAN5.js +6818 -0
- package/dist/chunk-G35UYQ6Z.js +69 -0
- package/dist/chunk-G36W2T2S.js +1143 -0
- package/dist/chunk-G3L2L5XZ.js +49 -0
- package/dist/chunk-G4THLFV3.js +148 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GFGYB2ZU.js +1689 -0
- package/dist/chunk-GG6KJYW6.js +85 -0
- package/dist/chunk-GHHFDU2U.js +55 -0
- package/dist/chunk-GHTZSQB7.js +57 -0
- package/dist/chunk-GK37ILCX.js +1231 -0
- package/dist/chunk-GKN3HMN4.js +2809 -0
- package/dist/chunk-GNVDBVJP.js +156 -0
- package/dist/chunk-GPEL3FBM.js +924 -0
- package/dist/chunk-GPSNGPZS.js +1604 -0
- package/dist/chunk-GSGD5E4C.js +30 -0
- package/dist/chunk-GSUQJJXQ.js +331 -0
- package/dist/chunk-GSV226X6.js +110 -0
- package/dist/chunk-H2AYQIHW.js +224 -0
- package/dist/chunk-H66DXFS5.js +1374 -0
- package/dist/chunk-H6G7QRMQ.js +35 -0
- package/dist/chunk-H7JAGHZT.js +59 -0
- package/dist/chunk-H7ZEFECA.js +316 -0
- package/dist/chunk-HAC6EHZZ.js +79 -0
- package/dist/chunk-HIO33P3G.js +19 -0
- package/dist/chunk-HMVQIPH3.js +547 -0
- package/dist/chunk-HOYBYAOA.js +257 -0
- package/dist/chunk-HX5P3CSP.js +1784 -0
- package/dist/chunk-I46EG2XQ.js +65 -0
- package/dist/chunk-I4NPDSCB.js +279 -0
- package/dist/chunk-I5WRT7ZR.js +514 -0
- package/dist/chunk-IC7I6YIJ.js +301 -0
- package/dist/chunk-IDCSOZVN.js +78 -0
- package/dist/chunk-IFCBYECK.js +112 -0
- package/dist/chunk-IFZCPEY2.js +66 -0
- package/dist/chunk-IKFP2KUR.js +48 -0
- package/dist/chunk-IRFBN336.js +878 -0
- package/dist/chunk-IUXNGKA6.js +152 -0
- package/dist/chunk-IV2RVAN7.js +1356 -0
- package/dist/chunk-IZYIQQ7X.js +53 -0
- package/dist/chunk-J3UUR7DE.js +210 -0
- package/dist/chunk-J54VUAY2.js +566 -0
- package/dist/chunk-J62KPGII.js +464 -0
- package/dist/chunk-J6P3FWWW.js +130 -0
- package/dist/chunk-JKBHJQMT.js +434 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JOSYEJKY.js +141 -0
- package/dist/chunk-JPKSXKNB.js +94 -0
- package/dist/chunk-JQNJB2BB.js +2440 -0
- package/dist/chunk-JRVNSWSQ.js +2626 -0
- package/dist/chunk-JSWMRQYG.js +28 -0
- package/dist/chunk-JZKNBBIB.js +152 -0
- package/dist/chunk-K54Y7MLK.js +160 -0
- package/dist/chunk-K65546RE.js +183 -0
- package/dist/chunk-KM3F7SYE.js +702 -0
- package/dist/chunk-KNDYMVWN.js +102 -0
- package/dist/chunk-KNT72VR6.js +132 -0
- package/dist/chunk-KP6YILMU.js +482 -0
- package/dist/chunk-KQEBSK3T.js +274 -0
- package/dist/chunk-KT6VHSPF.js +84 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KZ5CJVBP.js +1813 -0
- package/dist/chunk-L4427KOE.js +26 -0
- package/dist/chunk-L6OM4A22.js +189 -0
- package/dist/chunk-L7E6XCRA.js +1042 -0
- package/dist/chunk-L7JZLXQL.js +173 -0
- package/dist/chunk-LAMKBTXX.js +40 -0
- package/dist/chunk-LDCF2MKN.js +132 -0
- package/dist/chunk-LEIIWPWQ.js +78 -0
- package/dist/chunk-LFTQVYBB.js +31 -0
- package/dist/chunk-LG3PDRTR.js +2872 -0
- package/dist/chunk-LGHII5ET.js +23 -0
- package/dist/chunk-LH5QY22X.js +1205 -0
- package/dist/chunk-LIVJ6266.js +438 -0
- package/dist/chunk-LKRAR3IH.js +276 -0
- package/dist/chunk-LO23NABI.js +3063 -0
- package/dist/chunk-LO3X5NLS.js +326 -0
- package/dist/chunk-LPITVM7M.js +339 -0
- package/dist/chunk-LR5IJIGV.js +810 -0
- package/dist/chunk-LRGMHROI.js +79 -0
- package/dist/chunk-LUVKFFWR.js +315 -0
- package/dist/chunk-LWCBNGH6.js +35 -0
- package/dist/chunk-LWTN3WRV.js +260 -0
- package/dist/chunk-M3HQYKQX.js +308 -0
- package/dist/chunk-M6GVSPEH.js +1016 -0
- package/dist/chunk-MF5HP6XV.js +221 -0
- package/dist/chunk-MHLCBZJB.js +328 -0
- package/dist/chunk-MN6PL4AN.js +120 -0
- package/dist/chunk-MOEY65Z6.js +5678 -0
- package/dist/chunk-MQ4WA34C.js +206 -0
- package/dist/chunk-MWHF5V7U.js +223 -0
- package/dist/chunk-MWHLBPNU.js +211 -0
- package/dist/chunk-MWK5UZQ3.js +1068 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N4VTRA7K.js +131 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-N65O4XTH.js +5011 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NGK6PEOG.js +25 -0
- package/dist/chunk-NJVD4PWE.js +1033 -0
- package/dist/chunk-NM6PMYY4.js +768 -0
- package/dist/chunk-NN4PJDHP.js +708 -0
- package/dist/chunk-NP2V3L7K.js +226 -0
- package/dist/chunk-NRLY536M.js +260 -0
- package/dist/chunk-NRNE225N.js +1270 -0
- package/dist/chunk-NS2K2GTP.js +605 -0
- package/dist/chunk-NS33V3IM.js +51 -0
- package/dist/chunk-NSHASMZZ.js +71 -0
- package/dist/chunk-NWOYOA6H.js +378 -0
- package/dist/chunk-NWRUSEXG.js +322 -0
- package/dist/chunk-NZGDAWIP.js +1905 -0
- package/dist/chunk-O47UOKU3.js +395 -0
- package/dist/chunk-O4G2W3ST.js +9831 -0
- package/dist/chunk-O6V4PNLO.js +63 -0
- package/dist/chunk-OA4GPILI.js +624 -0
- package/dist/chunk-OGMSYL6J.js +227 -0
- package/dist/chunk-OIS7J26S.js +319 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ON3G73BU.js +264 -0
- package/dist/chunk-OPYFYTWQ.js +220 -0
- package/dist/chunk-OQ6K46CK.js +1734 -0
- package/dist/chunk-OYZYO7TV.js +47 -0
- package/dist/chunk-OZ4REHIN.js +4386 -0
- package/dist/chunk-P47VN5F4.js +282 -0
- package/dist/chunk-P5I4X7A7.js +353 -0
- package/dist/chunk-P6VQROCO.js +594 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-PCAGC5MR.js +1763 -0
- package/dist/chunk-PFTQBXS6.js +5181 -0
- package/dist/chunk-PGSPX4SU.js +15 -0
- package/dist/chunk-PIKLF7BM.js +432 -0
- package/dist/chunk-PJWJ3SAV.js +22 -0
- package/dist/chunk-PKIFMV72.js +100 -0
- package/dist/chunk-PL37JDNA.js +189 -0
- package/dist/chunk-PTH5E5XO.js +195 -0
- package/dist/chunk-PW6AXLQC.js +4128 -0
- package/dist/chunk-PYITM4N2.js +79 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3RO7N35.js +45 -0
- package/dist/chunk-Q3WIC6GQ.js +2670 -0
- package/dist/chunk-Q5XIXSHX.js +271 -0
- package/dist/chunk-QGJDMEOV.js +34 -0
- package/dist/chunk-QNOB37UH.js +58 -0
- package/dist/chunk-QOCOVLUH.js +462 -0
- package/dist/chunk-QTCJ5CC5.js +172 -0
- package/dist/chunk-QVFV5IP2.js +232 -0
- package/dist/chunk-QVZMFDYG.js +29 -0
- package/dist/chunk-R35PEUKH.js +511 -0
- package/dist/chunk-R4UWBC35.js +200 -0
- package/dist/chunk-R6MLCREW.js +44 -0
- package/dist/chunk-R6W23LKN.js +254 -0
- package/dist/chunk-R7XDDX2A.js +4007 -0
- package/dist/chunk-RDFRCT64.js +168 -0
- package/dist/chunk-RE5VZDFQ.js +58 -0
- package/dist/chunk-RGROYC2G.js +638 -0
- package/dist/chunk-RGWHUI5E.js +68 -0
- package/dist/chunk-RHVG6UNL.js +205 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RJAAKDRX.js +17 -0
- package/dist/chunk-ROMHOSMQ.js +185 -0
- package/dist/chunk-RWREC3KJ.js +517 -0
- package/dist/chunk-RYNETRDJ.js +118 -0
- package/dist/chunk-RZY7RKM5.js +500 -0
- package/dist/chunk-S2MNWFAG.js +52 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S54MKU6V.js +117 -0
- package/dist/chunk-S5OIDFRM.js +302 -0
- package/dist/chunk-SCW4ZF6R.js +150 -0
- package/dist/chunk-SLS2N4PH.js +2213 -0
- package/dist/chunk-SNLBO4BF.js +247 -0
- package/dist/chunk-SP3LFU6B.js +60 -0
- package/dist/chunk-SQQTDDQA.js +537 -0
- package/dist/chunk-STV6LYYE.js +45 -0
- package/dist/chunk-SURZ2WFE.js +326 -0
- package/dist/chunk-SZ7DL357.js +1047 -0
- package/dist/chunk-T5E4NQCM.js +5742 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TAOOYK3P.js +358 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TFTGP7Z7.js +174 -0
- package/dist/chunk-TGON4N4O.js +197 -0
- package/dist/chunk-TGQZHDY2.js +2373 -0
- package/dist/chunk-TJJZ2OKV.js +6783 -0
- package/dist/chunk-TRSVUPCX.js +25 -0
- package/dist/chunk-TSXGRLPR.js +107 -0
- package/dist/chunk-U2S3YEX3.js +224 -0
- package/dist/chunk-U45RTRGY.js +818 -0
- package/dist/chunk-U6C3VVWS.js +13 -0
- package/dist/chunk-UDMS5AD3.js +307 -0
- package/dist/chunk-UGX7GI37.js +94 -0
- package/dist/chunk-UH3JJSHK.js +1 -0
- package/dist/chunk-UHRBTZYY.js +208 -0
- package/dist/chunk-UHT2KRG5.js +193 -0
- package/dist/chunk-UI2F6DJ5.js +118 -0
- package/dist/chunk-UMAKSB4Z.js +284 -0
- package/dist/chunk-UR2IBGIM.js +521 -0
- package/dist/chunk-UUH2RVKS.js +585 -0
- package/dist/chunk-UVJ4PJDK.js +4533 -0
- package/dist/chunk-V53UG2OT.js +9924 -0
- package/dist/chunk-V72RMBM4.js +48 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VNSYKC25.js +24 -0
- package/dist/chunk-VPHVFK4A.js +1984 -0
- package/dist/chunk-VTLNJQ44.js +133 -0
- package/dist/chunk-VTW7HP3R.js +100 -0
- package/dist/chunk-VVGEPBPS.js +218 -0
- package/dist/chunk-VVLLL7I4.js +166 -0
- package/dist/chunk-VX3UMWB7.js +483 -0
- package/dist/chunk-VXF4Q3FW.js +20 -0
- package/dist/chunk-W3JMT2YY.js +712 -0
- package/dist/chunk-WDUORIHF.js +877 -0
- package/dist/chunk-WDVTY4U6.js +148 -0
- package/dist/chunk-WIFZCHEG.js +12566 -0
- package/dist/chunk-WIRN3F75.js +736 -0
- package/dist/chunk-WMPU2UOV.js +82 -0
- package/dist/chunk-WTOKKE2V.js +784 -0
- package/dist/chunk-WX5IRLF6.js +740 -0
- package/dist/chunk-WZ6U3O7I.js +1581 -0
- package/dist/chunk-XAONGNST.js +57 -0
- package/dist/chunk-XB5YLCLB.js +13 -0
- package/dist/chunk-XEP5NQ5Y.js +688 -0
- package/dist/chunk-XGYF2QMZ.js +351 -0
- package/dist/chunk-XHEKQENB.js +255 -0
- package/dist/chunk-XNO4FM6X.js +1124 -0
- package/dist/chunk-XPHKYHO4.js +878 -0
- package/dist/chunk-XPK2I4NC.js +405 -0
- package/dist/chunk-XPU2XADL.js +2175 -0
- package/dist/chunk-XQD2B6BB.js +507 -0
- package/dist/chunk-XT2ZQVTC.js +558 -0
- package/dist/chunk-XTXJGGUV.js +351 -0
- package/dist/chunk-XUXVDPSZ.js +50 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XZE4XARH.js +563 -0
- package/dist/chunk-XZYR5EDO.js +152 -0
- package/dist/chunk-Y3J4JMTU.js +61 -0
- package/dist/chunk-Y55LZN5U.js +52 -0
- package/dist/chunk-Y5VN7DMP.js +112 -0
- package/dist/chunk-Y67VAHN4.js +459 -0
- package/dist/chunk-YAXSKEUH.js +3544 -0
- package/dist/chunk-YB3JD4E6.js +975 -0
- package/dist/chunk-YESZJFK6.js +220 -0
- package/dist/chunk-YGXKOBQQ.js +292 -0
- package/dist/chunk-YMX76IOS.js +31 -0
- package/dist/chunk-YOTXESEH.js +257 -0
- package/dist/chunk-YTYIEZQY.js +119 -0
- package/dist/chunk-YVORHQ2S.js +579 -0
- package/dist/chunk-Z2CUQDAR.js +3429 -0
- package/dist/chunk-Z2KYOFHS.js +7000 -0
- package/dist/chunk-Z6PC7QX6.js +161 -0
- package/dist/chunk-ZDTGECTN.js +235 -0
- package/dist/chunk-ZIPWI2LY.js +151 -0
- package/dist/chunk-ZSKKMTQJ.js +554 -0
- package/dist/chunk-ZTT5IVPY.js +1160 -0
- package/dist/chunk-ZX65UI5W.js +78 -0
- package/dist/chunk-ZYG7GGKO.js +218 -0
- package/dist/chunk-ZZZUL4OF.js +38 -0
- package/dist/cli-utils-IXL26KJT.js +40 -0
- package/dist/cli-validation-WLXWLUJL.js +111 -0
- package/dist/commit-utils-P7CRJF5N.js +202 -0
- package/dist/compete-config-resolver-R5ULXMYH.js +76 -0
- package/dist/compete-request-62JESVET.js +364 -0
- package/dist/compete-results-QEPG6FZX.js +208 -0
- package/dist/competition-outcome-tracker-R32OOZ43.js +38 -0
- package/dist/config-JCOKXOQT.js +166 -0
- package/dist/config-audit-EL3GHXS7.js +360 -0
- package/dist/config-explain-TNILDEVJ.js +12 -0
- package/dist/config-loader-VPFPFO4B.js +38 -0
- package/dist/config-manager-7T4E2BQO.js +67 -0
- package/dist/config-validator-KKOHOW7O.js +72 -0
- package/dist/cost-YC42XUF3.js +53 -0
- package/dist/coverage-orchestrator-CISNKJC2.js +772 -0
- package/dist/coverage-scanner-YVD62MLH.js +12 -0
- package/dist/createCompetitionRunner-LJG6ZQSO.js +46 -0
- package/dist/data-migration-state-VJK64DLT.js +31 -0
- package/dist/db-migrator-XSEIOGWY.js +34 -0
- package/dist/discovery-JGYGDD25.js +25 -0
- package/dist/doctor-GJOYE7AE.js +186 -0
- package/dist/domain-analyzer-AWF3RP6E.js +29 -0
- package/dist/ensure-initialized-744DIZEZ.js +52 -0
- package/dist/entry.js +940 -0
- package/dist/environment-YS45U5ZI.js +81 -0
- package/dist/error-parser-core-PPZ36A4S.js +50 -0
- package/dist/escalation-ladder-MKTCEGSA.js +130 -0
- package/dist/evaluation-context-KV4GZNN7.js +35 -0
- package/dist/evidence-plan-resolver-GBSYSAPX.js +85 -0
- package/dist/execution-data-writer-3ODTBDLH.js +53 -0
- package/dist/execution-policy-applier-ZHD3DCF3.js +31 -0
- package/dist/execution-preflight-Z4Y64V3I.js +209 -0
- package/dist/execution-roles-R3DPPMD3.js +66 -0
- package/dist/exit-code-error-Z4SW2DVK.js +14 -0
- package/dist/external-temp-cleanup-N2RZH4QP.js +311 -0
- package/dist/factory-XBN7DFIM.js +149 -0
- package/dist/file-utils-XIVR2ZMA.js +47 -0
- package/dist/flywheel-autoloop-executor-UILGOFFK.js +249 -0
- package/dist/flywheel-competition-executor-JOBEXTRB.js +259 -0
- package/dist/flywheel-executor-6JFLA6J5.js +369 -0
- package/dist/flywheel-git-isolation-UC5NIEVU.js +48 -0
- package/dist/flywheel-manifest-5UC4VBMA.js +108 -0
- package/dist/flywheel-model-defaults-7CVKP53V.js +27 -0
- package/dist/flywheel-preflight-XEY5SHM6.js +104 -0
- package/dist/flywheel-resume-7HELH7QS.js +23 -0
- package/dist/flywheel-safety-7U7DPOAP.js +28 -0
- package/dist/flywheel-scope-decision-ZHKF6ZIN.js +11 -0
- package/dist/get-telemetry-logger-DPVWWIYY.js +73 -0
- package/dist/git-worktree-IY6V6DUO.js +18 -0
- package/dist/github-pr-manager-4LXEKJTA.js +211 -0
- package/dist/guidance-profile-config-E7XJL2VP.js +117 -0
- package/dist/guidance-profile-renderer-QK2YMAAP.js +21 -0
- package/dist/index-query-4JEUKA44.js +31 -0
- package/dist/index.js +108661 -0
- package/dist/instructions-6BURLQJC.js +64 -0
- package/dist/invoke-provider-auth-GRUYP44M.js +223 -0
- package/dist/judge-scoring.json +80 -0
- package/dist/judge-test.js +555 -0
- package/dist/lab-mode-OTWVHN63.js +39 -0
- package/dist/lab-utils-BJRFLGK4.js +23 -0
- package/dist/linux-host-class-UY4KVLFD.js +29 -0
- package/dist/llm-judge-executor-F7Z3HO77.js +133 -0
- package/dist/local-executor-CEHWE2PL.js +223 -0
- package/dist/logger-33TR6EE5.js +72 -0
- package/dist/loop-spec-XTLQAVZ7.js +191 -0
- package/dist/manager-2HJG5CLT.js +25 -0
- package/dist/matrix-prompt-builder-6UDN5X3F.js +497 -0
- package/dist/matrix-result-parser-7W35EUOV.js +15 -0
- package/dist/metadata-HQ43NDEK.js +22 -0
- package/dist/model-invocations-db-QBO4Y7WZ.js +38 -0
- package/dist/monitor-XGYBG2DW.js +72 -0
- package/dist/next-PB5ZXZKB.js +90 -0
- package/dist/next-ZBDK725T.js +69 -0
- package/dist/optimization-config-resolver-O6WKR47U.js +75 -0
- package/dist/package-3YCWIQQ5.js +10 -0
- package/dist/parallel-orchestrator-NL7YJJU2.js +297 -0
- package/dist/parallel-worker.js +279 -0
- package/dist/parse-git-status-PIAF3OTP.js +10 -0
- package/dist/path-security-ETTH6TZL.js +39 -0
- package/dist/pattern-bank-UNJR5ADV.js +15 -0
- package/dist/plan-backlog-list-fast-VAGYBV3J.js +270 -0
- package/dist/plan-backlog-show-fast-SG2CZPW3.js +406 -0
- package/dist/plan-sprint-list-fast-LURAX47V.js +162 -0
- package/dist/plan-sprint-status-fast-FJAODCHD.js +208 -0
- package/dist/plan-task-status-fast-HNYBMQI4.js +349 -0
- package/dist/plan-to-flywheel-Z4U3GWPN.js +456 -0
- package/dist/planning-backlog-3CQ5QQBQ.js +319 -0
- package/dist/poetic-root-V5DNXEAE.js +17 -0
- package/dist/preflight-OPANWMME.js +12 -0
- package/dist/process-registry-PDZJPTAS.js +27 -0
- package/dist/process-scanner-C22SYXYD.js +22 -0
- package/dist/processes-KMQUD7HY.js +29 -0
- package/dist/processes-json-fast-IBDXZ6WW.js +135 -0
- package/dist/profile-SE5EVXAP.js +149 -0
- package/dist/prompts-WZZPGWBX.js +168 -0
- package/dist/protected-branches-RNTEX7ET.js +57 -0
- package/dist/provider-aware-resource-manager-QBTZJMNP.js +18 -0
- package/dist/provider-matrix-N5X57V3Y.js +154 -0
- package/dist/provider-matrix-renderer-MZDWKKID.js +139 -0
- package/dist/provider-output-forwarding-DERID3TP.js +30 -0
- package/dist/provider-registry-J5NRBJKI.js +128 -0
- package/dist/provider-temp-cleanup-KYGLF6YU.js +14 -0
- package/dist/prune-engine-Q6E2DXBN.js +451 -0
- package/dist/quality-gate-YRHMQAEI.js +70 -0
- package/dist/quickstart-FIRLKGJI.js +62 -0
- package/dist/readiness-VZTMSG6J.js +96 -0
- package/dist/registry-YODFY77A.js +153 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-2IGD4H7X.js +22 -0
- package/dist/resolution-engine-D6TX6FRG.js +127 -0
- package/dist/resolve-provider-cli-O2P6XZBZ.js +35 -0
- package/dist/restore-telemetry-db-BQL6DTQF.js +360 -0
- package/dist/result-streamer-WEHW476R.js +19 -0
- package/dist/routing-events-DHPUZDDF.js +27 -0
- package/dist/routing-history-store-UWCEXAP3.js +46 -0
- package/dist/routing-planner-F5WQEUOM.js +160 -0
- package/dist/run-poetic-tui-J6LWRJUP.js +13517 -0
- package/dist/run-simulation.js +376 -0
- package/dist/runner-CS2TNGHA.js +1092 -0
- package/dist/runner-HHH7ZN2Z.js +1571 -0
- package/dist/safety-OCC4KJJR.js +64 -0
- package/dist/safety-stanza-7SV3HGG3.js +60 -0
- package/dist/sandbox-32QXJEMV.js +81 -0
- package/dist/sandbox-RSO7VP6V.js +94 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-2KI5XNDG.js +17 -0
- package/dist/setup-bb-plugin-QAQWZCV5.js +738 -0
- package/dist/setup-claude-code-P7BTTSPO.js +317 -0
- package/dist/setup-temp-cleanup-GASCWZ2I.js +20 -0
- package/dist/shared-utils-D6U6UREP.js +37 -0
- package/dist/simple-artifact-goal-OTXNGZY6.js +25 -0
- package/dist/sprint-UIXPYOXQ.js +338 -0
- package/dist/sprint-bridge-4BHMSC3M.js +68 -0
- package/dist/sprint-execution-service-2B7WHL5L.js +311 -0
- package/dist/sprint-manager-IDQUDNQI.js +91 -0
- package/dist/sqlite-wrapper-UDWCTNLJ.js +17 -0
- package/dist/stale-cleanup-orchestrator-XWXV7KCZ.js +62 -0
- package/dist/storage-access-F6ALDN4S.js +22 -0
- package/dist/synthesis-XD6EDVHF.js +185 -0
- package/dist/task-classifier-LNLXSHOM.js +494 -0
- package/dist/task-list-fast-PTMA4LUC.js +147 -0
- package/dist/task-manager-XAT7GOWB.js +93 -0
- package/dist/task-splitter-U7DJ7W6A.js +79 -0
- package/dist/task-type-detector-OLVOMXZO.js +21 -0
- package/dist/task-type-resolver-KKNQYSB4.js +42 -0
- package/dist/telemetry-5HWBBXLX.js +131 -0
- package/dist/telemetry-health-tracker-DMSSGBZ2.js +11 -0
- package/dist/telemetry-path-resolver-7ICSG276.js +28 -0
- package/dist/telemetry-reliability-XUOZNZZ6.js +125 -0
- package/dist/token-estimator-MEQUL3EJ.js +37 -0
- package/dist/transports-KNSSV2V4.js +255 -0
- package/dist/unified-cost-tracker-4TFRBVPR.js +35 -0
- package/dist/unified-state-cleanup-LJPF2BOW.js +276 -0
- package/dist/universal-cost-calculator-BAJV35GA.js +42 -0
- package/dist/user-agents-GHUGCSKN.js +22 -0
- package/dist/variant-delivery-state-KLRNG6XA.js +33 -0
- package/dist/variant-state-cleanup-MZGGLQAJ.js +280 -0
- package/dist/variant-success-PJXWE3ZV.js +12 -0
- package/dist/variant-worker-KUWKBLAH.js +4196 -0
- package/dist/variant-worker.js +24 -0
- package/dist/variants-JE2FSAQS.js +233 -0
- package/dist/verification-toolchains-V24UHW6Q.js +28 -0
- package/dist/verify-commands-H3DTCSGI.js +37 -0
- package/dist/warning-EGQGUUIO.js +12 -0
- package/dist/work-edge-RWEDWJZF.js +49 -0
- package/dist/work-item-writer-WHAVZXAY.js +19 -0
- package/dist/worker-7SA2VS2Y.js +1435 -0
- package/dist/worker.js +19 -0
- package/dist/worktree-metrics-3DFUYVYE.js +26 -0
- package/dist/writeback-VZKHYHSN.js +44 -0
- package/docs/CLI_REFERENCE.md +7042 -0
- package/docs/PROVIDER_SETUP.md +1570 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2232 -0
- package/docs/getting-started/QUICK_START.md +294 -0
- package/docs/getting-started/README.md +188 -0
- package/docs/getting-started/SETUP.md +52 -0
- package/docs/reference/AGENT_CONTEXT.md +38 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/README.md +381 -0
- package/docs/reference/SECURITY.md +262 -0
- package/integrations/bb-plugin-poetic/README.md +362 -0
- package/integrations/bb-plugin-poetic/app.css +341 -0
- package/integrations/bb-plugin-poetic/app.tsx +5059 -0
- package/integrations/bb-plugin-poetic/package.json +45 -0
- package/integrations/bb-plugin-poetic/server.ts +444 -0
- package/integrations/bb-plugin-poetic/src/adapter.ts +3312 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-schema.ts +355 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-service.ts +419 -0
- package/integrations/bb-plugin-poetic/src/backlog-authoring-view.ts +1026 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-poetic/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-poetic/src/contract.ts +615 -0
- package/integrations/bb-plugin-poetic/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-poetic/src/finalize-schema.ts +158 -0
- package/integrations/bb-plugin-poetic/src/finalize-service.ts +382 -0
- package/integrations/bb-plugin-poetic/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-poetic/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-poetic/src/model.ts +1302 -0
- package/integrations/bb-plugin-poetic/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-poetic/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-poetic/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-poetic/src/planning-readback.ts +204 -0
- package/integrations/bb-plugin-poetic/src/planning-service.ts +594 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace-schema.ts +95 -0
- package/integrations/bb-plugin-poetic/src/planning-workspace.ts +323 -0
- package/integrations/bb-plugin-poetic/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-poetic/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-poetic/src/project-target.ts +45 -0
- package/integrations/bb-plugin-poetic/src/reference-index.ts +192 -0
- package/integrations/bb-plugin-poetic/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-poetic/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-poetic/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-poetic/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-poetic/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-poetic/src/run-page-view.ts +177 -0
- package/integrations/bb-plugin-poetic/src/setup-config-schema.ts +291 -0
- package/integrations/bb-plugin-poetic/src/setup-config-service.ts +590 -0
- package/integrations/bb-plugin-poetic/src/setup-config-view.ts +413 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-schema.ts +136 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-service.ts +341 -0
- package/integrations/bb-plugin-poetic/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-model.ts +714 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-schema.ts +601 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-service.ts +775 -0
- package/integrations/bb-plugin-poetic/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-model.ts +567 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-schema.ts +635 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-poetic/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-poetic/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-poetic/src/task-detail-schema.ts +144 -0
- package/integrations/bb-plugin-poetic/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-poetic/src/workbench-route.ts +74 -0
- package/integrations/bb-plugin-poetic/tests/host-contract.test.ts +773 -0
- package/integrations/bb-plugin-poetic/tsconfig.json +18 -0
- package/integrations/bb-plugin-poetic/types/PROVENANCE.json +52 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk-app.d.ts +1444 -0
- package/integrations/bb-plugin-poetic/types/bb-plugin-sdk.d.ts +13030 -0
- package/integrations/bb-plugin-poetic/vitest.host.config.ts +71 -0
- package/npm-shrinkwrap.json +4950 -0
- package/package.json +331 -0
- package/schemas/README.md +80 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +370 -0
- package/schemas/poetic.config.schema.json +2214 -0
- package/schemas/provider-config.schema.json +203 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +140 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/preinstall-node-check.mjs +78 -0
- package/scripts/setup-git-hooks.mjs +24 -0
- package/scripts/sync.sh +2722 -0
- package/scripts/write-node-launcher.sh +108 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.poetic/README.md +37 -0
- package/templates/.poetic/agents/README.md +296 -0
- package/templates/.poetic/agents/api-documenter.md +147 -0
- package/templates/.poetic/agents/backend-architect.md +31 -0
- package/templates/.poetic/agents/code-reviewer.md +157 -0
- package/templates/.poetic/agents/data-scientist.md +179 -0
- package/templates/.poetic/agents/database-optimizer.md +145 -0
- package/templates/.poetic/agents/debugger.md +31 -0
- package/templates/.poetic/agents/deployment-engineer.md +164 -0
- package/templates/.poetic/agents/devops-troubleshooter.md +139 -0
- package/templates/.poetic/agents/frontend-developer.md +150 -0
- package/templates/.poetic/agents/javascript-pro.md +36 -0
- package/templates/.poetic/agents/performance-engineer.md +151 -0
- package/templates/.poetic/agents/python-pro.md +137 -0
- package/templates/.poetic/agents/test-automator.md +147 -0
- package/templates/.poetic/agents/typescript-pro.md +34 -0
- package/templates/.poetic/config/poetic.config.jsonc +69 -0
- package/templates/.poetic/config/project-context.template.json +6 -0
- package/templates/.poetic/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.poetic/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.poetic/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.poetic/config/task-type-aliases.yaml +28 -0
- package/templates/.poetic/gitignore.template +55 -0
- package/templates/.poetic/task-types/analysis.yaml +40 -0
- package/templates/.poetic/task-types/architecture.yaml +38 -0
- package/templates/.poetic/task-types/doc.yaml +35 -0
- package/templates/.poetic/task-types/feature.yaml +23 -0
- package/templates/.poetic/task-types/general.yaml +6 -0
- package/templates/.poetic/task-types/security.yaml +39 -0
- package/templates/AGENTS.template.md +99 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/compete-streak.yaml +78 -0
- package/templates/builtin-workflows/hello-verify.yaml +10 -0
- package/templates/builtin-workflows/judge-regression.yaml +114 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +60 -0
- package/templates/builtin-workflows/skills/hello-verify/SKILL.md +6 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +255 -0
- package/templates/guard-kit/check.mjs.template +777 -0
- package/templates/guard-kit/config.json.template +6 -0
- package/templates/guard-kit/poetic-guard.yml.template +189 -0
- package/templates/profiles/README.md +56 -0
- package/templates/profiles/frontier-claude.json +27 -0
- package/templates/profiles/frontier-codex.json +26 -0
|
@@ -0,0 +1,2904 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractChangedFilesMeta
|
|
3
|
+
} from "./chunk-CFBIG37O.js";
|
|
4
|
+
import {
|
|
5
|
+
hasRecordedTestExecutionEvidence,
|
|
6
|
+
hasUnrecognizedRecordedTestStatus,
|
|
7
|
+
normalizeRecordedTestStatus
|
|
8
|
+
} from "./chunk-KT6VHSPF.js";
|
|
9
|
+
import {
|
|
10
|
+
stripAnsiSequences
|
|
11
|
+
} from "./chunk-VTW7HP3R.js";
|
|
12
|
+
import {
|
|
13
|
+
changedFilesRequireVerification,
|
|
14
|
+
resolveVerificationChannelScope,
|
|
15
|
+
selectScopedTestCommands
|
|
16
|
+
} from "./chunk-KP6YILMU.js";
|
|
17
|
+
import {
|
|
18
|
+
extractDiffText
|
|
19
|
+
} from "./chunk-ETYDXCK7.js";
|
|
20
|
+
import {
|
|
21
|
+
JUDGE_FEATURE_FLAGS
|
|
22
|
+
} from "./chunk-BSE4R6XD.js";
|
|
23
|
+
import {
|
|
24
|
+
createVerificationCommandEnv,
|
|
25
|
+
runVerificationCommand
|
|
26
|
+
} from "./chunk-D5RYINO7.js";
|
|
27
|
+
import {
|
|
28
|
+
detectProjectCapabilities,
|
|
29
|
+
hasProjectLanguage
|
|
30
|
+
} from "./chunk-FHOWVDF5.js";
|
|
31
|
+
import {
|
|
32
|
+
isProjectTestPath,
|
|
33
|
+
languageForProjectPath
|
|
34
|
+
} from "./chunk-UDMS5AD3.js";
|
|
35
|
+
import {
|
|
36
|
+
tailLines,
|
|
37
|
+
truncateToByteLimit
|
|
38
|
+
} from "./chunk-TSXGRLPR.js";
|
|
39
|
+
import {
|
|
40
|
+
describeVerificationCommand,
|
|
41
|
+
detectExitZeroVerifyFailure,
|
|
42
|
+
detectVerificationToolUnavailable,
|
|
43
|
+
detectZeroCollectedTests,
|
|
44
|
+
isPythonStdlibUnittestCommand,
|
|
45
|
+
resolveVerificationExitCode,
|
|
46
|
+
selectFallbacksForCommand
|
|
47
|
+
} from "./chunk-2QLYTVYX.js";
|
|
48
|
+
|
|
49
|
+
// src/core/judge/lint-on-diff.ts
|
|
50
|
+
function isTimeoutLike(error) {
|
|
51
|
+
return error.timedOut === true || error.killed === true || error.signal === "SIGTERM";
|
|
52
|
+
}
|
|
53
|
+
function isToolUnavailable(exitCode, output, error) {
|
|
54
|
+
const normalized = output.toLowerCase();
|
|
55
|
+
return error?.code === "ENOENT" || exitCode === 127 || normalized.includes("command not found") || normalized.includes("not recognized as an internal or external command") || normalized.includes("no such file or directory");
|
|
56
|
+
}
|
|
57
|
+
function verificationCoverageForLint(params) {
|
|
58
|
+
if (params.error && isTimeoutLike(params.error)) {
|
|
59
|
+
return {
|
|
60
|
+
status: "unverified",
|
|
61
|
+
command: params.command,
|
|
62
|
+
reason: "Lint command timed out or was interrupted."
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (isToolUnavailable(params.exitCode, params.output, params.error)) {
|
|
66
|
+
return {
|
|
67
|
+
status: "skipped_tool_unavailable",
|
|
68
|
+
command: params.command,
|
|
69
|
+
reason: "Lint command was unavailable in the execution environment."
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { status: "verified", command: params.command };
|
|
73
|
+
}
|
|
74
|
+
function errorMessage(error) {
|
|
75
|
+
if (error instanceof Error) return error.message;
|
|
76
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
77
|
+
return String(error.message);
|
|
78
|
+
}
|
|
79
|
+
return String(error);
|
|
80
|
+
}
|
|
81
|
+
function extractJsonObject(text) {
|
|
82
|
+
const raw = String(text ?? "").trim();
|
|
83
|
+
if (!raw) return null;
|
|
84
|
+
const start = raw.indexOf("{");
|
|
85
|
+
const end = raw.lastIndexOf("}");
|
|
86
|
+
if (start < 0 || end < 0 || end <= start) return null;
|
|
87
|
+
const candidate = raw.slice(start, end + 1);
|
|
88
|
+
try {
|
|
89
|
+
return { text: candidate, parsed: JSON.parse(candidate) };
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function formatBiomeDiagnosticSummary(extracted) {
|
|
95
|
+
if (!extracted) return "";
|
|
96
|
+
try {
|
|
97
|
+
const json = extracted.parsed;
|
|
98
|
+
const diagnostics = Array.isArray(json?.diagnostics) ? json.diagnostics : [];
|
|
99
|
+
const lines = [];
|
|
100
|
+
for (const diag of diagnostics) {
|
|
101
|
+
const loc = diag?.location;
|
|
102
|
+
const filePath = loc?.path;
|
|
103
|
+
if (typeof filePath !== "string" || filePath.trim().length === 0) continue;
|
|
104
|
+
const span = loc?.span;
|
|
105
|
+
const start = span?.start;
|
|
106
|
+
const line = typeof start?.line === "number" ? start.line : 1;
|
|
107
|
+
const column = typeof start?.column === "number" ? start.column : 1;
|
|
108
|
+
const category = typeof diag?.category === "string" ? diag.category : typeof diag?.code === "string" ? diag.code : typeof diag?.rule === "string" ? diag.rule : typeof diag?.severity === "string" ? diag.severity : "issue";
|
|
109
|
+
lines.push(`${filePath}:${line}:${column} ${category}`);
|
|
110
|
+
}
|
|
111
|
+
return lines.length > 0 ? lines.slice(0, 200).join("\n") : "";
|
|
112
|
+
} catch {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function isBiomeLintCommand(command) {
|
|
117
|
+
return /\bbiome\s+check\b/.test(command);
|
|
118
|
+
}
|
|
119
|
+
function appendBiomeSinceArg(command, baseBranch) {
|
|
120
|
+
if (!baseBranch || !isBiomeLintCommand(command) || !command.includes("--changed")) {
|
|
121
|
+
return command;
|
|
122
|
+
}
|
|
123
|
+
if (command.includes("--since=")) {
|
|
124
|
+
return command;
|
|
125
|
+
}
|
|
126
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(baseBranch) || baseBranch.startsWith("-")) {
|
|
127
|
+
return command;
|
|
128
|
+
}
|
|
129
|
+
return command.replace(/\s--changed(?=\s|$)/, ` --changed --since=${baseBranch}`);
|
|
130
|
+
}
|
|
131
|
+
function buildBiomeArtifactResult(params) {
|
|
132
|
+
const extracted = extractJsonObject(params.stdoutRaw) ?? extractJsonObject(params.combined);
|
|
133
|
+
const jsonCandidateText = extracted?.text ?? params.stdoutRaw;
|
|
134
|
+
const diagnosticSummary = formatBiomeDiagnosticSummary(
|
|
135
|
+
extracted ?? (jsonCandidateText ? { text: jsonCandidateText, parsed: void 0 } : null)
|
|
136
|
+
);
|
|
137
|
+
const outputTail = truncateToByteLimit(
|
|
138
|
+
[diagnosticSummary, tailLines(params.combined, params.maxTailLines)].filter(Boolean).join("\n"),
|
|
139
|
+
1e5
|
|
140
|
+
);
|
|
141
|
+
const parsed = params.exitCode === 0 || params.exitCode === 1 ? parseBiomeOutput(jsonCandidateText, params.exitCode, extracted?.parsed) : null;
|
|
142
|
+
const noopLint = params.combined.includes("No files were processed") || params.combined.includes("No files were processed in the specified paths");
|
|
143
|
+
return {
|
|
144
|
+
exitCode: noopLint ? 0 : params.exitCode,
|
|
145
|
+
outputTail,
|
|
146
|
+
result: noopLint || params.exitCode === 0 && (!params.stdoutRaw.trim() || parsed === null) ? {
|
|
147
|
+
success: true,
|
|
148
|
+
errorCount: 0,
|
|
149
|
+
warningCount: 0,
|
|
150
|
+
fixableCount: 0,
|
|
151
|
+
filesWithIssues: [],
|
|
152
|
+
exitCode: 0
|
|
153
|
+
} : parsed,
|
|
154
|
+
verificationCoverage: {
|
|
155
|
+
status: noopLint ? "skipped_not_relevant" : "verified",
|
|
156
|
+
command: params.command
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async function runLintOnDiffWithArtifacts(workingDirectory, options) {
|
|
161
|
+
const start = Date.now();
|
|
162
|
+
const timeoutMs = options?.timeoutMs;
|
|
163
|
+
const maxTailLines = options?.tailLines ?? 200;
|
|
164
|
+
if (options?.command && options.command.trim().length > 0) {
|
|
165
|
+
const scopedCommand = options.command;
|
|
166
|
+
const scopedLanguage = describeVerificationCommand(scopedCommand).language;
|
|
167
|
+
try {
|
|
168
|
+
const result = await runVerificationCommand(scopedCommand, {
|
|
169
|
+
cwd: workingDirectory,
|
|
170
|
+
timeoutMs,
|
|
171
|
+
abortSignal: options?.abortSignal
|
|
172
|
+
});
|
|
173
|
+
const { exitCode: resolvedExitCode } = resolveVerificationExitCode(scopedCommand, result);
|
|
174
|
+
const rawOutput = String(result.all ?? "");
|
|
175
|
+
const exitZeroFailure = resolvedExitCode === 0 ? detectExitZeroVerifyFailure(scopedCommand, rawOutput)?.message : void 0;
|
|
176
|
+
const exitCode = exitZeroFailure ? 1 : resolvedExitCode;
|
|
177
|
+
const output = exitZeroFailure ? `${rawOutput}
|
|
178
|
+
${exitZeroFailure}`.trim() : rawOutput;
|
|
179
|
+
return {
|
|
180
|
+
command: scopedCommand,
|
|
181
|
+
exitCode,
|
|
182
|
+
durationMs: Date.now() - start,
|
|
183
|
+
outputTail: truncateToByteLimit(tailLines(output, maxTailLines), 1e5),
|
|
184
|
+
result: {
|
|
185
|
+
success: exitCode === 0,
|
|
186
|
+
errorCount: exitCode === 0 ? 0 : 1,
|
|
187
|
+
warningCount: 0,
|
|
188
|
+
fixableCount: 0,
|
|
189
|
+
filesWithIssues: [],
|
|
190
|
+
exitCode
|
|
191
|
+
},
|
|
192
|
+
verificationCoverage: verificationCoverageForLint({
|
|
193
|
+
command: scopedCommand,
|
|
194
|
+
exitCode,
|
|
195
|
+
output
|
|
196
|
+
}),
|
|
197
|
+
language: scopedLanguage
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
const err = error;
|
|
201
|
+
const message = errorMessage(error);
|
|
202
|
+
const timedOut = isTimeoutLike(err);
|
|
203
|
+
const exitCode = timedOut ? 124 : 1;
|
|
204
|
+
return {
|
|
205
|
+
command: scopedCommand,
|
|
206
|
+
exitCode,
|
|
207
|
+
durationMs: Date.now() - start,
|
|
208
|
+
outputTail: truncateToByteLimit(tailLines(message, maxTailLines), 1e5),
|
|
209
|
+
result: timedOut ? null : {
|
|
210
|
+
success: false,
|
|
211
|
+
errorCount: 1,
|
|
212
|
+
warningCount: 0,
|
|
213
|
+
fixableCount: 0,
|
|
214
|
+
filesWithIssues: [],
|
|
215
|
+
exitCode
|
|
216
|
+
},
|
|
217
|
+
verificationCoverage: verificationCoverageForLint({
|
|
218
|
+
command: scopedCommand,
|
|
219
|
+
exitCode,
|
|
220
|
+
output: message,
|
|
221
|
+
error: err
|
|
222
|
+
}),
|
|
223
|
+
language: scopedLanguage
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const detectedProjectLintCommand = detectProjectCapabilities(workingDirectory).verification.lint[0];
|
|
228
|
+
const projectLintCommand = detectedProjectLintCommand ? appendBiomeSinceArg(detectedProjectLintCommand, options?.baseBranch) : void 0;
|
|
229
|
+
const projectLintLanguage = projectLintCommand ? describeVerificationCommand(projectLintCommand).language : void 0;
|
|
230
|
+
if (projectLintCommand) {
|
|
231
|
+
try {
|
|
232
|
+
const result = await runVerificationCommand(projectLintCommand, {
|
|
233
|
+
cwd: workingDirectory,
|
|
234
|
+
timeoutMs,
|
|
235
|
+
abortSignal: options?.abortSignal
|
|
236
|
+
});
|
|
237
|
+
const { exitCode: resolvedExitCode } = resolveVerificationExitCode(
|
|
238
|
+
projectLintCommand,
|
|
239
|
+
result
|
|
240
|
+
);
|
|
241
|
+
const rawOutput = String(result.all ?? "");
|
|
242
|
+
const exitZeroFailure = resolvedExitCode === 0 ? detectExitZeroVerifyFailure(projectLintCommand, rawOutput)?.message : void 0;
|
|
243
|
+
const exitCode = exitZeroFailure ? 1 : resolvedExitCode;
|
|
244
|
+
const output = exitZeroFailure ? `${rawOutput}
|
|
245
|
+
${exitZeroFailure}`.trim() : rawOutput;
|
|
246
|
+
if (isBiomeLintCommand(projectLintCommand)) {
|
|
247
|
+
const biomeArtifact = buildBiomeArtifactResult({
|
|
248
|
+
command: projectLintCommand,
|
|
249
|
+
exitCode,
|
|
250
|
+
durationMs: Date.now() - start,
|
|
251
|
+
combined: output,
|
|
252
|
+
stdoutRaw: String(result.stdout ?? result.all ?? ""),
|
|
253
|
+
maxTailLines
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
command: projectLintCommand,
|
|
257
|
+
durationMs: Date.now() - start,
|
|
258
|
+
...biomeArtifact,
|
|
259
|
+
language: projectLintLanguage
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
command: projectLintCommand,
|
|
264
|
+
exitCode,
|
|
265
|
+
durationMs: Date.now() - start,
|
|
266
|
+
outputTail: truncateToByteLimit(tailLines(output, maxTailLines), 1e5),
|
|
267
|
+
result: {
|
|
268
|
+
success: exitCode === 0,
|
|
269
|
+
errorCount: exitCode === 0 ? 0 : 1,
|
|
270
|
+
warningCount: 0,
|
|
271
|
+
fixableCount: 0,
|
|
272
|
+
filesWithIssues: [],
|
|
273
|
+
exitCode
|
|
274
|
+
},
|
|
275
|
+
verificationCoverage: verificationCoverageForLint({
|
|
276
|
+
command: projectLintCommand,
|
|
277
|
+
exitCode,
|
|
278
|
+
output
|
|
279
|
+
}),
|
|
280
|
+
language: projectLintLanguage
|
|
281
|
+
};
|
|
282
|
+
} catch (error) {
|
|
283
|
+
const err = error;
|
|
284
|
+
const message = errorMessage(error);
|
|
285
|
+
const timedOut = isTimeoutLike(err);
|
|
286
|
+
const exitCode = timedOut ? 124 : 1;
|
|
287
|
+
return {
|
|
288
|
+
command: projectLintCommand,
|
|
289
|
+
exitCode,
|
|
290
|
+
durationMs: Date.now() - start,
|
|
291
|
+
outputTail: truncateToByteLimit(tailLines(message, maxTailLines), 1e5),
|
|
292
|
+
result: timedOut ? null : {
|
|
293
|
+
success: false,
|
|
294
|
+
errorCount: 1,
|
|
295
|
+
warningCount: 0,
|
|
296
|
+
fixableCount: 0,
|
|
297
|
+
filesWithIssues: [],
|
|
298
|
+
exitCode
|
|
299
|
+
},
|
|
300
|
+
verificationCoverage: verificationCoverageForLint({
|
|
301
|
+
command: projectLintCommand,
|
|
302
|
+
exitCode,
|
|
303
|
+
output: message,
|
|
304
|
+
error: err
|
|
305
|
+
}),
|
|
306
|
+
language: projectLintLanguage
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const outputTail = "Skipped lint gate: no project lint command detected by resolver.";
|
|
311
|
+
const skippedCommand = "lint (not configured)";
|
|
312
|
+
return {
|
|
313
|
+
command: skippedCommand,
|
|
314
|
+
exitCode: 127,
|
|
315
|
+
durationMs: Date.now() - start,
|
|
316
|
+
outputTail,
|
|
317
|
+
result: null,
|
|
318
|
+
verificationCoverage: {
|
|
319
|
+
status: "skipped_no_command",
|
|
320
|
+
command: skippedCommand,
|
|
321
|
+
reason: "No project lint command was detected by the capabilities resolver."
|
|
322
|
+
},
|
|
323
|
+
language: "unknown"
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function parseBiomeOutput(output, exitCode, preParsed) {
|
|
327
|
+
try {
|
|
328
|
+
const json = preParsed !== void 0 ? preParsed : JSON.parse(output);
|
|
329
|
+
const summary = json.summary ?? {};
|
|
330
|
+
const errorCount = summary.errors ?? 0;
|
|
331
|
+
const warningCount = summary.warnings ?? 0;
|
|
332
|
+
const diagnostics = json.diagnostics ?? [];
|
|
333
|
+
const filesWithIssues = Array.from(
|
|
334
|
+
new Set(
|
|
335
|
+
diagnostics.map((d) => d.location?.path).filter((path2) => Boolean(path2))
|
|
336
|
+
)
|
|
337
|
+
);
|
|
338
|
+
const fixableCount = diagnostics.filter((d) => d.canFix === true).length;
|
|
339
|
+
return {
|
|
340
|
+
success: exitCode === 0,
|
|
341
|
+
errorCount,
|
|
342
|
+
warningCount,
|
|
343
|
+
fixableCount,
|
|
344
|
+
filesWithIssues,
|
|
345
|
+
exitCode,
|
|
346
|
+
rawOutput: output
|
|
347
|
+
};
|
|
348
|
+
} catch (_parseError) {
|
|
349
|
+
console.debug("[lint-on-diff] Failed to parse Biome JSON, returning null");
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/core/project/poetic-vitest-harness.ts
|
|
355
|
+
import { existsSync } from "fs";
|
|
356
|
+
import path from "path";
|
|
357
|
+
function hasPoeticVitestHarness(root) {
|
|
358
|
+
return existsSync(path.join(root, "scripts", "dynamic-node-options.mjs"));
|
|
359
|
+
}
|
|
360
|
+
function displayArg(value) {
|
|
361
|
+
return /[\s"'\\]/.test(value) ? JSON.stringify(value) : value;
|
|
362
|
+
}
|
|
363
|
+
function buildExternalVitestRunInvocation(args = []) {
|
|
364
|
+
const commandArgs = ["--no-install", "vitest", "run", ...args, "--reporter=verbose"];
|
|
365
|
+
return {
|
|
366
|
+
display: ["npx", ...commandArgs].map(displayArg).join(" "),
|
|
367
|
+
cmd: "npx",
|
|
368
|
+
args: commandArgs
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/core/judge/test-gate.ts
|
|
373
|
+
import { existsSync as existsSync2, readdirSync, readFileSync } from "fs";
|
|
374
|
+
import { dirname, extname, join, posix as pathPosix, relative, resolve, sep } from "path";
|
|
375
|
+
import { execa } from "execa";
|
|
376
|
+
var DEFAULT_CONFIG = {
|
|
377
|
+
timeout: 12e4,
|
|
378
|
+
testFilePatterns: [
|
|
379
|
+
/\.(test|spec)\.(ts|js|tsx|jsx)$/,
|
|
380
|
+
/__tests__\/.*\.(ts|js|tsx|jsx)$/,
|
|
381
|
+
/(^|\/)test_.*\.py$/,
|
|
382
|
+
/(^|\/).*_test\.py$/,
|
|
383
|
+
/_test\.go$/,
|
|
384
|
+
/(^|\/)tests\/.*\.rs$/,
|
|
385
|
+
/\.tests\.ps1$/i
|
|
386
|
+
]
|
|
387
|
+
};
|
|
388
|
+
var TEST_DISCOVERY_ROOTS = ["tests", "test", "src"];
|
|
389
|
+
var SKIPPED_TEST_DISCOVERY_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".poetic", "dist", "build"]);
|
|
390
|
+
var SOURCE_FILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
|
|
391
|
+
var JS_LIKE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs"]);
|
|
392
|
+
var MULTI_COMMAND_TEST_BUDGET_MS = 12e4;
|
|
393
|
+
function createVerificationTestEnv() {
|
|
394
|
+
return createVerificationCommandEnv();
|
|
395
|
+
}
|
|
396
|
+
function extractFailingTestsFromVitestOutput(output) {
|
|
397
|
+
if (!output) return [];
|
|
398
|
+
const names = /* @__PURE__ */ new Set();
|
|
399
|
+
const failHeader = /^\s*FAIL\s+(.+?)\s+>\s+(.+)$/gm;
|
|
400
|
+
let match = failHeader.exec(output);
|
|
401
|
+
while (match !== null) {
|
|
402
|
+
const remainder = (match[2] ?? "").trim();
|
|
403
|
+
if (remainder) names.add(remainder);
|
|
404
|
+
match = failHeader.exec(output);
|
|
405
|
+
}
|
|
406
|
+
const cross = /^\s*[×]\s+(.+?)(?:\s+\(\d+ms\))?\s*$/gm;
|
|
407
|
+
let crossMatch = cross.exec(output);
|
|
408
|
+
while (crossMatch !== null) {
|
|
409
|
+
const name = (crossMatch[1] ?? "").trim();
|
|
410
|
+
if (name) names.add(name);
|
|
411
|
+
crossMatch = cross.exec(output);
|
|
412
|
+
}
|
|
413
|
+
return Array.from(names).slice(0, 20);
|
|
414
|
+
}
|
|
415
|
+
function asMeasuredMetrics(metrics) {
|
|
416
|
+
return { ...metrics, metricsMeasured: metrics.testsRun > 0 };
|
|
417
|
+
}
|
|
418
|
+
function inferSuccessStatusFromMetrics(metrics) {
|
|
419
|
+
if (metrics.testsRun === 0) {
|
|
420
|
+
return "error";
|
|
421
|
+
}
|
|
422
|
+
return metrics.testsFailed === 0 ? "passed" : "failed";
|
|
423
|
+
}
|
|
424
|
+
function inferSuccessStatusForRunner(_runner, metrics) {
|
|
425
|
+
return inferSuccessStatusFromMetrics(metrics);
|
|
426
|
+
}
|
|
427
|
+
function isPoeticSuiteSkipOutput(command, output) {
|
|
428
|
+
if (!/skipping\s+tests/i.test(output)) return false;
|
|
429
|
+
const normalizedCommand = command ?? "";
|
|
430
|
+
return /\bnpm\s+run\s+test:(?:fast|changed)\b/.test(normalizedCommand) || normalizedCommand.includes("scripts/dynamic-node-options.mjs");
|
|
431
|
+
}
|
|
432
|
+
function fullProvenanceCoversRequiredCommands(testsExecuted, worktreePath, changedFiles) {
|
|
433
|
+
if (changedFiles.length === 0) return true;
|
|
434
|
+
const changedLanguages = changedProjectLanguages(changedFiles);
|
|
435
|
+
if (changedLanguages.size === 0 && !changedFilesRequireVerification(changedFiles)) {
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
const scope = resolveVerificationChannelScope(worktreePath, changedFiles);
|
|
439
|
+
const testScope = scope.test;
|
|
440
|
+
if (testScope.availability === "unavailable") {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
const required = testScope.availability === "relevant" ? selectScopedTestCommands(scope) : [];
|
|
444
|
+
if (required.length === 0) return true;
|
|
445
|
+
const recorded = testsExecuted.commands;
|
|
446
|
+
if (!recorded || recorded.length === 0) return false;
|
|
447
|
+
const recordedSet = new Set(recorded);
|
|
448
|
+
return required.every((command) => recordedSet.has(command));
|
|
449
|
+
}
|
|
450
|
+
function canReuseRecordedProvenance(testsExecuted, mode, worktreePath, changedFiles) {
|
|
451
|
+
if (testsExecuted.scope === "none") return false;
|
|
452
|
+
if (mode !== "full") return true;
|
|
453
|
+
if (testsExecuted.scope !== "full") return false;
|
|
454
|
+
return fullProvenanceCoversRequiredCommands(testsExecuted, worktreePath, changedFiles);
|
|
455
|
+
}
|
|
456
|
+
async function executeRequiredTestCommands(worktreePath, commands, options) {
|
|
457
|
+
const abortSignal = options?.abortSignal;
|
|
458
|
+
const scope = options?.scope ?? "full";
|
|
459
|
+
const startedAt = Date.now();
|
|
460
|
+
const artifacts = [];
|
|
461
|
+
const fallbacks = detectProjectCapabilities(worktreePath).verification.testFallback ?? [];
|
|
462
|
+
const coveredPrimaries = [];
|
|
463
|
+
for (const command of commands) {
|
|
464
|
+
if (abortSignal?.aborted) {
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
const remaining = Math.max(0, MULTI_COMMAND_TEST_BUDGET_MS - (Date.now() - startedAt));
|
|
468
|
+
if (remaining <= 0) {
|
|
469
|
+
artifacts.push({
|
|
470
|
+
command,
|
|
471
|
+
exitCode: 124,
|
|
472
|
+
durationMs: 0,
|
|
473
|
+
outputTail: "Test command skipped: shared multi-command timeout budget exhausted.",
|
|
474
|
+
status: "timeout",
|
|
475
|
+
failingTests: []
|
|
476
|
+
});
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
const commandStart = Date.now();
|
|
480
|
+
artifacts.push(
|
|
481
|
+
await runCapabilityTestCommandWithFallback(worktreePath, command, fallbacks, {
|
|
482
|
+
timeoutMs: remaining,
|
|
483
|
+
tailLines: 200,
|
|
484
|
+
start: commandStart,
|
|
485
|
+
abortSignal
|
|
486
|
+
})
|
|
487
|
+
);
|
|
488
|
+
coveredPrimaries.push(command);
|
|
489
|
+
if (abortSignal?.aborted) break;
|
|
490
|
+
}
|
|
491
|
+
if (abortSignal?.aborted && artifacts.length !== commands.length) {
|
|
492
|
+
return {
|
|
493
|
+
testStatus: "error",
|
|
494
|
+
availability: "executed",
|
|
495
|
+
testsRun: 0,
|
|
496
|
+
testsPassed: 0,
|
|
497
|
+
testsFailed: 0,
|
|
498
|
+
metricsAvailable: false,
|
|
499
|
+
duration: Date.now() - startedAt,
|
|
500
|
+
filesTested: [],
|
|
501
|
+
command: artifacts.map((artifact) => artifact.command).join("\n"),
|
|
502
|
+
commands: coveredPrimaries,
|
|
503
|
+
scope,
|
|
504
|
+
exitCode: 130,
|
|
505
|
+
outputTail: "Test verification aborted before every selected command completed.",
|
|
506
|
+
failingTests: []
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
const combined = combineVitestPlanResults(artifacts);
|
|
510
|
+
const capabilityRunner = detectTestRunner(worktreePath) ?? "generic";
|
|
511
|
+
const commandMetrics = artifacts.map(
|
|
512
|
+
(artifact) => (
|
|
513
|
+
// Per-artifact runner: a multi-command plan can mix toolchains, and the
|
|
514
|
+
// project-wide runner id would parse a stdlib-unittest tail with the wrong
|
|
515
|
+
// parser (in a polyglot repo detectTestRunner reports the Node runner).
|
|
516
|
+
parseTestOutput(
|
|
517
|
+
resolveArtifactMetricRunner(artifact.command, capabilityRunner),
|
|
518
|
+
artifact.outputTail
|
|
519
|
+
)
|
|
520
|
+
)
|
|
521
|
+
);
|
|
522
|
+
const metrics = commandMetrics.reduce(
|
|
523
|
+
(total, current) => ({
|
|
524
|
+
testsRun: total.testsRun + current.testsRun,
|
|
525
|
+
testsPassed: total.testsPassed + current.testsPassed,
|
|
526
|
+
testsFailed: total.testsFailed + current.testsFailed
|
|
527
|
+
}),
|
|
528
|
+
{ testsRun: 0, testsPassed: 0, testsFailed: 0 }
|
|
529
|
+
);
|
|
530
|
+
const metricsAvailable = commandMetrics.length > 0 && commandMetrics.every((command) => command.metricsMeasured);
|
|
531
|
+
const combinedUnavailable = combined.status === "unavailable" || combined.status === "passed" && !metricsAvailable;
|
|
532
|
+
return {
|
|
533
|
+
testStatus: combinedUnavailable ? "not_run" : combined.status === "passed" ? "passed" : combined.status === "timeout" ? "timeout" : combined.status === "unavailable" ? "not_run" : combined.status === "error" ? "error" : "failed",
|
|
534
|
+
availability: combinedUnavailable ? "unavailable" : "executed",
|
|
535
|
+
...combinedUnavailable ? { skipReason: "no-supported-test-runner" } : {},
|
|
536
|
+
testsRun: metrics.testsRun,
|
|
537
|
+
testsPassed: metrics.testsPassed,
|
|
538
|
+
testsFailed: metrics.testsFailed,
|
|
539
|
+
metricsAvailable,
|
|
540
|
+
duration: Date.now() - startedAt,
|
|
541
|
+
filesTested: [],
|
|
542
|
+
// Display/audit: actual commands that ran (may be approved fallbacks).
|
|
543
|
+
command: combined.command,
|
|
544
|
+
// Structured coverage: resolver primaries for completed plan steps only.
|
|
545
|
+
commands: coveredPrimaries,
|
|
546
|
+
scope,
|
|
547
|
+
exitCode: combinedUnavailable ? void 0 : combined.exitCode,
|
|
548
|
+
outputTail: combined.outputTail,
|
|
549
|
+
failingTests: combined.failingTests
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async function runTestGate(variant, worktreePath, enabledOrOptions) {
|
|
553
|
+
const enabled = typeof enabledOrOptions === "boolean" ? enabledOrOptions : enabledOrOptions.enabled;
|
|
554
|
+
const mode = typeof enabledOrOptions === "boolean" ? "touched" : enabledOrOptions.mode ?? "touched";
|
|
555
|
+
const abortSignal = typeof enabledOrOptions === "boolean" ? void 0 : enabledOrOptions.abortSignal;
|
|
556
|
+
if (!enabled || !JUDGE_FEATURE_FLAGS.TEST_GATE_FOR_TEST_INTENT) {
|
|
557
|
+
return {
|
|
558
|
+
testStatus: "not_run",
|
|
559
|
+
availability: "skipped",
|
|
560
|
+
skipReason: "disabled",
|
|
561
|
+
testsRun: 0,
|
|
562
|
+
testsPassed: 0,
|
|
563
|
+
testsFailed: 0,
|
|
564
|
+
duration: 0,
|
|
565
|
+
filesTested: [],
|
|
566
|
+
command: "",
|
|
567
|
+
scope: "none"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
const recordedChangedFiles = extractChangedFilesMeta(extractDiffText(variant)).map(
|
|
571
|
+
(file) => file.path
|
|
572
|
+
);
|
|
573
|
+
if (variant.testsExecuted && canReuseRecordedProvenance(variant.testsExecuted, mode, worktreePath, recordedChangedFiles)) {
|
|
574
|
+
const scope = variant.testsExecuted.scope === "targeted" ? "touched" : variant.testsExecuted.scope;
|
|
575
|
+
const exitCode = variant.testsExecuted.exitCode;
|
|
576
|
+
const isEnvCommandFailure = (code) => code === 126 || code === 127;
|
|
577
|
+
const recordedStatus = normalizeRecordedTestStatus(variant.testStatus);
|
|
578
|
+
const inferredStatus = recordedStatus !== void 0 ? recordedStatus : (
|
|
579
|
+
// Fail closed: a status was recorded but is outside the vocabulary Poetic
|
|
580
|
+
// can interpret (VariantResult.testStatus is `... | string`, so a provider
|
|
581
|
+
// vocabulary, a stale value, or a fabricated one all type-check). A bare
|
|
582
|
+
// exit-0 beside such a status is not a pass observation. Failure inference
|
|
583
|
+
// from a non-zero exit is deliberately unchanged — downgrading an observed
|
|
584
|
+
// failure would fail open in the dangerous direction.
|
|
585
|
+
hasUnrecognizedRecordedTestStatus(variant.testStatus) && exitCode === 0 ? "not_run" : exitCode === 0 ? "passed" : exitCode === 124 ? "timeout" : isEnvCommandFailure(exitCode) ? "error" : exitCode === void 0 ? "not_run" : "failed"
|
|
586
|
+
);
|
|
587
|
+
const failedTests = variant.testMetrics?.failedTests ?? 0;
|
|
588
|
+
const totalTests = variant.testMetrics?.totalTests ?? 0;
|
|
589
|
+
const hasExecutionEvidence = hasRecordedTestExecutionEvidence({
|
|
590
|
+
command: variant.testsExecuted.command,
|
|
591
|
+
filesTested: variant.testsExecuted.filesTested,
|
|
592
|
+
totalTests,
|
|
593
|
+
exitCode: variant.testsExecuted.exitCode
|
|
594
|
+
});
|
|
595
|
+
if (!hasExecutionEvidence) {
|
|
596
|
+
if (!existsSync2(worktreePath)) {
|
|
597
|
+
return {
|
|
598
|
+
testStatus: "not_run",
|
|
599
|
+
availability: "skipped",
|
|
600
|
+
skipReason: "no-execution-provenance",
|
|
601
|
+
testsRun: 0,
|
|
602
|
+
testsPassed: 0,
|
|
603
|
+
testsFailed: 0,
|
|
604
|
+
duration: variant.testsExecuted.durationMs ?? variant.testMetrics?.durationMs ?? 0,
|
|
605
|
+
filesTested: [],
|
|
606
|
+
command: "",
|
|
607
|
+
scope: "none",
|
|
608
|
+
exitCode: void 0,
|
|
609
|
+
outputTail: void 0,
|
|
610
|
+
failingTests: [],
|
|
611
|
+
signalConsistency: "consistent"
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
const normalizedStatus = inferredStatus === "passed" && totalTests === 0 ? "not_run" : inferredStatus;
|
|
616
|
+
const isContradiction = inferredStatus === "failed" && failedTests === 0 && totalTests > 0;
|
|
617
|
+
const signalConsistency = isContradiction ? "inconsistent" : "consistent";
|
|
618
|
+
const recordedCommands = variant.testsExecuted.commands;
|
|
619
|
+
return {
|
|
620
|
+
testStatus: normalizedStatus,
|
|
621
|
+
availability: normalizedStatus === "not_run" ? "unavailable" : "executed",
|
|
622
|
+
skipReason: normalizedStatus === "not_run" ? "no-execution-provenance" : void 0,
|
|
623
|
+
testsRun: normalizedStatus === "not_run" ? 0 : totalTests,
|
|
624
|
+
testsPassed: normalizedStatus === "not_run" ? 0 : variant.testMetrics?.passedTests ?? 0,
|
|
625
|
+
testsFailed: normalizedStatus === "not_run" ? 0 : failedTests,
|
|
626
|
+
// Reused provenance carries no parse provenance of its own, so a recorded
|
|
627
|
+
// count is the only measurement evidence available. Reporting it keeps a
|
|
628
|
+
// zero-count reuse from reading as measured verification downstream.
|
|
629
|
+
...normalizedStatus === "not_run" ? { metricsAvailable: false } : { metricsAvailable: true },
|
|
630
|
+
duration: variant.testsExecuted.durationMs ?? variant.testMetrics?.durationMs ?? 0,
|
|
631
|
+
filesTested: normalizedStatus === "not_run" ? [] : variant.testsExecuted.filesTested ?? [],
|
|
632
|
+
command: normalizedStatus === "not_run" ? "" : variant.testsExecuted.command ?? "",
|
|
633
|
+
...normalizedStatus !== "not_run" && recordedCommands && recordedCommands.length > 0 ? { commands: recordedCommands } : {},
|
|
634
|
+
scope,
|
|
635
|
+
exitCode: normalizedStatus === "not_run" ? void 0 : exitCode,
|
|
636
|
+
outputTail: variant.testsExecuted.outputTail,
|
|
637
|
+
failingTests: variant.testsExecuted.failingTests ?? [],
|
|
638
|
+
signalConsistency,
|
|
639
|
+
evidenceSource: "reused_provenance"
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const diffText = extractDiffText(variant);
|
|
644
|
+
const changedTestFiles = getChangedTestFiles(diffText);
|
|
645
|
+
const changedFilesMeta = extractChangedFilesMeta(diffText);
|
|
646
|
+
const changedFiles = changedFilesMeta.map((file) => file.path);
|
|
647
|
+
if (mode === "full") {
|
|
648
|
+
const changedLanguages = changedProjectLanguages(changedFiles);
|
|
649
|
+
if (changedLanguages.size > 0 || changedFilesRequireVerification(changedFiles)) {
|
|
650
|
+
const scope = resolveVerificationChannelScope(worktreePath, changedFiles);
|
|
651
|
+
const commands = scope.test.availability === "relevant" ? selectScopedTestCommands(scope) : [];
|
|
652
|
+
if (commands.length === 0) {
|
|
653
|
+
return {
|
|
654
|
+
testStatus: "not_run",
|
|
655
|
+
availability: "unavailable",
|
|
656
|
+
skipReason: "no-supported-test-runner",
|
|
657
|
+
testsRun: 0,
|
|
658
|
+
testsPassed: 0,
|
|
659
|
+
testsFailed: 0,
|
|
660
|
+
metricsAvailable: false,
|
|
661
|
+
duration: 0,
|
|
662
|
+
filesTested: [],
|
|
663
|
+
command: "",
|
|
664
|
+
scope: "none",
|
|
665
|
+
exitCode: 127,
|
|
666
|
+
outputTail: "Full test verification unavailable: no command covers every changed language.",
|
|
667
|
+
failingTests: []
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return executeRequiredTestCommands(worktreePath, commands, {
|
|
671
|
+
abortSignal,
|
|
672
|
+
scope: "full"
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
return executeRequestedSuite(worktreePath, abortSignal);
|
|
676
|
+
}
|
|
677
|
+
{
|
|
678
|
+
const scope = resolveVerificationChannelScope(worktreePath, changedFiles);
|
|
679
|
+
const requiredCommands = scope.test.availability === "relevant" ? selectScopedTestCommands(scope) : [];
|
|
680
|
+
const changedLanguages = changedProjectLanguages(changedFiles);
|
|
681
|
+
if (changedLanguages.size > 1 && requiredCommands.length > 1) {
|
|
682
|
+
return executeRequiredTestCommands(worktreePath, requiredCommands, {
|
|
683
|
+
abortSignal,
|
|
684
|
+
scope: "full"
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
const selectedTestFiles = mode === "affected" ? selectTestFilesForAffectedMode(worktreePath, changedFilesMeta, changedTestFiles) : selectTestFilesForTouchedMode(worktreePath, changedFilesMeta, changedTestFiles);
|
|
689
|
+
const runner = detectTestRunner(worktreePath);
|
|
690
|
+
if (selectedTestFiles.files.length === 0) {
|
|
691
|
+
if (shouldRunFullSuiteForUnmappedSourceChange(runner, changedFilesMeta)) {
|
|
692
|
+
return executeRequestedSuite(worktreePath, abortSignal);
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
testStatus: "not_run",
|
|
696
|
+
availability: "skipped",
|
|
697
|
+
skipReason: "no-test-files-selected",
|
|
698
|
+
testsRun: 0,
|
|
699
|
+
testsPassed: 0,
|
|
700
|
+
testsFailed: 0,
|
|
701
|
+
duration: 0,
|
|
702
|
+
filesTested: [],
|
|
703
|
+
command: "",
|
|
704
|
+
scope: "none"
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
if (!runner) {
|
|
708
|
+
return {
|
|
709
|
+
testStatus: "not_run",
|
|
710
|
+
availability: "unavailable",
|
|
711
|
+
skipReason: "no-supported-test-runner",
|
|
712
|
+
testsRun: 0,
|
|
713
|
+
testsPassed: 0,
|
|
714
|
+
testsFailed: 0,
|
|
715
|
+
duration: 0,
|
|
716
|
+
filesTested: [],
|
|
717
|
+
command: "",
|
|
718
|
+
scope: selectedTestFiles.scope,
|
|
719
|
+
exitCode: void 0,
|
|
720
|
+
outputTail: "Skipped test gate: no supported test runner detected from project manifests.",
|
|
721
|
+
failingTests: []
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
const scopedNodeInvocation = runner === "vitest" || runner === "jest" || runner === "mocha" || runner === "ava" || runner === "node-test" ? resolveScopedNodeTestInvocation(runner, selectedTestFiles.files, worktreePath) : void 0;
|
|
725
|
+
if ((runner === "vitest" || runner === "jest" || runner === "mocha" || runner === "ava" || runner === "node-test") && !scopedNodeInvocation) {
|
|
726
|
+
return {
|
|
727
|
+
testStatus: "not_run",
|
|
728
|
+
availability: "unavailable",
|
|
729
|
+
skipReason: "no-supported-test-runner",
|
|
730
|
+
testsRun: 0,
|
|
731
|
+
testsPassed: 0,
|
|
732
|
+
testsFailed: 0,
|
|
733
|
+
duration: 0,
|
|
734
|
+
filesTested: [],
|
|
735
|
+
command: "",
|
|
736
|
+
scope: selectedTestFiles.scope,
|
|
737
|
+
exitCode: void 0,
|
|
738
|
+
outputTail: "Skipped test gate: no safely scoped or declared project test command is available.",
|
|
739
|
+
failingTests: []
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
const unscopedFullSuiteRunner = runner === "maven" || runner === "gradle" || runner === "unittest";
|
|
743
|
+
const executionScope = unscopedFullSuiteRunner || scopedNodeInvocation?.scope === "full" ? "full" : selectedTestFiles.scope;
|
|
744
|
+
const testCommand = scopedNodeInvocation?.display ?? buildTestCommand(runner, selectedTestFiles.files, worktreePath);
|
|
745
|
+
const result = await executeTests(
|
|
746
|
+
runner,
|
|
747
|
+
selectedTestFiles.files,
|
|
748
|
+
worktreePath,
|
|
749
|
+
abortSignal,
|
|
750
|
+
scopedNodeInvocation
|
|
751
|
+
);
|
|
752
|
+
const unavailableSignal = detectVerificationToolUnavailable(testCommand, result.outputTail ?? "");
|
|
753
|
+
if (result.status === "unavailable") {
|
|
754
|
+
return {
|
|
755
|
+
testStatus: "not_run",
|
|
756
|
+
availability: "unavailable",
|
|
757
|
+
skipReason: "no-matching-tests",
|
|
758
|
+
testsRun: 0,
|
|
759
|
+
testsPassed: 0,
|
|
760
|
+
testsFailed: 0,
|
|
761
|
+
duration: result.duration,
|
|
762
|
+
filesTested: [],
|
|
763
|
+
command: testCommand,
|
|
764
|
+
scope: executionScope,
|
|
765
|
+
exitCode: void 0,
|
|
766
|
+
outputTail: result.outputTail,
|
|
767
|
+
failingTests: []
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
const resultUnavailable = result.status === "error" && result.testsRun === 0 && (result.exitCode === 126 || result.exitCode === 127 || /\b(?:ENOENT|command not found)\b/i.test(result.outputTail ?? "") || unavailableSignal !== void 0);
|
|
771
|
+
return {
|
|
772
|
+
testStatus: resultUnavailable ? "not_run" : result.status,
|
|
773
|
+
availability: resultUnavailable ? "unavailable" : "executed",
|
|
774
|
+
skipReason: resultUnavailable ? "no-supported-test-runner" : void 0,
|
|
775
|
+
testsRun: result.testsRun,
|
|
776
|
+
testsPassed: result.testsPassed,
|
|
777
|
+
testsFailed: result.testsFailed,
|
|
778
|
+
// Carry the measurement flag on the executed path too: previously only the
|
|
779
|
+
// suite/multi-command paths set it, so a scoped run's unmeasured counts
|
|
780
|
+
// reached consumers as if they had been parsed.
|
|
781
|
+
...resultUnavailable ? {} : { metricsAvailable: result.metricsMeasured },
|
|
782
|
+
duration: result.duration,
|
|
783
|
+
filesTested: executionScope === "full" ? [] : selectedTestFiles.files,
|
|
784
|
+
command: testCommand,
|
|
785
|
+
scope: executionScope,
|
|
786
|
+
exitCode: resultUnavailable ? void 0 : result.exitCode,
|
|
787
|
+
outputTail: resultUnavailable && unavailableSignal ? truncateToByteLimit(
|
|
788
|
+
[result.outputTail, unavailableSignal.message].filter(Boolean).join("\n"),
|
|
789
|
+
1e5
|
|
790
|
+
) : result.outputTail,
|
|
791
|
+
failingTests: result.failingTests
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async function executeRequestedSuite(worktreePath, abortSignal) {
|
|
795
|
+
const startTime = Date.now();
|
|
796
|
+
const suiteCommand = detectSuiteCommand(worktreePath);
|
|
797
|
+
if (!suiteCommand) {
|
|
798
|
+
return {
|
|
799
|
+
testStatus: "not_run",
|
|
800
|
+
availability: "unavailable",
|
|
801
|
+
skipReason: "no-supported-test-runner",
|
|
802
|
+
testsRun: 0,
|
|
803
|
+
testsPassed: 0,
|
|
804
|
+
testsFailed: 0,
|
|
805
|
+
duration: 0,
|
|
806
|
+
filesTested: [],
|
|
807
|
+
command: "",
|
|
808
|
+
scope: "full",
|
|
809
|
+
exitCode: void 0,
|
|
810
|
+
outputTail: "No test suite command detected (package.json missing or no supported runner/scripts)",
|
|
811
|
+
failingTests: []
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
const { display, cmd, args, timeoutMs, runner } = suiteCommand;
|
|
815
|
+
try {
|
|
816
|
+
const result = await execa(cmd, args, {
|
|
817
|
+
cwd: worktreePath,
|
|
818
|
+
stdio: "pipe",
|
|
819
|
+
all: true,
|
|
820
|
+
reject: false,
|
|
821
|
+
timeout: timeoutMs,
|
|
822
|
+
env: createVerificationTestEnv(),
|
|
823
|
+
extendEnv: false,
|
|
824
|
+
cancelSignal: abortSignal
|
|
825
|
+
});
|
|
826
|
+
const duration = Date.now() - startTime;
|
|
827
|
+
const { exitCode } = resolveVerificationExitCode(display, result);
|
|
828
|
+
const all = String(result.all ?? "");
|
|
829
|
+
const outputTail = truncateToByteLimit(tailLines(all, 200), 1e5);
|
|
830
|
+
if (isPoeticSuiteSkipOutput(display, all)) {
|
|
831
|
+
return {
|
|
832
|
+
testStatus: "not_run",
|
|
833
|
+
availability: "skipped",
|
|
834
|
+
skipReason: "suite-command-skipped",
|
|
835
|
+
testsRun: 0,
|
|
836
|
+
testsPassed: 0,
|
|
837
|
+
testsFailed: 0,
|
|
838
|
+
duration,
|
|
839
|
+
filesTested: [],
|
|
840
|
+
command: display,
|
|
841
|
+
scope: "none",
|
|
842
|
+
exitCode: void 0,
|
|
843
|
+
outputTail,
|
|
844
|
+
failingTests: []
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
if (exitCode === 0 && detectZeroCollectedTests(
|
|
848
|
+
runner === "maven" ? "mvn test" : runner === "gradle" ? "gradle test" : display,
|
|
849
|
+
all
|
|
850
|
+
)) {
|
|
851
|
+
return {
|
|
852
|
+
testStatus: "not_run",
|
|
853
|
+
availability: "unavailable",
|
|
854
|
+
skipReason: "no-matching-tests",
|
|
855
|
+
testsRun: 0,
|
|
856
|
+
testsPassed: 0,
|
|
857
|
+
testsFailed: 0,
|
|
858
|
+
duration,
|
|
859
|
+
filesTested: [],
|
|
860
|
+
command: display,
|
|
861
|
+
scope: "full",
|
|
862
|
+
exitCode: void 0,
|
|
863
|
+
outputTail,
|
|
864
|
+
failingTests: []
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const failingTests = exitCode === 0 ? [] : runner === "vitest" ? extractFailingTestsFromVitestOutput(all) : extractGenericFailures(all);
|
|
868
|
+
const metrics = parseTestOutput(resolveArtifactMetricRunner(display, runner), all);
|
|
869
|
+
if (exitCode === 1 && /No test files found/i.test(all)) {
|
|
870
|
+
return {
|
|
871
|
+
testStatus: "not_run",
|
|
872
|
+
availability: "unavailable",
|
|
873
|
+
skipReason: "no-matching-tests",
|
|
874
|
+
testsRun: 0,
|
|
875
|
+
testsPassed: 0,
|
|
876
|
+
testsFailed: 0,
|
|
877
|
+
duration,
|
|
878
|
+
filesTested: [],
|
|
879
|
+
command: display,
|
|
880
|
+
scope: "full",
|
|
881
|
+
exitCode: void 0,
|
|
882
|
+
outputTail,
|
|
883
|
+
failingTests: []
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
const unavailableSignal = detectVerificationToolUnavailable(display, all);
|
|
887
|
+
if (unavailableSignal) {
|
|
888
|
+
return {
|
|
889
|
+
testStatus: "not_run",
|
|
890
|
+
availability: "unavailable",
|
|
891
|
+
skipReason: "no-supported-test-runner",
|
|
892
|
+
testsRun: 0,
|
|
893
|
+
testsPassed: 0,
|
|
894
|
+
testsFailed: 0,
|
|
895
|
+
duration,
|
|
896
|
+
filesTested: [],
|
|
897
|
+
command: display,
|
|
898
|
+
scope: "full",
|
|
899
|
+
exitCode: void 0,
|
|
900
|
+
outputTail: truncateToByteLimit(
|
|
901
|
+
[outputTail, unavailableSignal.message].filter(Boolean).join("\n"),
|
|
902
|
+
1e5
|
|
903
|
+
),
|
|
904
|
+
failingTests: []
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
const exitZeroFailure = exitCode === 0 ? detectExitZeroVerifyFailure(display, all) : void 0;
|
|
908
|
+
if (exitZeroFailure) {
|
|
909
|
+
return {
|
|
910
|
+
testStatus: "failed",
|
|
911
|
+
availability: "executed",
|
|
912
|
+
testsRun: metrics.testsRun,
|
|
913
|
+
testsPassed: metrics.testsPassed,
|
|
914
|
+
testsFailed: Math.max(metrics.testsFailed, 1),
|
|
915
|
+
duration,
|
|
916
|
+
filesTested: [],
|
|
917
|
+
command: display,
|
|
918
|
+
scope: "full",
|
|
919
|
+
exitCode: 1,
|
|
920
|
+
outputTail: truncateToByteLimit(
|
|
921
|
+
[outputTail, exitZeroFailure.message].filter(Boolean).join("\n"),
|
|
922
|
+
1e5
|
|
923
|
+
),
|
|
924
|
+
failingTests: [exitZeroFailure.message]
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
if (exitCode === 0) {
|
|
928
|
+
if (!metrics.metricsMeasured && metrics.testsFailed === 0) {
|
|
929
|
+
return {
|
|
930
|
+
testStatus: "not_run",
|
|
931
|
+
availability: "unavailable",
|
|
932
|
+
skipReason: "no-supported-test-runner",
|
|
933
|
+
testsRun: 0,
|
|
934
|
+
testsPassed: 0,
|
|
935
|
+
testsFailed: 0,
|
|
936
|
+
metricsAvailable: false,
|
|
937
|
+
duration,
|
|
938
|
+
filesTested: [],
|
|
939
|
+
command: display,
|
|
940
|
+
scope: "full",
|
|
941
|
+
exitCode: void 0,
|
|
942
|
+
outputTail,
|
|
943
|
+
failingTests: []
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const testStatus = inferSuccessStatusForRunner(runner, metrics);
|
|
947
|
+
const metricsAvailable = metrics.metricsMeasured;
|
|
948
|
+
return {
|
|
949
|
+
testStatus,
|
|
950
|
+
availability: "executed",
|
|
951
|
+
testsRun: metrics.testsRun,
|
|
952
|
+
testsPassed: metrics.testsPassed,
|
|
953
|
+
testsFailed: metrics.testsFailed,
|
|
954
|
+
metricsAvailable,
|
|
955
|
+
duration,
|
|
956
|
+
filesTested: [],
|
|
957
|
+
command: display,
|
|
958
|
+
scope: "full",
|
|
959
|
+
// DERIVED, not observed: failure prose on a clean process exit remains a
|
|
960
|
+
// non-zero verification result so exit-code-first consumers cannot launder
|
|
961
|
+
// it into a pass.
|
|
962
|
+
exitCode: testStatus === "passed" ? exitCode : 1,
|
|
963
|
+
outputTail,
|
|
964
|
+
failingTests
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
if (exitCode === 124) {
|
|
968
|
+
return {
|
|
969
|
+
testStatus: "timeout",
|
|
970
|
+
availability: "executed",
|
|
971
|
+
testsRun: metrics.testsRun,
|
|
972
|
+
testsPassed: metrics.testsPassed,
|
|
973
|
+
testsFailed: metrics.testsFailed,
|
|
974
|
+
duration,
|
|
975
|
+
filesTested: [],
|
|
976
|
+
command: display,
|
|
977
|
+
scope: "full",
|
|
978
|
+
exitCode,
|
|
979
|
+
outputTail,
|
|
980
|
+
failingTests
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
return {
|
|
984
|
+
testStatus: "failed",
|
|
985
|
+
availability: "executed",
|
|
986
|
+
testsRun: metrics.testsRun,
|
|
987
|
+
testsPassed: metrics.testsPassed,
|
|
988
|
+
testsFailed: metrics.testsFailed,
|
|
989
|
+
duration,
|
|
990
|
+
filesTested: [],
|
|
991
|
+
command: display,
|
|
992
|
+
scope: "full",
|
|
993
|
+
exitCode,
|
|
994
|
+
outputTail,
|
|
995
|
+
failingTests
|
|
996
|
+
};
|
|
997
|
+
} catch (error) {
|
|
998
|
+
const err = error;
|
|
999
|
+
const duration = Date.now() - startTime;
|
|
1000
|
+
const output = String(err?.all ?? err?.stdout ?? err?.stderr ?? err?.message ?? "");
|
|
1001
|
+
const isNotFound = err?.code === "ENOENT" || /\bENOENT\b/i.test(output) || /command not found/i.test(output);
|
|
1002
|
+
const isTimedOut = Boolean(err?.timedOut) || err?.killed && err?.signal === "SIGTERM";
|
|
1003
|
+
if (isNotFound) {
|
|
1004
|
+
return {
|
|
1005
|
+
testStatus: "error",
|
|
1006
|
+
availability: "unavailable",
|
|
1007
|
+
testsRun: 0,
|
|
1008
|
+
testsPassed: 0,
|
|
1009
|
+
testsFailed: 0,
|
|
1010
|
+
duration,
|
|
1011
|
+
filesTested: [],
|
|
1012
|
+
command: display,
|
|
1013
|
+
scope: "full",
|
|
1014
|
+
exitCode: 127,
|
|
1015
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
1016
|
+
failingTests: runner === "vitest" ? extractFailingTestsFromVitestOutput(output) : extractGenericFailures(output)
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
if (isTimedOut) {
|
|
1020
|
+
return {
|
|
1021
|
+
testStatus: "timeout",
|
|
1022
|
+
availability: "executed",
|
|
1023
|
+
testsRun: 0,
|
|
1024
|
+
testsPassed: 0,
|
|
1025
|
+
testsFailed: 0,
|
|
1026
|
+
duration,
|
|
1027
|
+
filesTested: [],
|
|
1028
|
+
command: display,
|
|
1029
|
+
scope: "full",
|
|
1030
|
+
exitCode: 124,
|
|
1031
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
1032
|
+
failingTests: runner === "vitest" ? extractFailingTestsFromVitestOutput(output) : extractGenericFailures(output)
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
testStatus: "error",
|
|
1037
|
+
availability: "unavailable",
|
|
1038
|
+
testsRun: 0,
|
|
1039
|
+
testsPassed: 0,
|
|
1040
|
+
testsFailed: 0,
|
|
1041
|
+
duration,
|
|
1042
|
+
filesTested: [],
|
|
1043
|
+
command: display,
|
|
1044
|
+
scope: "full",
|
|
1045
|
+
exitCode: 1,
|
|
1046
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
1047
|
+
failingTests: runner === "vitest" ? extractFailingTestsFromVitestOutput(output) : extractGenericFailures(output)
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
function detectSuiteCommand(worktreePath) {
|
|
1052
|
+
try {
|
|
1053
|
+
const pkgJsonPath = join(worktreePath, "package.json");
|
|
1054
|
+
const scripts = existsSync2(pkgJsonPath) ? JSON.parse(readFileSync(pkgJsonPath, "utf-8"))?.scripts ?? {} : {};
|
|
1055
|
+
const runner = detectTestRunner(worktreePath);
|
|
1056
|
+
const capabilities = detectProjectCapabilities(worktreePath);
|
|
1057
|
+
const isPoeticSelfHarness = hasPoeticVitestHarness(worktreePath);
|
|
1058
|
+
if (isPoeticSelfHarness && typeof scripts["test:fast"] === "string") {
|
|
1059
|
+
return {
|
|
1060
|
+
display: "npm run test:fast",
|
|
1061
|
+
cmd: "npm",
|
|
1062
|
+
args: ["run", "test:fast"],
|
|
1063
|
+
timeoutMs: 12e4,
|
|
1064
|
+
runner: runner === "jest" ? "jest" : "vitest"
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
if (isPoeticSelfHarness && typeof scripts["test:changed"] === "string") {
|
|
1068
|
+
return {
|
|
1069
|
+
display: "npm run test:changed -- --local",
|
|
1070
|
+
cmd: "npm",
|
|
1071
|
+
args: ["run", "test:changed", "--", "--local"],
|
|
1072
|
+
timeoutMs: 12e4,
|
|
1073
|
+
runner: runner === "jest" ? "jest" : "vitest"
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
const resolverCommand = selectSuiteResolverCommand(capabilities.verification.test);
|
|
1077
|
+
if (!runner || runner === "vitest" && !isPoeticSelfHarness && resolverCommand) {
|
|
1078
|
+
const command = resolverCommand;
|
|
1079
|
+
if (command) {
|
|
1080
|
+
const [cmd, ...args] = splitCommand(command);
|
|
1081
|
+
if (cmd) {
|
|
1082
|
+
return {
|
|
1083
|
+
display: command,
|
|
1084
|
+
cmd,
|
|
1085
|
+
args,
|
|
1086
|
+
timeoutMs: 12e4,
|
|
1087
|
+
runner: "generic"
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (runner === "vitest") {
|
|
1093
|
+
if (!isPoeticSelfHarness) {
|
|
1094
|
+
const invocation = buildExternalVitestRunInvocation();
|
|
1095
|
+
return {
|
|
1096
|
+
display: invocation.display,
|
|
1097
|
+
cmd: invocation.cmd,
|
|
1098
|
+
args: invocation.args,
|
|
1099
|
+
timeoutMs: 12e4,
|
|
1100
|
+
runner
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
return {
|
|
1104
|
+
display: "node scripts/dynamic-node-options.mjs vitest run --changed --reporter=verbose",
|
|
1105
|
+
cmd: "node",
|
|
1106
|
+
args: [
|
|
1107
|
+
"scripts/dynamic-node-options.mjs",
|
|
1108
|
+
"vitest",
|
|
1109
|
+
"run",
|
|
1110
|
+
"--changed",
|
|
1111
|
+
"--reporter=verbose"
|
|
1112
|
+
],
|
|
1113
|
+
timeoutMs: 12e4,
|
|
1114
|
+
runner
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
if (runner === "jest") {
|
|
1118
|
+
return {
|
|
1119
|
+
display: "npx --no-install jest --verbose",
|
|
1120
|
+
cmd: "npx",
|
|
1121
|
+
args: ["--no-install", "jest", "--verbose"],
|
|
1122
|
+
timeoutMs: 12e4,
|
|
1123
|
+
runner
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (runner === "mocha" || runner === "ava" || runner === "node-test") {
|
|
1127
|
+
const fallbackCommand = runner === "mocha" ? "npx --no-install mocha" : runner === "ava" ? "npx --no-install ava" : "node --test";
|
|
1128
|
+
const command = resolverCommand ?? fallbackCommand;
|
|
1129
|
+
const [cmd, ...args] = splitCommand(command);
|
|
1130
|
+
if (!cmd) return null;
|
|
1131
|
+
return { display: command, cmd, args, timeoutMs: 12e4, runner };
|
|
1132
|
+
}
|
|
1133
|
+
if (runner === "pytest" || runner === "unittest") {
|
|
1134
|
+
const command = (runner === "unittest" ? detectPythonUnittestCommand(capabilities) : void 0) ?? capabilities.verification.test[0] ?? "pytest";
|
|
1135
|
+
const [cmd, ...args] = splitCommand(command);
|
|
1136
|
+
if (!cmd) return null;
|
|
1137
|
+
return { display: command, cmd, args, timeoutMs: 12e4, runner };
|
|
1138
|
+
}
|
|
1139
|
+
if (runner === "go" || runner === "cargo") {
|
|
1140
|
+
const fallbackCommand = runner === "go" ? "go test ./..." : "cargo test --locked";
|
|
1141
|
+
const command = selectRunnerLanguageResolverCommand(capabilities.verification.test, runner) ?? fallbackCommand;
|
|
1142
|
+
const [cmd, ...args] = splitCommand(command);
|
|
1143
|
+
if (!cmd) return null;
|
|
1144
|
+
return { display: command, cmd, args, timeoutMs: 12e4, runner };
|
|
1145
|
+
}
|
|
1146
|
+
if (runner === "maven" || runner === "gradle") {
|
|
1147
|
+
const command = selectRunnerLanguageResolverCommand(capabilities.verification.test, runner) ?? capabilities.verification.test[0];
|
|
1148
|
+
if (!command) return null;
|
|
1149
|
+
const [cmd, ...args] = splitCommand(command);
|
|
1150
|
+
if (!cmd) return null;
|
|
1151
|
+
return { display: command, cmd, args, timeoutMs: 12e4, runner };
|
|
1152
|
+
}
|
|
1153
|
+
return null;
|
|
1154
|
+
} catch {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function selectSuiteResolverCommand(commands) {
|
|
1159
|
+
return commands.find((command) => {
|
|
1160
|
+
const normalized = command.trim();
|
|
1161
|
+
return !/\b(?:npm|pnpm|yarn|bun)\s+run\s+test:(?:fast|changed)\b/.test(normalized);
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
var RUNNER_VERIFICATION_LANGUAGES = {
|
|
1165
|
+
go: "go",
|
|
1166
|
+
cargo: "rust",
|
|
1167
|
+
maven: "jvm",
|
|
1168
|
+
gradle: "jvm"
|
|
1169
|
+
};
|
|
1170
|
+
function selectRunnerLanguageResolverCommand(commands, runner) {
|
|
1171
|
+
const language = RUNNER_VERIFICATION_LANGUAGES[runner];
|
|
1172
|
+
if (!language) return void 0;
|
|
1173
|
+
return commands.find((command) => {
|
|
1174
|
+
const described = describeVerificationCommand(command).language;
|
|
1175
|
+
return described === language || described === "unknown";
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
var NODE_SCOPED_RUNNERS = /* @__PURE__ */ new Set([
|
|
1179
|
+
"vitest",
|
|
1180
|
+
"jest",
|
|
1181
|
+
"mocha",
|
|
1182
|
+
"ava",
|
|
1183
|
+
"node-test"
|
|
1184
|
+
]);
|
|
1185
|
+
var SCOPED_SAFE_OPTIONS_COMMON = {
|
|
1186
|
+
"--reporter": { takesValue: true },
|
|
1187
|
+
"--require": { takesValue: true },
|
|
1188
|
+
"-r": { takesValue: true },
|
|
1189
|
+
"--verbose": {},
|
|
1190
|
+
"--silent": {},
|
|
1191
|
+
"--bail": {},
|
|
1192
|
+
"--color": {},
|
|
1193
|
+
"--colors": {},
|
|
1194
|
+
"--no-color": {},
|
|
1195
|
+
"--no-colors": {},
|
|
1196
|
+
"--coverage": {},
|
|
1197
|
+
"--no-coverage": {}
|
|
1198
|
+
};
|
|
1199
|
+
var NODE_SCOPED_SAFE_OPTIONS = {
|
|
1200
|
+
vitest: {
|
|
1201
|
+
...SCOPED_SAFE_OPTIONS_COMMON,
|
|
1202
|
+
"--preload": { takesValue: true },
|
|
1203
|
+
"--pool": { takesValue: true },
|
|
1204
|
+
"--sequencer": { takesValue: true },
|
|
1205
|
+
"--transformMode": { takesValue: true },
|
|
1206
|
+
"--environment": { takesValue: true },
|
|
1207
|
+
"--no-file-parallelism": {},
|
|
1208
|
+
"--isolate": {},
|
|
1209
|
+
"--no-isolate": {}
|
|
1210
|
+
},
|
|
1211
|
+
jest: {
|
|
1212
|
+
...SCOPED_SAFE_OPTIONS_COMMON,
|
|
1213
|
+
"--runInBand": {},
|
|
1214
|
+
"--forceExit": {},
|
|
1215
|
+
"--detectOpenHandles": {},
|
|
1216
|
+
"--ci": {},
|
|
1217
|
+
"--json": {},
|
|
1218
|
+
"--maxWorkers": { takesValue: true },
|
|
1219
|
+
"--testTimeout": { takesValue: true },
|
|
1220
|
+
"--outputFile": { takesValue: true }
|
|
1221
|
+
},
|
|
1222
|
+
mocha: {
|
|
1223
|
+
...SCOPED_SAFE_OPTIONS_COMMON,
|
|
1224
|
+
// Mocha `-t` / `--timeout` are duration, not name selectors (unlike Jest/Vitest `-t`).
|
|
1225
|
+
"--timeout": { takesValue: true },
|
|
1226
|
+
"-t": { takesValue: true },
|
|
1227
|
+
"--ui": { takesValue: true },
|
|
1228
|
+
"-u": { takesValue: true },
|
|
1229
|
+
"--slow": { takesValue: true },
|
|
1230
|
+
"-R": { takesValue: true },
|
|
1231
|
+
"-b": {},
|
|
1232
|
+
"--exit": {},
|
|
1233
|
+
"--recursive": {},
|
|
1234
|
+
"--full-trace": {},
|
|
1235
|
+
"--async-only": {},
|
|
1236
|
+
"-A": {},
|
|
1237
|
+
"--delay": {},
|
|
1238
|
+
"--forbid-only": {},
|
|
1239
|
+
"--forbid-pending": {},
|
|
1240
|
+
"--parallel": {},
|
|
1241
|
+
"--jobs": { takesValue: true }
|
|
1242
|
+
},
|
|
1243
|
+
ava: {
|
|
1244
|
+
...SCOPED_SAFE_OPTIONS_COMMON,
|
|
1245
|
+
"--timeout": { takesValue: true },
|
|
1246
|
+
"--concurrency": { takesValue: true },
|
|
1247
|
+
"--serial": {},
|
|
1248
|
+
"--tap": {}
|
|
1249
|
+
},
|
|
1250
|
+
"node-test": {
|
|
1251
|
+
"--test-reporter": { takesValue: true },
|
|
1252
|
+
"--test-timeout": { takesValue: true },
|
|
1253
|
+
"--experimental-test-coverage": {}
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
function extractPackageManagerScriptName(command) {
|
|
1257
|
+
const normalized = command.trim();
|
|
1258
|
+
const runMatch = normalized.match(/^(?:npm|pnpm|yarn|bun)\s+run\s+([^\s]+)/);
|
|
1259
|
+
if (runMatch?.[1]) return runMatch[1];
|
|
1260
|
+
if (/^(?:npm|pnpm|yarn|bun)\s+test\b/.test(normalized)) return "test";
|
|
1261
|
+
return void 0;
|
|
1262
|
+
}
|
|
1263
|
+
function packageManagerForwardedArgs(command) {
|
|
1264
|
+
const tokens = splitCommand(command.trim());
|
|
1265
|
+
if (tokens[1] === "run" && tokens[2]) {
|
|
1266
|
+
return tokens.slice(3);
|
|
1267
|
+
}
|
|
1268
|
+
if (tokens[1] === "test") {
|
|
1269
|
+
return tokens.slice(2);
|
|
1270
|
+
}
|
|
1271
|
+
return [];
|
|
1272
|
+
}
|
|
1273
|
+
function readPackageScriptBody(worktreePath, scriptName) {
|
|
1274
|
+
try {
|
|
1275
|
+
const pkgJsonPath = join(worktreePath, "package.json");
|
|
1276
|
+
if (!existsSync2(pkgJsonPath)) return void 0;
|
|
1277
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
1278
|
+
const body = pkgJson.scripts?.[scriptName];
|
|
1279
|
+
return typeof body === "string" && body.trim().length > 0 ? body.trim() : void 0;
|
|
1280
|
+
} catch {
|
|
1281
|
+
return void 0;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function hasOnlySafeScopedOptions(flagTail, runner) {
|
|
1285
|
+
const safeOptions = NODE_SCOPED_SAFE_OPTIONS[runner];
|
|
1286
|
+
const tokens = splitCommand(flagTail.trim());
|
|
1287
|
+
let i = 0;
|
|
1288
|
+
while (i < tokens.length) {
|
|
1289
|
+
const token = tokens[i];
|
|
1290
|
+
if (token === "--") {
|
|
1291
|
+
return i + 1 >= tokens.length;
|
|
1292
|
+
}
|
|
1293
|
+
if (token.startsWith("-")) {
|
|
1294
|
+
const flagName = token.split("=", 1)[0] ?? token;
|
|
1295
|
+
const option = safeOptions[flagName];
|
|
1296
|
+
if (!option) {
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
if (option.takesValue && !token.includes("=")) {
|
|
1300
|
+
if (i + 1 >= tokens.length || tokens[i + 1].startsWith("-")) {
|
|
1301
|
+
return false;
|
|
1302
|
+
}
|
|
1303
|
+
i += 2;
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
i += 1;
|
|
1307
|
+
continue;
|
|
1308
|
+
}
|
|
1309
|
+
return false;
|
|
1310
|
+
}
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
function isSimpleScopedNodeRunnerInvocation(command, runner) {
|
|
1314
|
+
let normalized = command.trim();
|
|
1315
|
+
if (!normalized) return false;
|
|
1316
|
+
if (/(?:&&|\|\||[;|<>`]|\$\()/.test(normalized)) return false;
|
|
1317
|
+
normalized = normalized.replace(/^(?:npx|bunx)(?:\s+--no-install)?\s+/u, "").replace(/^(?:pnpm|yarn)\s+(?:exec|dlx)\s+/u, "").replace(/^node\s+scripts\/dynamic-node-options\.mjs\s+/u, "").replace(/^(?:\.\/)?node_modules\/\.bin\//u, "");
|
|
1318
|
+
switch (runner) {
|
|
1319
|
+
case "vitest": {
|
|
1320
|
+
if (!/^vitest\b/.test(normalized)) return false;
|
|
1321
|
+
const rest = normalized.replace(/^vitest(?:\s+run)?\b/, "");
|
|
1322
|
+
return hasOnlySafeScopedOptions(rest, "vitest");
|
|
1323
|
+
}
|
|
1324
|
+
case "jest": {
|
|
1325
|
+
if (!/^jest\b/.test(normalized)) return false;
|
|
1326
|
+
return hasOnlySafeScopedOptions(normalized.replace(/^jest\b/, ""), "jest");
|
|
1327
|
+
}
|
|
1328
|
+
case "mocha": {
|
|
1329
|
+
if (!/^mocha\b/.test(normalized)) return false;
|
|
1330
|
+
return hasOnlySafeScopedOptions(normalized.replace(/^mocha\b/, ""), "mocha");
|
|
1331
|
+
}
|
|
1332
|
+
case "ava": {
|
|
1333
|
+
if (!/^ava\b/.test(normalized)) return false;
|
|
1334
|
+
return hasOnlySafeScopedOptions(normalized.replace(/^ava\b/, ""), "ava");
|
|
1335
|
+
}
|
|
1336
|
+
case "node-test": {
|
|
1337
|
+
const tokens = splitCommand(normalized);
|
|
1338
|
+
if (tokens[0] !== "node") return false;
|
|
1339
|
+
const testIdx = tokens.findIndex((token, index) => index > 0 && token === "--test");
|
|
1340
|
+
if (testIdx < 0) return false;
|
|
1341
|
+
const before = tokens.slice(1, testIdx).join(" ");
|
|
1342
|
+
const after = tokens.slice(testIdx + 1).join(" ");
|
|
1343
|
+
return hasOnlySafeScopedOptions(before, "node-test") && hasOnlySafeScopedOptions(after, "node-test");
|
|
1344
|
+
}
|
|
1345
|
+
default:
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
function isScopedFileCompatibleNodeCommand(command, runner, worktreePath) {
|
|
1350
|
+
const normalized = command.trim();
|
|
1351
|
+
if (!normalized) return false;
|
|
1352
|
+
if (/\b(?:npm|pnpm|yarn|bun)\s+run\s+test:(?:smoke|fast|changed)\b/.test(normalized)) {
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
if (/\b(?:npm|pnpm|yarn|bun)\s+test:(?:smoke|fast|changed)\b/.test(normalized)) {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
if (/^(?:npm|pnpm|yarn|bun)\b/.test(normalized)) {
|
|
1359
|
+
const scriptName = extractPackageManagerScriptName(normalized);
|
|
1360
|
+
if (!scriptName) return false;
|
|
1361
|
+
if (scriptName !== "test" && scriptName !== "test:unit") return false;
|
|
1362
|
+
const scriptBody = readPackageScriptBody(worktreePath, scriptName);
|
|
1363
|
+
if (!scriptBody) return false;
|
|
1364
|
+
const forwardedArgs = packageManagerForwardedArgs(normalized);
|
|
1365
|
+
if (forwardedArgs.length > 0 && !(forwardedArgs.length === 1 && forwardedArgs[0] === "--")) {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
return isSimpleScopedNodeRunnerInvocation(scriptBody, runner);
|
|
1369
|
+
}
|
|
1370
|
+
return isSimpleScopedNodeRunnerInvocation(normalized, runner);
|
|
1371
|
+
}
|
|
1372
|
+
function selectNodeScopedResolverCommand(worktreePath, runner) {
|
|
1373
|
+
if (!NODE_SCOPED_RUNNERS.has(runner)) return void 0;
|
|
1374
|
+
const commands = detectProjectCapabilities(worktreePath).verification.test;
|
|
1375
|
+
return commands.find((command) => {
|
|
1376
|
+
if (!isNodeVerificationCommand(command)) return false;
|
|
1377
|
+
return isScopedFileCompatibleNodeCommand(command, runner, worktreePath);
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
function selectNodeSuiteResolverCommand(worktreePath) {
|
|
1381
|
+
return detectProjectCapabilities(worktreePath).verification.test.find(isNodeVerificationCommand);
|
|
1382
|
+
}
|
|
1383
|
+
function isNodeVerificationCommand(command) {
|
|
1384
|
+
const { language } = describeVerificationCommand(command);
|
|
1385
|
+
return language === "node" || language === "unknown" || commandMatchesLanguage(command, "javascript") || commandMatchesLanguage(command, "typescript");
|
|
1386
|
+
}
|
|
1387
|
+
function formatScopedDisplay(cmd, args) {
|
|
1388
|
+
const bare = /* @__PURE__ */ new Set([
|
|
1389
|
+
"npx",
|
|
1390
|
+
"npm",
|
|
1391
|
+
"pnpm",
|
|
1392
|
+
"yarn",
|
|
1393
|
+
"bun",
|
|
1394
|
+
"node",
|
|
1395
|
+
"run",
|
|
1396
|
+
"test",
|
|
1397
|
+
"vitest",
|
|
1398
|
+
"jest",
|
|
1399
|
+
"mocha",
|
|
1400
|
+
"ava",
|
|
1401
|
+
"--",
|
|
1402
|
+
"--no-install",
|
|
1403
|
+
"--reporter=verbose",
|
|
1404
|
+
"--verbose",
|
|
1405
|
+
"--test"
|
|
1406
|
+
]);
|
|
1407
|
+
const formatArg = (part) => {
|
|
1408
|
+
if (bare.has(part) || part.startsWith("--") && !part.includes("/")) return part;
|
|
1409
|
+
if (part.includes("/") || part.includes(".") || /\s/.test(part)) return `"${part}"`;
|
|
1410
|
+
return part;
|
|
1411
|
+
};
|
|
1412
|
+
return [cmd, ...args].map((part, index) => index === 0 ? part : formatArg(part)).join(" ");
|
|
1413
|
+
}
|
|
1414
|
+
function appendScopedFilesToCommand(command, testFiles) {
|
|
1415
|
+
const tokens = splitCommand(command);
|
|
1416
|
+
const cmd = tokens[0] ?? command;
|
|
1417
|
+
const baseArgs = tokens.slice(1);
|
|
1418
|
+
if (cmd === "yarn") {
|
|
1419
|
+
const args2 = [...baseArgs, ...testFiles];
|
|
1420
|
+
return { cmd, args: args2, display: formatScopedDisplay(cmd, args2) };
|
|
1421
|
+
}
|
|
1422
|
+
if (cmd === "npm" || cmd === "pnpm" || cmd === "bun") {
|
|
1423
|
+
if (baseArgs.includes("--")) {
|
|
1424
|
+
const args3 = [...baseArgs, ...testFiles];
|
|
1425
|
+
return { cmd, args: args3, display: formatScopedDisplay(cmd, args3) };
|
|
1426
|
+
}
|
|
1427
|
+
const args2 = [...baseArgs, "--", ...testFiles];
|
|
1428
|
+
return { cmd, args: args2, display: formatScopedDisplay(cmd, args2) };
|
|
1429
|
+
}
|
|
1430
|
+
const args = [...baseArgs, ...testFiles];
|
|
1431
|
+
return { cmd, args, display: formatScopedDisplay(cmd, args) };
|
|
1432
|
+
}
|
|
1433
|
+
function resolveScopedNodeTestInvocation(runner, testFiles, worktreePath) {
|
|
1434
|
+
if (runner === "vitest" && hasPoeticVitestHarness(worktreePath)) {
|
|
1435
|
+
const args2 = [
|
|
1436
|
+
"scripts/dynamic-node-options.mjs",
|
|
1437
|
+
"vitest",
|
|
1438
|
+
"run",
|
|
1439
|
+
...testFiles,
|
|
1440
|
+
"--reporter=verbose"
|
|
1441
|
+
];
|
|
1442
|
+
return { cmd: "node", args: args2, display: formatScopedDisplay("node", args2), scope: "scoped" };
|
|
1443
|
+
}
|
|
1444
|
+
const resolverCommand = selectNodeScopedResolverCommand(worktreePath, runner);
|
|
1445
|
+
if (resolverCommand) {
|
|
1446
|
+
return { ...appendScopedFilesToCommand(resolverCommand, testFiles), scope: "scoped" };
|
|
1447
|
+
}
|
|
1448
|
+
const suiteCommand = selectNodeSuiteResolverCommand(worktreePath);
|
|
1449
|
+
if (!suiteCommand) return void 0;
|
|
1450
|
+
const [cmd, ...args] = splitCommand(suiteCommand);
|
|
1451
|
+
if (!cmd) return void 0;
|
|
1452
|
+
return { cmd, args, display: suiteCommand, scope: "full" };
|
|
1453
|
+
}
|
|
1454
|
+
function detectTestRunner(worktreePath) {
|
|
1455
|
+
try {
|
|
1456
|
+
const capabilities = detectProjectCapabilities(worktreePath);
|
|
1457
|
+
const isNodeTarget = hasProjectLanguage(capabilities, "javascript") || hasProjectLanguage(capabilities, "typescript");
|
|
1458
|
+
if (isNodeTarget) {
|
|
1459
|
+
const pkgJsonPath = join(worktreePath, "package.json");
|
|
1460
|
+
if (existsSync2(pkgJsonPath)) {
|
|
1461
|
+
const pkgJsonContent = readFileSync(pkgJsonPath, "utf-8");
|
|
1462
|
+
const pkgJson = JSON.parse(pkgJsonContent);
|
|
1463
|
+
const allDeps = {
|
|
1464
|
+
...pkgJson.dependencies,
|
|
1465
|
+
...pkgJson.devDependencies
|
|
1466
|
+
};
|
|
1467
|
+
if (allDeps.vitest) {
|
|
1468
|
+
return "vitest";
|
|
1469
|
+
}
|
|
1470
|
+
if (allDeps.jest || allDeps["@types/jest"]) {
|
|
1471
|
+
return "jest";
|
|
1472
|
+
}
|
|
1473
|
+
if (allDeps.mocha) {
|
|
1474
|
+
return "mocha";
|
|
1475
|
+
}
|
|
1476
|
+
if (allDeps.ava) {
|
|
1477
|
+
return "ava";
|
|
1478
|
+
}
|
|
1479
|
+
const scripts = pkgJson.scripts ?? {};
|
|
1480
|
+
const testScript = typeof scripts.test === "string" ? scripts.test : "";
|
|
1481
|
+
if (/\bnode\s+(?:--[^\s]+\s+)*--test(?=\s|$)/.test(testScript)) {
|
|
1482
|
+
return "node-test";
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
if (hasProjectLanguage(capabilities, "python")) {
|
|
1487
|
+
return detectPythonUnittestCommand(capabilities) !== void 0 ? "unittest" : "pytest";
|
|
1488
|
+
}
|
|
1489
|
+
if (hasProjectLanguage(capabilities, "go")) return "go";
|
|
1490
|
+
if (hasProjectLanguage(capabilities, "rust")) return "cargo";
|
|
1491
|
+
if (capabilities.manifests.pomXml) return "maven";
|
|
1492
|
+
if (capabilities.manifests.gradle) return "gradle";
|
|
1493
|
+
if (detectPowerShellTestCommand(worktreePath)) return "powershell";
|
|
1494
|
+
return null;
|
|
1495
|
+
} catch {
|
|
1496
|
+
return null;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
function resolveArtifactMetricRunner(command, fallback) {
|
|
1500
|
+
return isPythonStdlibUnittestCommand(command) ? "unittest" : fallback;
|
|
1501
|
+
}
|
|
1502
|
+
function detectPythonUnittestCommand(capabilities) {
|
|
1503
|
+
const primary = capabilities.verification.test.find(
|
|
1504
|
+
(command) => describeVerificationCommand(command).language === "python"
|
|
1505
|
+
);
|
|
1506
|
+
if (primary === void 0) return void 0;
|
|
1507
|
+
return isPythonStdlibUnittestCommand(primary) ? primary : void 0;
|
|
1508
|
+
}
|
|
1509
|
+
function detectPowerShellTestCommand(worktreePath) {
|
|
1510
|
+
return detectProjectCapabilities(worktreePath).verification.test.find(
|
|
1511
|
+
(command) => /^(?:pwsh|powershell)(?:\.exe)?\b.*\bInvoke-Pester\b/i.test(command)
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
function commandMatchesLanguage(command, language) {
|
|
1515
|
+
const normalized = command.trim();
|
|
1516
|
+
const descriptor = describeVerificationCommand(normalized);
|
|
1517
|
+
if (descriptor.language !== "unknown") {
|
|
1518
|
+
if (descriptor.kind !== "test") return false;
|
|
1519
|
+
switch (descriptor.language) {
|
|
1520
|
+
case "node":
|
|
1521
|
+
return language === "javascript" || language === "typescript";
|
|
1522
|
+
case "jvm":
|
|
1523
|
+
return language === "java" || language === "kotlin";
|
|
1524
|
+
case "native":
|
|
1525
|
+
return language === "c" || language === "cpp";
|
|
1526
|
+
default:
|
|
1527
|
+
return descriptor.language === language;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
switch (language) {
|
|
1531
|
+
case "javascript":
|
|
1532
|
+
case "typescript":
|
|
1533
|
+
return /^(?:npm|pnpm|yarn|bun)\b.*\btest\b|^bun\s+test\b/.test(normalized);
|
|
1534
|
+
case "python":
|
|
1535
|
+
return /\b(?:pytest|tox)\b/.test(normalized);
|
|
1536
|
+
case "go":
|
|
1537
|
+
return /^go\s+test\b/.test(normalized);
|
|
1538
|
+
case "rust":
|
|
1539
|
+
return /^cargo\s+test\b/.test(normalized);
|
|
1540
|
+
case "java":
|
|
1541
|
+
case "kotlin":
|
|
1542
|
+
return /\b(?:gradle|mvn|mvnw|gradlew)\b.*\btest\b/.test(normalized);
|
|
1543
|
+
case "dotnet":
|
|
1544
|
+
return /^dotnet\s+test\b/.test(normalized);
|
|
1545
|
+
case "swift":
|
|
1546
|
+
return /^swift\s+test\b/.test(normalized);
|
|
1547
|
+
case "dart":
|
|
1548
|
+
return /^(?:dart|flutter)\s+test\b/.test(normalized);
|
|
1549
|
+
case "ruby":
|
|
1550
|
+
return /\b(?:rspec|rake\s+test)\b/.test(normalized);
|
|
1551
|
+
case "php":
|
|
1552
|
+
return /\b(?:phpunit|composer\s+exec\s+--\s+phpunit)\b/.test(normalized);
|
|
1553
|
+
case "c":
|
|
1554
|
+
case "cpp":
|
|
1555
|
+
return /^(?:ctest|meson\s+test|make\s+(?:check|test))\b/.test(normalized);
|
|
1556
|
+
case "r":
|
|
1557
|
+
return /^R\s+CMD\s+check\b/.test(normalized);
|
|
1558
|
+
case "sql":
|
|
1559
|
+
return /^dbt\s+test\b/.test(normalized);
|
|
1560
|
+
case "powershell":
|
|
1561
|
+
return /^(?:pwsh|powershell)(?:\.exe)?\b.*\bInvoke-Pester\b/i.test(normalized);
|
|
1562
|
+
default:
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
function selectCapabilityTestCommandsForChanges(worktreePath, changedFiles) {
|
|
1567
|
+
const changedLanguages = changedProjectLanguages(changedFiles);
|
|
1568
|
+
if (changedLanguages.size === 0) {
|
|
1569
|
+
return detectProjectCapabilities(worktreePath).verification.test.slice(0, 1);
|
|
1570
|
+
}
|
|
1571
|
+
return selectCapabilityTestCommandsForLanguages(worktreePath, changedLanguages);
|
|
1572
|
+
}
|
|
1573
|
+
function selectCapabilityTestCommandsForLanguages(worktreePath, changedLanguages) {
|
|
1574
|
+
const capabilities = detectProjectCapabilities(worktreePath);
|
|
1575
|
+
if (capabilities.verification.test.length === 0) return [];
|
|
1576
|
+
if (changedLanguages.size === 0) return [];
|
|
1577
|
+
const selected = [];
|
|
1578
|
+
for (const language of changedLanguages) {
|
|
1579
|
+
const command = capabilities.verification.test.find(
|
|
1580
|
+
(candidate) => commandMatchesLanguage(candidate, language)
|
|
1581
|
+
);
|
|
1582
|
+
if (command) selected.push(command);
|
|
1583
|
+
}
|
|
1584
|
+
const unique = Array.from(new Set(selected));
|
|
1585
|
+
const uncovered = [...changedLanguages].filter(
|
|
1586
|
+
(language) => !unique.some((command) => commandMatchesLanguage(command, language))
|
|
1587
|
+
);
|
|
1588
|
+
if (uncovered.length > 0) return [];
|
|
1589
|
+
return unique;
|
|
1590
|
+
}
|
|
1591
|
+
function changedProjectLanguages(changedFiles) {
|
|
1592
|
+
const languages = /* @__PURE__ */ new Set();
|
|
1593
|
+
for (const file of changedFiles) {
|
|
1594
|
+
const inferred = languageForProjectPath(file);
|
|
1595
|
+
const language = inferred && inferred !== "unknown" ? inferred : languageForProjectConfigPath(file);
|
|
1596
|
+
if (language && language !== "unknown" && language !== "docs") languages.add(language);
|
|
1597
|
+
}
|
|
1598
|
+
return languages;
|
|
1599
|
+
}
|
|
1600
|
+
function languageForProjectConfigPath(filePath) {
|
|
1601
|
+
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
1602
|
+
const base = normalized.split("/").pop() ?? normalized;
|
|
1603
|
+
if (base === "package.json" || base.startsWith("tsconfig")) return "typescript";
|
|
1604
|
+
if (base === "pyproject.toml" || base === "poetry.lock" || base === "pipfile" || base === "pipfile.lock" || base.startsWith("requirements") || base === "pytest.ini" || base === "tox.ini") {
|
|
1605
|
+
return "python";
|
|
1606
|
+
}
|
|
1607
|
+
if (base === "go.mod" || base === "go.sum") return "go";
|
|
1608
|
+
if (base === "cargo.toml" || base === "cargo.lock") return "rust";
|
|
1609
|
+
if (base === "pom.xml" || base.startsWith("build.gradle") || base.startsWith("settings.gradle")) {
|
|
1610
|
+
return "java";
|
|
1611
|
+
}
|
|
1612
|
+
if (/\.(?:csproj|fsproj|vbproj)$/.test(base) || base.endsWith(".sln")) return "dotnet";
|
|
1613
|
+
if (base === "package.swift") return "swift";
|
|
1614
|
+
if (base === "pubspec.yaml") return "dart";
|
|
1615
|
+
if (base === "gemfile" || base === "rakefile" || base.endsWith(".gemspec")) return "ruby";
|
|
1616
|
+
if (base === "composer.json" || base === "composer.lock") return "php";
|
|
1617
|
+
if (base === "cmakelists.txt" || base === "cmakepresets.json" || base === "meson.build") {
|
|
1618
|
+
return "cpp";
|
|
1619
|
+
}
|
|
1620
|
+
if (base === "pesterconfiguration.ps1") return "powershell";
|
|
1621
|
+
if (base === "description" || base === "renv.lock" || base.endsWith(".rproj")) return "r";
|
|
1622
|
+
if (base === "dbt_project.yml" || base === "dbt_project.yaml") return "sql";
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
function getChangedTestFiles(diff) {
|
|
1626
|
+
const testFiles = /* @__PURE__ */ new Set();
|
|
1627
|
+
const diffHeaderPattern = /^\+\+\+ b\/(.+)$/gm;
|
|
1628
|
+
let match = diffHeaderPattern.exec(diff);
|
|
1629
|
+
while (match !== null) {
|
|
1630
|
+
const filePath = match[1];
|
|
1631
|
+
if (filePath && isTestFile(filePath)) {
|
|
1632
|
+
testFiles.add(filePath);
|
|
1633
|
+
}
|
|
1634
|
+
match = diffHeaderPattern.exec(diff);
|
|
1635
|
+
}
|
|
1636
|
+
return Array.from(testFiles);
|
|
1637
|
+
}
|
|
1638
|
+
function selectTestFilesForTouchedMode(worktreePath, changedFilesMeta, changedTestFiles) {
|
|
1639
|
+
const selectedFiles = new Set(changedTestFiles);
|
|
1640
|
+
const riskExpandedFiles = /* @__PURE__ */ new Set();
|
|
1641
|
+
for (const file of changedFilesMeta) {
|
|
1642
|
+
for (const mappedTest of getRiskExpandedTestFiles(worktreePath, file.path, file.category)) {
|
|
1643
|
+
riskExpandedFiles.add(mappedTest);
|
|
1644
|
+
selectedFiles.add(mappedTest);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
const files = Array.from(selectedFiles);
|
|
1648
|
+
if (files.length === 0) {
|
|
1649
|
+
return { files: [], scope: "none" };
|
|
1650
|
+
}
|
|
1651
|
+
if (riskExpandedFiles.size > 0) {
|
|
1652
|
+
return { files, scope: "targeted" };
|
|
1653
|
+
}
|
|
1654
|
+
return { files, scope: "touched" };
|
|
1655
|
+
}
|
|
1656
|
+
function selectTestFilesForAffectedMode(worktreePath, changedFilesMeta, changedTestFiles) {
|
|
1657
|
+
const touchedSelection = selectTestFilesForTouchedMode(
|
|
1658
|
+
worktreePath,
|
|
1659
|
+
changedFilesMeta,
|
|
1660
|
+
changedTestFiles
|
|
1661
|
+
);
|
|
1662
|
+
const selectedFiles = new Set(touchedSelection.files);
|
|
1663
|
+
const reverseImportMatches = findAffectedImportingTests(worktreePath, changedFilesMeta);
|
|
1664
|
+
for (const testFile of reverseImportMatches) {
|
|
1665
|
+
selectedFiles.add(testFile);
|
|
1666
|
+
}
|
|
1667
|
+
const files = Array.from(selectedFiles).sort();
|
|
1668
|
+
if (files.length === 0) {
|
|
1669
|
+
return { files: [], scope: "none" };
|
|
1670
|
+
}
|
|
1671
|
+
if (reverseImportMatches.length > 0) {
|
|
1672
|
+
return { files, scope: "affected" };
|
|
1673
|
+
}
|
|
1674
|
+
return { files, scope: touchedSelection.scope };
|
|
1675
|
+
}
|
|
1676
|
+
function shouldRunFullSuiteForUnmappedSourceChange(runner, changedFilesMeta) {
|
|
1677
|
+
if (!runner || runner === "vitest" || runner === "jest") {
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
return changedFilesMeta.some((file) => file.category === "source" && !isTestFile(file.path));
|
|
1681
|
+
}
|
|
1682
|
+
function splitCommand(command) {
|
|
1683
|
+
const args = [];
|
|
1684
|
+
let current = "";
|
|
1685
|
+
let quote = null;
|
|
1686
|
+
let escaping = false;
|
|
1687
|
+
for (const char of command.trim()) {
|
|
1688
|
+
if (escaping) {
|
|
1689
|
+
current += char;
|
|
1690
|
+
escaping = false;
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
if (char === "\\") {
|
|
1694
|
+
escaping = true;
|
|
1695
|
+
continue;
|
|
1696
|
+
}
|
|
1697
|
+
if (quote) {
|
|
1698
|
+
if (char === quote) {
|
|
1699
|
+
quote = null;
|
|
1700
|
+
} else {
|
|
1701
|
+
current += char;
|
|
1702
|
+
}
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
if (char === '"' || char === "'") {
|
|
1706
|
+
quote = char;
|
|
1707
|
+
continue;
|
|
1708
|
+
}
|
|
1709
|
+
if (/\s/.test(char)) {
|
|
1710
|
+
if (current.length > 0) {
|
|
1711
|
+
args.push(current);
|
|
1712
|
+
current = "";
|
|
1713
|
+
}
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
current += char;
|
|
1717
|
+
}
|
|
1718
|
+
if (escaping) current += "\\";
|
|
1719
|
+
if (current.length > 0) args.push(current);
|
|
1720
|
+
return args;
|
|
1721
|
+
}
|
|
1722
|
+
function findAffectedImportingTests(worktreePath, changedFilesMeta) {
|
|
1723
|
+
const capabilities = detectProjectCapabilities(worktreePath);
|
|
1724
|
+
const isJsTsTarget = hasProjectLanguage(capabilities, "javascript") || hasProjectLanguage(capabilities, "typescript");
|
|
1725
|
+
if (!isJsTsTarget) {
|
|
1726
|
+
return [];
|
|
1727
|
+
}
|
|
1728
|
+
const changedSourceFiles = new Set(
|
|
1729
|
+
changedFilesMeta.filter((file) => file.category === "source" && !isTestFile(file.path)).map((file) => normalizeRepoPath(file.path))
|
|
1730
|
+
);
|
|
1731
|
+
if (changedSourceFiles.size === 0) {
|
|
1732
|
+
return [];
|
|
1733
|
+
}
|
|
1734
|
+
const affectedTests = /* @__PURE__ */ new Set();
|
|
1735
|
+
for (const testFile of listRepoTestFiles(worktreePath)) {
|
|
1736
|
+
const resolvedImports = resolveDirectRepoImportsForTest(testFile, worktreePath);
|
|
1737
|
+
if (resolvedImports.some((importedPath) => changedSourceFiles.has(importedPath))) {
|
|
1738
|
+
affectedTests.add(testFile);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return Array.from(affectedTests).sort();
|
|
1742
|
+
}
|
|
1743
|
+
function listRepoTestFiles(worktreePath) {
|
|
1744
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
1745
|
+
for (const root of TEST_DISCOVERY_ROOTS) {
|
|
1746
|
+
const absoluteRoot = join(worktreePath, root);
|
|
1747
|
+
if (!existsSync2(absoluteRoot)) {
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
walkTestFiles(absoluteRoot, worktreePath, discovered);
|
|
1751
|
+
}
|
|
1752
|
+
return Array.from(discovered).sort();
|
|
1753
|
+
}
|
|
1754
|
+
function walkTestFiles(currentPath, worktreePath, discovered) {
|
|
1755
|
+
let entries;
|
|
1756
|
+
try {
|
|
1757
|
+
entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1758
|
+
} catch {
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
for (const entry of entries) {
|
|
1762
|
+
if (entry.isDirectory()) {
|
|
1763
|
+
if (SKIPPED_TEST_DISCOVERY_DIRS.has(entry.name)) {
|
|
1764
|
+
continue;
|
|
1765
|
+
}
|
|
1766
|
+
walkTestFiles(join(currentPath, entry.name), worktreePath, discovered);
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
if (!entry.isFile()) {
|
|
1770
|
+
continue;
|
|
1771
|
+
}
|
|
1772
|
+
const repoRelative = normalizeRepoPath(relative(worktreePath, join(currentPath, entry.name)));
|
|
1773
|
+
if (isTestFile(repoRelative)) {
|
|
1774
|
+
discovered.add(repoRelative);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
function resolveDirectRepoImportsForTest(testFile, worktreePath) {
|
|
1779
|
+
const absolutePath = join(worktreePath, testFile);
|
|
1780
|
+
let content;
|
|
1781
|
+
try {
|
|
1782
|
+
content = readFileSync(absolutePath, "utf8");
|
|
1783
|
+
} catch {
|
|
1784
|
+
return [];
|
|
1785
|
+
}
|
|
1786
|
+
const imports = /* @__PURE__ */ new Set();
|
|
1787
|
+
const addMatches = (pattern, captureIndex) => {
|
|
1788
|
+
let match = pattern.exec(content);
|
|
1789
|
+
while (match !== null) {
|
|
1790
|
+
const specifier = match[captureIndex]?.trim();
|
|
1791
|
+
if (specifier) {
|
|
1792
|
+
imports.add(specifier);
|
|
1793
|
+
}
|
|
1794
|
+
match = pattern.exec(content);
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
addMatches(/\b(?:from|import)\s+['"]([^'"]+)['"]/g, 1);
|
|
1798
|
+
addMatches(/\brequire\(\s*['"]([^'"]+)['"]\s*\)/g, 1);
|
|
1799
|
+
addMatches(/\bimport\(\s*['"]([^'"]+)['"]\s*\)/g, 1);
|
|
1800
|
+
const resolvedImports = /* @__PURE__ */ new Set();
|
|
1801
|
+
for (const specifier of imports) {
|
|
1802
|
+
const resolvedImport = resolveRelativeRepoImport(testFile, specifier, worktreePath);
|
|
1803
|
+
if (resolvedImport) {
|
|
1804
|
+
resolvedImports.add(resolvedImport);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return Array.from(resolvedImports);
|
|
1808
|
+
}
|
|
1809
|
+
function resolveRelativeRepoImport(importerPath, specifier, worktreePath) {
|
|
1810
|
+
if (!specifier.startsWith(".")) {
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
const baseTarget = resolve(worktreePath, dirname(importerPath), specifier);
|
|
1814
|
+
for (const candidate of buildImportResolutionCandidates(baseTarget)) {
|
|
1815
|
+
if (!existsSync2(candidate)) {
|
|
1816
|
+
continue;
|
|
1817
|
+
}
|
|
1818
|
+
const repoRelative = normalizeRepoPath(relative(worktreePath, candidate));
|
|
1819
|
+
if (repoRelative.startsWith("..")) {
|
|
1820
|
+
continue;
|
|
1821
|
+
}
|
|
1822
|
+
return repoRelative;
|
|
1823
|
+
}
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
function buildImportResolutionCandidates(baseTarget) {
|
|
1827
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1828
|
+
const addCandidate = (candidate) => {
|
|
1829
|
+
candidates.add(candidate);
|
|
1830
|
+
};
|
|
1831
|
+
addCandidate(baseTarget);
|
|
1832
|
+
const baseExtension = extname(baseTarget);
|
|
1833
|
+
if (baseExtension) {
|
|
1834
|
+
if (JS_LIKE_EXTENSIONS.has(baseExtension)) {
|
|
1835
|
+
const stem = baseTarget.slice(0, -baseExtension.length);
|
|
1836
|
+
for (const sourceExtension of SOURCE_FILE_EXTENSIONS) {
|
|
1837
|
+
addCandidate(`${stem}${sourceExtension}`);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
} else {
|
|
1841
|
+
for (const extension of SOURCE_FILE_EXTENSIONS) {
|
|
1842
|
+
addCandidate(`${baseTarget}${extension}`);
|
|
1843
|
+
addCandidate(join(baseTarget, `index${extension}`));
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
return Array.from(candidates);
|
|
1847
|
+
}
|
|
1848
|
+
function normalizeRepoPath(filePath) {
|
|
1849
|
+
return filePath.split(sep).join("/");
|
|
1850
|
+
}
|
|
1851
|
+
function getRiskExpandedTestFiles(worktreePath, filePath, category) {
|
|
1852
|
+
if (!hasPoeticVitestHarness(worktreePath)) {
|
|
1853
|
+
return [];
|
|
1854
|
+
}
|
|
1855
|
+
if (filePath === "schemas/judge-scoring.schema.json") {
|
|
1856
|
+
return ["tests/unit/core/config/judge-scoring-schema.test.ts"];
|
|
1857
|
+
}
|
|
1858
|
+
if (category === "config" || category === "lockfile") {
|
|
1859
|
+
if (filePath === ".poetic/config/providers.yaml") {
|
|
1860
|
+
return ["src/core/providers/model-update/__tests__/pricing/passthrough-resolver.test.ts"];
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return [];
|
|
1864
|
+
}
|
|
1865
|
+
function isTestFile(filePath) {
|
|
1866
|
+
if (DEFAULT_CONFIG.testFilePatterns.some((pattern) => pattern.test(filePath))) return true;
|
|
1867
|
+
const base = pathPosix.basename(filePath.replace(/\\/g, "/"));
|
|
1868
|
+
return isProjectTestPath(base);
|
|
1869
|
+
}
|
|
1870
|
+
async function executeTests(runner, testFiles, worktreePath, abortSignal, scopedNodeInvocation) {
|
|
1871
|
+
if (!runner) {
|
|
1872
|
+
return {
|
|
1873
|
+
status: "error",
|
|
1874
|
+
testsRun: 0,
|
|
1875
|
+
testsPassed: 0,
|
|
1876
|
+
testsFailed: 0,
|
|
1877
|
+
metricsMeasured: false,
|
|
1878
|
+
duration: 0,
|
|
1879
|
+
exitCode: 1,
|
|
1880
|
+
outputTail: "",
|
|
1881
|
+
failingTests: []
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
const startTime = Date.now();
|
|
1885
|
+
if (runner === "powershell") {
|
|
1886
|
+
const command = detectPowerShellTestCommand(worktreePath) ?? "pwsh -NoProfile -Command Invoke-Pester";
|
|
1887
|
+
const result = await runVerificationCommand(command, {
|
|
1888
|
+
cwd: worktreePath,
|
|
1889
|
+
timeoutMs: DEFAULT_CONFIG.timeout,
|
|
1890
|
+
abortSignal
|
|
1891
|
+
});
|
|
1892
|
+
const output = String(result.all ?? result.stdout ?? result.stderr ?? "");
|
|
1893
|
+
const { exitCode } = resolveVerificationExitCode(command, result);
|
|
1894
|
+
const metrics = parseTestOutput(runner, output);
|
|
1895
|
+
const duration = Date.now() - startTime;
|
|
1896
|
+
const inferredStatus = exitCode === 0 ? inferSuccessStatusFromMetrics(metrics) : "failed";
|
|
1897
|
+
return {
|
|
1898
|
+
status: exitCode === 0 && !metrics.metricsMeasured && inferredStatus !== "failed" ? "unavailable" : inferredStatus,
|
|
1899
|
+
testsRun: metrics.testsRun,
|
|
1900
|
+
testsPassed: metrics.testsPassed,
|
|
1901
|
+
testsFailed: metrics.testsFailed,
|
|
1902
|
+
metricsMeasured: metrics.metricsMeasured,
|
|
1903
|
+
duration,
|
|
1904
|
+
exitCode,
|
|
1905
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
1906
|
+
failingTests: exitCode === 0 ? [] : extractGenericFailures(output)
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
const execaCommand = runner === "vitest" || runner === "jest" || runner === "mocha" || runner === "ava" || runner === "node-test" ? scopedNodeInvocation : runner === "unittest" ? (
|
|
1910
|
+
// `python -m unittest discover` takes a start DIRECTORY; appending test
|
|
1911
|
+
// file paths makes it abort with "Start directory is not importable"
|
|
1912
|
+
// (verified on CPython 3.14). So the resolved command runs UNSCOPED,
|
|
1913
|
+
// like the maven/gradle full-suite runners; the caller reports honest
|
|
1914
|
+
// `full` scope rather than claiming the touched file was executed.
|
|
1915
|
+
(() => {
|
|
1916
|
+
const capabilities = detectProjectCapabilities(worktreePath);
|
|
1917
|
+
const [cmd, ...args] = splitCommand(
|
|
1918
|
+
detectPythonUnittestCommand(capabilities) ?? capabilities.verification.test[0] ?? "python3 -m unittest discover"
|
|
1919
|
+
);
|
|
1920
|
+
return { cmd: cmd ?? "python3", args };
|
|
1921
|
+
})()
|
|
1922
|
+
) : runner === "pytest" ? (() => {
|
|
1923
|
+
const [cmd, ...baseArgs] = splitCommand(
|
|
1924
|
+
detectProjectCapabilities(worktreePath).verification.test[0] ?? "pytest"
|
|
1925
|
+
);
|
|
1926
|
+
return {
|
|
1927
|
+
cmd: cmd ?? "pytest",
|
|
1928
|
+
args: [...baseArgs, ...testFiles]
|
|
1929
|
+
};
|
|
1930
|
+
})() : runner === "go" || runner === "cargo" ? (
|
|
1931
|
+
// Resolver-first (same contract as maven/gradle/pytest below):
|
|
1932
|
+
// honor the target's OWN declared test command (Makefile
|
|
1933
|
+
// `test:` target, poetic.config `verification.test` override)
|
|
1934
|
+
// instead of a hardcoded default. The cargo `--locked`
|
|
1935
|
+
// fallback matches the resolver's lockfile-safe policy
|
|
1936
|
+
// (capabilities.ts) — never mutate the target's Cargo.lock. [B6]
|
|
1937
|
+
(() => {
|
|
1938
|
+
const fallback = runner === "go" ? "go test ./..." : "cargo test --locked";
|
|
1939
|
+
const [cmd, ...args] = splitCommand(
|
|
1940
|
+
selectRunnerLanguageResolverCommand(
|
|
1941
|
+
detectProjectCapabilities(worktreePath).verification.test,
|
|
1942
|
+
runner
|
|
1943
|
+
) ?? fallback
|
|
1944
|
+
);
|
|
1945
|
+
return { cmd: cmd ?? fallback.split(" ")[0], args };
|
|
1946
|
+
})()
|
|
1947
|
+
) : runner === "maven" || runner === "gradle" ? (() => {
|
|
1948
|
+
const fallback = runner === "maven" ? "mvn test" : "gradle test";
|
|
1949
|
+
const [cmd, ...args] = splitCommand(
|
|
1950
|
+
selectRunnerLanguageResolverCommand(
|
|
1951
|
+
detectProjectCapabilities(worktreePath).verification.test,
|
|
1952
|
+
runner
|
|
1953
|
+
) ?? fallback
|
|
1954
|
+
);
|
|
1955
|
+
return { cmd: cmd ?? fallback.split(" ")[0], args };
|
|
1956
|
+
})() : { cmd: "gradle", args: ["test"] };
|
|
1957
|
+
if (!execaCommand) {
|
|
1958
|
+
return {
|
|
1959
|
+
status: "error",
|
|
1960
|
+
testsRun: 0,
|
|
1961
|
+
testsPassed: 0,
|
|
1962
|
+
testsFailed: 0,
|
|
1963
|
+
metricsMeasured: false,
|
|
1964
|
+
duration: Date.now() - startTime,
|
|
1965
|
+
exitCode: 1,
|
|
1966
|
+
outputTail: "No safely scoped or declared project test command is available.",
|
|
1967
|
+
failingTests: []
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
try {
|
|
1971
|
+
const result = await execa(execaCommand.cmd, execaCommand.args, {
|
|
1972
|
+
cwd: worktreePath,
|
|
1973
|
+
stdio: "pipe",
|
|
1974
|
+
all: true,
|
|
1975
|
+
reject: false,
|
|
1976
|
+
timeout: DEFAULT_CONFIG.timeout,
|
|
1977
|
+
env: createVerificationTestEnv(),
|
|
1978
|
+
extendEnv: false,
|
|
1979
|
+
cancelSignal: abortSignal
|
|
1980
|
+
});
|
|
1981
|
+
const output = String(result.all ?? "");
|
|
1982
|
+
const commandDisplay = `${execaCommand.cmd} ${execaCommand.args.join(" ")}`.trim();
|
|
1983
|
+
const { exitCode } = resolveVerificationExitCode(commandDisplay, result);
|
|
1984
|
+
const duration = Date.now() - startTime;
|
|
1985
|
+
const outputTail = truncateToByteLimit(tailLines(output, 200), 1e5);
|
|
1986
|
+
if (exitCode === 0 && detectZeroCollectedTests(
|
|
1987
|
+
runner === "maven" ? "mvn test" : runner === "gradle" ? "gradle test" : commandDisplay,
|
|
1988
|
+
output
|
|
1989
|
+
)) {
|
|
1990
|
+
return {
|
|
1991
|
+
status: "unavailable",
|
|
1992
|
+
testsRun: 0,
|
|
1993
|
+
testsPassed: 0,
|
|
1994
|
+
testsFailed: 0,
|
|
1995
|
+
metricsMeasured: false,
|
|
1996
|
+
duration,
|
|
1997
|
+
exitCode,
|
|
1998
|
+
outputTail,
|
|
1999
|
+
failingTests: []
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
const metrics = parseTestOutput(runner, output);
|
|
2003
|
+
const failingTests = runner === "vitest" ? extractFailingTestsFromVitestOutput(output) : extractGenericFailures(output);
|
|
2004
|
+
if (exitCode === 124) {
|
|
2005
|
+
return {
|
|
2006
|
+
status: "timeout",
|
|
2007
|
+
testsRun: metrics.testsRun,
|
|
2008
|
+
testsPassed: metrics.testsPassed,
|
|
2009
|
+
testsFailed: metrics.testsFailed,
|
|
2010
|
+
metricsMeasured: metrics.metricsMeasured,
|
|
2011
|
+
duration,
|
|
2012
|
+
exitCode,
|
|
2013
|
+
outputTail,
|
|
2014
|
+
failingTests
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
if (exitCode !== 0) {
|
|
2018
|
+
return {
|
|
2019
|
+
status: metrics.testsRun > 0 ? "failed" : "error",
|
|
2020
|
+
testsRun: metrics.testsRun,
|
|
2021
|
+
testsPassed: metrics.testsPassed,
|
|
2022
|
+
testsFailed: metrics.testsFailed,
|
|
2023
|
+
metricsMeasured: metrics.metricsMeasured,
|
|
2024
|
+
duration,
|
|
2025
|
+
exitCode,
|
|
2026
|
+
outputTail,
|
|
2027
|
+
failingTests
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
const inferredStatus = inferSuccessStatusForRunner(runner, metrics);
|
|
2031
|
+
return {
|
|
2032
|
+
status: !metrics.metricsMeasured && inferredStatus !== "failed" ? "unavailable" : inferredStatus,
|
|
2033
|
+
testsRun: metrics.testsRun,
|
|
2034
|
+
testsPassed: metrics.testsPassed,
|
|
2035
|
+
testsFailed: metrics.testsFailed,
|
|
2036
|
+
metricsMeasured: metrics.metricsMeasured,
|
|
2037
|
+
duration,
|
|
2038
|
+
exitCode,
|
|
2039
|
+
outputTail,
|
|
2040
|
+
failingTests
|
|
2041
|
+
};
|
|
2042
|
+
} catch (error) {
|
|
2043
|
+
const err = error;
|
|
2044
|
+
const duration = Date.now() - startTime;
|
|
2045
|
+
const output = String(err?.all ?? err?.stdout ?? err?.stderr ?? err?.message ?? "");
|
|
2046
|
+
const isAborted = err?.name === "AbortError" || err?.code === "ABORT_ERR" || err?.code === "ERR_ABORTED";
|
|
2047
|
+
const isTimedOut = Boolean(err?.timedOut) || err?.killed && err?.signal === "SIGTERM";
|
|
2048
|
+
if (isAborted || isTimedOut) {
|
|
2049
|
+
return {
|
|
2050
|
+
status: "timeout",
|
|
2051
|
+
testsRun: 0,
|
|
2052
|
+
testsPassed: 0,
|
|
2053
|
+
testsFailed: 0,
|
|
2054
|
+
metricsMeasured: false,
|
|
2055
|
+
duration,
|
|
2056
|
+
exitCode: 124,
|
|
2057
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
2058
|
+
failingTests: []
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
const metrics = parseTestOutput(runner, output);
|
|
2062
|
+
const failingTests = runner === "vitest" ? extractFailingTestsFromVitestOutput(output) : extractGenericFailures(output);
|
|
2063
|
+
const isNotFound = err?.code === "ENOENT" || /\b(?:ENOENT|command not found)\b/i.test(output);
|
|
2064
|
+
const exitCode = isNotFound ? 127 : typeof err.status === "number" ? err.status : 1;
|
|
2065
|
+
return {
|
|
2066
|
+
status: metrics.testsRun > 0 ? "failed" : "error",
|
|
2067
|
+
testsRun: metrics.testsRun,
|
|
2068
|
+
testsPassed: metrics.testsPassed,
|
|
2069
|
+
testsFailed: metrics.testsFailed,
|
|
2070
|
+
metricsMeasured: metrics.metricsMeasured,
|
|
2071
|
+
duration,
|
|
2072
|
+
exitCode,
|
|
2073
|
+
outputTail: truncateToByteLimit(tailLines(output, 200), 1e5),
|
|
2074
|
+
failingTests
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
async function getChangedFilesSinceRef(worktreePath, baseRef) {
|
|
2079
|
+
if (!baseRef) return [];
|
|
2080
|
+
const result = await execa("git", ["diff", "--name-only", baseRef, "--"], {
|
|
2081
|
+
cwd: worktreePath,
|
|
2082
|
+
stdio: "pipe",
|
|
2083
|
+
reject: false
|
|
2084
|
+
});
|
|
2085
|
+
return String(result.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).sort();
|
|
2086
|
+
}
|
|
2087
|
+
function shellDisplayArg(value) {
|
|
2088
|
+
return /[\s"'\\]/.test(value) ? JSON.stringify(value) : value;
|
|
2089
|
+
}
|
|
2090
|
+
async function runVitestPlanStep(worktreePath, step, options) {
|
|
2091
|
+
const start = Date.now();
|
|
2092
|
+
const command = `node scripts/dynamic-node-options.mjs vitest run ${step.displayArgs} --reporter=verbose`;
|
|
2093
|
+
try {
|
|
2094
|
+
const result = await execa(
|
|
2095
|
+
"node",
|
|
2096
|
+
["scripts/dynamic-node-options.mjs", "vitest", "run", ...step.args, "--reporter=verbose"],
|
|
2097
|
+
{
|
|
2098
|
+
cwd: worktreePath,
|
|
2099
|
+
stdio: "pipe",
|
|
2100
|
+
all: true,
|
|
2101
|
+
reject: false,
|
|
2102
|
+
timeout: options.timeoutMs,
|
|
2103
|
+
env: createVerificationTestEnv(),
|
|
2104
|
+
extendEnv: false
|
|
2105
|
+
}
|
|
2106
|
+
);
|
|
2107
|
+
const durationMs = Date.now() - start;
|
|
2108
|
+
const { exitCode } = resolveVerificationExitCode(command, result);
|
|
2109
|
+
const all = String(result.all ?? "");
|
|
2110
|
+
const outputTail = truncateToByteLimit(tailLines(all, options.tailLines), 1e5);
|
|
2111
|
+
const failingTests = exitCode === 0 ? [] : extractFailingTestsFromVitestOutput(all);
|
|
2112
|
+
if (exitCode === 1 && /No test files found/i.test(all)) {
|
|
2113
|
+
return {
|
|
2114
|
+
command,
|
|
2115
|
+
exitCode: -1,
|
|
2116
|
+
durationMs,
|
|
2117
|
+
outputTail,
|
|
2118
|
+
status: "unavailable",
|
|
2119
|
+
skipReason: "no-matching-tests",
|
|
2120
|
+
failingTests: [],
|
|
2121
|
+
language: "node"
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
const exitZeroFailure = exitCode === 0 ? detectExitZeroVerifyFailure(command, all) : void 0;
|
|
2125
|
+
if (exitZeroFailure) {
|
|
2126
|
+
return {
|
|
2127
|
+
command,
|
|
2128
|
+
exitCode: 1,
|
|
2129
|
+
durationMs,
|
|
2130
|
+
outputTail: truncateToByteLimit(
|
|
2131
|
+
[outputTail, exitZeroFailure.message].filter(Boolean).join("\n"),
|
|
2132
|
+
1e5
|
|
2133
|
+
),
|
|
2134
|
+
status: "failed",
|
|
2135
|
+
failingTests: [exitZeroFailure.message],
|
|
2136
|
+
language: "node"
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
if (exitCode === 0) {
|
|
2140
|
+
return {
|
|
2141
|
+
command,
|
|
2142
|
+
exitCode,
|
|
2143
|
+
durationMs,
|
|
2144
|
+
outputTail,
|
|
2145
|
+
status: "passed",
|
|
2146
|
+
failingTests,
|
|
2147
|
+
language: "node"
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
if (exitCode === 124) {
|
|
2151
|
+
return {
|
|
2152
|
+
command,
|
|
2153
|
+
exitCode,
|
|
2154
|
+
durationMs,
|
|
2155
|
+
outputTail,
|
|
2156
|
+
status: "timeout",
|
|
2157
|
+
failingTests,
|
|
2158
|
+
language: "node"
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
return {
|
|
2162
|
+
command,
|
|
2163
|
+
exitCode,
|
|
2164
|
+
durationMs,
|
|
2165
|
+
outputTail,
|
|
2166
|
+
status: "failed",
|
|
2167
|
+
failingTests,
|
|
2168
|
+
language: "node"
|
|
2169
|
+
};
|
|
2170
|
+
} catch (error) {
|
|
2171
|
+
const durationMs = Date.now() - start;
|
|
2172
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2173
|
+
return {
|
|
2174
|
+
command,
|
|
2175
|
+
exitCode: 1,
|
|
2176
|
+
durationMs,
|
|
2177
|
+
outputTail: truncateToByteLimit(tailLines(message, options.tailLines), 1e5),
|
|
2178
|
+
status: "error",
|
|
2179
|
+
failingTests: [],
|
|
2180
|
+
language: "node"
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
async function runExternalVitestPlanStep(worktreePath, step, options) {
|
|
2185
|
+
const start = Date.now();
|
|
2186
|
+
const invocation = buildExternalVitestRunInvocation(step.args);
|
|
2187
|
+
const command = invocation.display;
|
|
2188
|
+
try {
|
|
2189
|
+
const result = await execa(invocation.cmd, invocation.args, {
|
|
2190
|
+
cwd: worktreePath,
|
|
2191
|
+
stdio: "pipe",
|
|
2192
|
+
all: true,
|
|
2193
|
+
reject: false,
|
|
2194
|
+
timeout: options.timeoutMs,
|
|
2195
|
+
env: createVerificationTestEnv(),
|
|
2196
|
+
extendEnv: false
|
|
2197
|
+
});
|
|
2198
|
+
const durationMs = Date.now() - start;
|
|
2199
|
+
const { exitCode } = resolveVerificationExitCode(command, result);
|
|
2200
|
+
const all = String(result.all ?? "");
|
|
2201
|
+
const outputTail = truncateToByteLimit(tailLines(all, options.tailLines), 1e5);
|
|
2202
|
+
const failingTests = exitCode === 0 ? [] : extractFailingTestsFromVitestOutput(all);
|
|
2203
|
+
if (exitCode === 1 && /No test files found/i.test(all)) {
|
|
2204
|
+
return {
|
|
2205
|
+
command,
|
|
2206
|
+
exitCode: -1,
|
|
2207
|
+
durationMs,
|
|
2208
|
+
outputTail,
|
|
2209
|
+
status: "unavailable",
|
|
2210
|
+
skipReason: "no-matching-tests",
|
|
2211
|
+
failingTests: [],
|
|
2212
|
+
language: "node"
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
const unavailableSignal = detectVerificationToolUnavailable(command, all);
|
|
2216
|
+
if (unavailableSignal) {
|
|
2217
|
+
return {
|
|
2218
|
+
command,
|
|
2219
|
+
exitCode,
|
|
2220
|
+
durationMs,
|
|
2221
|
+
outputTail: truncateToByteLimit(
|
|
2222
|
+
[outputTail, unavailableSignal.message].filter(Boolean).join("\n"),
|
|
2223
|
+
1e5
|
|
2224
|
+
),
|
|
2225
|
+
status: "unavailable",
|
|
2226
|
+
failingTests: [],
|
|
2227
|
+
language: "node"
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
const exitZeroFailure = exitCode === 0 ? detectExitZeroVerifyFailure(command, all) : void 0;
|
|
2231
|
+
if (exitZeroFailure) {
|
|
2232
|
+
return {
|
|
2233
|
+
command,
|
|
2234
|
+
exitCode: 1,
|
|
2235
|
+
durationMs,
|
|
2236
|
+
outputTail: truncateToByteLimit(
|
|
2237
|
+
[outputTail, exitZeroFailure.message].filter(Boolean).join("\n"),
|
|
2238
|
+
1e5
|
|
2239
|
+
),
|
|
2240
|
+
status: "failed",
|
|
2241
|
+
failingTests: [exitZeroFailure.message],
|
|
2242
|
+
language: "node"
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
if (exitCode === 0) {
|
|
2246
|
+
return {
|
|
2247
|
+
command,
|
|
2248
|
+
exitCode,
|
|
2249
|
+
durationMs,
|
|
2250
|
+
outputTail,
|
|
2251
|
+
status: "passed",
|
|
2252
|
+
failingTests,
|
|
2253
|
+
language: "node"
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
if (exitCode === 124) {
|
|
2257
|
+
return {
|
|
2258
|
+
command,
|
|
2259
|
+
exitCode,
|
|
2260
|
+
durationMs,
|
|
2261
|
+
outputTail,
|
|
2262
|
+
status: "timeout",
|
|
2263
|
+
failingTests,
|
|
2264
|
+
language: "node"
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
return {
|
|
2268
|
+
command,
|
|
2269
|
+
exitCode,
|
|
2270
|
+
durationMs,
|
|
2271
|
+
outputTail,
|
|
2272
|
+
status: "failed",
|
|
2273
|
+
failingTests,
|
|
2274
|
+
language: "node"
|
|
2275
|
+
};
|
|
2276
|
+
} catch (error) {
|
|
2277
|
+
const durationMs = Date.now() - start;
|
|
2278
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2279
|
+
return {
|
|
2280
|
+
command,
|
|
2281
|
+
exitCode: 1,
|
|
2282
|
+
durationMs,
|
|
2283
|
+
outputTail: truncateToByteLimit(tailLines(message, options.tailLines), 1e5),
|
|
2284
|
+
status: "error",
|
|
2285
|
+
failingTests: [],
|
|
2286
|
+
language: "node"
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
async function runCapabilityTestCommand(worktreePath, capabilityCommand, options) {
|
|
2291
|
+
const language = describeVerificationCommand(capabilityCommand).language;
|
|
2292
|
+
try {
|
|
2293
|
+
const result = await runVerificationCommand(capabilityCommand, {
|
|
2294
|
+
cwd: worktreePath,
|
|
2295
|
+
timeoutMs: options.timeoutMs,
|
|
2296
|
+
abortSignal: options.abortSignal
|
|
2297
|
+
});
|
|
2298
|
+
const durationMs = Date.now() - options.start;
|
|
2299
|
+
const { exitCode } = resolveVerificationExitCode(capabilityCommand, result);
|
|
2300
|
+
const all = String(result.all ?? "");
|
|
2301
|
+
const outputTail = truncateToByteLimit(tailLines(all, options.tailLines), 1e5);
|
|
2302
|
+
const failingTests = exitCode === 0 ? [] : extractGenericFailures(all);
|
|
2303
|
+
const exitZeroFailure = exitCode === 0 ? detectExitZeroVerifyFailure(capabilityCommand, all) : void 0;
|
|
2304
|
+
if (exitZeroFailure) {
|
|
2305
|
+
return {
|
|
2306
|
+
command: capabilityCommand,
|
|
2307
|
+
language,
|
|
2308
|
+
exitCode: 1,
|
|
2309
|
+
durationMs,
|
|
2310
|
+
outputTail: truncateToByteLimit(
|
|
2311
|
+
[outputTail, exitZeroFailure.message].filter(Boolean).join("\n"),
|
|
2312
|
+
1e5
|
|
2313
|
+
),
|
|
2314
|
+
status: "failed",
|
|
2315
|
+
failingTests: [exitZeroFailure.message]
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
if (exitCode === 0 && detectZeroCollectedTests(capabilityCommand, all)) {
|
|
2319
|
+
return {
|
|
2320
|
+
command: capabilityCommand,
|
|
2321
|
+
language,
|
|
2322
|
+
exitCode,
|
|
2323
|
+
durationMs,
|
|
2324
|
+
outputTail,
|
|
2325
|
+
status: "unavailable",
|
|
2326
|
+
failingTests: []
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
if (exitCode === 0) {
|
|
2330
|
+
return {
|
|
2331
|
+
command: capabilityCommand,
|
|
2332
|
+
language,
|
|
2333
|
+
exitCode,
|
|
2334
|
+
durationMs,
|
|
2335
|
+
outputTail,
|
|
2336
|
+
status: "passed",
|
|
2337
|
+
failingTests
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
if (exitCode === 124) {
|
|
2341
|
+
return {
|
|
2342
|
+
command: capabilityCommand,
|
|
2343
|
+
language,
|
|
2344
|
+
exitCode,
|
|
2345
|
+
durationMs,
|
|
2346
|
+
outputTail,
|
|
2347
|
+
status: "timeout",
|
|
2348
|
+
failingTests
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
if (detectVerificationToolUnavailable(capabilityCommand, all) !== void 0) {
|
|
2352
|
+
return {
|
|
2353
|
+
command: capabilityCommand,
|
|
2354
|
+
language,
|
|
2355
|
+
exitCode,
|
|
2356
|
+
durationMs,
|
|
2357
|
+
outputTail,
|
|
2358
|
+
status: "unavailable",
|
|
2359
|
+
failingTests: []
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
if (exitCode === 5 && isPythonStdlibUnittestCommand(capabilityCommand) && detectZeroCollectedTests(capabilityCommand, all)) {
|
|
2363
|
+
return {
|
|
2364
|
+
command: capabilityCommand,
|
|
2365
|
+
language,
|
|
2366
|
+
exitCode,
|
|
2367
|
+
durationMs,
|
|
2368
|
+
outputTail,
|
|
2369
|
+
status: "unavailable",
|
|
2370
|
+
failingTests: []
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
return {
|
|
2374
|
+
command: capabilityCommand,
|
|
2375
|
+
language,
|
|
2376
|
+
exitCode,
|
|
2377
|
+
durationMs,
|
|
2378
|
+
outputTail,
|
|
2379
|
+
status: "failed",
|
|
2380
|
+
failingTests
|
|
2381
|
+
};
|
|
2382
|
+
} catch (error) {
|
|
2383
|
+
const durationMs = Date.now() - options.start;
|
|
2384
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2385
|
+
return {
|
|
2386
|
+
command: capabilityCommand,
|
|
2387
|
+
language,
|
|
2388
|
+
exitCode: 1,
|
|
2389
|
+
durationMs,
|
|
2390
|
+
outputTail: truncateToByteLimit(tailLines(message, options.tailLines), 1e5),
|
|
2391
|
+
status: "error",
|
|
2392
|
+
failingTests: []
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
async function runCapabilityTestCommandWithFallback(worktreePath, capabilityCommand, fallbackCommands, options) {
|
|
2397
|
+
let result = await runCapabilityTestCommand(worktreePath, capabilityCommand, options);
|
|
2398
|
+
const scopedFallbacks = selectFallbacksForCommand(capabilityCommand, fallbackCommands);
|
|
2399
|
+
for (const fallbackCommand of scopedFallbacks) {
|
|
2400
|
+
if (result.status !== "unavailable" || options.abortSignal?.aborted) break;
|
|
2401
|
+
let fallbackOptions = options;
|
|
2402
|
+
if (options.timeoutMs !== void 0) {
|
|
2403
|
+
const remainingMs = Math.max(0, options.timeoutMs - (Date.now() - options.start));
|
|
2404
|
+
if (remainingMs <= 0) {
|
|
2405
|
+
return {
|
|
2406
|
+
command: result.command,
|
|
2407
|
+
language: result.language,
|
|
2408
|
+
exitCode: 124,
|
|
2409
|
+
durationMs: Date.now() - options.start,
|
|
2410
|
+
outputTail: truncateToByteLimit(
|
|
2411
|
+
[result.outputTail, "Fallback skipped: per-step timeout budget exhausted."].filter(Boolean).join("\n"),
|
|
2412
|
+
1e5
|
|
2413
|
+
),
|
|
2414
|
+
status: "timeout",
|
|
2415
|
+
failingTests: []
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
fallbackOptions = { ...options, timeoutMs: remainingMs };
|
|
2419
|
+
}
|
|
2420
|
+
result = await runCapabilityTestCommand(worktreePath, fallbackCommand, fallbackOptions);
|
|
2421
|
+
}
|
|
2422
|
+
return result;
|
|
2423
|
+
}
|
|
2424
|
+
function combineVitestPlanResults(results) {
|
|
2425
|
+
if (results.length === 0) {
|
|
2426
|
+
return {
|
|
2427
|
+
command: "",
|
|
2428
|
+
exitCode: 130,
|
|
2429
|
+
durationMs: 0,
|
|
2430
|
+
outputTail: "Verification plan completed no commands.",
|
|
2431
|
+
status: "error",
|
|
2432
|
+
failingTests: []
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
if (results.length === 1) return results[0];
|
|
2436
|
+
const representative = results.find((result) => result.status === "failed") ?? results.find((result) => result.status === "error") ?? results.find((result) => result.status === "timeout") ?? results.find((result) => result.status === "unavailable") ?? results.find((result) => result.status !== "passed") ?? results[0];
|
|
2437
|
+
const exitCode = representative.exitCode ?? 0;
|
|
2438
|
+
const status = representative.status ?? "passed";
|
|
2439
|
+
const languages = new Set(results.map((result) => result.language).filter(Boolean));
|
|
2440
|
+
const outputTail = truncateToByteLimit(
|
|
2441
|
+
results.map((result) => [`$ ${result.command}`, result.outputTail].filter(Boolean).join("\n")).join("\n\n"),
|
|
2442
|
+
1e5
|
|
2443
|
+
);
|
|
2444
|
+
return {
|
|
2445
|
+
// Full ordered audit provenance of every executed plan-step command.
|
|
2446
|
+
command: results.map((result) => result.command).filter(Boolean).join("\n"),
|
|
2447
|
+
exitCode,
|
|
2448
|
+
durationMs: results.reduce((sum, result) => sum + result.durationMs, 0),
|
|
2449
|
+
outputTail,
|
|
2450
|
+
status,
|
|
2451
|
+
failingTests: Array.from(new Set(results.flatMap((result) => result.failingTests))),
|
|
2452
|
+
// Preserve language only when every step agrees; otherwise leave unset so
|
|
2453
|
+
// consumers do not re-tag multi-language output under one language.
|
|
2454
|
+
language: languages.size === 1 ? results[0]?.language : void 0
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
async function runDeterministicTestGateWithArtifacts(worktreePath, options) {
|
|
2458
|
+
const start = Date.now();
|
|
2459
|
+
const timeoutMs = options.timeoutMs;
|
|
2460
|
+
const maxTailLines = options.tailLines ?? 200;
|
|
2461
|
+
if (options.command && options.command.trim().length > 0) {
|
|
2462
|
+
const fallbackCommands = detectProjectCapabilities(worktreePath).verification.testFallback ?? [];
|
|
2463
|
+
return runCapabilityTestCommandWithFallback(worktreePath, options.command, fallbackCommands, {
|
|
2464
|
+
timeoutMs,
|
|
2465
|
+
tailLines: maxTailLines,
|
|
2466
|
+
start
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
const hasHarness = hasPoeticVitestHarness(worktreePath);
|
|
2470
|
+
let changedFiles = [];
|
|
2471
|
+
let changedTestFiles = [];
|
|
2472
|
+
try {
|
|
2473
|
+
changedFiles = await getChangedFilesSinceRef(worktreePath, options.baseRef);
|
|
2474
|
+
changedTestFiles = hasHarness ? changedFiles.filter(isTestFile) : [];
|
|
2475
|
+
} catch {
|
|
2476
|
+
changedFiles = [];
|
|
2477
|
+
changedTestFiles = [];
|
|
2478
|
+
}
|
|
2479
|
+
const changedArgs = options.baseRef ? ["--changed", options.baseRef] : ["--changed"];
|
|
2480
|
+
const changedDisplayArgs = options.baseRef ? `--changed ${options.baseRef}` : "--changed";
|
|
2481
|
+
const hasNonTestChanges = changedFiles.some((file) => !isTestFile(file));
|
|
2482
|
+
const skippedAffectedGraphReason = changedTestFiles.length > 0 && hasNonTestChanges ? `Skipped Vitest affected graph: touched tests already ran; skipped broad --changed sweep for ${changedFiles.length} changed files.` : void 0;
|
|
2483
|
+
const planSteps = changedTestFiles.length > 0 ? [
|
|
2484
|
+
{
|
|
2485
|
+
args: changedTestFiles,
|
|
2486
|
+
displayArgs: changedTestFiles.map(shellDisplayArg).join(" ")
|
|
2487
|
+
}
|
|
2488
|
+
] : [{ args: changedArgs, displayArgs: changedDisplayArgs }];
|
|
2489
|
+
const command = planSteps.map(
|
|
2490
|
+
(step) => hasHarness ? `node scripts/dynamic-node-options.mjs vitest run ${step.displayArgs} --reporter=verbose` : buildExternalVitestRunInvocation(step.args).display
|
|
2491
|
+
).join(" && ");
|
|
2492
|
+
if (!hasHarness) {
|
|
2493
|
+
const capabilityCommands = selectCapabilityTestCommandsForChanges(worktreePath, changedFiles);
|
|
2494
|
+
if (capabilityCommands.length > 0) {
|
|
2495
|
+
const fallbackCommands = detectProjectCapabilities(worktreePath).verification.testFallback ?? [];
|
|
2496
|
+
const results2 = [];
|
|
2497
|
+
for (const capabilityCommand of capabilityCommands) {
|
|
2498
|
+
const result = await runCapabilityTestCommandWithFallback(
|
|
2499
|
+
worktreePath,
|
|
2500
|
+
capabilityCommand,
|
|
2501
|
+
fallbackCommands,
|
|
2502
|
+
{ timeoutMs, tailLines: maxTailLines, start }
|
|
2503
|
+
);
|
|
2504
|
+
results2.push(result);
|
|
2505
|
+
if (result.status !== "passed") break;
|
|
2506
|
+
}
|
|
2507
|
+
return combineVitestPlanResults(results2);
|
|
2508
|
+
}
|
|
2509
|
+
if (changedProjectLanguages(changedFiles).size > 0) {
|
|
2510
|
+
return {
|
|
2511
|
+
command,
|
|
2512
|
+
exitCode: -1,
|
|
2513
|
+
durationMs: Date.now() - start,
|
|
2514
|
+
outputTail: "Skipped deterministic test gate: no matching project test command detected.",
|
|
2515
|
+
status: "unavailable",
|
|
2516
|
+
skipReason: "no-project-test-command",
|
|
2517
|
+
failingTests: []
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
if (detectTestRunner(worktreePath) === "vitest") {
|
|
2521
|
+
const results2 = [];
|
|
2522
|
+
for (const step of planSteps) {
|
|
2523
|
+
const result = await runExternalVitestPlanStep(worktreePath, step, {
|
|
2524
|
+
timeoutMs,
|
|
2525
|
+
tailLines: maxTailLines
|
|
2526
|
+
});
|
|
2527
|
+
results2.push(result);
|
|
2528
|
+
if (result.status !== "passed") break;
|
|
2529
|
+
}
|
|
2530
|
+
return combineVitestPlanResults(results2);
|
|
2531
|
+
}
|
|
2532
|
+
const outputTail = "Skipped Vitest gate: no project test command detected.";
|
|
2533
|
+
return {
|
|
2534
|
+
command,
|
|
2535
|
+
exitCode: -1,
|
|
2536
|
+
durationMs: Date.now() - start,
|
|
2537
|
+
outputTail,
|
|
2538
|
+
status: "unavailable",
|
|
2539
|
+
skipReason: "no-project-test-command",
|
|
2540
|
+
failingTests: [],
|
|
2541
|
+
language: "node"
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
const results = [];
|
|
2545
|
+
const changedLanguages = changedProjectLanguages(changedFiles);
|
|
2546
|
+
const shouldRunVitest = changedLanguages.size === 0 || changedLanguages.has("javascript") || changedLanguages.has("typescript");
|
|
2547
|
+
const hasNonNodeChangedLanguage = [...changedLanguages].some(
|
|
2548
|
+
(language) => language !== "javascript" && language !== "typescript" && language !== "node"
|
|
2549
|
+
);
|
|
2550
|
+
for (const step of planSteps) {
|
|
2551
|
+
if (!shouldRunVitest) break;
|
|
2552
|
+
const result = await runVitestPlanStep(worktreePath, step, {
|
|
2553
|
+
timeoutMs,
|
|
2554
|
+
tailLines: maxTailLines
|
|
2555
|
+
});
|
|
2556
|
+
results.push(result);
|
|
2557
|
+
if (result.status !== "passed") break;
|
|
2558
|
+
}
|
|
2559
|
+
if (results.every((result) => result.status === "passed")) {
|
|
2560
|
+
const nonNodeLanguages = new Set(
|
|
2561
|
+
[...changedLanguages].filter(
|
|
2562
|
+
(language) => language !== "javascript" && language !== "typescript" && language !== "node"
|
|
2563
|
+
)
|
|
2564
|
+
);
|
|
2565
|
+
const nonNodeCommands = selectCapabilityTestCommandsForLanguages(
|
|
2566
|
+
worktreePath,
|
|
2567
|
+
nonNodeLanguages
|
|
2568
|
+
);
|
|
2569
|
+
if (hasNonNodeChangedLanguage && nonNodeCommands.length === 0) {
|
|
2570
|
+
return {
|
|
2571
|
+
command,
|
|
2572
|
+
exitCode: -1,
|
|
2573
|
+
durationMs: Date.now() - start,
|
|
2574
|
+
outputTail: "Skipped deterministic test gate: no matching project test command detected.",
|
|
2575
|
+
status: "unavailable",
|
|
2576
|
+
skipReason: "no-project-test-command",
|
|
2577
|
+
failingTests: []
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
const fallbackCommands = detectProjectCapabilities(worktreePath).verification.testFallback ?? [];
|
|
2581
|
+
for (const capabilityCommand of nonNodeCommands) {
|
|
2582
|
+
const result = await runCapabilityTestCommandWithFallback(
|
|
2583
|
+
worktreePath,
|
|
2584
|
+
capabilityCommand,
|
|
2585
|
+
fallbackCommands,
|
|
2586
|
+
{ timeoutMs, tailLines: maxTailLines, start }
|
|
2587
|
+
);
|
|
2588
|
+
results.push(result);
|
|
2589
|
+
if (result.status !== "passed") break;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
if (results.length === 0) {
|
|
2593
|
+
return {
|
|
2594
|
+
command,
|
|
2595
|
+
exitCode: -1,
|
|
2596
|
+
durationMs: Date.now() - start,
|
|
2597
|
+
outputTail: "Skipped deterministic test gate: no matching project test command detected.",
|
|
2598
|
+
status: "unavailable",
|
|
2599
|
+
skipReason: "no-project-test-command",
|
|
2600
|
+
failingTests: [],
|
|
2601
|
+
language: "node"
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
const combined = combineVitestPlanResults(results);
|
|
2605
|
+
if (skippedAffectedGraphReason) {
|
|
2606
|
+
combined.outputTail = truncateToByteLimit(
|
|
2607
|
+
[combined.outputTail, skippedAffectedGraphReason].filter(Boolean).join("\n\n"),
|
|
2608
|
+
1e5
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
return combined;
|
|
2612
|
+
}
|
|
2613
|
+
function buildTestCommand(runner, testFiles, worktreePath) {
|
|
2614
|
+
const filesArg = testFiles.map((f) => `"${f}"`).join(" ");
|
|
2615
|
+
const root = worktreePath ?? "";
|
|
2616
|
+
switch (runner) {
|
|
2617
|
+
case "vitest":
|
|
2618
|
+
case "jest":
|
|
2619
|
+
case "mocha":
|
|
2620
|
+
case "ava":
|
|
2621
|
+
case "node-test":
|
|
2622
|
+
return resolveScopedNodeTestInvocation(runner, testFiles, root)?.display ?? "";
|
|
2623
|
+
case "pytest":
|
|
2624
|
+
return `pytest ${filesArg}`.trim();
|
|
2625
|
+
case "unittest":
|
|
2626
|
+
return detectPythonUnittestCommand(detectProjectCapabilities(root)) ?? "python3 -m unittest discover";
|
|
2627
|
+
case "go":
|
|
2628
|
+
return selectRunnerLanguageResolverCommand(
|
|
2629
|
+
detectProjectCapabilities(root).verification.test,
|
|
2630
|
+
"go"
|
|
2631
|
+
) ?? "go test ./...";
|
|
2632
|
+
case "cargo":
|
|
2633
|
+
return selectRunnerLanguageResolverCommand(
|
|
2634
|
+
detectProjectCapabilities(root).verification.test,
|
|
2635
|
+
"cargo"
|
|
2636
|
+
) ?? "cargo test --locked";
|
|
2637
|
+
case "maven":
|
|
2638
|
+
return selectRunnerLanguageResolverCommand(
|
|
2639
|
+
detectProjectCapabilities(root).verification.test,
|
|
2640
|
+
"maven"
|
|
2641
|
+
) ?? "mvn test";
|
|
2642
|
+
case "gradle":
|
|
2643
|
+
return selectRunnerLanguageResolverCommand(
|
|
2644
|
+
detectProjectCapabilities(root).verification.test,
|
|
2645
|
+
"gradle"
|
|
2646
|
+
) ?? "gradle test";
|
|
2647
|
+
case "powershell":
|
|
2648
|
+
return "pwsh -NoProfile -Command Invoke-Pester";
|
|
2649
|
+
default:
|
|
2650
|
+
throw new Error(`Unknown test runner: ${runner}`);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
function parseTestOutput(runner, output) {
|
|
2654
|
+
const fallback = {
|
|
2655
|
+
testsRun: 0,
|
|
2656
|
+
testsPassed: 0,
|
|
2657
|
+
testsFailed: 0,
|
|
2658
|
+
metricsMeasured: false
|
|
2659
|
+
};
|
|
2660
|
+
if (!output) {
|
|
2661
|
+
return fallback;
|
|
2662
|
+
}
|
|
2663
|
+
try {
|
|
2664
|
+
if (runner === "vitest") {
|
|
2665
|
+
return asMeasuredMetrics(parseVitestOutput(output));
|
|
2666
|
+
} else if (runner === "jest") {
|
|
2667
|
+
return asMeasuredMetrics(parseJestOutput(output));
|
|
2668
|
+
} else if (runner === "mocha" || runner === "ava") {
|
|
2669
|
+
return asMeasuredMetrics(parseMochaStyleOutput(output));
|
|
2670
|
+
} else if (runner === "pytest") {
|
|
2671
|
+
return asMeasuredMetrics(parsePytestOutput(output));
|
|
2672
|
+
} else if (runner === "unittest") {
|
|
2673
|
+
return asMeasuredMetrics(parseUnittestOutput(output));
|
|
2674
|
+
} else if (
|
|
2675
|
+
// node:test emits TAP or (Node 24 default) spec; the generic parser
|
|
2676
|
+
// reads both the TAP plan/trailer and the spec tests/pass/fail footer.
|
|
2677
|
+
runner === "node-test" || runner === "go" || runner === "cargo" || runner === "maven" || runner === "gradle" || runner === "powershell" || runner === "generic"
|
|
2678
|
+
) {
|
|
2679
|
+
return parseGenericPassFailOutput(output);
|
|
2680
|
+
}
|
|
2681
|
+
} catch {
|
|
2682
|
+
return fallback;
|
|
2683
|
+
}
|
|
2684
|
+
return fallback;
|
|
2685
|
+
}
|
|
2686
|
+
function extractGenericFailures(output) {
|
|
2687
|
+
return output.split(/\r?\n/).filter((line) => /\b(fail|failed|error)\b/i.test(line)).map((line) => line.trim()).filter(Boolean).slice(0, 20);
|
|
2688
|
+
}
|
|
2689
|
+
function parseVitestOutput(output) {
|
|
2690
|
+
const testMatch = output.match(/Tests\s+(.+?)(?:\n|$)/);
|
|
2691
|
+
if (testMatch) {
|
|
2692
|
+
const summary = testMatch[1] ?? "";
|
|
2693
|
+
const passed = parseInt(summary.match(/(\d+)\s+passed/)?.[1] || "0", 10);
|
|
2694
|
+
const failed = parseInt(summary.match(/(\d+)\s+failed/)?.[1] || "0", 10);
|
|
2695
|
+
return {
|
|
2696
|
+
testsRun: passed + failed,
|
|
2697
|
+
testsPassed: passed,
|
|
2698
|
+
testsFailed: failed
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
return { testsRun: 0, testsPassed: 0, testsFailed: 0 };
|
|
2702
|
+
}
|
|
2703
|
+
function parseJestOutput(output) {
|
|
2704
|
+
const testMatch = output.match(/Tests:\s+(?:(\d+)\s+failed,\s+)?(\d+)\s+passed,\s+(\d+)\s+total/);
|
|
2705
|
+
if (testMatch) {
|
|
2706
|
+
const failed = parseInt(testMatch[1] || "0", 10);
|
|
2707
|
+
const passed = parseInt(testMatch[2] || "0", 10);
|
|
2708
|
+
const total = parseInt(testMatch[3] || "0", 10);
|
|
2709
|
+
return {
|
|
2710
|
+
testsRun: total,
|
|
2711
|
+
testsPassed: passed,
|
|
2712
|
+
testsFailed: failed
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
return { testsRun: 0, testsPassed: 0, testsFailed: 0 };
|
|
2716
|
+
}
|
|
2717
|
+
function parseMochaStyleOutput(output) {
|
|
2718
|
+
const mochaPassing = output.match(/^\s*(\d+)\s+passing\b/m);
|
|
2719
|
+
const mochaFailing = output.match(/^\s*(\d+)\s+failing\b/m);
|
|
2720
|
+
if (mochaPassing || mochaFailing) {
|
|
2721
|
+
const passed = Number(mochaPassing?.[1] ?? 0);
|
|
2722
|
+
const failed = Number(mochaFailing?.[1] ?? 0);
|
|
2723
|
+
return { testsRun: passed + failed, testsPassed: passed, testsFailed: failed };
|
|
2724
|
+
}
|
|
2725
|
+
const avaPassed = output.match(/^\s*(\d+)\s+tests?\s+passed\b/m);
|
|
2726
|
+
const avaFailed = output.match(/^\s*(\d+)\s+tests?\s+failed\b/m);
|
|
2727
|
+
if (avaPassed || avaFailed) {
|
|
2728
|
+
const passed = Number(avaPassed?.[1] ?? 0);
|
|
2729
|
+
const failed = Number(avaFailed?.[1] ?? 0);
|
|
2730
|
+
return { testsRun: passed + failed, testsPassed: passed, testsFailed: failed };
|
|
2731
|
+
}
|
|
2732
|
+
const tap = parseTapSummary(output);
|
|
2733
|
+
if (tap) return tap;
|
|
2734
|
+
return { testsRun: 0, testsPassed: 0, testsFailed: 0 };
|
|
2735
|
+
}
|
|
2736
|
+
function parseTapSummary(output) {
|
|
2737
|
+
const tapTests = Number(output.match(/^#\s+tests\s+(\d+)\s*$/im)?.[1] ?? NaN);
|
|
2738
|
+
if (Number.isFinite(tapTests)) {
|
|
2739
|
+
const tapPassed = Number(output.match(/^#\s+pass\s+(\d+)\s*$/im)?.[1] ?? 0);
|
|
2740
|
+
const tapFailed = Number(output.match(/^#\s+fail\s+(\d+)\s*$/im)?.[1] ?? 0);
|
|
2741
|
+
const tapCancelled = Number(output.match(/^#\s+cancelled\s+(\d+)\s*$/im)?.[1] ?? 0);
|
|
2742
|
+
return {
|
|
2743
|
+
testsRun: tapTests,
|
|
2744
|
+
testsPassed: tapPassed,
|
|
2745
|
+
testsFailed: tapFailed + tapCancelled
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
const resultLines = output.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^(?:not\s+ok|ok)\s+\d+\b/i.test(line));
|
|
2749
|
+
if (resultLines.length > 0) {
|
|
2750
|
+
const failed = resultLines.filter((line) => /^not\s+ok\b/i.test(line)).length;
|
|
2751
|
+
const passed = resultLines.length - failed;
|
|
2752
|
+
return { testsRun: resultLines.length, testsPassed: passed, testsFailed: failed };
|
|
2753
|
+
}
|
|
2754
|
+
return void 0;
|
|
2755
|
+
}
|
|
2756
|
+
function parseUnittestOutput(output) {
|
|
2757
|
+
const sumMatches = (pattern) => {
|
|
2758
|
+
let total = 0;
|
|
2759
|
+
for (const match of output.matchAll(pattern)) {
|
|
2760
|
+
total += Number(match[1] ?? 0);
|
|
2761
|
+
}
|
|
2762
|
+
return total;
|
|
2763
|
+
};
|
|
2764
|
+
const testsRun = sumMatches(/^[ \t]*Ran[ \t]+(\d+)[ \t]+tests?\b/gim);
|
|
2765
|
+
if (testsRun === 0) {
|
|
2766
|
+
return { testsRun: 0, testsPassed: 0, testsFailed: 0 };
|
|
2767
|
+
}
|
|
2768
|
+
const outcomeLines = Array.from(
|
|
2769
|
+
output.matchAll(/^[ \t]*(?:OK|FAILED)\b[ \t]*(?:\(([^)]*)\))?[ \t]*$/gim)
|
|
2770
|
+
).map((match) => match[1] ?? "").join(", ");
|
|
2771
|
+
const countIn = (pattern) => {
|
|
2772
|
+
let total = 0;
|
|
2773
|
+
for (const match of outcomeLines.matchAll(pattern)) {
|
|
2774
|
+
total += Number(match[1] ?? 0);
|
|
2775
|
+
}
|
|
2776
|
+
return total;
|
|
2777
|
+
};
|
|
2778
|
+
const failures = countIn(/\bfailures=(\d+)/gi);
|
|
2779
|
+
const errors = countIn(/\berrors=(\d+)/gi);
|
|
2780
|
+
const unexpectedSuccesses = countIn(/\bunexpected\s+successes=(\d+)/gi);
|
|
2781
|
+
const skipped = countIn(/\bskipped=(\d+)/gi);
|
|
2782
|
+
const testsFailed = failures + errors + unexpectedSuccesses;
|
|
2783
|
+
const testsPassed = Math.max(0, testsRun - testsFailed - skipped);
|
|
2784
|
+
return { testsRun, testsPassed, testsFailed };
|
|
2785
|
+
}
|
|
2786
|
+
function parsePytestOutput(output) {
|
|
2787
|
+
const summary = output.match(/=+\s*(.+?)\s*=+\s*$/m)?.[1] ?? output;
|
|
2788
|
+
const passed = Number(summary.match(/(\d+)\s+passed/)?.[1] ?? 0);
|
|
2789
|
+
const failed = Number(summary.match(/(\d+)\s+failed/)?.[1] ?? 0);
|
|
2790
|
+
const errors = Number(summary.match(/(\d+)\s+error/)?.[1] ?? 0);
|
|
2791
|
+
return {
|
|
2792
|
+
testsRun: passed + failed + errors,
|
|
2793
|
+
testsPassed: passed,
|
|
2794
|
+
testsFailed: failed + errors
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
function parseNodeSpecSummary(output) {
|
|
2798
|
+
const cleaned = stripAnsiSequences(output);
|
|
2799
|
+
const count = (label) => {
|
|
2800
|
+
const match = cleaned.match(
|
|
2801
|
+
new RegExp(String.raw`^\s*(?:\u2139\s+)?${label}\s+(\d+)\s*$`, "im")
|
|
2802
|
+
)?.[1];
|
|
2803
|
+
return match === void 0 ? void 0 : Number(match);
|
|
2804
|
+
};
|
|
2805
|
+
const tests = count("tests");
|
|
2806
|
+
const pass = count("pass");
|
|
2807
|
+
const fail = count("fail");
|
|
2808
|
+
if (tests === void 0 || pass === void 0 || fail === void 0) {
|
|
2809
|
+
return void 0;
|
|
2810
|
+
}
|
|
2811
|
+
if (!Number.isFinite(tests) || !Number.isFinite(pass) || !Number.isFinite(fail)) {
|
|
2812
|
+
return void 0;
|
|
2813
|
+
}
|
|
2814
|
+
const cancelled = count("cancelled") ?? 0;
|
|
2815
|
+
const skipped = count("skipped") ?? 0;
|
|
2816
|
+
const todo = count("todo") ?? 0;
|
|
2817
|
+
const testsFailed = fail + cancelled;
|
|
2818
|
+
const accounted = pass + fail + cancelled + skipped + todo;
|
|
2819
|
+
return {
|
|
2820
|
+
testsRun: tests,
|
|
2821
|
+
testsPassed: pass,
|
|
2822
|
+
testsFailed,
|
|
2823
|
+
metricsMeasured: tests > 0 && accounted === tests
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
function parseGenericPassFailOutput(output) {
|
|
2827
|
+
const tap = parseTapSummary(output);
|
|
2828
|
+
if (tap) {
|
|
2829
|
+
return { ...tap, metricsMeasured: tap.testsRun > 0 };
|
|
2830
|
+
}
|
|
2831
|
+
const spec = parseNodeSpecSummary(output);
|
|
2832
|
+
if (spec) {
|
|
2833
|
+
return spec;
|
|
2834
|
+
}
|
|
2835
|
+
const pester = output.match(
|
|
2836
|
+
/Tests\s+Passed:\s*(\d+)\s*,\s*Failed:\s*(\d+)\s*,\s*Skipped:\s*(\d+)/i
|
|
2837
|
+
);
|
|
2838
|
+
if (pester) {
|
|
2839
|
+
const passed = Number(pester[1] ?? 0);
|
|
2840
|
+
const failed = Number(pester[2] ?? 0);
|
|
2841
|
+
const skipped = Number(pester[3] ?? 0);
|
|
2842
|
+
return {
|
|
2843
|
+
testsRun: passed + failed + skipped,
|
|
2844
|
+
testsPassed: passed,
|
|
2845
|
+
testsFailed: failed,
|
|
2846
|
+
metricsMeasured: passed + failed + skipped > 0
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
const surefireMatches = Array.from(
|
|
2850
|
+
output.matchAll(
|
|
2851
|
+
/^\s*(?:\[INFO\]\s*)?Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+)(?:,\s*Skipped:\s*(\d+))?/gim
|
|
2852
|
+
)
|
|
2853
|
+
);
|
|
2854
|
+
if (surefireMatches.length > 0) {
|
|
2855
|
+
let testsRun = 0;
|
|
2856
|
+
let testsFailed = 0;
|
|
2857
|
+
let skipped = 0;
|
|
2858
|
+
for (const match of surefireMatches) {
|
|
2859
|
+
testsRun += Number(match[1] ?? 0);
|
|
2860
|
+
testsFailed += Number(match[2] ?? 0) + Number(match[3] ?? 0);
|
|
2861
|
+
skipped += Number(match[4] ?? 0);
|
|
2862
|
+
}
|
|
2863
|
+
return {
|
|
2864
|
+
testsRun,
|
|
2865
|
+
testsPassed: Math.max(0, testsRun - testsFailed - skipped),
|
|
2866
|
+
testsFailed,
|
|
2867
|
+
metricsMeasured: testsRun > 0
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
const gradleCompleted = output.match(/(\d+)\s+tests?\s+completed\b/i);
|
|
2871
|
+
if (gradleCompleted) {
|
|
2872
|
+
const testsRun = Number(gradleCompleted[1] ?? 0);
|
|
2873
|
+
const failedMatch = output.match(/(\d+)\s+failed\b/i);
|
|
2874
|
+
const testsFailed = Number(failedMatch?.[1] ?? 0);
|
|
2875
|
+
return {
|
|
2876
|
+
testsRun,
|
|
2877
|
+
testsPassed: Math.max(0, testsRun - testsFailed),
|
|
2878
|
+
testsFailed,
|
|
2879
|
+
metricsMeasured: testsRun > 0
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
const failureZeroStripped = output.replace(/\b(?:failures?|failed|errors?):\s*0\b/gi, "").replace(/\b0\s+(?:failures?|failed|errors?)\b/gi, "").replace(/\u2139\s+fail\s+0\b/gi, "").replace(/\bfail\s+0\b/gi, "");
|
|
2883
|
+
if (/\b(fail|failed|error)\b/i.test(failureZeroStripped)) {
|
|
2884
|
+
return { testsRun: 1, testsPassed: 0, testsFailed: 1, metricsMeasured: false };
|
|
2885
|
+
}
|
|
2886
|
+
const successProse = /\b(ok|pass|passed|test result: ok|BUILD SUCCESS|BUILD SUCCESSFUL)\b/i.test(
|
|
2887
|
+
output
|
|
2888
|
+
);
|
|
2889
|
+
return {
|
|
2890
|
+
testsRun: 0,
|
|
2891
|
+
testsPassed: 0,
|
|
2892
|
+
testsFailed: 0,
|
|
2893
|
+
metricsMeasured: false,
|
|
2894
|
+
successProse
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
export {
|
|
2899
|
+
runLintOnDiffWithArtifacts,
|
|
2900
|
+
hasPoeticVitestHarness,
|
|
2901
|
+
runTestGate,
|
|
2902
|
+
runDeterministicTestGateWithArtifacts
|
|
2903
|
+
};
|
|
2904
|
+
//# sourceMappingURL=chunk-3ZPQLGV7.js.map
|