@qwen-code/qwen-code 0.21.3 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +13 -0
  2. package/bundled/qc-helper/docs/features/channels/github.md +12 -8
  3. package/bundled/qc-helper/docs/features/code-review.md +14 -1
  4. package/bundled/qc-helper/docs/features/commands.md +46 -40
  5. package/bundled/qc-helper/docs/qwen-serve.md +27 -26
  6. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +50 -19
  7. package/bundled/review/SKILL.md +67 -22
  8. package/chunks/{MaxSizedBox-OJR636GP.js → MaxSizedBox-5XDFQHTQ.js} +8 -7
  9. package/chunks/{StandaloneSessionPicker-AMMBF5UY.js → StandaloneSessionPicker-DLADA54K.js} +21 -20
  10. package/chunks/{acpAgent-M57LVIQA.js → acpAgent-MVEFZSBS.js} +48 -42
  11. package/chunks/{agent-PE37ARLM.js → agent-RB6LCRZX.js} +8 -7
  12. package/chunks/{agent-headless-45ASME4R.js → agent-headless-272ZACJV.js} +8 -7
  13. package/chunks/{anthropicContentGenerator-WCBYB6JG.js → anthropicContentGenerator-3CAHEZBM.js} +4 -2
  14. package/chunks/{bridge-K3ZRPO4D.js → bridge-GXUXFSHK.js} +9 -8
  15. package/chunks/{ca-4OJN75WT.js → ca-NYKNH222.js} +4 -0
  16. package/chunks/{channel-management-service-IXJ2MZTS.js → channel-management-service-UHX56NBO.js} +1 -1
  17. package/chunks/{channel-settings-store-PEAR3IME.js → channel-settings-store-5UA2SABE.js} +14 -13
  18. package/chunks/{chunk-IWGTGH4G.js → chunk-2CCUHZP5.js} +2 -2
  19. package/chunks/{chunk-HUIQ4FYC.js → chunk-2JN5V3I7.js} +3 -3
  20. package/chunks/{chunk-BSKUTHLO.js → chunk-2ON2O4MD.js} +16 -3
  21. package/chunks/{chunk-QPSYIV2O.js → chunk-2T3RYG4L.js} +2039 -563
  22. package/chunks/{chunk-ZGSUFIL5.js → chunk-3PHUGFPH.js} +1 -1
  23. package/chunks/{chunk-QWW6I2UO.js → chunk-4CTV3FZH.js} +400 -33
  24. package/chunks/{chunk-6NE5NTHJ.js → chunk-4PZ7SPD5.js} +1 -1
  25. package/chunks/{chunk-2LZCLQIS.js → chunk-5XGI4IPK.js} +4 -4
  26. package/chunks/{chunk-GMSBXEH4.js → chunk-6KQUOXT5.js} +6 -6
  27. package/chunks/{chunk-2MI7ZTRT.js → chunk-6ME3PVMM.js} +4 -4
  28. package/chunks/{chunk-SRY26X4H.js → chunk-7UXI6P72.js} +11 -2
  29. package/chunks/{chunk-YU6TBRTA.js → chunk-A7PONO5G.js} +1 -1
  30. package/chunks/{chunk-N42C2FU3.js → chunk-AFJSRJTO.js} +1 -1
  31. package/chunks/{chunk-H6FIBBXK.js → chunk-AWTLFUVN.js} +11 -11
  32. package/chunks/{chunk-HIXVWF7I.js → chunk-B3EBXISV.js} +2 -2
  33. package/chunks/{chunk-3XF4WT3B.js → chunk-BC4LTKEG.js} +1 -1
  34. package/chunks/{chunk-ZJES4C3I.js → chunk-BOWKLLWA.js} +2 -2
  35. package/chunks/{chunk-SKSF3LGQ.js → chunk-C5YFS6VR.js} +3 -3
  36. package/chunks/{chunk-IF6K42YJ.js → chunk-C6FU76AA.js} +1 -1
  37. package/chunks/{chunk-VUNRNRN6.js → chunk-DHNFB7NC.js} +4 -4
  38. package/chunks/{chunk-LZV2BZPL.js → chunk-DJA7YBLJ.js} +2 -2
  39. package/chunks/{chunk-K2J5JZ6B.js → chunk-DOCBJIBL.js} +1 -1
  40. package/chunks/{chunk-X6WUYMOV.js → chunk-DTAPI453.js} +2 -2
  41. package/chunks/{chunk-XUMAD5IT.js → chunk-DZPOYHBT.js} +1 -1
  42. package/chunks/{chunk-UQDFEU47.js → chunk-E2SYUEIA.js} +463 -114
  43. package/chunks/{chunk-DSGJLHM3.js → chunk-EIAPOG76.js} +3 -3
  44. package/chunks/{chunk-EGYJRIDF.js → chunk-EJXATV5R.js} +2 -2
  45. package/chunks/{chunk-BKGA75MU.js → chunk-FPPVCCAH.js} +1 -1
  46. package/chunks/{chunk-KXWERR6P.js → chunk-FV4TUUDE.js} +10 -10
  47. package/chunks/{chunk-WY2ENRDN.js → chunk-G6KI6B5L.js} +1 -1
  48. package/chunks/{chunk-H6ZYP74A.js → chunk-GDREW3U5.js} +1 -1
  49. package/chunks/{chunk-BKXTHMOZ.js → chunk-HBCKZF2A.js} +1 -1
  50. package/chunks/chunk-HGCQ5SE7.js +136 -0
  51. package/chunks/{chunk-HEBW74ZW.js → chunk-HI4YGRJD.js} +3 -3
  52. package/chunks/{chunk-5JPVOYWA.js → chunk-I32URQI7.js} +4 -4
  53. package/chunks/{chunk-5VYFXHY2.js → chunk-IMGNGOGC.js} +3 -3
  54. package/chunks/{chunk-4K3RV3OA.js → chunk-JDJ7SC4E.js} +7 -3
  55. package/chunks/{chunk-22YFMJBW.js → chunk-KB6VSG2U.js} +58 -6
  56. package/chunks/{chunk-R7XNRHYD.js → chunk-KHSJSMFS.js} +1 -1
  57. package/chunks/{chunk-LGT3YMLN.js → chunk-LEZFUYPM.js} +1 -1
  58. package/chunks/{chunk-TZTBZ25J.js → chunk-LLMGP5IC.js} +1 -1
  59. package/chunks/{chunk-NW57GHNL.js → chunk-LPLMUGIM.js} +3 -3
  60. package/chunks/{chunk-L7FTDKCD.js → chunk-M2L4TMH3.js} +21 -3
  61. package/chunks/{chunk-RCXCGZG6.js → chunk-M6RNMBUO.js} +3 -3
  62. package/chunks/{chunk-D2GPKXCQ.js → chunk-O4IWFYUW.js} +4 -4
  63. package/chunks/{chunk-Q3G5L5KF.js → chunk-OOPFAIWP.js} +1 -1
  64. package/chunks/{chunk-MTR4JWQJ.js → chunk-P5UJMCVS.js} +4 -4
  65. package/chunks/{chunk-FF75UUB2.js → chunk-PCVMRBLN.js} +1 -1
  66. package/chunks/{chunk-XMJO6C44.js → chunk-PQ5JFCNL.js} +3 -3
  67. package/chunks/{chunk-SQ3YD5MI.js → chunk-PQEIKQXN.js} +1 -1
  68. package/chunks/{chunk-3MWNROHB.js → chunk-QXTGHZCX.js} +3 -3
  69. package/chunks/{chunk-TXABHVZD.js → chunk-QZQ72YF6.js} +1 -1
  70. package/chunks/{chunk-YPUHMQXV.js → chunk-RL2NPV5E.js} +1 -1
  71. package/chunks/{chunk-FXVHVCNA.js → chunk-RNKGMZEW.js} +1 -1
  72. package/chunks/{chunk-EIMKVOSF.js → chunk-SBBHZ3YT.js} +1 -1
  73. package/chunks/{chunk-QDWLUXKP.js → chunk-SV5X2EUV.js} +1 -1
  74. package/chunks/{chunk-YWMDTMAU.js → chunk-THLV7YGY.js} +2 -2
  75. package/chunks/{chunk-H35Q5CEG.js → chunk-TXZDRD36.js} +6 -6
  76. package/chunks/{chunk-MUEJ4R3E.js → chunk-UATJZOAJ.js} +3 -3
  77. package/chunks/{chunk-M5LIY7Y6.js → chunk-URVJ4AZC.js} +1 -1
  78. package/chunks/{chunk-4AGGSHCH.js → chunk-WDLQGFWP.js} +196 -52
  79. package/chunks/{chunk-RL6J3BPE.js → chunk-XGA6XHH3.js} +3 -3
  80. package/chunks/{chunk-QWDVZH24.js → chunk-XPAY4EMH.js} +3 -3
  81. package/chunks/{chunk-MCXMWLFT.js → chunk-XSOAW7FC.js} +5 -5
  82. package/chunks/{chunk-RR224BUP.js → chunk-ZLJQIGCH.js} +45 -1
  83. package/chunks/{chunk-FG63DZQA.js → chunk-ZRQY5BBS.js} +9 -9
  84. package/chunks/{computer-use-UJMK6JQU.js → computer-use-MDOMIVTL.js} +8 -7
  85. package/chunks/{config-utils-7COTJMWK.js → config-utils-E3ZQTHGM.js} +2 -2
  86. package/chunks/{contextCommand-4YABGF4S.js → contextCommand-FCK3CB37.js} +10 -9
  87. package/chunks/{core-runtime-7M4VM3DA.js → core-runtime-UHRHHU2O.js} +8 -7
  88. package/chunks/{create-sub-session-HOFDW4N7.js → create-sub-session-MIXUVLWW.js} +20 -13
  89. package/chunks/{daemon-status-provider-W2U5Q3J5.js → daemon-status-provider-6LC7LUNR.js} +13 -12
  90. package/chunks/{daemon-trust-policy-IFICWPXP.js → daemon-trust-policy-Y36HGRAM.js} +13 -12
  91. package/chunks/{daemon-trust-policy-monitor-GX7EO6FW.js → daemon-trust-policy-monitor-ROTBBDOH.js} +13 -12
  92. package/chunks/{de-A6TI4LBB.js → de-VL76CPLV.js} +4 -0
  93. package/chunks/{deferred-core-runtime-U36OOOKE.js → deferred-core-runtime-ZQ4JIAJD.js} +8 -7
  94. package/chunks/{display-image-VS3TXYQH.js → display-image-2WOG27DY.js} +1 -1
  95. package/chunks/{dist-NEBRI7WO.js → dist-C5U4LJKW.js} +95 -0
  96. package/chunks/{dist-4KAX7CCL.js → dist-IKGVITP3.js} +401 -24
  97. package/chunks/{earlyInputCapture-5ZWDH3SB.js → earlyInputCapture-GPRUNXTP.js} +8 -7
  98. package/chunks/{edit-J7SW6UP6.js → edit-YK3UP6D2.js} +8 -7
  99. package/chunks/{en-L4UQDLWW.js → en-IRJ2WN5T.js} +4 -0
  100. package/chunks/{enter-worktree-NRLOHRB6.js → enter-worktree-QMVQW7UG.js} +8 -7
  101. package/chunks/{enterPlanMode-GDSTEFSM.js → enterPlanMode-TZAYQ5MP.js} +8 -7
  102. package/chunks/{environment-KHYEKG5W.js → environment-3UQURCZG.js} +10 -9
  103. package/chunks/{errors-VNLAJV3O.js → errors-VBIMTNNI.js} +10 -9
  104. package/chunks/{exit-worktree-ARVID7DF.js → exit-worktree-4EC7LBBC.js} +8 -7
  105. package/chunks/{exitPlanMode-4ZFY7KUH.js → exitPlanMode-K5Y3REU2.js} +8 -7
  106. package/chunks/{fast-path-KA65VREJ.js → fast-path-KS6IE7YR.js} +11 -2
  107. package/chunks/{fr-QF7RI5ZE.js → fr-GK3HQYVB.js} +4 -0
  108. package/chunks/{gemini-5PMCNPF7.js → gemini-LTUCUOJH.js} +42 -40
  109. package/chunks/{geminiContentGenerator-NFSYJ4HA.js → geminiContentGenerator-M3HJTHKR.js} +1 -1
  110. package/chunks/{glob-F3EAUDAX.js → glob-SKUYGTF2.js} +8 -7
  111. package/chunks/{grep-UJMPVZER.js → grep-SQ7RD7SV.js} +8 -7
  112. package/chunks/{handleAutoUpdate-MG4FRZPN.js → handleAutoUpdate-OK7OX23A.js} +12 -11
  113. package/chunks/{i18n-EVHV4ZF3.js → i18n-P77YBKY4.js} +9 -8
  114. package/chunks/{image-gen-UC7XVHI7.js → image-gen-2ZWPS42A.js} +1 -1
  115. package/chunks/{initializer-O5B6HUBI.js → initializer-PIOYXVW7.js} +13 -12
  116. package/chunks/{installationInfo-2LIKCNVX.js → installationInfo-IWMM6DPV.js} +9 -8
  117. package/chunks/{ja-URQESVGW.js → ja-3FA46IKD.js} +4 -0
  118. package/chunks/{list-BPKIE3Z4.js → list-3DCR4TRI.js} +16 -15
  119. package/chunks/{loadedSettingsAdapter-NGLFF4V4.js → loadedSettingsAdapter-EJC6CU5N.js} +13 -12
  120. package/chunks/{loggingContentGenerator-7TFESRG5.js → loggingContentGenerator-2JEFU25T.js} +3 -3
  121. package/chunks/{managed-npm-update-CLZGGV7F.js → managed-npm-update-EGVQPSBR.js} +9 -8
  122. package/chunks/{mcp-X5OZOOXX.js → mcp-KFFH2MBV.js} +13 -12
  123. package/chunks/{monitor-TGSVSAZZ.js → monitor-6Y3NDMTI.js} +8 -7
  124. package/chunks/{nonInteractiveCli-GUMJ5JLG.js → nonInteractiveCli-426IQDPE.js} +40 -38
  125. package/chunks/{notebook-edit-EZISE7IC.js → notebook-edit-373IQQLE.js} +8 -7
  126. package/chunks/{openaiContentGenerator-ZAKS5A6P.js → openaiContentGenerator-PGFSOMIQ.js} +3 -3
  127. package/chunks/{pidfile-PCMBX4LR.js → pidfile-Y4INDMHG.js} +8 -7
  128. package/chunks/{processUtils-2CSVE5BW.js → processUtils-6BCPCJZW.js} +2 -2
  129. package/chunks/{pt-EEEQXRUI.js → pt-3AOBKPIJ.js} +4 -0
  130. package/chunks/{qwenContentGenerator-S3H6PIC4.js → qwenContentGenerator-QC3KSHSX.js} +8 -8
  131. package/chunks/{qwenOAuth2-MNU425VE.js → qwenOAuth2-IOAXKJ3L.js} +1 -1
  132. package/chunks/{read-file-HXJXHBI6.js → read-file-2T54FYZV.js} +2 -2
  133. package/chunks/{resumeHistoryUtils-4QAMC2S6.js → resumeHistoryUtils-O62RDCZ3.js} +11 -10
  134. package/chunks/{ripGrep-MCETPF2E.js → ripGrep-3H72KY7S.js} +8 -7
  135. package/chunks/{ru-WGZDYQAB.js → ru-VJRAMC4W.js} +4 -0
  136. package/chunks/{run-qwen-serve-3NMODTXV.js → run-qwen-serve-3AIDNNHO.js} +82 -35
  137. package/chunks/{runtime-AQOV7DVP.js → runtime-AZ4EHHFV.js} +16 -15
  138. package/chunks/{scheduler-DN7SZ6ZE.js → scheduler-L4UUCU4Z.js} +8 -7
  139. package/chunks/{send-message-HGCVVDTZ.js → send-message-LHJLVWFD.js} +2 -2
  140. package/chunks/{serve-WZB45HVT.js → serve-CLXGSYOE.js} +14 -12
  141. package/chunks/{server-GOTZFEEZ.js → server-MEAE3WMM.js} +36 -34
  142. package/chunks/{session-23LOFLWN.js → session-OSLD2LT5.js} +41 -39
  143. package/chunks/{settings-LI2ORVXC.js → settings-ILWAOKA4.js} +12 -11
  144. package/chunks/{shell-GLC6OP3C.js → shell-RT5DBH6B.js} +8 -7
  145. package/chunks/{skill-C7W6M4GO.js → skill-LVGA3ATZ.js} +2 -2
  146. package/chunks/{skill-settings-W25NCK5I.js → skill-settings-WTZE7EPE.js} +12 -11
  147. package/chunks/{spawnChannel-OHJLEBLD.js → spawnChannel-BOBS7DVM.js} +8 -7
  148. package/chunks/{standalone-update-E4V5V7WT.js → standalone-update-Y5JB4S2K.js} +10 -9
  149. package/chunks/{startInteractiveUI-FKILLW7Q.js → startInteractiveUI-ZV25CD2S.js} +62 -50
  150. package/chunks/{task-create-CSCEG4SM.js → task-create-4ALZMGP5.js} +3 -3
  151. package/chunks/{task-update-C52LNFU5.js → task-update-IKCEEWJ6.js} +3 -3
  152. package/chunks/{team-create-HBIJ7NHT.js → team-create-ZVPQIXCP.js} +8 -7
  153. package/chunks/{team-plan-approval-IBJOKS4J.js → team-plan-approval-5UHUBY3W.js} +8 -7
  154. package/chunks/{terminal-image-renderer-BOQJPVLB.js → terminal-image-renderer-NWC2RALG.js} +8 -7
  155. package/chunks/{theme-manager-NSS7VLUD.js → theme-manager-5KICSBOB.js} +8 -7
  156. package/chunks/{todoWrite-YKAA57W2.js → todoWrite-FTZWQRZH.js} +2 -1
  157. package/chunks/{tool-search-DEAS54VG.js → tool-search-2AOU77H4.js} +4 -4
  158. package/chunks/{total-session-admission-4LWTX3WD.js → total-session-admission-FR7RMHVL.js} +9 -8
  159. package/chunks/{trustedFolders-NF3J2TA7.js → trustedFolders-V7UUPQXO.js} +9 -8
  160. package/chunks/{update-relaunch-WUHRH5V3.js → update-relaunch-TBWRMQD6.js} +5 -5
  161. package/chunks/{updateCheck-7EG4BLBH.js → updateCheck-42SPCLH7.js} +11 -10
  162. package/chunks/{useAutoAcceptIndicator-ZRV56GVP.js → useAutoAcceptIndicator-4C2KM345.js} +14 -13
  163. package/chunks/{validateNonInterActiveAuth-WC2FPE3I.js → validateNonInterActiveAuth-UVFRXV3Z.js} +37 -35
  164. package/chunks/{version-6SS7KCUP.js → version-HKTV6TPN.js} +1 -1
  165. package/chunks/{web-fetch-Y5QQMPKA.js → web-fetch-ZRXSV5AN.js} +2 -2
  166. package/chunks/{web-search-QKKEU723.js → web-search-WOLV3L6T.js} +2 -2
  167. package/chunks/{workflow-PWIU6Q2M.js → workflow-F63ES3OB.js} +9 -8
  168. package/chunks/{workspace-providers-status-B2DM6FNK.js → workspace-providers-status-PJMGMYMC.js} +15 -14
  169. package/chunks/{workspace-registration-store-7Y33TTHJ.js → workspace-registration-store-LNBOX5TJ.js} +1 -1
  170. package/chunks/{workspace-registry-TSNJKLPC.js → workspace-registry-4PR7VDQ3.js} +9 -8
  171. package/chunks/{workspace-service-4I2QHDX4.js → workspace-service-PVI3VUNQ.js} +16 -15
  172. package/chunks/{workspace-skills-status-HVXNWVGR.js → workspace-skills-status-2Q5ZISO6.js} +13 -12
  173. package/chunks/{workspace-trust-reconciler-RDBBGMWP.js → workspace-trust-reconciler-NOILUH3C.js} +14 -13
  174. package/chunks/{write-file-RQ4DG5RY.js → write-file-MOM4JDHG.js} +8 -7
  175. package/chunks/{zh-72XPUG5P.js → zh-ARWDLDLR.js} +4 -0
  176. package/chunks/{zh-TW-LTB4SRH3.js → zh-TW-Y4RGI3P7.js} +4 -0
  177. package/chunks/{zoom-image-O5YCB7WN.js → zoom-image-E3VHRCFU.js} +2 -2
  178. package/cli.js +12 -12
  179. package/locales/ca.js +8 -0
  180. package/locales/de.js +8 -0
  181. package/locales/en.js +8 -0
  182. package/locales/fr.js +8 -0
  183. package/locales/ja.js +8 -0
  184. package/locales/pt.js +8 -0
  185. package/locales/ru.js +8 -0
  186. package/locales/zh-TW.js +7 -0
  187. package/locales/zh.js +7 -0
  188. package/package.json +3 -3
  189. package/web-shell/assets/{arc-Bd7d781i.js → arc-GpdmhIxr.js} +1 -1
  190. package/web-shell/assets/{architectureDiagram-3BPJPVTR-CpnOJqNE.js → architectureDiagram-3BPJPVTR-C-LhCgH_.js} +1 -1
  191. package/web-shell/assets/{blockDiagram-GPEHLZMM-NxVTiWnk.js → blockDiagram-GPEHLZMM-CzNdTCm-.js} +1 -1
  192. package/web-shell/assets/{c4Diagram-AAUBKEIU-BWT75l82.js → c4Diagram-AAUBKEIU-Ce7hbrRM.js} +1 -1
  193. package/web-shell/assets/channel-95y6Rcp3.js +1 -0
  194. package/web-shell/assets/{chunk-2J33WTMH-Dzz03hk6.js → chunk-2J33WTMH-BBSEn1PK.js} +1 -1
  195. package/web-shell/assets/{chunk-4BX2VUAB-DRF-egfm.js → chunk-4BX2VUAB-_llgjfe5.js} +1 -1
  196. package/web-shell/assets/{chunk-55IACEB6-Co15vq5x.js → chunk-55IACEB6-9fJZ_oZE.js} +1 -1
  197. package/web-shell/assets/{chunk-727SXJPM-DHSUk1J7.js → chunk-727SXJPM-DgEzQWOu.js} +1 -1
  198. package/web-shell/assets/{chunk-AQP2D5EJ-Br8u_E2R.js → chunk-AQP2D5EJ-B4RthUTc.js} +1 -1
  199. package/web-shell/assets/{chunk-FMBD7UC4-DXbPTnqF.js → chunk-FMBD7UC4-CDI-emMW.js} +1 -1
  200. package/web-shell/assets/{chunk-ND2GUHAM-BleQdi4d.js → chunk-ND2GUHAM-jM2-yOd8.js} +1 -1
  201. package/web-shell/assets/{chunk-QZHKN3VN-CqgBf-95.js → chunk-QZHKN3VN-XGZHlKPF.js} +1 -1
  202. package/web-shell/assets/classDiagram-4FO5ZUOK-pfzVkmjQ.js +1 -0
  203. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-pfzVkmjQ.js +1 -0
  204. package/web-shell/assets/{cose-bilkent-S5V4N54A-WS7b13Yg.js → cose-bilkent-S5V4N54A-90nbW1xQ.js} +1 -1
  205. package/web-shell/assets/{dagre-BM42HDAG-v_55ki8o.js → dagre-BM42HDAG-BtzlyqZ9.js} +1 -1
  206. package/web-shell/assets/{diagram-2AECGRRQ-DaYAjbdK.js → diagram-2AECGRRQ-DAaqGWaH.js} +1 -1
  207. package/web-shell/assets/{diagram-5GNKFQAL-D_Hy-L64.js → diagram-5GNKFQAL-CxlzSIFa.js} +1 -1
  208. package/web-shell/assets/{diagram-KO2AKTUF-dB06cZyD.js → diagram-KO2AKTUF-8kj-1H6u.js} +1 -1
  209. package/web-shell/assets/{diagram-LMA3HP47-DzSluD4F.js → diagram-LMA3HP47-6NhjFwRF.js} +1 -1
  210. package/web-shell/assets/{diagram-OG6HWLK6-Cvb6CeXQ.js → diagram-OG6HWLK6-Bn9kvInz.js} +1 -1
  211. package/web-shell/assets/{erDiagram-TEJ5UH35-7eB2Hmov.js → erDiagram-TEJ5UH35-CaRoGsAt.js} +1 -1
  212. package/web-shell/assets/{flowDiagram-I6XJVG4X-B08iGCw_.js → flowDiagram-I6XJVG4X-TkLweHdf.js} +1 -1
  213. package/web-shell/assets/{ganttDiagram-6RSMTGT7-BgsrCHq-.js → ganttDiagram-6RSMTGT7-ruwhCKBl.js} +1 -1
  214. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DQLurQ2i.js → gitGraphDiagram-PVQCEYII-DNI_PxoU.js} +1 -1
  215. package/web-shell/assets/{index-DJ-z1Oba.js → index-T4KluHvu.js} +336 -336
  216. package/web-shell/assets/{index-Bi1dP2mU.css → index-WxSvj2tI.css} +2 -2
  217. package/web-shell/assets/{index-B_1Z0Mgr.js → index-p1gz-sxE.js} +1 -1
  218. package/web-shell/assets/{infoDiagram-5YYISTIA-DXZGO8qB.js → infoDiagram-5YYISTIA-DarXq1-A.js} +1 -1
  219. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-g7gbuH2w.js → ishikawaDiagram-YF4QCWOH-GxDaDlT0.js} +1 -1
  220. package/web-shell/assets/{journeyDiagram-JHISSGLW-euefICGn.js → journeyDiagram-JHISSGLW-mDEoGBzY.js} +1 -1
  221. package/web-shell/assets/{kanban-definition-UN3LZRKU-Dws8-mjG.js → kanban-definition-UN3LZRKU-Ds0t-Mup.js} +1 -1
  222. package/web-shell/assets/{linear-C63F0tyh.js → linear-05nJ0xZ7.js} +1 -1
  223. package/web-shell/assets/{mermaid.core-fLrhp_4-.js → mermaid.core-Bua24ThM.js} +5 -5
  224. package/web-shell/assets/{mindmap-definition-RKZ34NQL-qRkydfT-.js → mindmap-definition-RKZ34NQL-D_zRdvUS.js} +1 -1
  225. package/web-shell/assets/{pieDiagram-4H26LBE5-BZ0wQaKN.js → pieDiagram-4H26LBE5-DZHr1x16.js} +1 -1
  226. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-Fjq6GbSZ.js → quadrantDiagram-W4KKPZXB-HA06Wf0h.js} +1 -1
  227. package/web-shell/assets/{requirementDiagram-4Y6WPE33-CqWvjLHU.js → requirementDiagram-4Y6WPE33-DH3urmJf.js} +1 -1
  228. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-VcXX6PVZ.js → sankeyDiagram-5OEKKPKP-C4xgz0TT.js} +1 -1
  229. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BJSRtxFr.js → sequenceDiagram-3UESZ5HK-DAUoZWgv.js} +1 -1
  230. package/web-shell/assets/{stateDiagram-AJRCARHV-Dt9zUwmZ.js → stateDiagram-AJRCARHV-v7NzXy4l.js} +1 -1
  231. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CtdFexEf.js +1 -0
  232. package/web-shell/assets/{timeline-definition-PNZ67QCA-DQwlNRws.js → timeline-definition-PNZ67QCA-B3GTYR2W.js} +1 -1
  233. package/web-shell/assets/{vennDiagram-CIIHVFJN-DwxjUEtQ.js → vennDiagram-CIIHVFJN-BWHjSPAq.js} +1 -1
  234. package/web-shell/assets/{wardley-L42UT6IY-D3QdMSpJ.js → wardley-L42UT6IY-m9r6IUBC.js} +1 -1
  235. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BddN8oK8.js → wardleyDiagram-YWT4CUSO-kT0FkVfK.js} +1 -1
  236. package/web-shell/assets/{xychartDiagram-2RQKCTM6-BUNuBrRJ.js → xychartDiagram-2RQKCTM6-CYRm10z5.js} +1 -1
  237. package/web-shell/index.html +2 -2
  238. package/web-shell/assets/channel-A434sWK5.js +0 -1
  239. package/web-shell/assets/classDiagram-4FO5ZUOK-CwPuhldB.js +0 -1
  240. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CwPuhldB.js +0 -1
  241. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CCxQt56w.js +0 -1
@@ -2,13 +2,13 @@
2
2
  "use strict";
3
3
  import {
4
4
  serveCommand
5
- } from "./chunk-BSKUTHLO.js";
5
+ } from "./chunk-2ON2O4MD.js";
6
6
  import {
7
7
  listCommand
8
- } from "./chunk-MTR4JWQJ.js";
8
+ } from "./chunk-P5UJMCVS.js";
9
9
  import {
10
10
  getErrorMessage as getErrorMessage2
11
- } from "./chunk-EGYJRIDF.js";
11
+ } from "./chunk-EJXATV5R.js";
12
12
  import {
13
13
  ObservedChannelContactStore
14
14
  } from "./chunk-DYA25Y7U.js";
@@ -46,22 +46,22 @@ import {
46
46
  registerToolCallDispatch,
47
47
  selectFirstModel,
48
48
  sessionsPath
49
- } from "./chunk-2LZCLQIS.js";
49
+ } from "./chunk-5XGI4IPK.js";
50
50
  import {
51
51
  getExtensionManager,
52
52
  requestChoicePluginNonInteractive,
53
53
  requestConsentNonInteractive,
54
54
  requestConsentOrFail,
55
55
  resolveExtensionCommandScope
56
- } from "./chunk-D2GPKXCQ.js";
56
+ } from "./chunk-O4IWFYUW.js";
57
57
  import {
58
58
  collectProviderModelsForProtocol,
59
59
  getAuthTypeFromEnv,
60
60
  resolveCliGenerationConfig
61
- } from "./chunk-5VYFXHY2.js";
61
+ } from "./chunk-IMGNGOGC.js";
62
62
  import {
63
63
  parseChannelConfig
64
- } from "./chunk-LGT3YMLN.js";
64
+ } from "./chunk-LEZFUYPM.js";
65
65
  import {
66
66
  findCliEntryPath
67
67
  } from "./chunk-V6WHFGST.js";
@@ -128,29 +128,29 @@ import {
128
128
  assembleMcpServers,
129
129
  getPendingGatedMcpServers,
130
130
  mcpCommand
131
- } from "./chunk-2MI7ZTRT.js";
131
+ } from "./chunk-6ME3PVMM.js";
132
132
  import {
133
133
  loadSettings
134
- } from "./chunk-22YFMJBW.js";
134
+ } from "./chunk-KB6VSG2U.js";
135
135
  import {
136
136
  detectSystemLanguage,
137
137
  getCurrentLanguage,
138
138
  initializeI18n,
139
139
  resolveLanguageSetting,
140
140
  t
141
- } from "./chunk-KXWERR6P.js";
141
+ } from "./chunk-FV4TUUDE.js";
142
142
  import {
143
143
  loadEnvironment
144
- } from "./chunk-6NE5NTHJ.js";
144
+ } from "./chunk-4PZ7SPD5.js";
145
145
  import {
146
146
  getCliVersion
147
- } from "./chunk-BKXTHMOZ.js";
147
+ } from "./chunk-HBCKZF2A.js";
148
148
  import {
149
149
  getPackageJson
150
150
  } from "./chunk-VJNBRRZG.js";
151
151
  import {
152
152
  isWorkspaceTrusted
153
- } from "./chunk-M5LIY7Y6.js";
153
+ } from "./chunk-URVJ4AZC.js";
154
154
  import {
155
155
  require_strip_json_comments
156
156
  } from "./chunk-ALGGS7UH.js";
@@ -191,7 +191,7 @@ import {
191
191
  unquoteCStylePath,
192
192
  updateChannelMemoryEntry,
193
193
  updateSetting
194
- } from "./chunk-UQDFEU47.js";
194
+ } from "./chunk-E2SYUEIA.js";
195
195
  import {
196
196
  nextFireTime,
197
197
  parseCron
@@ -206,14 +206,14 @@ import {
206
206
  import {
207
207
  getAllGeminiMdFilenames,
208
208
  setGeminiMdFilename
209
- } from "./chunk-4K3RV3OA.js";
209
+ } from "./chunk-JDJ7SC4E.js";
210
210
  import {
211
211
  SchemaValidator,
212
212
  ToolNames
213
213
  } from "./chunk-CFDR3GNL.js";
214
214
  import {
215
215
  DEFAULT_QWEN_EMBEDDING_MODEL
216
- } from "./chunk-H35Q5CEG.js";
216
+ } from "./chunk-TXZDRD36.js";
217
217
  import {
218
218
  isTlsVerificationDisabled
219
219
  } from "./chunk-BFHXDAR7.js";
@@ -358,9 +358,9 @@ var require_command_exists = __commonJS({
358
358
  cleanInput = /* @__PURE__ */ __name(function(s) {
359
359
  var isPathName = /[\\]/.test(s);
360
360
  if (isPathName) {
361
- var dirname19 = '"' + path3.dirname(s) + '"';
362
- var basename5 = '"' + path3.basename(s) + '"';
363
- return dirname19 + ":" + basename5;
361
+ var dirname23 = '"' + path3.dirname(s) + '"';
362
+ var basename6 = '"' + path3.basename(s) + '"';
363
+ return dirname23 + ":" + basename6;
364
364
  }
365
365
  return '"' + s + '"';
366
366
  }, "cleanInput");
@@ -368,10 +368,10 @@ var require_command_exists = __commonJS({
368
368
  module.exports = /* @__PURE__ */ __name(function commandExists2(commandName, callback) {
369
369
  var cleanedCommandName = cleanInput(commandName);
370
370
  if (!callback && typeof Promise !== "undefined") {
371
- return new Promise(function(resolve20, reject) {
371
+ return new Promise(function(resolve23, reject) {
372
372
  commandExists2(commandName, function(error, output) {
373
373
  if (output) {
374
- resolve20(commandName);
374
+ resolve23(commandName);
375
375
  } else {
376
376
  reject(error);
377
377
  }
@@ -2255,8 +2255,8 @@ function createBridgeReadinessGate() {
2255
2255
  current: /* @__PURE__ */ __name(() => pending, "current"),
2256
2256
  block: /* @__PURE__ */ __name(() => {
2257
2257
  if (pending) return;
2258
- pending = new Promise((resolve20) => {
2259
- releasePending = resolve20;
2258
+ pending = new Promise((resolve23) => {
2259
+ releasePending = resolve23;
2260
2260
  });
2261
2261
  }, "block"),
2262
2262
  release: /* @__PURE__ */ __name(() => {
@@ -2338,7 +2338,7 @@ function createBridgeRecovery(options) {
2338
2338
  writeStderrLine(
2339
2339
  `[Channel] Bridge crashed (${recentCrashCount}/${MAX_CRASH_RESTARTS} in window). Restarting in ${RESTART_DELAY_MS / 1e3}s...`
2340
2340
  );
2341
- await new Promise((resolve20) => setTimeout(resolve20, RESTART_DELAY_MS));
2341
+ await new Promise((resolve23) => setTimeout(resolve23, RESTART_DELAY_MS));
2342
2342
  const bridge = new AcpBridge(bridgeOpts);
2343
2343
  setBridge(bridge);
2344
2344
  attachDisconnectHandler(bridge);
@@ -3206,12 +3206,12 @@ async function abortableStartup(value, signal) {
3206
3206
  const promise = Promise.resolve(value);
3207
3207
  if (!signal) return await promise;
3208
3208
  if (signal.aborted) throw startupAbortError();
3209
- return await new Promise((resolve20, reject) => {
3209
+ return await new Promise((resolve23, reject) => {
3210
3210
  const onAbort = /* @__PURE__ */ __name(() => {
3211
3211
  reject(startupAbortError());
3212
3212
  }, "onAbort");
3213
3213
  signal.addEventListener("abort", onAbort, { once: true });
3214
- promise.then(resolve20, reject).finally(() => {
3214
+ promise.then(resolve23, reject).finally(() => {
3215
3215
  signal.removeEventListener("abort", onAbort);
3216
3216
  });
3217
3217
  });
@@ -3619,7 +3619,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
3619
3619
  if (!send) {
3620
3620
  return Promise.reject(new Error("Channel worker IPC is unavailable."));
3621
3621
  }
3622
- return new Promise((resolve20, reject) => {
3622
+ return new Promise((resolve23, reject) => {
3623
3623
  let settled = false;
3624
3624
  let unsubscribeMessage = /* @__PURE__ */ __name(() => {
3625
3625
  }, "unsubscribeMessage");
@@ -3633,7 +3633,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
3633
3633
  settled = true;
3634
3634
  cleanup();
3635
3635
  if (error) reject(error);
3636
- else resolve20();
3636
+ else resolve23();
3637
3637
  }, "finish");
3638
3638
  const onMessage = /* @__PURE__ */ __name((value) => {
3639
3639
  if (isChannelStartupReportAckMessage(value)) {
@@ -3881,8 +3881,8 @@ var daemonWorkerCommand = {
3881
3881
  let shuttingDown = false;
3882
3882
  let exitCode = 0;
3883
3883
  let finish;
3884
- const finished = new Promise((resolve20) => {
3885
- finish = resolve20;
3884
+ const finished = new Promise((resolve23) => {
3885
+ finish = resolve23;
3886
3886
  });
3887
3887
  const shutdown = /* @__PURE__ */ __name(async (reason) => {
3888
3888
  if (shuttingDown) {
@@ -3910,8 +3910,8 @@ var daemonWorkerCommand = {
3910
3910
  ...activeChannelDeliveries.values(),
3911
3911
  ...activeWebhookTasks.values()
3912
3912
  ]),
3913
- new Promise((resolve20) => {
3914
- const timer = setTimeout(resolve20, WORKER_SHUTDOWN_DRAIN_MS);
3913
+ new Promise((resolve23) => {
3914
+ const timer = setTimeout(resolve23, WORKER_SHUTDOWN_DRAIN_MS);
3915
3915
  timer.unref();
3916
3916
  })
3917
3917
  ]);
@@ -6127,6 +6127,22 @@ A finding an A/B settled carries \`Source: [probe]\` like any other run-produced
6127
6127
 
6128
6128
  It writes the \`run:\` script **verbatim** as an executable and reports what the runner would have supplied: the effective \`env:\` with all three levels merged and each key's level named, every \`\${{ \u2026 }}\` site listed **unevaluated** \u2014 that list is precisely what you have to stub, because the command refuses to invent values for it \u2014 the resolved \`shell\` and \`working-directory\`, and the commands the script invokes. Stubbing and input are yours: shim \`gh\`/\`curl\` onto \`PATH\`, export the env, run it, observe. **Combined with \`base-tree\`, a workflow A/B is two invocations** \u2014 extract the same step from both trees, feed both the same input, diff what each would have done. That is how the strongest workflow finding in this pipeline's history was produced: the real composer step from both arms, a stubbed \`gh\`, and a byte-for-byte comparison against a comment the workflow had actually posted. Three limits worth knowing before you spend the step: a \`uses:\` step has no \`run:\` and is refused rather than simulated; a step NAME that two steps in the job share is refused as ambiguous rather than resolved to the first, so pass the index (which is what an A/B wants anyway \u2014 the two trees must select the same step, and a name that moved between them is exactly how they stop doing that); and the \`invokes\` list is a labelled heuristic \u2014 the verbatim script beside it is the authority.
6129
6129
 
6130
+ **When the claim is about what the product DOES at runtime, drive it \u2014 two commands make that mechanical.** A finding about behaviour ("this hangs when the provider 429s", "the retry never fires", "the daemon answers before it is ready") is settled by running the built product and watching, and the two halves that used to be hand-written every time are now commands.
6131
+
6132
+ \`\`\`bash
6133
+ "\${QWEN_CODE_CLI:-qwen}" review mock-provider --responder <a module you write> \\
6134
+ --log <plan dir>/mock.jsonl --ttl 600 --out <plan dir>/mock.json &
6135
+ until [ -s <plan dir>/mock.json ]; do sleep 0.1; done # its port is in that report
6136
+ "\${QWEN_CODE_CLI:-qwen}" review drive --cwd <the worktree> --script <what to run> \\
6137
+ --ready <a command polled until it exits 0> --timeout 300 --out <plan dir>/drive.json
6138
+ \`\`\`
6139
+
6140
+ \`mock-provider\` serves \`/v1/chat/completions\` (OpenAI) and \`/v1/messages\` (Anthropic) on an OS-assigned port it reports back, and appends every request to a JSONL log; your responder module exports \`respond(req)\` returning \`{text}\`, \`{tool, args}\` or \`{status, body}\`, and never has to get SSE framing right. **It serves for the whole \`--ttl\` and returns only when that expires** \u2014 so background it and wait, as above; run sequentially it is already shut down by the time the next line starts. Its report is written once the port is bound, which is what makes the file's appearance a readiness signal rather than a guess, and the TTL is the only thing that ends it \u2014 set it to bound the drive, not to match it. **The log is the A/B evidence** \u2014 drive the same script against the PR worktree and the \`base-tree\` path, then diff the two request sequences; a difference is evidence, a reading is not.
6141
+
6142
+ \`drive\` owns the three things that used to be guesswork, and its \`outcome\` is what you rule on, never the captured text alone: \`completed\` carries the script's own \`exitCode\` and is the only value that licenses a behavioural claim; \`not-ready\` means the readiness probe never passed, so **nothing was driven and nothing observed is evidence either way**; \`timed-out\` and \`overflowed\` mean the capture is PARTIAL \u2014 a partial capture is not evidence that the run produced nothing; \`unavailable\` (no tmux) is an environment gap and explicitly not a finding. Pass \`--ready\` for anything that binds a port: without it the drive starts immediately, and an empty capture reads as "the feature does not work" when it means "the daemon had not finished starting".
6143
+
6144
+ For anything that is not one of those two wires \u2014 the project's own HTTP service, an MCP server, an OAuth endpoint \u2014 stand it up yourself and let \`drive\` own the lifecycle.
6145
+
6130
6146
  **When the claim is about GITHUB's behaviour, neither tree can settle it \u2014 only GitHub can.** A claim like "this encoding renders identically and can never ping", "GitHub strips this tag", "this markdown shape closes the fold" is about the comment pipeline's parser, sanitizer allowlist and notification path, none of which exist in this environment \u2014 a local markdown library is a model of GitHub, and judging a sanitizer claim against a model of the authority is exactly the parser-divergence failure under review. Measured live: an \`@\` \u2192 \`&#64;\` defusal read as sound in every local trace, and GitHub's real renderer registered the mention and fired the notification. So:
6131
6147
 
6132
6148
  - **If the environment variable \`QWEN_REVIEW_SCRATCH_REPO\` is set** (an \`owner/repo\` the user designated for disposable test posts), you may adjudicate on the real renderer: post the payload as an issue comment there \u2014 \`gh api repos/$QWEN_REVIEW_SCRATCH_REPO/issues/<n>/comments -f body=@<file>\` against an issue you created there for this purpose \u2014 read it back with \`-H "Accept: application/vnd.github.html+json"\`, and rule on the returned HTML (and, for mention claims, the timeline events). The observation is the verdict; quote it. This is the ONLY write destination other than \`submit\`'s that any part of this review may touch, it is user-designated, and nothing about the PR under review, its code, or its authors may appear in what you post there \u2014 post the minimal payload shape, not the report.
@@ -6142,6 +6158,13 @@ Return, for each finding, one verdict:
6142
6158
 
6143
6159
  **For anything non-Critical, when uncertain, downgrade to low confidence rather than rejecting.** Reserve outright rejection for a finding that clearly does not match the code (it describes behaviour the code does not have) or matches an Exclusion Criterion. Low confidence is for "likely real, needs human judgement", not for "I have no idea" \u2014 a vague suspicion with no concrete evidence in the code can still be rejected.
6144
6160
 
6161
+ **Your job is to falsify, not to fail-to-verify \u2014 and the two feel identical from inside a trace that went nowhere.** Rejection is a claim that you hold **direct counter-evidence**: the quoted contradicting code, the observed probe output, the matched Exclusion Criterion (the one exception stays as stated above: a finding that names nothing checkable at all is rejectable for that reason). Two states reliably masquerade as grounds to reject, and neither is (the split is measured practice from a reflection filter that ran over production reviews at millions-of-comments scale, whose single operating rule was this asymmetry):
6162
+
6163
+ - **"I could not verify it."** A trace that fails to confirm a claim is information about your trace, not about the claim \u2014 the trigger may need state you did not construct, a platform you are not on, a timing window your read-through cannot open. If the trace neither confirms nor contradicts, the verdict is \`confirmed (low confidence)\` with what-would-settle-it named. "Could not reproduce the reasoning" is already listed above as insufficient to reject a Critical; it is equally insufficient for a Suggestion whose evidence is merely out of your reach.
6164
+ - **"Its evidence is somewhere I did not look."** A finding may rest on evidence the finder gathered and you have not \u2014 a caller grepped in a file the diff never touches, issue evidence fetched from GitHub, a behaviour observed in a run. The evidence being absent from *your* view is not evidence of absence: you have the same tools the finder had, so **go read the claimed source first** \u2014 the named file, the quoted issue text in the launch message, the cited output. Reject only if what you find there contradicts the claim. If the source is genuinely unreachable from this environment (a lightweight diff-only review, an external service), the floor is the low-confidence downgrade, never rejection.
6165
+
6166
+ The asymmetry cuts both ways: confirming also requires the trace, and a finding that merely *sounds* right confirms nothing. What it forbids is only the shortcut in the rejecting direction, because that direction is the irreversible one.
6167
+
6145
6168
  **Do not reject an issue-fidelity / root-cause-ownership finding merely because the code compiles, runs, or has a passing test.** A working sanitizer with a green "malformed-shape" test does not disprove an issue-grounded claim that the root cause belongs upstream. Verify such a finding against the issue evidence quoted in the message that launched you; if that evidence is absent or genuinely inconclusive, downgrade rather than reject.`
6146
6169
  },
6147
6170
  "reverse-audit": {
@@ -6245,8 +6268,8 @@ function publicRoleLabelZh(req) {
6245
6268
  return req.file ? `${base}\uFF08${req.file}\uFF09` : base;
6246
6269
  }
6247
6270
  __name(publicRoleLabelZh, "publicRoleLabelZh");
6248
- function label(rec, chunk) {
6249
- if (chunk !== null) return `chunk ${chunk}`;
6271
+ function label(rec, chunk2) {
6272
+ if (chunk2 !== null) return `chunk ${chunk2}`;
6250
6273
  const first = rec.launchPrompt.split("\n")[0]?.trim() ?? "";
6251
6274
  if (first) return first.length > 60 ? `${first.slice(0, 57)}...` : first;
6252
6275
  return rec.agentName || rec.agentId;
@@ -6301,26 +6324,26 @@ function coverageFromTranscripts(planPath, env = process.env) {
6301
6324
  }
6302
6325
  return false;
6303
6326
  }, "keySatisfied");
6304
- const superseded = /* @__PURE__ */ __name((rec, chunk) => chunk !== null ? chunkSatisfied(chunk, rec) : keySatisfied(rec), "superseded");
6327
+ const superseded = /* @__PURE__ */ __name((rec, chunk2) => chunk2 !== null ? chunkSatisfied(chunk2, rec) : keySatisfied(rec), "superseded");
6305
6328
  for (const rec of records) {
6306
- const chunk = assignedChunk(rec);
6307
- const name = label(rec, chunk);
6329
+ const chunk2 = assignedChunk(rec);
6330
+ const name = label(rec, chunk2);
6308
6331
  const given = wasGivenTheDiff(rec, plan.diffPathAbsolute);
6309
- if (chunk !== null && !given) {
6310
- if (!superseded(rec, chunk)) blindAgents.push(name);
6332
+ if (chunk2 !== null && !given) {
6333
+ if (!superseded(rec, chunk2)) blindAgents.push(name);
6311
6334
  continue;
6312
6335
  }
6313
6336
  if (rec.successfulToolCalls === 0) {
6314
- if (!superseded(rec, chunk)) idleAgents.push(name);
6337
+ if (!superseded(rec, chunk2)) idleAgents.push(name);
6315
6338
  continue;
6316
6339
  }
6317
6340
  if (!given) continue;
6318
6341
  let rewrittenThisRecord = false;
6319
- if (chunk !== null) {
6320
- const b = builtOf(`chunk-${chunk}`);
6342
+ if (chunk2 !== null) {
6343
+ const b = builtOf(`chunk-${chunk2}`);
6321
6344
  if (b === void 0) {
6322
6345
  rewrittenThisRecord = true;
6323
- if (!nothingBuiltAtAll && !superseded(rec, chunk)) {
6346
+ if (!nothingBuiltAtAll && !superseded(rec, chunk2)) {
6324
6347
  rewrittenPrompts.push(
6325
6348
  disclose(
6326
6349
  name,
@@ -6333,7 +6356,7 @@ function coverageFromTranscripts(planPath, env = process.env) {
6333
6356
  }
6334
6357
  } else if (!wasDeliveredVerbatim(rec.launchPrompt, b)) {
6335
6358
  rewrittenThisRecord = true;
6336
- if (!superseded(rec, chunk)) {
6359
+ if (!superseded(rec, chunk2)) {
6337
6360
  rewrittenPrompts.push(
6338
6361
  disclose(
6339
6362
  name,
@@ -6346,7 +6369,7 @@ function coverageFromTranscripts(planPath, env = process.env) {
6346
6369
  }
6347
6370
  const told = pointedAt(rec.launchPrompt, plan);
6348
6371
  if (told.length > 0 && rec.diffToolCalls === 0) {
6349
- if (!rewrittenThisRecord && !superseded(rec, chunk)) {
6372
+ if (!rewrittenThisRecord && !superseded(rec, chunk2)) {
6350
6373
  unopenedAgents.push(name);
6351
6374
  }
6352
6375
  continue;
@@ -6354,8 +6377,8 @@ function coverageFromTranscripts(planPath, env = process.env) {
6354
6377
  const ranges = merge([...told, ...rec.diffReads]);
6355
6378
  if (ranges.length === 0) continue;
6356
6379
  const u = UNCOVERABLE_RE.exec(rec.finalText);
6357
- if (u && chunk !== null && Number(u[1]) === chunk) {
6358
- uncoverable.add(chunk);
6380
+ if (u && chunk2 !== null && Number(u[1]) === chunk2) {
6381
+ uncoverable.add(chunk2);
6359
6382
  continue;
6360
6383
  }
6361
6384
  for (const c of plan.chunks) {
@@ -6751,6 +6774,10 @@ function gh(...args) {
6751
6774
  return execGhWithRetry(args, {});
6752
6775
  }
6753
6776
  __name(gh, "gh");
6777
+ function ghWithInputRetried(input, ...args) {
6778
+ return execGhWithRetry(args, { input });
6779
+ }
6780
+ __name(ghWithInputRetried, "ghWithInputRetried");
6754
6781
  function ghWithInput(input, ...args) {
6755
6782
  return execFileSync2("gh", args, {
6756
6783
  encoding: "utf8",
@@ -7821,7 +7848,7 @@ __name(verdictLine, "verdictLine");
7821
7848
 
7822
7849
  // packages/cli/src/commands/review/findings.ts
7823
7850
  init_esbuild_shims();
7824
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "node:fs";
7851
+ import { existsSync as existsSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "node:fs";
7825
7852
  import { dirname as dirname5, resolve as resolve5 } from "node:path";
7826
7853
  var SEVERITIES = ["Critical", "Suggestion", "Nice to have"];
7827
7854
  var CONFIDENCES = ["high", "low"];
@@ -7845,6 +7872,18 @@ function asString(o, key) {
7845
7872
  return typeof v === "string" && v.trim() !== "" ? v : void 0;
7846
7873
  }
7847
7874
  __name(asString, "asString");
7875
+ function stringArray(o, key, index) {
7876
+ const v = o[key];
7877
+ if (v == null) return void 0;
7878
+ if (!Array.isArray(v) || v.some((x) => typeof x !== "string" || x.trim() === "")) {
7879
+ fail(
7880
+ index,
7881
+ `"${key}" must be an array of non-empty strings, got ${JSON.stringify(v)}.`
7882
+ );
7883
+ }
7884
+ return v.length > 0 ? v : void 0;
7885
+ }
7886
+ __name(stringArray, "stringArray");
7848
7887
  function oneOf(value, allowed) {
7849
7888
  return typeof value === "string" && allowed.includes(value) ? value : void 0;
7850
7889
  }
@@ -7939,7 +7978,11 @@ function validateFindings(raw) {
7939
7978
  `has outcome ${JSON.stringify(o["outcome"])}; expected one of ${OUTCOMES.map((s) => JSON.stringify(s)).join(", ")}.`
7940
7979
  );
7941
7980
  }
7981
+ const assetFiles = stringArray(o, "assetFiles", i) ?? stringArray(o, "asset_files", i);
7982
+ const assets = stringArray(o, "assets", i);
7942
7983
  const outcomeNote = asString(o, "outcomeNote") ?? asString(o, "outcome_note");
7984
+ const heldRaw = o.heldByMeasurement;
7985
+ const heldFile = heldRaw && typeof heldRaw === "object" ? asString(heldRaw, "file") : void 0;
7943
7986
  const shortSummary = asString(o, "shortSummary") ?? asString(o, "short_summary");
7944
7987
  return {
7945
7988
  id,
@@ -7954,6 +7997,9 @@ function validateFindings(raw) {
7954
7997
  } : {},
7955
7998
  ...asString(o, "category") ? { category: asString(o, "category") } : {},
7956
7999
  locations: parseLocations(o, i),
8000
+ ...assetFiles ? { assetFiles } : {},
8001
+ ...assets ? { assets } : {},
8002
+ ...heldFile ? { heldByMeasurement: { file: heldFile } } : {},
7957
8003
  ...outcome ? { outcome } : {},
7958
8004
  ...outcome && outcomeNote ? { outcomeNote } : {}
7959
8005
  };
@@ -7970,6 +8016,89 @@ function validateFindings(raw) {
7970
8016
  return findings;
7971
8017
  }
7972
8018
  __name(validateFindings, "validateFindings");
8019
+ function namesPath(text, probe) {
8020
+ const isNameChar = /* @__PURE__ */ __name((c) => c !== void 0 && /[A-Za-z0-9._-]/.test(c), "isNameChar");
8021
+ const isBoundary = /* @__PURE__ */ __name((c) => c === void 0 || !isNameChar(c) && c !== "/", "isBoundary");
8022
+ let from = 0;
8023
+ for (; ; ) {
8024
+ const at = text.indexOf(probe, from);
8025
+ if (at < 0) return false;
8026
+ const after = text[at + probe.length];
8027
+ const extendsName = after !== void 0 && (/[A-Za-z0-9_-]/.test(after) || after === "." && /[A-Za-z0-9]/.test(text[at + probe.length + 1] ?? ""));
8028
+ if (isBoundary(at === 0 ? void 0 : text[at - 1]) && !extendsName) {
8029
+ return true;
8030
+ }
8031
+ from = at + 1;
8032
+ }
8033
+ }
8034
+ __name(namesPath, "namesPath");
8035
+ function holdCriticalsFailingOnBase(findings, sharedFailingFiles) {
8036
+ const held = [];
8037
+ const readjudicated = [];
8038
+ const out = findings.map((f) => {
8039
+ if (f.severity !== "Critical") return f;
8040
+ const haystack = [f.summary, f.failureScenario].join("\n");
8041
+ const hit = sharedFailingFiles.find((p) => p && namesPath(haystack, p));
8042
+ if (!hit) return f;
8043
+ if (f.heldByMeasurement?.file === hit) {
8044
+ readjudicated.push({ id: f.id, file: hit });
8045
+ return f;
8046
+ }
8047
+ held.push({ id: f.id, file: hit });
8048
+ return {
8049
+ ...f,
8050
+ severity: "Suggestion",
8051
+ heldByMeasurement: { file: hit },
8052
+ failureScenario: `${f.failureScenario}
8053
+
8054
+ Held back from Critical by measurement: \`test-delta\` reran the failing test command on the merge base and ${hit} failed there too, so this is not a passing test the PR turns red. If the PR makes an already-red test fail for a NEW reason, say which test, quote both sides, and file it at Critical again: a finding that already carries this measurement and is raised anyway is left where you put it.`
8055
+ };
8056
+ });
8057
+ return { findings: out, held, readjudicated };
8058
+ }
8059
+ __name(holdCriticalsFailingOnBase, "holdCriticalsFailingOnBase");
8060
+ var WORKSPACE_IN_COMMAND_RE = /--workspace="([^"]+)"/;
8061
+ function repoRelative(path3, workspace) {
8062
+ const at = path3.indexOf("::");
8063
+ const bare = at < 0 ? path3 : path3.slice(at + 2);
8064
+ if (at >= 0 && !workspace) return void 0;
8065
+ if (!workspace || bare.startsWith(`${workspace}/`)) return bare;
8066
+ return `${workspace}/${bare}`;
8067
+ }
8068
+ __name(repoRelative, "repoRelative");
8069
+ function sharedFailingFilesOf(raw) {
8070
+ if (!raw || typeof raw !== "object")
8071
+ return { shared: [], unidentifiable: [] };
8072
+ const shared = /* @__PURE__ */ new Set();
8073
+ const netNew = /* @__PURE__ */ new Set();
8074
+ const unidentifiable = /* @__PURE__ */ new Set();
8075
+ const take = /* @__PURE__ */ __name((into, v, workspace) => {
8076
+ if (!Array.isArray(v)) return;
8077
+ for (const e of v) {
8078
+ if (typeof e !== "string" || !e) continue;
8079
+ const qualified = repoRelative(e, workspace);
8080
+ if (qualified === void 0) {
8081
+ if (into === shared) unidentifiable.add(e);
8082
+ } else into.add(qualified);
8083
+ }
8084
+ }, "take");
8085
+ const entries = raw.entries;
8086
+ const rows = Array.isArray(entries) ? entries : [];
8087
+ if (rows.length > 0) {
8088
+ for (const e of rows) {
8089
+ if (!e || typeof e !== "object") continue;
8090
+ const command = e.command;
8091
+ const workspace = typeof command === "string" ? WORKSPACE_IN_COMMAND_RE.exec(command)?.[1] ?? void 0 : void 0;
8092
+ take(shared, e.shared, workspace);
8093
+ take(netNew, e.netNew, workspace);
8094
+ }
8095
+ }
8096
+ return {
8097
+ shared: [...shared].filter((f) => !netNew.has(f)),
8098
+ unidentifiable: [...unidentifiable]
8099
+ };
8100
+ }
8101
+ __name(sharedFailingFilesOf, "sharedFailingFilesOf");
7973
8102
  function sortFindings(findings) {
7974
8103
  return [...findings].sort((a, b) => {
7975
8104
  const bySeverity = SEVERITIES.indexOf(a.severity) - SEVERITIES.indexOf(b.severity);
@@ -8072,6 +8201,7 @@ function buildReport(findings) {
8072
8201
  total: sorted.length,
8073
8202
  bySeverity,
8074
8203
  byConfidence,
8204
+ held: sorted.filter((f) => f.heldByMeasurement !== void 0).length,
8075
8205
  ...byOutcome ? { byOutcome } : {}
8076
8206
  },
8077
8207
  outcomesRecorded
@@ -8121,12 +8251,15 @@ var findingsCommand = {
8121
8251
  }).option("outcomes", {
8122
8252
  type: "string",
8123
8253
  describe: "JSON array of {id, outcome, note?} recording what --fix did to each finding. Must cover every finding."
8254
+ }).option("test-delta", {
8255
+ type: "string",
8256
+ describe: "The test-delta artifact. A Critical naming a test file that also failed on the merge base is held back to Suggestion, carrying the measurement that demoted it."
8124
8257
  }).option("print", {
8125
8258
  type: "boolean",
8126
8259
  describe: "Also print one line per finding to stdout"
8127
8260
  }), "builder"),
8128
8261
  handler: /* @__PURE__ */ __name((argv) => {
8129
- const { input, out, outcomes, print } = argv;
8262
+ const { input, out, outcomes, print, testDelta } = argv;
8130
8263
  let findings = validateFindings(readJson(input, "findings"));
8131
8264
  if (outcomes !== void 0) {
8132
8265
  findings = applyOutcomes(
@@ -8134,6 +8267,30 @@ var findingsCommand = {
8134
8267
  validateOutcomes(readJson(outcomes, "outcomes"))
8135
8268
  );
8136
8269
  }
8270
+ let held = [];
8271
+ let readjudicated = [];
8272
+ if (testDelta !== void 0) {
8273
+ let measurement;
8274
+ if (existsSync3(resolve5(testDelta))) {
8275
+ try {
8276
+ measurement = readJson(testDelta, "test-delta");
8277
+ } catch (err) {
8278
+ writeStderrLine(
8279
+ `findings: no holds applied \u2014 ${err instanceof Error ? err.message : String(err)}`
8280
+ );
8281
+ }
8282
+ }
8283
+ const { shared, unidentifiable } = sharedFailingFilesOf(measurement);
8284
+ for (const f of unidentifiable) {
8285
+ writeStderrLine(
8286
+ `findings: ignored the measured file ${f} \u2014 it carries a vitest project key and no workspace to resolve it against, so which project it names cannot be established`
8287
+ );
8288
+ }
8289
+ ({ findings, held, readjudicated } = holdCriticalsFailingOnBase(
8290
+ findings,
8291
+ shared
8292
+ ));
8293
+ }
8137
8294
  const report = buildReport(findings);
8138
8295
  const target = resolve5(out);
8139
8296
  mkdirSync6(dirname5(target), { recursive: true });
@@ -8143,6 +8300,16 @@ var findingsCommand = {
8143
8300
  writeStderrLine(
8144
8301
  `findings: ${report.counts.total} total \u2014 ${bySeverity["Critical"]} Critical, ${bySeverity["Suggestion"]} Suggestion, ${bySeverity["Nice to have"]} Nice to have; ${byConfidence["low"]} low-confidence. Wrote ${target}`
8145
8302
  );
8303
+ for (const h of held) {
8304
+ writeStderrLine(
8305
+ `findings: ${h.id} held back from Critical \u2014 test-delta measured ${h.file} as failing on the merge base too`
8306
+ );
8307
+ }
8308
+ for (const r of readjudicated) {
8309
+ writeStderrLine(
8310
+ `findings: ${r.id} left at Critical \u2014 it already carries the ${r.file} measurement and was raised again anyway`
8311
+ );
8312
+ }
8146
8313
  if (report.counts.byOutcome) {
8147
8314
  const o = report.counts.byOutcome;
8148
8315
  writeStderrLine(
@@ -8164,7 +8331,7 @@ import { dirname as dirname6, resolve as resolve6 } from "node:path";
8164
8331
  // packages/cli/src/commands/review/lib/git.ts
8165
8332
  init_esbuild_shims();
8166
8333
  import { execFileSync as execFileSync3 } from "node:child_process";
8167
- import { existsSync as existsSync3, rmSync as rmSync3 } from "node:fs";
8334
+ import { existsSync as existsSync4, rmSync as rmSync3 } from "node:fs";
8168
8335
  var GIT_TIMEOUT_MS2 = 12e4;
8169
8336
  function gitOpts() {
8170
8337
  return {
@@ -8210,7 +8377,7 @@ function worktreeReleaseResult(existed, stillThere, removeError) {
8210
8377
  }
8211
8378
  __name(worktreeReleaseResult, "worktreeReleaseResult");
8212
8379
  function releaseWorktree(worktreePath2) {
8213
- const existed = existsSync3(worktreePath2);
8380
+ const existed = existsSync4(worktreePath2);
8214
8381
  let removeError;
8215
8382
  if (existed) {
8216
8383
  gitOpt("worktree", "remove", worktreePath2, "--force");
@@ -8221,7 +8388,7 @@ function releaseWorktree(worktreePath2) {
8221
8388
  }
8222
8389
  }
8223
8390
  gitOpt("worktree", "prune");
8224
- return worktreeReleaseResult(existed, existsSync3(worktreePath2), removeError);
8391
+ return worktreeReleaseResult(existed, existsSync4(worktreePath2), removeError);
8225
8392
  }
8226
8393
  __name(releaseWorktree, "releaseWorktree");
8227
8394
  function gitRaw(...args) {
@@ -10087,8 +10254,8 @@ var loadRulesCommand = {
10087
10254
  // packages/cli/src/commands/review/presubmit.ts
10088
10255
  init_esbuild_shims();
10089
10256
  import { writeFileSync as writeFileSync13, readFileSync as readFileSync13 } from "node:fs";
10090
- function verdictStamp(run3) {
10091
- return run3.completed_at ?? run3.started_at ?? "";
10257
+ function verdictStamp(run4) {
10258
+ return run4.completed_at ?? run4.started_at ?? "";
10092
10259
  }
10093
10260
  __name(verdictStamp, "verdictStamp");
10094
10261
  var FAIL_CONCLUSIONS = /* @__PURE__ */ new Set([
@@ -10110,11 +10277,11 @@ var PENDING_STATES = /* @__PURE__ */ new Set([
10110
10277
  "requested"
10111
10278
  ]);
10112
10279
  var NOT_RUN_CONCLUSIONS = /* @__PURE__ */ new Set(["skipped", "neutral", "stale"]);
10113
- function isCurrentActionsRunCheck(run3) {
10280
+ function isCurrentActionsRunCheck(run4) {
10114
10281
  const runId = process.env["GITHUB_RUN_ID"];
10115
10282
  if (!runId) return false;
10116
10283
  const runUrlMarker = `/actions/runs/${runId}/`;
10117
- return [run3.details_url, run3.html_url].some(
10284
+ return [run4.details_url, run4.html_url].some(
10118
10285
  (url) => typeof url === "string" && url.includes(runUrlMarker)
10119
10286
  );
10120
10287
  }
@@ -10179,33 +10346,33 @@ function classifyCi(checkRuns, statuses) {
10179
10346
  const failedCheckNames = [];
10180
10347
  let hasPending = false;
10181
10348
  const relevantCheckRuns = checkRuns.filter(
10182
- (run3) => !isCurrentActionsRunCheck(run3)
10349
+ (run4) => !isCurrentActionsRunCheck(run4)
10183
10350
  );
10184
10351
  const executedNames = /* @__PURE__ */ new Set();
10185
10352
  const notRunNames = /* @__PURE__ */ new Set();
10186
- for (const run3 of relevantCheckRuns) {
10187
- if (run3.status !== "completed") continue;
10188
- if (!run3.conclusion || NOT_RUN_CONCLUSIONS.has(run3.conclusion)) {
10189
- notRunNames.add(run3.name);
10353
+ for (const run4 of relevantCheckRuns) {
10354
+ if (run4.status !== "completed") continue;
10355
+ if (!run4.conclusion || NOT_RUN_CONCLUSIONS.has(run4.conclusion)) {
10356
+ notRunNames.add(run4.name);
10190
10357
  } else {
10191
- executedNames.add(run3.name);
10358
+ executedNames.add(run4.name);
10192
10359
  }
10193
10360
  }
10194
10361
  const skippedCheckNames = [...notRunNames].filter((n) => !executedNames.has(n)).sort();
10195
10362
  const latestVerdicts = /* @__PURE__ */ new Map();
10196
- for (const run3 of relevantCheckRuns) {
10197
- if (run3.status !== "completed") {
10198
- if (PENDING_STATES.has(run3.status)) hasPending = true;
10363
+ for (const run4 of relevantCheckRuns) {
10364
+ if (run4.status !== "completed") {
10365
+ if (PENDING_STATES.has(run4.status)) hasPending = true;
10199
10366
  continue;
10200
10367
  }
10201
- if (!run3.conclusion || NOT_RUN_CONCLUSIONS.has(run3.conclusion)) continue;
10202
- const prev = latestVerdicts.get(run3.name);
10203
- if (!prev || verdictStamp(run3) > verdictStamp(prev)) {
10204
- latestVerdicts.set(run3.name, run3);
10368
+ if (!run4.conclusion || NOT_RUN_CONCLUSIONS.has(run4.conclusion)) continue;
10369
+ const prev = latestVerdicts.get(run4.name);
10370
+ if (!prev || verdictStamp(run4) > verdictStamp(prev)) {
10371
+ latestVerdicts.set(run4.name, run4);
10205
10372
  }
10206
10373
  }
10207
- for (const [name, run3] of latestVerdicts) {
10208
- if (FAIL_CONCLUSIONS.has(run3.conclusion)) {
10374
+ for (const [name, run4] of latestVerdicts) {
10375
+ if (FAIL_CONCLUSIONS.has(run4.conclusion)) {
10209
10376
  failedCheckNames.push(name);
10210
10377
  }
10211
10378
  }
@@ -10461,120 +10628,690 @@ var presubmitCommand = {
10461
10628
  }, "handler")
10462
10629
  };
10463
10630
 
10464
- // packages/cli/src/commands/review/resolve-anchors.ts
10631
+ // packages/cli/src/commands/review/publish-assets.ts
10465
10632
  init_esbuild_shims();
10466
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13 } from "node:fs";
10467
- import { dirname as dirname11, resolve as resolve10 } from "node:path";
10633
+ import { createHash as createHash2 } from "node:crypto";
10634
+ import { readFileSync as readFileSync15, statSync as statSync5, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14 } from "node:fs";
10635
+ import { basename as basename5, dirname as dirname11, resolve as resolve10 } from "node:path";
10468
10636
 
10469
- // packages/cli/src/commands/review/lib/anchors.ts
10637
+ // packages/cli/src/commands/review/lib/authorization.ts
10470
10638
  init_esbuild_shims();
10471
- function collectNewSideLines(diffText, file) {
10472
- const lines2 = diffText.split("\n");
10473
- const out = [];
10474
- for (const hunk of file.hunks) {
10475
- if (hunk.newCount === 0) continue;
10476
- let newCursor = hunk.newStart;
10477
- for (let n = hunk.diffStart + 1; n <= hunk.diffEnd; n++) {
10478
- const raw = lines2[n - 1];
10479
- if (raw === void 0) break;
10480
- if (raw.startsWith("+")) {
10481
- out.push({ newLine: newCursor, text: raw.slice(1), added: true });
10482
- newCursor++;
10483
- } else if (raw.startsWith("-")) {
10484
- } else if (raw.startsWith(" ") || raw === "") {
10485
- out.push({
10486
- newLine: newCursor,
10487
- text: raw.startsWith(" ") ? raw.slice(1) : "",
10488
- added: false
10489
- });
10490
- newCursor++;
10491
- }
10492
- }
10493
- }
10494
- return out;
10639
+ import { readFileSync as readFileSync14 } from "node:fs";
10640
+
10641
+ // packages/cli/src/services/skill-args-file.ts
10642
+ init_esbuild_shims();
10643
+ import {
10644
+ mkdirSync as mkdirSync13,
10645
+ openSync as openSync2,
10646
+ writeSync,
10647
+ closeSync as closeSync2,
10648
+ rmSync as rmSync4,
10649
+ lstatSync as lstatSync3,
10650
+ existsSync as existsSync5,
10651
+ constants
10652
+ } from "node:fs";
10653
+ import { join as join11 } from "node:path";
10654
+ var debugLogger3 = createDebugLogger("SKILL_ARGS_FILE");
10655
+ var SKILL_ARGS_DIR = join11(".qwen", "tmp");
10656
+ function safe(s) {
10657
+ return s.replace(/[^A-Za-z0-9._-]/g, "_");
10495
10658
  }
10496
- __name(collectNewSideLines, "collectNewSideLines");
10497
- function normalizeExact(s) {
10498
- return s.replace(/\s+$/, "");
10659
+ __name(safe, "safe");
10660
+ function currentSessionId() {
10661
+ return sessionIdContext.getStore()?.trim() || process.env["QWEN_CODE_SESSION_ID"]?.trim() || "";
10499
10662
  }
10500
- __name(normalizeExact, "normalizeExact");
10501
- function normalizeLoose(s) {
10502
- return s.trim();
10663
+ __name(currentSessionId, "currentSessionId");
10664
+ function skillArgsDir(sessionId = currentSessionId()) {
10665
+ return sessionId ? join11(SKILL_ARGS_DIR, `s-${safe(sessionId)}`) : SKILL_ARGS_DIR;
10503
10666
  }
10504
- __name(normalizeLoose, "normalizeLoose");
10505
- function anchorVariants(anchor) {
10506
- const lines2 = anchor.replace(/\r\n/g, "\n").split("\n");
10507
- while (lines2.length > 0 && lines2[0].trim() === "") lines2.shift();
10508
- while (lines2.length > 0 && lines2[lines2.length - 1].trim() === "") lines2.pop();
10509
- if (lines2.length === 0) return [];
10510
- const variants = [lines2];
10511
- const meaningful = lines2.filter((l) => l.trim() !== "");
10512
- if (meaningful.length > 0 && meaningful.every((l) => /^\+/.test(l))) {
10513
- variants.push(lines2.map((l) => l.startsWith("+") ? l.slice(1) : l));
10514
- }
10515
- const NO_NEWLINE = /^\$/;
10516
- const marked = lines2.filter((l) => l !== "" && !NO_NEWLINE.test(l));
10517
- if (marked.length > 0 && marked.every((l) => /^[+\- ]/.test(l)) && marked.some((l) => l.startsWith("+"))) {
10518
- variants.push(
10519
- lines2.filter((l) => !l.startsWith("-") && !NO_NEWLINE.test(l)).map((l) => l.slice(1))
10520
- );
10521
- }
10522
- return variants;
10667
+ __name(skillArgsDir, "skillArgsDir");
10668
+ function skillArgsPath(skillName, sessionId = currentSessionId()) {
10669
+ return join11(
10670
+ skillArgsDir(sessionId),
10671
+ `qwen-skill-args-${safe(skillName)}.txt`
10672
+ );
10523
10673
  }
10524
- __name(anchorVariants, "anchorVariants");
10525
- function matchRuns(hay, needle, norm) {
10526
- const starts = [];
10527
- if (needle.length === 0 || hay.length < needle.length) return starts;
10528
- const normNeedle = needle.map(norm);
10529
- for (let i = 0; i + normNeedle.length <= hay.length; i++) {
10530
- let ok = true;
10531
- for (let j = 0; j < normNeedle.length; j++) {
10532
- const cell = hay[i + j];
10533
- if (norm(cell.text) !== normNeedle[j]) {
10534
- ok = false;
10535
- break;
10536
- }
10537
- if (j > 0 && cell.newLine !== hay[i + j - 1].newLine + 1) {
10538
- ok = false;
10539
- break;
10674
+ __name(skillArgsPath, "skillArgsPath");
10675
+ function writeSkillArgs(skillName, args) {
10676
+ const path3 = skillArgsPath(skillName);
10677
+ try {
10678
+ const dir = skillArgsDir();
10679
+ mkdirSync13(dir, { recursive: true });
10680
+ if (lstatSync3(dir).isSymbolicLink()) {
10681
+ debugLogger3.warn(
10682
+ `Skill args directory ${dir} is a symlink; refusing to write through it.`
10683
+ );
10684
+ return null;
10685
+ }
10686
+ if (lstatSync3(path3, { throwIfNoEntry: false })?.isSymbolicLink()) {
10687
+ debugLogger3.warn(
10688
+ `Skill args file ${path3} is a symlink; refusing to write through it.`
10689
+ );
10690
+ return null;
10691
+ }
10692
+ const fd = openSync2(
10693
+ path3,
10694
+ constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW,
10695
+ 384
10696
+ );
10697
+ try {
10698
+ const buf = Buffer.from(args, "utf8");
10699
+ let off = 0;
10700
+ while (off < buf.length) {
10701
+ off += writeSync(fd, buf, off, buf.length - off);
10540
10702
  }
10703
+ } finally {
10704
+ closeSync2(fd);
10541
10705
  }
10542
- if (ok) starts.push(i);
10706
+ return path3;
10707
+ } catch (err) {
10708
+ debugLogger3.warn(
10709
+ `Could not write skill args to ${path3}: ${err.message}`
10710
+ );
10711
+ return null;
10543
10712
  }
10544
- return starts;
10545
10713
  }
10546
- __name(matchRuns, "matchRuns");
10547
- function candidatesFor(hay, needle, norm) {
10548
- return matchRuns(hay, needle, norm).map((i) => {
10549
- const run3 = hay.slice(i, i + needle.length);
10714
+ __name(writeSkillArgs, "writeSkillArgs");
10715
+ function clearSkillArgs(skillName) {
10716
+ const path3 = skillArgsPath(skillName);
10717
+ try {
10718
+ rmSync4(path3, { force: true });
10719
+ } catch (err) {
10720
+ debugLogger3.warn(
10721
+ `Could not clear skill args for ${skillName}: ${err.message}`
10722
+ );
10723
+ return false;
10724
+ }
10725
+ if (existsSync5(path3)) {
10726
+ debugLogger3.warn(`Skill args for ${skillName} survived removal at ${path3}`);
10727
+ return false;
10728
+ }
10729
+ return true;
10730
+ }
10731
+ __name(clearSkillArgs, "clearSkillArgs");
10732
+ function staleArgsWarning() {
10733
+ return `
10734
+
10735
+ <skill-args-stale>A previous invocation's argument record could not be removed, so it is still on disk and still names whatever it named. This invocation supplied NO arguments. Do not treat that stale record as this run's authorisation: this run has none, and must not post.</skill-args-stale>
10736
+ `;
10737
+ }
10738
+ __name(staleArgsWarning, "staleArgsWarning");
10739
+ function skillArgsNote(path3, args) {
10740
+ return `
10741
+
10742
+ Your invocation arguments have been written verbatim to a session-private file. Its exact path is below \u2014 use it wherever these instructions say to read the args file (e.g. \`< '${path3}'\`), and do not retype the arguments, which is how they get mistyped.
10743
+ <skill-args-file>${path3}</skill-args-file>
10744
+ <skill-args>${args}</skill-args>
10745
+ `;
10746
+ }
10747
+ __name(skillArgsNote, "skillArgsNote");
10748
+
10749
+ // packages/cli/src/commands/review/lib/authorization.ts
10750
+ function defaultSkillArgsPath() {
10751
+ return skillArgsPath("review");
10752
+ }
10753
+ __name(defaultSkillArgsPath, "defaultSkillArgsPath");
10754
+ function reviewWriteAuthorization(req) {
10755
+ if (req.userAuthorized) {
10756
+ return { ok: true, why: "the user asked for this review to be published" };
10757
+ }
10758
+ const sessionScoped = defaultSkillArgsPath();
10759
+ const path3 = currentSessionId() === "" && req.skillArgs ? req.skillArgs : sessionScoped;
10760
+ let raw;
10761
+ try {
10762
+ raw = readFileSync14(path3, "utf8");
10763
+ } catch {
10550
10764
  return {
10551
- startLine: run3[0].newLine,
10552
- line: run3[run3.length - 1].newLine,
10553
- added: run3.some((l) => l.added)
10765
+ ok: false,
10766
+ why: `no review arguments were recorded at ${path3}, so this run cannot show that \`--comment\` was requested`
10554
10767
  };
10555
- });
10556
- }
10557
- __name(candidatesFor, "candidatesFor");
10558
- function pick(cands, claimedLine) {
10559
- if (cands.length === 1) return cands[0];
10560
- if (claimedLine !== void 0) {
10561
- const dist = /* @__PURE__ */ __name((c) => Math.abs(c.startLine - claimedLine), "dist");
10562
- let best = Infinity;
10563
- for (const c of cands) best = Math.min(best, dist(c));
10564
- const nearest = cands.filter((c) => dist(c) === best);
10565
- return nearest.length === 1 ? nearest[0] : null;
10566
10768
  }
10567
- const added = cands.filter((c) => c.added);
10568
- return added.length === 1 ? added[0] : null;
10569
- }
10570
- __name(pick, "pick");
10571
- function resolveAnchor(newSideLines, anchor, claimedLine) {
10572
- const variants = anchorVariants(anchor);
10573
- if (variants.length === 0) {
10574
- return { status: "unmatched", reason: "anchor is empty" };
10769
+ const verdict = parseReviewArgs(raw);
10770
+ if (!verdict.comment.effective) {
10771
+ return {
10772
+ ok: false,
10773
+ why: `\`--comment\` was not in the review arguments (${JSON.stringify(raw.trim())})`
10774
+ };
10575
10775
  }
10576
- for (const [vi, needle] of variants.entries()) {
10577
- const norms = vi === 0 ? [
10776
+ const t2 = verdict.target;
10777
+ const authorisedPr = t2.type === "pr-number" || t2.type === "pr-url" ? t2.number : void 0;
10778
+ if (authorisedPr === void 0) {
10779
+ return {
10780
+ ok: false,
10781
+ why: `the review arguments (${JSON.stringify(raw.trim())}) do not name a pull request, so they cannot authorise posting to one`
10782
+ };
10783
+ }
10784
+ if (authorisedPr !== req.pr) {
10785
+ return {
10786
+ ok: false,
10787
+ why: `the review arguments authorise pull request #${authorisedPr}, but this submission targets #${req.pr}`
10788
+ };
10789
+ }
10790
+ if (t2.type === "pr-url") {
10791
+ if (req.repo !== void 0) {
10792
+ const authorisedRepo = `${t2.owner}/${t2.repo}`;
10793
+ if (authorisedRepo.toLowerCase() !== req.repo.toLowerCase()) {
10794
+ return {
10795
+ ok: false,
10796
+ why: `the review arguments authorise ${authorisedRepo}, but this submission targets ${req.repo}`
10797
+ };
10798
+ }
10799
+ }
10800
+ const writeHost = (req.host ?? "github.com").toLowerCase();
10801
+ if (t2.host.toLowerCase() !== writeHost) {
10802
+ return {
10803
+ ok: false,
10804
+ why: `the review arguments authorise ${t2.host}, but this submission targets ${req.host ?? "github.com"}`
10805
+ };
10806
+ }
10807
+ }
10808
+ return {
10809
+ ok: true,
10810
+ why: `\`--comment\` was in the review arguments for #${authorisedPr}`
10811
+ };
10812
+ }
10813
+ __name(reviewWriteAuthorization, "reviewWriteAuthorization");
10814
+
10815
+ // packages/cli/src/commands/review/lib/assets.ts
10816
+ init_esbuild_shims();
10817
+ var ASSET_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp"]);
10818
+ var MAX_ASSET_BYTES = 10 * 1024 * 1024;
10819
+ var MAX_TOTAL_ASSET_BYTES = 40 * 1024 * 1024;
10820
+ function assetsBranch(pr) {
10821
+ return `pr-assets/${pr}-review`;
10822
+ }
10823
+ __name(assetsBranch, "assetsBranch");
10824
+ function remoteAssetPath(pr, basename6, sha256Hex) {
10825
+ const safe2 = basename6.replace(/[^A-Za-z0-9._-]/g, "_");
10826
+ return `${pr}-review/${sha256Hex.slice(0, 12)}-${safe2}`;
10827
+ }
10828
+ __name(remoteAssetPath, "remoteAssetPath");
10829
+ function rawAssetUrl(opts) {
10830
+ const host = opts.host && opts.host.trim() !== "" ? opts.host : "github.com";
10831
+ return `https://${host}/${opts.repo}/raw/${opts.commitSha}/${opts.remotePath}`;
10832
+ }
10833
+ __name(rawAssetUrl, "rawAssetUrl");
10834
+ function parseAssetsRepo(value) {
10835
+ const v = (value ?? "").trim();
10836
+ if (v === "") {
10837
+ return {
10838
+ error: "QWEN_REVIEW_ASSETS_REPO is not set. Designate an assets repository (owner/repo you can push to \u2014 the repo under review for maintainers, a fork or scratch repo otherwise) to publish review evidence images."
10839
+ };
10840
+ }
10841
+ const parts = v.split("/");
10842
+ const segmentOk = /* @__PURE__ */ __name((p) => /^[A-Za-z0-9_.-]+$/.test(p) && p !== "." && p !== "..", "segmentOk");
10843
+ if (parts.length !== 2 || !parts.every(segmentOk)) {
10844
+ return {
10845
+ error: `QWEN_REVIEW_ASSETS_REPO must be owner/repo, got ${JSON.stringify(v)}.`
10846
+ };
10847
+ }
10848
+ return { repo: v };
10849
+ }
10850
+ __name(parseAssetsRepo, "parseAssetsRepo");
10851
+ function validateAssetFile(basename6, bytes) {
10852
+ const ext = basename6.includes(".") ? basename6.slice(basename6.lastIndexOf(".") + 1).toLowerCase() : "";
10853
+ if (!ASSET_EXTENSIONS.has(ext)) {
10854
+ return {
10855
+ ok: false,
10856
+ reason: `${basename6}: extension ${JSON.stringify(ext)} is not an allowed evidence image type (${[...ASSET_EXTENSIONS].join(", ")})`
10857
+ };
10858
+ }
10859
+ if (bytes <= 0) {
10860
+ return { ok: false, reason: `${basename6}: empty file` };
10861
+ }
10862
+ if (bytes > MAX_ASSET_BYTES) {
10863
+ return {
10864
+ ok: false,
10865
+ reason: `${basename6}: ${bytes} bytes exceeds the per-file cap of ${MAX_ASSET_BYTES} bytes`
10866
+ };
10867
+ }
10868
+ return { ok: true };
10869
+ }
10870
+ __name(validateAssetFile, "validateAssetFile");
10871
+ function validateAssetBatch(files) {
10872
+ let total = 0;
10873
+ for (const f of files) {
10874
+ const one = validateAssetFile(f.basename, f.bytes);
10875
+ if (!one.ok) return one;
10876
+ total += f.bytes;
10877
+ if (total > MAX_TOTAL_ASSET_BYTES) {
10878
+ return {
10879
+ ok: false,
10880
+ reason: `total size exceeds ${MAX_TOTAL_ASSET_BYTES} bytes`
10881
+ };
10882
+ }
10883
+ }
10884
+ return { ok: true };
10885
+ }
10886
+ __name(validateAssetBatch, "validateAssetBatch");
10887
+
10888
+ // packages/cli/src/commands/review/publish-assets.ts
10889
+ function putContent(repo, branch, remotePath, contentBase64, pr) {
10890
+ const api = `repos/${repo}/contents/${remotePath}`;
10891
+ const message = `review evidence for PR #${pr}`;
10892
+ const payload = /* @__PURE__ */ __name((sha) => JSON.stringify({
10893
+ message,
10894
+ content: contentBase64,
10895
+ branch,
10896
+ ...sha ? { sha } : {}
10897
+ }), "payload");
10898
+ try {
10899
+ ghWithInputRetried(payload(), "api", "-X", "PUT", api, "--input", "-");
10900
+ } catch (err) {
10901
+ const msg = err instanceof Error ? err.message : String(err);
10902
+ if (!/HTTP 422|"sha"|sha wasn't supplied|already exists/i.test(msg)) {
10903
+ throw err;
10904
+ }
10905
+ let existing;
10906
+ try {
10907
+ existing = gh(
10908
+ "api",
10909
+ `${api}?ref=${encodeURIComponent(branch)}`,
10910
+ "--jq",
10911
+ ".sha"
10912
+ ).trim();
10913
+ } catch {
10914
+ throw err;
10915
+ }
10916
+ if (existing === "" || existing === "null") {
10917
+ throw err;
10918
+ }
10919
+ ghWithInputRetried(
10920
+ payload(existing),
10921
+ "api",
10922
+ "-X",
10923
+ "PUT",
10924
+ api,
10925
+ "--input",
10926
+ "-"
10927
+ );
10928
+ }
10929
+ }
10930
+ __name(putContent, "putContent");
10931
+ function ensureBranch(repo, branch) {
10932
+ try {
10933
+ gh("api", `repos/${repo}/git/ref/heads/${branch}`);
10934
+ return;
10935
+ } catch (err) {
10936
+ const msg = err instanceof Error ? err.message : String(err);
10937
+ if (!/HTTP 404|Not Found/i.test(msg)) throw err;
10938
+ }
10939
+ const defaultBranch = gh("api", `repos/${repo}`, "--jq", ".default_branch");
10940
+ let baseSha;
10941
+ try {
10942
+ baseSha = gh(
10943
+ "api",
10944
+ `repos/${repo}/git/ref/heads/${defaultBranch.trim()}`,
10945
+ "--jq",
10946
+ ".object.sha"
10947
+ );
10948
+ } catch (err) {
10949
+ const msg = err instanceof Error ? err.message : String(err);
10950
+ if (/HTTP 404|Not Found/i.test(msg)) {
10951
+ throw new Error(
10952
+ `assets repository ${repo} appears to be empty (its default branch ${defaultBranch.trim()} has no commits). Push an initial commit to it once; publish-assets creates and reuses its own branch from there.`
10953
+ );
10954
+ }
10955
+ throw err;
10956
+ }
10957
+ const base = baseSha.trim();
10958
+ if (base === "" || base === "null") {
10959
+ throw new Error(
10960
+ `could not resolve the head of ${repo}@${defaultBranch.trim()} \u2014 the ref lookup returned nothing to branch from.`
10961
+ );
10962
+ }
10963
+ try {
10964
+ ghWithInputRetried(
10965
+ JSON.stringify({ ref: `refs/heads/${branch}`, sha: base }),
10966
+ "api",
10967
+ "-X",
10968
+ "POST",
10969
+ `repos/${repo}/git/refs`,
10970
+ "--input",
10971
+ "-"
10972
+ );
10973
+ } catch (err) {
10974
+ const msg = err instanceof Error ? err.message : String(err);
10975
+ if (!/already exists/i.test(msg)) throw err;
10976
+ }
10977
+ }
10978
+ __name(ensureBranch, "ensureBranch");
10979
+ function runPublishAssets(args) {
10980
+ const refuse = /* @__PURE__ */ __name((reason) => {
10981
+ writeStderrLine(`publish-assets: refused \u2014 ${reason}`);
10982
+ writeStdoutLine(JSON.stringify({ published: false }));
10983
+ process.exitCode = 3;
10984
+ }, "refuse");
10985
+ if (!Number.isInteger(args.pr) || args.pr <= 0) {
10986
+ refuse(`--pr must be a positive integer, got ${String(args.pr)}.`);
10987
+ return;
10988
+ }
10989
+ const repoResult = parseAssetsRepo(process.env["QWEN_REVIEW_ASSETS_REPO"]);
10990
+ if ("error" in repoResult) {
10991
+ refuse(repoResult.error);
10992
+ return;
10993
+ }
10994
+ const repo = repoResult.repo;
10995
+ const effectiveHost = args.host ?? (process.env["GH_HOST"]?.trim() || void 0);
10996
+ const auth = reviewWriteAuthorization({
10997
+ userAuthorized: args.userAuthorized,
10998
+ skillArgs: args.skillArgs,
10999
+ pr: args.pr,
11000
+ // Bind the REVIEWED repo when the caller names it, never the assets repo:
11001
+ // the designation itself is the consent for the destination, and binding a
11002
+ // URL-shaped authorisation against a fork-hosted assets repo refused
11003
+ // legitimately authorised runs. Without --reviewed-repo the gate binds the
11004
+ // PR number (and host) alone.
11005
+ repo: args.reviewedRepo,
11006
+ host: effectiveHost
11007
+ });
11008
+ if (!auth.ok) {
11009
+ refuse(
11010
+ `not authorised \u2014 ${auth.why}. Evidence images are published only for a run that is authorised to post the review itself; the findings and their local file paths remain in the terminal output and the saved report.`
11011
+ );
11012
+ return;
11013
+ }
11014
+ if (effectiveHost) {
11015
+ try {
11016
+ setGhHost(effectiveHost);
11017
+ } catch (err) {
11018
+ refuse(
11019
+ `${err instanceof Error ? err.message : String(err)} (from ${args.host !== void 0 ? "--host" : "the GH_HOST environment variable"})`
11020
+ );
11021
+ return;
11022
+ }
11023
+ }
11024
+ let findings;
11025
+ const fileList = [...args.files ?? []];
11026
+ if (args.findings) {
11027
+ try {
11028
+ const artifact = JSON.parse(readFileSync15(resolve10(args.findings), "utf8"));
11029
+ findings = validateFindings(
11030
+ Array.isArray(artifact) ? artifact : artifact.findings
11031
+ );
11032
+ } catch (err) {
11033
+ refuse(
11034
+ `cannot use the findings artifact at ${JSON.stringify(args.findings)}: ${err instanceof Error ? err.message : String(err)}`
11035
+ );
11036
+ return;
11037
+ }
11038
+ for (const f of findings) {
11039
+ for (const a of f.assetFiles ?? []) fileList.push(a);
11040
+ }
11041
+ }
11042
+ const unique = [...new Set(fileList)];
11043
+ if (unique.length === 0) {
11044
+ if (args.findings) {
11045
+ writeStderrLine(
11046
+ "publish-assets: no finding carries assetFiles \u2014 nothing to publish."
11047
+ );
11048
+ writeStdoutLine(JSON.stringify({ published: false, count: 0 }));
11049
+ return;
11050
+ }
11051
+ refuse("no files to publish.");
11052
+ return;
11053
+ }
11054
+ const stats = [];
11055
+ for (const file of unique) {
11056
+ const abs = resolve10(file);
11057
+ try {
11058
+ const st = statSync5(abs);
11059
+ if (!st.isFile()) throw new Error("not a regular file");
11060
+ stats.push({ file, abs, basename: basename5(abs), bytes: st.size });
11061
+ } catch (err) {
11062
+ refuse(
11063
+ `cannot read ${JSON.stringify(file)}: ${err instanceof Error ? err.message : String(err)}`
11064
+ );
11065
+ return;
11066
+ }
11067
+ }
11068
+ const batch = validateAssetBatch(stats);
11069
+ if (!batch.ok) {
11070
+ refuse(batch.reason);
11071
+ return;
11072
+ }
11073
+ const prepared = [];
11074
+ for (const st of stats) {
11075
+ let content;
11076
+ try {
11077
+ content = readFileSync15(st.abs);
11078
+ } catch (err) {
11079
+ refuse(
11080
+ `cannot read ${JSON.stringify(st.file)}: ${err instanceof Error ? err.message : String(err)}`
11081
+ );
11082
+ return;
11083
+ }
11084
+ const sha256 = createHash2("sha256").update(content).digest("hex");
11085
+ prepared.push({
11086
+ file: st.file,
11087
+ bytes: st.bytes,
11088
+ sha256,
11089
+ remotePath: remoteAssetPath(args.pr, st.basename, sha256),
11090
+ contentBase64: content.toString("base64")
11091
+ });
11092
+ }
11093
+ const branch = assetsBranch(args.pr);
11094
+ ensureBranch(repo, branch);
11095
+ for (const p of prepared) {
11096
+ putContent(repo, branch, p.remotePath, p.contentBase64, args.pr);
11097
+ }
11098
+ const headSha = gh(
11099
+ "api",
11100
+ `repos/${repo}/git/ref/heads/${branch}`,
11101
+ "--jq",
11102
+ ".object.sha"
11103
+ ).trim();
11104
+ const published = prepared.map((p) => ({
11105
+ file: p.file,
11106
+ remotePath: p.remotePath,
11107
+ url: rawAssetUrl({
11108
+ host: effectiveHost,
11109
+ repo,
11110
+ commitSha: headSha,
11111
+ remotePath: p.remotePath
11112
+ }),
11113
+ bytes: p.bytes,
11114
+ sha256: p.sha256
11115
+ }));
11116
+ const manifest = {
11117
+ repo,
11118
+ branch,
11119
+ commitSha: headSha,
11120
+ pr: args.pr,
11121
+ published
11122
+ };
11123
+ const outPath = resolve10(args.out);
11124
+ mkdirSync14(dirname11(outPath), { recursive: true });
11125
+ writeFileSync14(outPath, `${JSON.stringify(manifest, null, 2)}
11126
+ `, "utf8");
11127
+ if (findings && !args.findingsOut) {
11128
+ writeStderrLine(
11129
+ "publish-assets: --findings given without --findings-out \u2014 the URLs are in the manifest only; the artifact keeps local paths."
11130
+ );
11131
+ }
11132
+ if (findings && args.findingsOut) {
11133
+ const urlByFile = new Map(published.map((p) => [p.file, p.url]));
11134
+ const updated = findings.map((f) => {
11135
+ const urls = (f.assetFiles ?? []).map((a) => urlByFile.get(a)).filter((u) => u !== void 0);
11136
+ return urls.length > 0 ? { ...f, assets: urls } : f;
11137
+ });
11138
+ const report = buildReport(updated);
11139
+ const fOut = resolve10(args.findingsOut);
11140
+ mkdirSync14(dirname11(fOut), { recursive: true });
11141
+ writeFileSync14(fOut, `${JSON.stringify(report, null, 2)}
11142
+ `, "utf8");
11143
+ }
11144
+ writeStderrLine(
11145
+ `publish-assets: ${published.length} file(s) \u2192 ${repo}@${branch} (commit ${headSha.slice(0, 9)})`
11146
+ );
11147
+ writeStdoutLine(JSON.stringify({ published: true, count: published.length }));
11148
+ }
11149
+ __name(runPublishAssets, "runPublishAssets");
11150
+ var publishAssetsCommand = {
11151
+ command: "publish-assets",
11152
+ describe: "Publish review evidence images to the user-designated assets repository (QWEN_REVIEW_ASSETS_REPO) and emit commit-pinned URLs \u2014 gated on the same authorisation as submit",
11153
+ builder: /* @__PURE__ */ __name((yargs) => yargs.option("pr", {
11154
+ type: "number",
11155
+ demandOption: true,
11156
+ describe: "The pull request this evidence belongs to"
11157
+ }).option("reviewed-repo", {
11158
+ type: "string",
11159
+ describe: "The owner/repo the reviewed PR lives in \u2014 strengthens the authorisation binding for URL-shaped review arguments; omit to bind by PR number alone"
11160
+ }).option("files", {
11161
+ type: "string",
11162
+ array: true,
11163
+ describe: "Evidence image files to publish"
11164
+ }).option("findings", {
11165
+ type: "string",
11166
+ describe: "Findings artifact whose per-finding assetFiles should be published"
11167
+ }).option("findings-out", {
11168
+ type: "string",
11169
+ implies: "findings",
11170
+ describe: "Where to write the findings artifact with published URLs woven into each finding (requires --findings)"
11171
+ }).option("out", {
11172
+ type: "string",
11173
+ demandOption: true,
11174
+ describe: "Where to write the publish manifest"
11175
+ }).option("host", {
11176
+ type: "string",
11177
+ describe: "GitHub Enterprise host (defaults to github.com)"
11178
+ }).option("user-authorized", {
11179
+ type: "boolean",
11180
+ default: false,
11181
+ describe: "Pass ONLY when the user asked, in a message they typed this session, for this review to be published"
11182
+ }).option("skill-args", {
11183
+ type: "string",
11184
+ describe: "Test seam: path to the recorded review arguments (ignored when a session id is present)"
11185
+ }), "builder"),
11186
+ handler: /* @__PURE__ */ __name((argv) => {
11187
+ runPublishAssets({
11188
+ pr: argv["pr"],
11189
+ reviewedRepo: argv["reviewed-repo"],
11190
+ files: argv["files"],
11191
+ findings: argv["findings"],
11192
+ findingsOut: argv["findings-out"],
11193
+ out: argv["out"],
11194
+ host: argv["host"],
11195
+ userAuthorized: Boolean(argv["user-authorized"]),
11196
+ skillArgs: argv["skill-args"]
11197
+ });
11198
+ }, "handler")
11199
+ };
11200
+
11201
+ // packages/cli/src/commands/review/resolve-anchors.ts
11202
+ init_esbuild_shims();
11203
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15 } from "node:fs";
11204
+ import { dirname as dirname12, resolve as resolve11 } from "node:path";
11205
+
11206
+ // packages/cli/src/commands/review/lib/anchors.ts
11207
+ init_esbuild_shims();
11208
+ function collectNewSideLines(diffText, file) {
11209
+ const lines2 = diffText.split("\n");
11210
+ const out = [];
11211
+ for (const hunk of file.hunks) {
11212
+ if (hunk.newCount === 0) continue;
11213
+ let newCursor = hunk.newStart;
11214
+ for (let n = hunk.diffStart + 1; n <= hunk.diffEnd; n++) {
11215
+ const raw = lines2[n - 1];
11216
+ if (raw === void 0) break;
11217
+ if (raw.startsWith("+")) {
11218
+ out.push({ newLine: newCursor, text: raw.slice(1), added: true });
11219
+ newCursor++;
11220
+ } else if (raw.startsWith("-")) {
11221
+ } else if (raw.startsWith(" ") || raw === "") {
11222
+ out.push({
11223
+ newLine: newCursor,
11224
+ text: raw.startsWith(" ") ? raw.slice(1) : "",
11225
+ added: false
11226
+ });
11227
+ newCursor++;
11228
+ }
11229
+ }
11230
+ }
11231
+ return out;
11232
+ }
11233
+ __name(collectNewSideLines, "collectNewSideLines");
11234
+ function normalizeExact(s) {
11235
+ return s.replace(/\s+$/, "");
11236
+ }
11237
+ __name(normalizeExact, "normalizeExact");
11238
+ function normalizeLoose(s) {
11239
+ return s.trim();
11240
+ }
11241
+ __name(normalizeLoose, "normalizeLoose");
11242
+ function anchorVariants(anchor) {
11243
+ const lines2 = anchor.replace(/\r\n/g, "\n").split("\n");
11244
+ while (lines2.length > 0 && lines2[0].trim() === "") lines2.shift();
11245
+ while (lines2.length > 0 && lines2[lines2.length - 1].trim() === "") lines2.pop();
11246
+ if (lines2.length === 0) return [];
11247
+ const variants = [lines2];
11248
+ const meaningful = lines2.filter((l) => l.trim() !== "");
11249
+ if (meaningful.length > 0 && meaningful.every((l) => /^\+/.test(l))) {
11250
+ variants.push(lines2.map((l) => l.startsWith("+") ? l.slice(1) : l));
11251
+ }
11252
+ const NO_NEWLINE = /^\$/;
11253
+ const marked = lines2.filter((l) => l !== "" && !NO_NEWLINE.test(l));
11254
+ if (marked.length > 0 && marked.every((l) => /^[+\- ]/.test(l)) && marked.some((l) => l.startsWith("+"))) {
11255
+ variants.push(
11256
+ lines2.filter((l) => !l.startsWith("-") && !NO_NEWLINE.test(l)).map((l) => l.slice(1))
11257
+ );
11258
+ }
11259
+ return variants;
11260
+ }
11261
+ __name(anchorVariants, "anchorVariants");
11262
+ function matchRuns(hay, needle, norm) {
11263
+ const starts = [];
11264
+ if (needle.length === 0 || hay.length < needle.length) return starts;
11265
+ const normNeedle = needle.map(norm);
11266
+ for (let i = 0; i + normNeedle.length <= hay.length; i++) {
11267
+ let ok = true;
11268
+ for (let j = 0; j < normNeedle.length; j++) {
11269
+ const cell = hay[i + j];
11270
+ if (norm(cell.text) !== normNeedle[j]) {
11271
+ ok = false;
11272
+ break;
11273
+ }
11274
+ if (j > 0 && cell.newLine !== hay[i + j - 1].newLine + 1) {
11275
+ ok = false;
11276
+ break;
11277
+ }
11278
+ }
11279
+ if (ok) starts.push(i);
11280
+ }
11281
+ return starts;
11282
+ }
11283
+ __name(matchRuns, "matchRuns");
11284
+ function candidatesFor(hay, needle, norm) {
11285
+ return matchRuns(hay, needle, norm).map((i) => {
11286
+ const run4 = hay.slice(i, i + needle.length);
11287
+ return {
11288
+ startLine: run4[0].newLine,
11289
+ line: run4[run4.length - 1].newLine,
11290
+ added: run4.some((l) => l.added)
11291
+ };
11292
+ });
11293
+ }
11294
+ __name(candidatesFor, "candidatesFor");
11295
+ function pick(cands, claimedLine) {
11296
+ if (cands.length === 1) return cands[0];
11297
+ if (claimedLine !== void 0) {
11298
+ const dist = /* @__PURE__ */ __name((c) => Math.abs(c.startLine - claimedLine), "dist");
11299
+ let best = Infinity;
11300
+ for (const c of cands) best = Math.min(best, dist(c));
11301
+ const nearest = cands.filter((c) => dist(c) === best);
11302
+ return nearest.length === 1 ? nearest[0] : null;
11303
+ }
11304
+ const added = cands.filter((c) => c.added);
11305
+ return added.length === 1 ? added[0] : null;
11306
+ }
11307
+ __name(pick, "pick");
11308
+ function resolveAnchor(newSideLines, anchor, claimedLine) {
11309
+ const variants = anchorVariants(anchor);
11310
+ if (variants.length === 0) {
11311
+ return { status: "unmatched", reason: "anchor is empty" };
11312
+ }
11313
+ for (const [vi, needle] of variants.entries()) {
11314
+ const norms = vi === 0 ? [
10578
11315
  [true, normalizeExact],
10579
11316
  [false, normalizeLoose]
10580
11317
  ] : [[true, normalizeExact]];
@@ -10688,7 +11425,7 @@ __name(validateRequests, "validateRequests");
10688
11425
  function runResolveAnchors(args) {
10689
11426
  let diffText;
10690
11427
  try {
10691
- diffText = readFileSync14(args.diff, "utf8");
11428
+ diffText = readFileSync16(args.diff, "utf8");
10692
11429
  } catch (err) {
10693
11430
  throw new Error(
10694
11431
  `Cannot read diff file ${args.diff}: ${err.message}`
@@ -10696,7 +11433,7 @@ function runResolveAnchors(args) {
10696
11433
  }
10697
11434
  let rawText;
10698
11435
  try {
10699
- rawText = readFileSync14(args.input, "utf8");
11436
+ rawText = readFileSync16(args.input, "utf8");
10700
11437
  } catch (err) {
10701
11438
  throw new Error(
10702
11439
  `Cannot read findings file ${args.input}: ${err.message}`
@@ -10727,8 +11464,8 @@ function runResolveAnchors(args) {
10727
11464
  loose: resolved.filter((r) => r.tier?.startsWith("loose")).length
10728
11465
  }
10729
11466
  };
10730
- mkdirSync13(dirname11(resolve10(args.out)), { recursive: true });
10731
- writeFileSync14(args.out, JSON.stringify(report, null, 2), "utf8");
11467
+ mkdirSync15(dirname12(resolve11(args.out)), { recursive: true });
11468
+ writeFileSync15(args.out, JSON.stringify(report, null, 2), "utf8");
10732
11469
  writeStdoutLine(`Wrote resolved anchors to ${args.out}`);
10733
11470
  const s = report.stats;
10734
11471
  writeStderrLine(
@@ -10775,8 +11512,8 @@ var resolveAnchorsCommand = {
10775
11512
 
10776
11513
  // packages/cli/src/commands/review/check-coverage.ts
10777
11514
  init_esbuild_shims();
10778
- import { writeFileSync as writeFileSync15, mkdirSync as mkdirSync14 } from "node:fs";
10779
- import { dirname as dirname12, resolve as resolve11 } from "node:path";
11515
+ import { writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
11516
+ import { dirname as dirname13, resolve as resolve12 } from "node:path";
10780
11517
  function runCheckCoverage(args) {
10781
11518
  let report;
10782
11519
  try {
@@ -10792,8 +11529,8 @@ This is an environment problem, not a finding about the agents. Coverage cannot
10792
11529
  }
10793
11530
  throw err;
10794
11531
  }
10795
- mkdirSync14(dirname12(resolve11(args.out)), { recursive: true });
10796
- writeFileSync15(args.out, JSON.stringify(report, null, 2));
11532
+ mkdirSync16(dirname13(resolve12(args.out)), { recursive: true });
11533
+ writeFileSync16(args.out, JSON.stringify(report, null, 2));
10797
11534
  writeStdoutLine(`Wrote coverage report to ${args.out}`);
10798
11535
  const totalChunks = report.coveredChunks.length + report.missingChunks.length + report.uncoverableChunks.length;
10799
11536
  const worked = report.agents - report.blindAgents.length - report.idleAgents.length;
@@ -10886,9 +11623,9 @@ var checkCoverageCommand = {
10886
11623
 
10887
11624
  // packages/cli/src/commands/review/agent-prompt.ts
10888
11625
  init_esbuild_shims();
10889
- import { createHash as createHash2 } from "node:crypto";
10890
- import { readFileSync as readFileSync15 } from "node:fs";
10891
- import { dirname as dirname13, join as join11, resolve as resolve12 } from "node:path";
11626
+ import { createHash as createHash3 } from "node:crypto";
11627
+ import { readFileSync as readFileSync17 } from "node:fs";
11628
+ import { dirname as dirname14, join as join12, resolve as resolve13 } from "node:path";
10892
11629
 
10893
11630
  // packages/cli/src/commands/review/lib/path-rules.ts
10894
11631
  init_esbuild_shims();
@@ -10918,14 +11655,85 @@ var GITHUB_ACTIONS = {
10918
11655
 
10919
11656
  **Favour precision over recall here.** A false alarm on a workflow costs more reviewer trust than a missed minor nit, because a YAML finding is the easiest kind for an author to dismiss. Every finding needs the concrete trigger and the concrete outcome, like any other.`
10920
11657
  };
10921
- var PATH_RULES = [GITHUB_ACTIONS];
11658
+ var JAVA = {
11659
+ title: "Java / JVM performance",
11660
+ matches: /* @__PURE__ */ __name((p) => /\.java$/i.test(p), "matches"),
11661
+ checklist: `A Java change's most expensive regressions are decided by the JVM, not by anything a reader can see in the source: HotSpot chooses what to inline, what to scalar-replace, and what to compile at all \u2014 and a one-line change can flip each of those on a hot path. The general performance lens (N+1, repeated work, data structures) sees none of it.
11662
+
11663
+ **You are reviewing this diff, not auditing this file.** A JVM-cost weakness the code already had, on a line this change does not touch, is out of scope \u2014 the same rule as everywhere else. What is in scope: a line this diff **adds or changes**, and a cheap path this diff **makes hot** (a new caller in a request loop, a call moved inside a loop). Test sources (\`src/test/**\`), \`package-info\`/\`module-info\`, and generated sources are out of scope for the hot-path items \u2014 nothing there is hot.
11664
+
11665
+ **Correctness traps dressed as concurrency or performance code (Critical):**
11666
+
11667
+ - **A \`SimpleDateFormat\` shared across threads.** It is not thread-safe: concurrent \`parse\`/\`format\` corrupts its calendar state and returns silently wrong values or throws. If the diff adds a shared (static or instance field) \`SimpleDateFormat\`, or makes an existing one reachable from more than one thread, that is wrong, not slow. The fix is \`java.time.format.DateTimeFormatter\` (immutable) or a \`ThreadLocal\`.
11668
+ - **A compound action on \`ConcurrentHashMap\` built from two calls.** \`get\`-then-\`put\`, \`containsKey\`-then-\`put\`, check-then-act of any shape: the map's thread safety covers each call, not the pair, and the race loses an update or runs the guarded work twice. Say what the race corrupts \u2014 that is the finding. Fix with \`putIfAbsent\`, \`computeIfAbsent\`, or \`merge\`.
11669
+ - **Double-checked locking without \`volatile\` on the field.** The reading thread can observe a partially constructed object. If the diff adds or touches the idiom, the field must be \`volatile\` (or replaced with the holder idiom).
11670
+
11671
+ **JVM-cost defects provable from the source (Suggestion \u2014 a cost, not a wrongness):**
11672
+
11673
+ - **A regex compiled per call.** \`Pattern.compile(...)\` in a method body or loop, and the \`String\` conveniences that hide it \u2014 \`matches\`, \`replaceAll\`, \`replaceFirst\`, and \`split\` with a real regex (a one- or two-character \`split\` argument takes a fast path only when it is not a regex construct \u2014 \`split(".")\` does not, since \`.\` is a metacharacter; the escaped two-character form \`split("\\\\.")\` does) \u2014 recompile the pattern on every invocation. On a per-request or per-record path that is real CPU. Fix: \`private static final Pattern\`.
11674
+ - **\`+=\` on a \`String\` inside a loop.** Each iteration rebuilds and copies the whole accumulated prefix; the loop is O(n\xB2) in the final length. Fix: one \`StringBuilder\` outside the loop.
11675
+ - **Boxing on a hot path.** A \`Long\`/\`Integer\` loop accumulator, a \`Map<Long, \u2026>\` over dense \`int\` keys, \`.boxed()\` in a counted stream: every box past the small-integer cache is an allocation plus a pointer chase per iteration. Fix: primitives, or the primitive streams/collections.
11676
+ - **A capturing lambda or method reference inside a hot loop.** A non-capturing lambda is a singleton; one that closes over a local allocates per iteration. If the diff moves a capturing lambda into a loop, hoist it or restructure it to capture nothing.
11677
+ - **A log message built whether or not it is logged.** \`log.debug("result: " + value)\` pays the concatenation with the level off, and even \`log.debug("{}", expensive())\` still pays \`expensive()\`. Fix: parameterized messages, and guard expensive arguments with \`isDebugEnabled()\` or a supplier-based logging API.
11678
+ - **A collection sized after the fact.** A \`HashMap\`/\`ArrayList\` the code then fills with a known N pays repeated rehash/grow-copy chains; presize it (a \`HashMap\` needs \`N/0.75 + 1\` to avoid rehashing at all \u2014 \`HashMap.newHashMap(n)\`, JDK 19+, does that arithmetic for you).
11679
+ - **Legacy synchronized types in new code.** \`Vector\`, \`Hashtable\`, \`StringBuffer\` put a monitor on every call; the unsynchronized equivalents are the default for a reason.
11680
+ - **An exception used as control flow.** Validating by catching \`NumberFormatException\`, looping until \`EOFException\`: \`fillInStackTrace\` costs more than the work being guarded. Validate instead.
11681
+ - **Reflection rediscovered per call.** \`getMethod\`/\`getField\` on every invocation of a hot path; cache the \`Method\`/\`Field\` \u2014 or a \`MethodHandle\` \u2014 once.
11682
+
11683
+ **JIT inlining \u2014 the regression no dimension can see, and you cannot prove from source:**
11684
+
11685
+ HotSpot's C2 compiler gates inlining on the **bytecode size of the callee** among other criteria. These are the size caps in its normal policy (a project's own \`-XX:\` flags, or a different JVM, change them \u2014 check before citing):
11686
+
11687
+ | callee bytecode size | C2 normal-policy size cap |
11688
+ | --- | --- |
11689
+ | \u2264 6 (\`MaxTrivialSize\`) | under the trivial cap \u2014 size alone does not block it |
11690
+ | \u2264 35 (\`MaxInlineSize\`) | under the normal cap \u2014 size alone does not block it |
11691
+ | \u2264 325 (\`FreqInlineSize\`) | under the hot cap \u2014 size blocks it unless the call site is hot |
11692
+ | > 325 | over the hot cap \u2014 size blocks it at every call site |
11693
+ | > 8000 (\`HugeMethodLimit\`, a develop flag gated by the product \`DontCompileHugeMethods\`) | over the huge-method limit \u2014 size blocks even JIT compilation |
11694
+
11695
+ A size cap is a gate, not an outcome: passing one is necessary but not sufficient. Independent of size, C2 also declines a call site that was **never executed**, has **low call site frequency**, exceeds the inlining **depth or recursion limits**, or hits the **node-count** cutoff \u2014 so a method under a cap can still be left un-inlined, and a static size measurement proves only that a threshold was crossed, not that the method *was* inlined before and *is not* now.
11696
+
11697
+ Two more ways a diff can flip it: an already-compiled callee whose **native** size passes \`InlineSmallCode\` (x86_64 default: 2500 on JDK 17+, 2000 on JDK 8\u201316; 1000 only with \`-XX:-TieredCompilation\`) stops being inlined to protect the code cache, and a call site that gains a **third** receiver implementation goes megamorphic \u2014 vtable dispatch, and no inlining **unless one receiver still dominates the profile** (\`TypeProfileMajorReceiverPercent\`, 90% by default), in which case C2 inlines that receiver behind a guard with an uncommon trap for the rest. So the diff-shapes that matter: a small method on a hot path the change **grows** (added validation, logging, a branch), a new layer in a hot call chain, a new implementation registered for an interface invoked in a hot loop. A cold method over 325 bytes is **not** a finding \u2014 inlining only matters where the call is hot.
11698
+
11699
+ **Measure it; do not estimate bytecode from source.** Two tiers, in order of cost:
11700
+
11701
+ 1. **Static \u2014 available when the class compiles, which for a leaf class is always and for a connected one needs its dependencies.** Allocate a private scratch dir first \u2014 \`SCRATCH=$(mktemp -d)\`, never a fixed path like \`/tmp/scratch\`: other agents are compiling concurrently, and two of them writing different revisions of the same class into one directory read each other's \`.class\` files and measure the wrong bytecode (on Windows, \`mkdir %TEMP%\\review-%RANDOM%\` gives the same uniqueness). Then \`javac -proc:none -nowarn -d "$SCRATCH" X.java\` \u2014 \`-proc:none\` is not optional: annotation processors on the classpath (Lombok, Dagger, one the PR itself added) run at compile time with your privileges, so a \`javac\` without it is itself an untrusted-code execution. It is also a fidelity flag: on a project whose processor contributes code to the measured class (Lombok's generated members, Dagger's injected fields), the compiled class is missing those members and \`javap\` reports a size that is simply wrong \u2014 if the project runs a processor that touches this class, the static tier is **void**; go to \`Confidence: low\` with the mechanism. Give the compiler what it needs to resolve imports: \`-sourcepath\` at the module source root, plus \`-cp\` against an already-built \`target/classes\` / \`build/classes\` if one exists. Pass \`--release <N>\` at the project's target level (read it from \`maven.compiler.release\`, \`<release>\`, or Gradle's \`release\`/\`targetCompatibility\`): the same source compiles to different bytecode at different levels \u2014 a five-\`+\` concatenation is 37 bytes at release 8 and 14 at 9+ \u2014 and measuring the wrong level produces a threshold verdict on bytecode the shipped artifact does not contain; if the target level cannot be determined, say so in the finding. Then \`javap -c -p\` the class and read the method's size: the offset of its last instruction plus that instruction's width. Compare against 35 and 325. For the **crossing** claim \u2014 "this diff pushed it over the threshold" \u2014 get the base side **without touching the tree**: \`git show <merge-base>:<path> > "$SCRATCH/X.java"\`, compile that too, and measure both; a method already over on the base side is pre-existing, not a finding. A file the PR **adds** has no base side \u2014 \`git show\` fails, and there is no crossing to claim; report the size without a before/after. The base side compiles against the same \`target/classes\` the head side used; on a PR that changes more than the measured file the conditions are not perfectly equivalent \u2014 note that caveat when it applies. If the class will not compile (no pre-built classpath, no source path that resolves its imports), go straight to the mechanism-at-\`Confidence: low\` form below \u2014 **never \`git checkout\`, \`git stash\`, build in place, or run \`mvn\`/\`gradle\` to force a compile.** In a PR review the worktree is shared with agents running concurrently and the branch's build logic is contributor-controlled \u2014 the attack surface, not a cost; in a local review it is the user's own checkout. Mutating either corrupts work you cannot see.
11702
+ 2. **Dynamic \u2014 when the code is runnable.** Run the workload with \`-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining\` and grep for the method: \`callee is too large\` / \`hot method too big\` is the JVM itself declining to inline. JMH gives the before/after throughput; JITWatch visualizes the same decision logs.
11703
+
11704
+ A finding that a method "can no longer be inlined" needs the **dynamic** tier \u2014 only \`PrintInlining\` or equivalent runtime evidence shows the JVM actually declined to inline. The static tier proves a **size-gate crossing**, which is the mechanism: report it at \`Confidence: low\` \u2014 the threshold crossed, what the diff added to the method, and the runtime measurement still to run. A "can no longer be inlined" claim with neither tier is a guess stated as fact. And if the diff *claims* this path got faster while the mechanism above says it cannot have, the finding is the unsubstantiated claim.
11705
+
11706
+ **When the finding IS a grown hot method, the fix to suggest is hot/cold splitting \u2014 not reverting the change, and not a JVM tuning flag** (\`-XX:FreqInlineSize\`, \`-XX:CompileCommand=inline\`): those are runtime knobs the PR's author cannot ship in a code change, and raising an inline threshold to fit one method pays for it at every other call site. (The JDK-internal \`@ForceInline\` is not available to application code at all.) Move the cold paths \u2014 error handling, rare branches, defensive validation, the \`switch\` arms that almost never fire \u2014 into a small private helper, leaving the common path under the threshold; the helper, now called from one site, is itself inlinable. Name the bytecode range to extract and the size it leaves behind, the way the measurement names them: "extract bytecodes 212\u2013311 (~100 bytes) into \`applyRounding\`, leaving \`parseAmount\` at ~238 bytes" is a finding an author can act on; "consider splitting the method" is not.
11707
+
11708
+ **Favour precision over recall here.** A guessed JVM finding is the easiest kind for an author to dismiss, and one dismissal teaches them to skip the rest of the review. Every finding needs the concrete hot path and the concrete cost, like any other. Performance findings are **Suggestions** \u2014 slow is a cost, not incorrect behaviour \u2014 **except where the cost is itself the wrongness**: unbounded allocation, quadratic work, or unbounded cache growth on attacker-reachable input is a denial-of-service hole, which the severity ladder grades Critical, not a Suggestion. Name the reachable input that triggers it; "this loop is slow" with no attacker-reachable trigger stays a Suggestion.`
11709
+ };
11710
+ var PATH_RULES = [GITHUB_ACTIONS, JAVA];
11711
+ function isOutOfScope(p) {
11712
+ return /src\/(test|integrationTest|integTest|androidTest|testFixtures)\//i.test(
11713
+ p
11714
+ ) || /(?:^|\/)(?:package-info|module-info)\.java$/i.test(p) || /(?:^|\/)(?:target|build)\/(?:generated-sources|generated-test-sources|generated)\//i.test(
11715
+ p
11716
+ );
11717
+ }
11718
+ __name(isOutOfScope, "isOutOfScope");
11719
+ function describePaths(which) {
11720
+ const CAP = 10;
11721
+ const prod = which.filter((p) => !isOutOfScope(p));
11722
+ const rest = which.filter((p) => isOutOfScope(p));
11723
+ const ordered = [...prod, ...rest];
11724
+ if (ordered.length <= CAP) {
11725
+ return ordered.join(", ");
11726
+ }
11727
+ return `${ordered.slice(0, CAP).join(", ")}, \u2026and ${ordered.length - CAP} more`;
11728
+ }
11729
+ __name(describePaths, "describePaths");
10922
11730
  function pathRulesFor(paths) {
10923
11731
  const hit = PATH_RULES.filter((r) => paths.some((p) => r.matches(p)));
10924
11732
  if (hit.length === 0) return "";
10925
11733
  const parts = ["## Rules for the files in front of you", ""];
10926
11734
  for (const r of hit) {
10927
11735
  const which = paths.filter((p) => r.matches(p));
10928
- parts.push(`### ${r.title} \u2014 ${which.join(", ")}`, "", r.checklist, "");
11736
+ parts.push(`### ${r.title} \u2014 ${describePaths(which)}`, "", r.checklist, "");
10929
11737
  }
10930
11738
  return parts.join("\n").trimEnd();
10931
11739
  }
@@ -10990,32 +11798,32 @@ function chunkFrom(report, id) {
10990
11798
  throw new Error("agent-prompt: the plan has no `chunks[]`.");
10991
11799
  }
10992
11800
  const chunks = report.chunks;
10993
- const chunk = chunks.find((c) => c?.id === id);
10994
- if (!chunk) {
11801
+ const chunk2 = chunks.find((c) => c?.id === id);
11802
+ if (!chunk2) {
10995
11803
  throw new Error(
10996
11804
  `agent-prompt: the plan has no chunk ${id} (it has ${chunks.length}: ${chunks.map((c) => c?.id).join(", ")}).`
10997
11805
  );
10998
11806
  }
10999
- if (!Number.isSafeInteger(chunk.startLine) || !Number.isSafeInteger(chunk.endLine) || chunk.startLine < 1 || chunk.endLine < chunk.startLine) {
11807
+ if (!Number.isSafeInteger(chunk2.startLine) || !Number.isSafeInteger(chunk2.endLine) || chunk2.startLine < 1 || chunk2.endLine < chunk2.startLine) {
11000
11808
  throw new Error(
11001
- `agent-prompt: chunk ${id} has no usable line range (startLine=${chunk.startLine}, endLine=${chunk.endLine}).`
11809
+ `agent-prompt: chunk ${id} has no usable line range (startLine=${chunk2.startLine}, endLine=${chunk2.endLine}).`
11002
11810
  );
11003
11811
  }
11004
- return { diffPath, chunk, total: chunks.length };
11812
+ return { diffPath, chunk: chunk2, total: chunks.length };
11005
11813
  }
11006
11814
  __name(chunkFrom, "chunkFrom");
11007
11815
  function buildChunkAgentPrompt(report, id, rules) {
11008
- const { chunk, total } = chunkFrom(report, id);
11009
- const files = (Array.isArray(chunk.files) ? chunk.files : []).filter(
11816
+ const { chunk: chunk2, total } = chunkFrom(report, id);
11817
+ const files = (Array.isArray(chunk2.files) ? chunk2.files : []).filter(
11010
11818
  (f) => !!f && typeof f.path === "string" && f.path.length > 0
11011
11819
  ).map(
11012
11820
  (f) => `- ${inertPath(f.path)} (new-side lines ${f.newStart}-${f.newEnd})`
11013
11821
  ).join("\n");
11014
- const unreachable = chunk.maxLineChars > READ_FILE_CHAR_CAP;
11822
+ const unreachable = chunk2.maxLineChars > READ_FILE_CHAR_CAP;
11015
11823
  const parts = [
11016
- `You are reviewing chunk ${chunk.id} of ${total} of a code diff.`,
11824
+ `You are reviewing chunk ${chunk2.id} of ${total} of a code diff.`,
11017
11825
  "",
11018
- `Your territory: lines ${chunk.startLine}-${chunk.endLine} of the diff (${chunk.lines} lines, ${chunk.chars} characters). The surrounding chunks belong to other agents \u2014 do not review them.`,
11826
+ `Your territory: lines ${chunk2.startLine}-${chunk2.endLine} of the diff (${chunk2.lines} lines, ${chunk2.chars} characters). The surrounding chunks belong to other agents \u2014 do not review them.`,
11019
11827
  "",
11020
11828
  "It covers these source files:",
11021
11829
  files || "- (none recorded)",
@@ -11025,11 +11833,11 @@ function buildChunkAgentPrompt(report, id, rules) {
11025
11833
  if (unreachable) {
11026
11834
  parts.push(
11027
11835
  "",
11028
- `**This chunk contains a single line of ${chunk.maxLineChars} characters** \u2014 longer than one read returns, and paging cannot reach its tail (every page starts at a line boundary). Do not claim to have reviewed it. Return exactly:`,
11836
+ `**This chunk contains a single line of ${chunk2.maxLineChars} characters** \u2014 longer than one read returns, and paging cannot reach its tail (every page starts at a line boundary). Do not claim to have reviewed it. Return exactly:`,
11029
11837
  "",
11030
- ` Uncoverable: chunk ${chunk.id} \u2014 line exceeds the read limit`
11838
+ ` Uncoverable: chunk ${chunk2.id} \u2014 line exceeds the read limit`
11031
11839
  );
11032
- } else if (chunk.oversized) {
11840
+ } else if (chunk2.oversized) {
11033
11841
  parts.push(
11034
11842
  "",
11035
11843
  "**This chunk is oversized** \u2014 it is a single hunk with no safe place to cut, and it may exceed one read. Expect to page."
@@ -11049,7 +11857,7 @@ function buildChunkAgentPrompt(report, id, rules) {
11049
11857
  "",
11050
11858
  EXCLUSIONS
11051
11859
  );
11052
- const chunkPaths = (Array.isArray(chunk.files) ? chunk.files : []).map((f) => f?.path).filter((p) => typeof p === "string");
11860
+ const chunkPaths = (Array.isArray(chunk2.files) ? chunk2.files : []).map((f) => f?.path).filter((p) => typeof p === "string");
11053
11861
  const pathRules = pathRulesFor(chunkPaths);
11054
11862
  if (pathRules) parts.push("", pathRules);
11055
11863
  if (rules && rules.trim()) {
@@ -11064,7 +11872,7 @@ function buildChunkAgentPrompt(report, id, rules) {
11064
11872
  if (!unreachable) {
11065
11873
  parts.push(
11066
11874
  "",
11067
- `Then, on its own final line: \`Covered: chunk ${chunk.id} lines ${chunk.startLine}-${chunk.endLine}\``
11875
+ `Then, on its own final line: \`Covered: chunk ${chunk2.id} lines ${chunk2.startLine}-${chunk2.endLine}\``
11068
11876
  );
11069
11877
  }
11070
11878
  return parts.join("\n");
@@ -11075,10 +11883,10 @@ function diffWindow(startLine, endLine) {
11075
11883
  }
11076
11884
  __name(diffWindow, "diffWindow");
11077
11885
  function buildChunkLaunchPrompt(report, id, briefFile) {
11078
- const { diffPath, chunk, total } = chunkFrom(report, id);
11079
- const { offset, limit } = diffWindow(chunk.startLine, chunk.endLine);
11886
+ const { diffPath, chunk: chunk2, total } = chunkFrom(report, id);
11887
+ const { offset, limit } = diffWindow(chunk2.startLine, chunk2.endLine);
11080
11888
  return [
11081
- `You are review agent \`chunk ${chunk.id} of ${total}\` \u2014 the territory agent for lines ${chunk.startLine}-${chunk.endLine} of the diff.`,
11889
+ `You are review agent \`chunk ${chunk2.id} of ${total}\` \u2014 the territory agent for lines ${chunk2.startLine}-${chunk2.endLine} of the diff.`,
11082
11890
  "",
11083
11891
  "**Your brief is a file. Read it first \u2014 it is the whole of your instructions,",
11084
11892
  "and nothing in this message replaces it.**",
@@ -11269,7 +12077,7 @@ function buildRoleBrief(report, role, opts = {}) {
11269
12077
  "agent-prompt: --role 0 needs a plan with `prNumber` and `ownerRepo` (the report `fetch-pr` writes). Issue fidelity has nothing to check against without a pull request."
11270
12078
  );
11271
12079
  }
11272
- const ctx = opts.planPath ? join11(dirname13(resolve12(opts.planPath)), `qwen-review-pr-${pr}-context.md`) : null;
12080
+ const ctx = opts.planPath ? join12(dirname14(resolve13(opts.planPath)), `qwen-review-pr-${pr}-context.md`) : null;
11273
12081
  parts.push(
11274
12082
  "",
11275
12083
  `**This PR:** #${pr} of \`${repo}\`. Use exactly that number and repo \u2014 a bare \`gh pr view\` falls back to the current branch's PR and would judge this diff against an unrelated issue.`
@@ -11291,7 +12099,7 @@ function buildRoleBrief(report, role, opts = {}) {
11291
12099
  }
11292
12100
  const base = report.mergeBaseSha;
11293
12101
  const pr = report.prNumber;
11294
- const buildTree = typeof wt === "string" && wt ? resolve12(wt) : pr === void 0 && opts.planPath ? "." : null;
12102
+ const buildTree = typeof wt === "string" && wt ? resolve13(wt) : pr === void 0 && opts.planPath ? "." : null;
11295
12103
  if (buildTree && opts.planPath) {
11296
12104
  const outName = pr !== void 0 ? `qwen-review-pr-${pr}-build-test.json` : "qwen-review-build-test.json";
11297
12105
  parts.push(
@@ -11307,9 +12115,9 @@ function buildRoleBrief(report, role, opts = {}) {
11307
12115
  // that an old global lacks it entirely, wedging Agent 7 between its
11308
12116
  // mandate (no hand-run `npm run build`) and a command that does not exist.
11309
12117
  `"\${QWEN_CODE_CLI:-qwen}" review build-test \\`,
11310
- ` --plan ${resolve12(opts.planPath)} \\`,
11311
- ` --worktree ${resolve12(buildTree)} \\`,
11312
- ` --out ${resolve12(dirname13(opts.planPath), outName)}`,
12118
+ ` --plan ${resolve13(opts.planPath)} \\`,
12119
+ ` --worktree ${resolve13(buildTree)} \\`,
12120
+ ` --out ${resolve13(dirname14(opts.planPath), outName)}`,
11313
12121
  "```"
11314
12122
  );
11315
12123
  }
@@ -11319,10 +12127,10 @@ function buildRoleBrief(report, role, opts = {}) {
11319
12127
  "**Then run the test-efficacy probe.** A green suite says the tests pass. It does not say they would have failed had the change been wrong, and those are different claims. Give this call `timeout: 600000` too \u2014 besides the revert probe it runs up to 8 single-statement deletion mutants and up to 6 per-hunk reverse-apply probes, each a suite run, and it budgets itself to finish inside that ceiling:",
11320
12128
  "",
11321
12129
  "```bash",
11322
- `"\${QWEN_CODE_CLI:-qwen}" review test-efficacy ${resolve12(opts.planPath)} \\`,
11323
- ` --worktree ${typeof wt === "string" ? resolve12(wt) : "<worktree>"} \\`,
12130
+ `"\${QWEN_CODE_CLI:-qwen}" review test-efficacy ${resolve13(opts.planPath)} \\`,
12131
+ ` --worktree ${typeof wt === "string" ? resolve13(wt) : "<worktree>"} \\`,
11324
12132
  ` --base ${base} \\`,
11325
- ` --out ${resolve12(dirname13(opts.planPath), `qwen-review-pr-${pr}-efficacy.json`)}`,
12133
+ ` --out ${resolve13(dirname14(opts.planPath), `qwen-review-pr-${pr}-efficacy.json`)}`,
11326
12134
  "```",
11327
12135
  "",
11328
12136
  'Read its `findings[]`. `kind: "unreachable"` is a test the project\'s test command never collects \u2014 it did not run here and it does not run in CI. `kind: "inert"` is a test that **still passed with the change reverted**: it is green whether or not the feature exists, so it cannot catch a regression in it. `kind: "mutant-survived"` is a single safety statement the diff added (a `.clear()`, an `.abort(\u2026)`, a reset-to-empty) that was **deleted and every affected test stayed green** \u2014 no test in the diff fails when it is removed, which the whole-file revert cannot see when the file\'s other, tested behaviours mask it. `kind: "hunk-survived"` is one HUNK reverted on its own with every affected test still green \u2014 that specific change ships with nothing gating it. Report each of the four as a **Suggestion** with `Source: [test]`, saying plainly which behaviour has no test in this diff that would catch its removal. `harnessValidated` has THREE values and they are not two: `false` means the positive control failed and every would-be survivor was re-classed \u2014 say the harness could not be validated instead of implying clean coverage; `null` means the control never ran, so the run is neither validated nor refuted and any survivor below is unconfirmed by a control; only `true` licenses reading a survivor as a coverage gap. **`inconclusive` is not a finding** \u2014 for probes, mutants and hunks alike, reverting or mutating the source often breaks the test\'s own compile, and that is not the test catching anything. Entries counted in `mutants.skipped*` or `hunks.skipped*` never ran \u2014 not findings either, and `skippedForControl` in particular means the control stopped the run, NOT that the window ran out. `mutants.note`, when present, explains why no mutants ran at all. Note them and move on.'
@@ -11468,7 +12276,7 @@ function buildLaunch(report, planPath, spec, rules) {
11468
12276
  }
11469
12277
  __name(buildLaunch, "buildLaunch");
11470
12278
  function findingsDigest(content, rules) {
11471
- return createHash2("sha256").update(JSON.stringify([content, rules ?? null])).digest("hex").slice(0, 12);
12279
+ return createHash3("sha256").update(JSON.stringify([content, rules ?? null])).digest("hex").slice(0, 12);
11472
12280
  }
11473
12281
  __name(findingsDigest, "findingsDigest");
11474
12282
  function foldFindings(role, content, prompt) {
@@ -11674,7 +12482,7 @@ function runAgentPrompt(args) {
11674
12482
  }
11675
12483
  let report;
11676
12484
  try {
11677
- report = JSON.parse(readFileSync15(args.plan, "utf8"));
12485
+ report = JSON.parse(readFileSync17(args.plan, "utf8"));
11678
12486
  } catch (err) {
11679
12487
  throw new Error(
11680
12488
  `agent-prompt: cannot read the plan ${args.plan}: ${err.message}`
@@ -11683,7 +12491,7 @@ function runAgentPrompt(args) {
11683
12491
  let rules;
11684
12492
  if (args.rules) {
11685
12493
  try {
11686
- rules = readFileSync15(args.rules, "utf8");
12494
+ rules = readFileSync17(args.rules, "utf8");
11687
12495
  } catch (err) {
11688
12496
  throw new Error(
11689
12497
  `agent-prompt: cannot read the rules ${args.rules}: ${err.message}. Omit --rules if this review has none; passing a path that does not resolve would silently review without the project rules it was told to enforce.`
@@ -11698,7 +12506,7 @@ function runAgentPrompt(args) {
11698
12506
  if (hasFindings && args.role) {
11699
12507
  const role = args.role;
11700
12508
  try {
11701
- findingsContent = readFileSync15(args.findings, "utf8");
12509
+ findingsContent = readFileSync17(args.findings, "utf8");
11702
12510
  } catch (err) {
11703
12511
  throw new Error(
11704
12512
  `agent-prompt: cannot read the findings ${args.findings}: ${err.message}. Pass a path that resolves \u2014 --findings is required for this role, so omitting it only fails one guard earlier. An early reverse-audit round with nothing confirmed passes an empty file (create it first).`
@@ -11807,18 +12615,18 @@ var agentPromptCommand = {
11807
12615
  init_esbuild_shims();
11808
12616
  import { spawnSync as spawnSync2 } from "node:child_process";
11809
12617
  import {
11810
- existsSync as existsSync5,
11811
- readFileSync as readFileSync17,
11812
- rmSync as rmSync4,
12618
+ existsSync as existsSync7,
12619
+ readFileSync as readFileSync19,
12620
+ rmSync as rmSync5,
11813
12621
  statfsSync,
11814
- writeFileSync as writeFileSync16
12622
+ writeFileSync as writeFileSync17
11815
12623
  } from "node:fs";
11816
- import { join as join13, resolve as resolve13 } from "node:path";
12624
+ import { join as join14, resolve as resolve14 } from "node:path";
11817
12625
 
11818
12626
  // packages/cli/src/commands/review/lib/workspaces.ts
11819
12627
  init_esbuild_shims();
11820
- import { existsSync as existsSync4, readFileSync as readFileSync16, readdirSync as readdirSync4 } from "node:fs";
11821
- import { join as join12 } from "node:path";
12628
+ import { existsSync as existsSync6, readFileSync as readFileSync18, readdirSync as readdirSync4 } from "node:fs";
12629
+ import { join as join13 } from "node:path";
11822
12630
  function isWorkspaceMember(filePath, workspaceGlobs) {
11823
12631
  return workspaceDirFor(filePath, workspaceGlobs) !== null;
11824
12632
  }
@@ -11856,7 +12664,7 @@ __name(hasUnmodeledWorkspaceGlob, "hasUnmodeledWorkspaceGlob");
11856
12664
  function readWorkspaceGlobs(root) {
11857
12665
  try {
11858
12666
  const pkg = JSON.parse(
11859
- readFileSync16(join12(root, "package.json"), "utf8")
12667
+ readFileSync18(join13(root, "package.json"), "utf8")
11860
12668
  );
11861
12669
  const ws = pkg.workspaces;
11862
12670
  const globs = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
@@ -11869,7 +12677,7 @@ __name(readWorkspaceGlobs, "readWorkspaceGlobs");
11869
12677
  function readRootPackage(root) {
11870
12678
  let pkg;
11871
12679
  try {
11872
- pkg = JSON.parse(readFileSync16(join12(root, "package.json"), "utf8"));
12680
+ pkg = JSON.parse(readFileSync18(join13(root, "package.json"), "utf8"));
11873
12681
  } catch {
11874
12682
  return null;
11875
12683
  }
@@ -11893,7 +12701,7 @@ function readWorkspacePackages(root) {
11893
12701
  const base = g.slice(0, -2);
11894
12702
  let entries;
11895
12703
  try {
11896
- entries = readdirSync4(join12(root, base), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12704
+ entries = readdirSync4(join13(root, base), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
11897
12705
  } catch {
11898
12706
  continue;
11899
12707
  }
@@ -11905,11 +12713,11 @@ function readWorkspacePackages(root) {
11905
12713
  const pkgs = [];
11906
12714
  for (const dir of dirs) {
11907
12715
  if (workspaceDirFor(`${dir}/package.json`, globs) !== dir) continue;
11908
- const manifest = join12(root, dir, "package.json");
11909
- if (!existsSync4(manifest)) continue;
12716
+ const manifest = join13(root, dir, "package.json");
12717
+ if (!existsSync6(manifest)) continue;
11910
12718
  let pkg;
11911
12719
  try {
11912
- pkg = JSON.parse(readFileSync16(manifest, "utf8"));
12720
+ pkg = JSON.parse(readFileSync18(manifest, "utf8"));
11913
12721
  } catch {
11914
12722
  continue;
11915
12723
  }
@@ -12082,7 +12890,7 @@ __name(unresolvedWorkspaceDeps, "unresolvedWorkspaceDeps");
12082
12890
  function changedFilesFrom(planPath) {
12083
12891
  let parsed;
12084
12892
  try {
12085
- parsed = JSON.parse(readFileSync17(planPath, "utf8"));
12893
+ parsed = JSON.parse(readFileSync19(planPath, "utf8"));
12086
12894
  } catch (err) {
12087
12895
  throw new Error(
12088
12896
  `build-test: cannot read the plan ${planPath}: ${err.message}`
@@ -12099,7 +12907,7 @@ function changedFilesFrom(planPath) {
12099
12907
  }
12100
12908
  __name(changedFilesFrom, "changedFilesFrom");
12101
12909
  function runBuildTest(args) {
12102
- const root = resolve13(args.worktree);
12910
+ const root = resolve14(args.worktree);
12103
12911
  const perCommandMs = args.timeout * 1e3;
12104
12912
  const exec = args.exec ?? run;
12105
12913
  const changed = changedFilesFrom(args.plan);
@@ -12165,15 +12973,15 @@ function runBuildTest(args) {
12165
12973
  timedOut: [],
12166
12974
  note: ""
12167
12975
  };
12168
- const npmLock = existsSync5(join13(root, "package-lock.json"));
12169
- const installComplete = /* @__PURE__ */ __name(() => existsSync5(join13(root, "node_modules", ".package-lock.json")), "installComplete");
12170
- if (args.install && !npmLock && !existsSync5(join13(root, "node_modules"))) {
12976
+ const npmLock = existsSync7(join14(root, "package-lock.json"));
12977
+ const installComplete = /* @__PURE__ */ __name(() => existsSync7(join14(root, "node_modules", ".package-lock.json")), "installComplete");
12978
+ if (args.install && !npmLock && !existsSync7(join14(root, "node_modules"))) {
12171
12979
  const altLock = [
12172
12980
  ["yarn.lock", "yarn install --frozen-lockfile"],
12173
12981
  ["pnpm-lock.yaml", "pnpm install --frozen-lockfile"],
12174
12982
  ["bun.lockb", "bun install --frozen-lockfile"],
12175
12983
  ["bun.lock", "bun install --frozen-lockfile"]
12176
- ].find(([f]) => existsSync5(join13(root, f)));
12984
+ ].find(([f]) => existsSync7(join14(root, f)));
12177
12985
  return unsupportedReport(
12178
12986
  altLock ? `This is a ${altLock[0]} repo with no installed \`node_modules\`, so \`npm ci\` cannot install it. Run \`${altLock[1]}\` first, then fall back to the build/test precedence in your brief, each command with a deadline it can meet.` : "There is no lockfile and no `node_modules` here, so nothing can be installed deterministically. Install dependencies first, then fall back to the build/test precedence in your brief."
12179
12987
  );
@@ -12191,7 +12999,7 @@ function runBuildTest(args) {
12191
12999
  if (install.timedOut) results.timedOut.push(install.command);
12192
13000
  if (install.timedOut) {
12193
13001
  try {
12194
- rmSync4(join13(root, "node_modules"), {
13002
+ rmSync5(join14(root, "node_modules"), {
12195
13003
  recursive: true,
12196
13004
  force: true,
12197
13005
  maxRetries: 3
@@ -12314,7 +13122,7 @@ var buildTestCommand = {
12314
13122
  try {
12315
13123
  const report = runBuildTest(args);
12316
13124
  if (args.out) {
12317
- writeFileSync16(args.out, JSON.stringify(report, null, 2));
13125
+ writeFileSync17(args.out, JSON.stringify(report, null, 2));
12318
13126
  }
12319
13127
  writeStdoutLine(JSON.stringify(report, null, 2));
12320
13128
  } catch (err) {
@@ -12328,25 +13136,25 @@ var buildTestCommand = {
12328
13136
  init_esbuild_shims();
12329
13137
  import { spawnSync as spawnSync4 } from "node:child_process";
12330
13138
  import {
12331
- existsSync as existsSync6,
12332
- mkdirSync as mkdirSync15,
12333
- readFileSync as readFileSync18,
12334
- rmSync as rmSync6,
12335
- statSync as statSync5,
12336
- writeFileSync as writeFileSync17
13139
+ existsSync as existsSync8,
13140
+ mkdirSync as mkdirSync17,
13141
+ readFileSync as readFileSync20,
13142
+ rmSync as rmSync7,
13143
+ statSync as statSync6,
13144
+ writeFileSync as writeFileSync18
12337
13145
  } from "node:fs";
12338
- import { dirname as dirname14, join as join14, resolve as resolve14 } from "node:path";
13146
+ import { dirname as dirname15, join as join15, resolve as resolve15 } from "node:path";
12339
13147
 
12340
13148
  // packages/cli/src/commands/review/lib/worktree.ts
12341
13149
  init_esbuild_shims();
12342
13150
  import { spawnSync as spawnSync3 } from "node:child_process";
12343
- import { rmSync as rmSync5 } from "node:fs";
13151
+ import { rmSync as rmSync6 } from "node:fs";
12344
13152
  function discardWorktree(cwd, tree) {
12345
13153
  const sweep = spawnSync3("git", ["worktree", "remove", "--force", tree], {
12346
13154
  cwd,
12347
13155
  encoding: "utf8"
12348
13156
  });
12349
- rmSync5(tree, { recursive: true, force: true });
13157
+ rmSync6(tree, { recursive: true, force: true });
12350
13158
  return sweep;
12351
13159
  }
12352
13160
  __name(discardWorktree, "discardWorktree");
@@ -12382,7 +13190,7 @@ function runBaseTree(args) {
12382
13190
  }), "unavailable");
12383
13191
  let plan;
12384
13192
  try {
12385
- plan = JSON.parse(readFileSync18(args.plan, "utf8"));
13193
+ plan = JSON.parse(readFileSync20(args.plan, "utf8"));
12386
13194
  } catch (err) {
12387
13195
  return unavailable(
12388
13196
  `cannot read the plan ${args.plan}: ${err.message}`
@@ -12399,15 +13207,15 @@ function runBaseTree(args) {
12399
13207
  "the base branch could not be fetched, so mergeBaseSha may be stale; an A/B against it would attribute the base branch's own commits to this PR"
12400
13208
  );
12401
13209
  }
12402
- const worktree = resolve14(args.worktree);
12403
- if (!existsSync6(worktree)) {
13210
+ const worktree = resolve15(args.worktree);
13211
+ if (!existsSync8(worktree)) {
12404
13212
  return unavailable(`the review worktree ${worktree} does not exist`);
12405
13213
  }
12406
13214
  const tree = baseWorktreePath(worktree);
12407
- const marker = /* @__PURE__ */ __name(() => join14(tree, ".qwen-review-base-ok"), "marker");
12408
- const failedMarker = /* @__PURE__ */ __name(() => join14(tree, ".qwen-review-base-failed"), "failedMarker");
13215
+ const marker = /* @__PURE__ */ __name(() => join15(tree, ".qwen-review-base-ok"), "marker");
13216
+ const failedMarker = /* @__PURE__ */ __name(() => join15(tree, ".qwen-review-base-failed"), "failedMarker");
12409
13217
  try {
12410
- if (existsSync6(tree) && readFileSync18(marker(), "utf8").trim() === baseSha && gitOut(tree, "rev-parse", "HEAD") === baseSha) {
13218
+ if (existsSync8(tree) && readFileSync20(marker(), "utf8").trim() === baseSha && gitOut(tree, "rev-parse", "HEAD") === baseSha) {
12411
13219
  return {
12412
13220
  available: true,
12413
13221
  path: tree,
@@ -12419,7 +13227,7 @@ function runBaseTree(args) {
12419
13227
  } catch {
12420
13228
  }
12421
13229
  try {
12422
- if (existsSync6(tree) && readFileSync18(failedMarker(), "utf8").trim() === baseSha) {
13230
+ if (existsSync8(tree) && readFileSync20(failedMarker(), "utf8").trim() === baseSha) {
12423
13231
  return {
12424
13232
  available: false,
12425
13233
  path: tree,
@@ -12432,14 +13240,14 @@ function runBaseTree(args) {
12432
13240
  }
12433
13241
  const lock = `${tree}.lock`;
12434
13242
  try {
12435
- const age = Date.now() - statSync5(lock).mtimeMs;
13243
+ const age = Date.now() - statSync6(lock).mtimeMs;
12436
13244
  if (age > 30 * 60 * 1e3) {
12437
- rmSync6(lock, { recursive: true, force: true });
13245
+ rmSync7(lock, { recursive: true, force: true });
12438
13246
  }
12439
13247
  } catch {
12440
13248
  }
12441
13249
  try {
12442
- mkdirSync15(lock);
13250
+ mkdirSync17(lock);
12443
13251
  } catch (err) {
12444
13252
  if (err.code !== "EEXIST") {
12445
13253
  return unavailable(
@@ -12453,7 +13261,7 @@ function runBaseTree(args) {
12453
13261
  try {
12454
13262
  return buildBaseTree(baseSha);
12455
13263
  } finally {
12456
- rmSync6(lock, { recursive: true, force: true });
13264
+ rmSync7(lock, { recursive: true, force: true });
12457
13265
  }
12458
13266
  function buildBaseTree(baseSha2) {
12459
13267
  let sweep;
@@ -12477,7 +13285,7 @@ function runBaseTree(args) {
12477
13285
  });
12478
13286
  if (!build.ok || build.toolchain !== "npm" || build.build.length === 0) {
12479
13287
  try {
12480
- writeFileSync17(failedMarker(), `${baseSha2}
13288
+ writeFileSync18(failedMarker(), `${baseSha2}
12481
13289
  `);
12482
13290
  } catch {
12483
13291
  }
@@ -12490,7 +13298,7 @@ function runBaseTree(args) {
12490
13298
  };
12491
13299
  }
12492
13300
  try {
12493
- writeFileSync17(marker(), `${baseSha2}
13301
+ writeFileSync18(marker(), `${baseSha2}
12494
13302
  `);
12495
13303
  } catch {
12496
13304
  }
@@ -12530,8 +13338,8 @@ var baseTreeCommand = {
12530
13338
  try {
12531
13339
  const report = runBaseTree(args);
12532
13340
  if (args.out) {
12533
- mkdirSync15(dirname14(resolve14(args.out)), { recursive: true });
12534
- writeFileSync17(resolve14(args.out), JSON.stringify(report, null, 2));
13341
+ mkdirSync17(dirname15(resolve15(args.out)), { recursive: true });
13342
+ writeFileSync18(resolve15(args.out), JSON.stringify(report, null, 2));
12535
13343
  }
12536
13344
  writeStdoutLine(JSON.stringify(report, null, 2));
12537
13345
  writeStderrLine(`base-tree: ${report.note}`);
@@ -12545,8 +13353,8 @@ var baseTreeCommand = {
12545
13353
  // packages/cli/src/commands/review/test-delta.ts
12546
13354
  init_esbuild_shims();
12547
13355
  import { spawnSync as spawnSync5 } from "node:child_process";
12548
- import { existsSync as existsSync7, mkdirSync as mkdirSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync18 } from "node:fs";
12549
- import { dirname as dirname15, resolve as resolve15 } from "node:path";
13356
+ import { existsSync as existsSync9, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
13357
+ import { dirname as dirname16, resolve as resolve16 } from "node:path";
12550
13358
  var ANSI_SGR_RE2 = /\x1b\[[0-9;]*m/g;
12551
13359
  var RERUNNABLE_COMMAND_RE = /^npm test(?: --workspace="[\w@./-]+")?$/;
12552
13360
  var TRIM_MARKER_RE = /\.\.\. \[\d+ characters omitted/;
@@ -12609,7 +13417,7 @@ var TOTAL_BUDGET_MS = 54e4;
12609
13417
  var DEFAULT_TIMEOUT_S = 300;
12610
13418
  function runTestDelta(args) {
12611
13419
  const exec = args.exec ?? run2;
12612
- const baseline = resolve15(args.baseline);
13420
+ const baseline = resolve16(args.baseline);
12613
13421
  const empty = /* @__PURE__ */ __name((note) => ({
12614
13422
  entries: [],
12615
13423
  netNew: [],
@@ -12619,13 +13427,13 @@ function runTestDelta(args) {
12619
13427
  }), "empty");
12620
13428
  let report;
12621
13429
  try {
12622
- report = JSON.parse(readFileSync19(args.report, "utf8"));
13430
+ report = JSON.parse(readFileSync21(args.report, "utf8"));
12623
13431
  } catch (err) {
12624
13432
  return empty(
12625
13433
  `cannot read the build-test report ${args.report}: ${err.message}`
12626
13434
  );
12627
13435
  }
12628
- if (!existsSync7(baseline)) {
13436
+ if (!existsSync9(baseline)) {
12629
13437
  return empty(
12630
13438
  `the base tree ${baseline} does not exist \u2014 run \`qwen review base-tree\` first`
12631
13439
  );
@@ -12730,50 +13538,737 @@ function runTestDelta(args) {
12730
13538
  );
12731
13539
  }
12732
13540
  return {
12733
- entries,
12734
- netNew,
12735
- shared,
12736
- skippedForBudget,
12737
- note: parts.join(". ") || "nothing to report"
13541
+ entries,
13542
+ netNew,
13543
+ shared,
13544
+ skippedForBudget,
13545
+ note: parts.join(". ") || "nothing to report"
13546
+ };
13547
+ }
13548
+ __name(runTestDelta, "runTestDelta");
13549
+ var testDeltaCommand = {
13550
+ command: "test-delta",
13551
+ describe: "Rerun the PR side's failed test commands on the base tree and report which failing files are the PR's own (net-new) vs pre-existing (shared)",
13552
+ builder: /* @__PURE__ */ __name((yargs) => yargs.option("report", {
13553
+ type: "string",
13554
+ demandOption: true,
13555
+ describe: "Agent 7's build-test report (its failed commands and outputs)"
13556
+ }).option("baseline", {
13557
+ type: "string",
13558
+ demandOption: true,
13559
+ describe: "The BUILT base tree from `qwen review base-tree`"
13560
+ }).option("pr-worktree", {
13561
+ type: "string",
13562
+ describe: "The PR worktree the report's failures were produced in \u2014 its root is stripped so both sides compare as repo-relative paths"
13563
+ }).option("out", { type: "string", describe: "Write the JSON report here" }).option("timeout", {
13564
+ type: "number",
13565
+ default: 300,
13566
+ describe: "Per-command deadline in seconds, as build-test"
13567
+ }), "builder"),
13568
+ handler: /* @__PURE__ */ __name((argv) => {
13569
+ const args = argv;
13570
+ const report = runTestDelta(args);
13571
+ if (args.out) {
13572
+ mkdirSync18(dirname16(resolve16(args.out)), { recursive: true });
13573
+ writeFileSync19(resolve16(args.out), JSON.stringify(report, null, 2));
13574
+ }
13575
+ writeStdoutLine(JSON.stringify(report, null, 2));
13576
+ writeStderrLine(`test-delta: ${report.note}`);
13577
+ }, "handler")
13578
+ };
13579
+
13580
+ // packages/cli/src/commands/review/drive.ts
13581
+ init_esbuild_shims();
13582
+ import { spawnSync as spawnSync6 } from "node:child_process";
13583
+ import {
13584
+ mkdirSync as mkdirSync19,
13585
+ writeFileSync as writeFileSync20,
13586
+ readFileSync as readFileSync22,
13587
+ existsSync as existsSync10,
13588
+ rmSync as rmSync8,
13589
+ statSync as statSync7
13590
+ } from "node:fs";
13591
+ import { dirname as dirname17, join as join16, resolve as resolve17 } from "node:path";
13592
+ import { tmpdir as tmpdir2 } from "node:os";
13593
+ var SERVER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
13594
+ function shellQuote(v) {
13595
+ return `'${v.split("'").join(`'\\''`)}'`;
13596
+ }
13597
+ __name(shellQuote, "shellQuote");
13598
+ var CAPTURE_MAX = 2e5;
13599
+ var LOG_MAX_BYTES = 8 * 1024 * 1024;
13600
+ var POLL_MS = 250;
13601
+ function logBytes(p) {
13602
+ try {
13603
+ return statSync7(p).size;
13604
+ } catch {
13605
+ return 0;
13606
+ }
13607
+ }
13608
+ __name(logBytes, "logBytes");
13609
+ function waitMs(ms) {
13610
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
13611
+ }
13612
+ __name(waitMs, "waitMs");
13613
+ function run3(cmd, args, input) {
13614
+ const r = spawnSync6(cmd, args, {
13615
+ encoding: "utf8",
13616
+ input,
13617
+ timeout: 3e4,
13618
+ maxBuffer: 64 * 1024 * 1024,
13619
+ stdio: ["pipe", "pipe", "pipe"]
13620
+ });
13621
+ return {
13622
+ status: r.status ?? null,
13623
+ stdout: r.stdout ?? "",
13624
+ stderr: r.stderr ?? ""
13625
+ };
13626
+ }
13627
+ __name(run3, "run");
13628
+ var DRIVE_SENTINEL = "__QWEN_REVIEW_DRIVE_DONE__";
13629
+ function wrapScript(script, sentinelPath, sentinel = DRIVE_SENTINEL) {
13630
+ return `trap '__qwen_rc=$?; echo "${sentinel} rc=\${__qwen_rc}" > ${shellQuote(sentinelPath)}' EXIT
13631
+ set +e
13632
+ ${script}
13633
+ `;
13634
+ }
13635
+ __name(wrapScript, "wrapScript");
13636
+ function sentinelExitCode(capture, sentinel = DRIVE_SENTINEL) {
13637
+ const re = new RegExp(`${sentinel} rc=(\\d+)`, "g");
13638
+ let last = null;
13639
+ for (let m = re.exec(capture); m; m = re.exec(capture)) last = m;
13640
+ return last ? Number(last[1]) : null;
13641
+ }
13642
+ __name(sentinelExitCode, "sentinelExitCode");
13643
+ function trimCapture(s) {
13644
+ if (s.length <= CAPTURE_MAX) return { text: s, truncated: false };
13645
+ return {
13646
+ text: `... [${s.length - CAPTURE_MAX} characters omitted from the head] ...
13647
+ ${s.slice(-CAPTURE_MAX)}`,
13648
+ truncated: true
13649
+ };
13650
+ }
13651
+ __name(trimCapture, "trimCapture");
13652
+ function runDrive(args) {
13653
+ const exec = args.exec ?? run3;
13654
+ const server = args.server;
13655
+ if (!SERVER_NAME_RE.test(server)) {
13656
+ return {
13657
+ outcome: "unavailable",
13658
+ observed: false,
13659
+ exitCode: null,
13660
+ readyAfterMs: null,
13661
+ droveForMs: 0,
13662
+ output: "",
13663
+ truncated: false,
13664
+ killedStale: false,
13665
+ note: `--server ${JSON.stringify(server)} is not a name this command will own: it becomes both a path under the temp dir and a word in the shell line tmux runs, so it is restricted to letters, digits, dot, dash and underscore (max 64). Nothing was started.`
13666
+ };
13667
+ }
13668
+ const tmux = /* @__PURE__ */ __name((...a) => exec("tmux", ["-L", server, ...a]), "tmux");
13669
+ if (exec("tmux", ["-V"]).status !== 0) {
13670
+ return {
13671
+ outcome: "unavailable",
13672
+ observed: false,
13673
+ exitCode: null,
13674
+ readyAfterMs: null,
13675
+ droveForMs: 0,
13676
+ output: "",
13677
+ truncated: false,
13678
+ killedStale: false,
13679
+ note: "tmux is not available, so nothing could be driven \u2014 an environment gap, not a finding about the diff"
13680
+ };
13681
+ }
13682
+ const killedStale = tmux("kill-server").status === 0;
13683
+ const started = Date.now();
13684
+ let readyAfterMs = null;
13685
+ if (args.ready) {
13686
+ const deadline = started + args.readyTimeout * 1e3;
13687
+ for (; ; ) {
13688
+ if (exec("bash", ["-lc", args.ready]).status === 0) {
13689
+ readyAfterMs = Date.now() - started;
13690
+ break;
13691
+ }
13692
+ if (Date.now() >= deadline) {
13693
+ tmux("kill-server");
13694
+ return {
13695
+ outcome: "not-ready",
13696
+ observed: false,
13697
+ exitCode: null,
13698
+ readyAfterMs: null,
13699
+ droveForMs: 0,
13700
+ output: "",
13701
+ truncated: false,
13702
+ killedStale,
13703
+ note: `readiness probe never succeeded within ${args.readyTimeout}s (\`${args.ready}\`) \u2014 nothing was driven, so nothing here is evidence about the diff. A slower machine needs a larger --ready-timeout; a probe that can never pass needs a different probe.`
13704
+ };
13705
+ }
13706
+ waitMs(POLL_MS);
13707
+ }
13708
+ }
13709
+ const dir = join16(tmpdir2(), `qwen-review-drive-${server}`);
13710
+ mkdirSync19(dir, { recursive: true });
13711
+ const scriptPath = join16(dir, "drive.sh");
13712
+ const logPath = args.logPath ?? join16(dir, "drive.log");
13713
+ const sentinelPath = join16(dir, "drive.rc");
13714
+ rmSync8(sentinelPath, { force: true });
13715
+ writeFileSync20(scriptPath, wrapScript(args.script, sentinelPath), "utf8");
13716
+ const droveFrom = Date.now();
13717
+ let output = "";
13718
+ let exitCode = null;
13719
+ let outcome = "timed-out";
13720
+ try {
13721
+ const create = tmux(
13722
+ "new-session",
13723
+ "-d",
13724
+ "-c",
13725
+ args.cwd,
13726
+ "bash",
13727
+ "-lc",
13728
+ `bash ${shellQuote(scriptPath)} > ${shellQuote(logPath)} 2>&1`
13729
+ );
13730
+ if (create.status !== 0) {
13731
+ return {
13732
+ outcome: "unavailable",
13733
+ observed: false,
13734
+ exitCode: null,
13735
+ readyAfterMs,
13736
+ droveForMs: 0,
13737
+ output: "",
13738
+ truncated: false,
13739
+ killedStale,
13740
+ note: `tmux could not start a session: ${create.stderr.trim() || "no error text"} \u2014 an environment gap, not a finding`
13741
+ };
13742
+ }
13743
+ const deadline = droveFrom + args.timeout * 1e3;
13744
+ for (; ; ) {
13745
+ output = existsSync10(logPath) ? readFileSync22(logPath, "utf8") : "";
13746
+ exitCode = existsSync10(sentinelPath) ? sentinelExitCode(readFileSync22(sentinelPath, "utf8")) : null;
13747
+ if (exitCode !== null) {
13748
+ outcome = "completed";
13749
+ break;
13750
+ }
13751
+ if (logBytes(logPath) > LOG_MAX_BYTES) {
13752
+ outcome = "overflowed";
13753
+ break;
13754
+ }
13755
+ if (Date.now() >= deadline) break;
13756
+ waitMs(POLL_MS);
13757
+ }
13758
+ } finally {
13759
+ tmux("kill-server");
13760
+ if (!args.logPath) rmSync8(dir, { recursive: true, force: true });
13761
+ }
13762
+ const { text, truncated } = trimCapture(output);
13763
+ const droveForMs = Date.now() - droveFrom;
13764
+ const note = outcome === "overflowed" ? `the drive wrote more than ${Math.round(LOG_MAX_BYTES / 1024 / 1024)} MiB and was stopped \u2014 no exit code is reported because it never gave one, and a run this command had to stop is not evidence about the diff either way. Quieten the script, or have it manage its own output file.` : outcome === "completed" ? `drove for ${Math.round(droveForMs / 1e3)}s and reached its sentinel with exit ${exitCode}${readyAfterMs === null ? "" : ` (ready after ${Math.round(readyAfterMs / 1e3)}s)`}${truncated ? "; the capture was trimmed at the head, so early output is missing" : ""}` : `the drive did not finish within ${args.timeout}s \u2014 the capture below is PARTIAL, and a partial capture is not evidence that the run produced nothing. Raise --timeout, or give the script a smaller job.`;
13765
+ return {
13766
+ outcome,
13767
+ observed: outcome === "completed",
13768
+ exitCode,
13769
+ readyAfterMs,
13770
+ droveForMs,
13771
+ output: text,
13772
+ truncated,
13773
+ killedStale,
13774
+ note
13775
+ };
13776
+ }
13777
+ __name(runDrive, "runDrive");
13778
+ var driveCommand = {
13779
+ command: "drive",
13780
+ describe: "Start something, wait until it is really up, drive it, and capture what it did \u2014 readiness polled rather than slept on, completion proven by a sentinel, cleanup guaranteed",
13781
+ builder: /* @__PURE__ */ __name((yargs) => yargs.option("script", {
13782
+ type: "string",
13783
+ demandOption: true,
13784
+ describe: "Shell script to drive (its exit code is captured)"
13785
+ }).option("cwd", {
13786
+ type: "string",
13787
+ demandOption: true,
13788
+ describe: "Working directory \u2014 usually the PR or base worktree"
13789
+ }).option("ready", {
13790
+ type: "string",
13791
+ describe: "Command polled until it exits 0 before driving (omit only when nothing needs to come up first)"
13792
+ }).option("ready-timeout", {
13793
+ type: "number",
13794
+ default: 60,
13795
+ describe: "Seconds to wait for readiness"
13796
+ }).option("timeout", {
13797
+ type: "number",
13798
+ default: 300,
13799
+ describe: "Seconds to wait for the script to reach its sentinel"
13800
+ }).option("server", {
13801
+ type: "string",
13802
+ default: `qr-${process.pid}`,
13803
+ describe: "tmux server name \u2014 namespaced so runs cannot capture each other"
13804
+ }).option("out", {
13805
+ type: "string",
13806
+ describe: "Write the JSON report here"
13807
+ }), "builder"),
13808
+ handler: /* @__PURE__ */ __name((argv) => {
13809
+ try {
13810
+ const args = argv;
13811
+ const report = runDrive(args);
13812
+ if (args.out) {
13813
+ mkdirSync19(dirname17(resolve17(args.out)), { recursive: true });
13814
+ writeFileSync20(resolve17(args.out), JSON.stringify(report, null, 2));
13815
+ }
13816
+ writeStdoutLine(JSON.stringify(report, null, 2));
13817
+ writeStderrLine(`drive: ${report.note}`);
13818
+ if (!report.observed) process.exitCode = 1;
13819
+ } catch (err) {
13820
+ writeStderrLine(err.message);
13821
+ process.exitCode = 1;
13822
+ }
13823
+ }, "handler")
13824
+ };
13825
+
13826
+ // packages/cli/src/commands/review/mock-provider.ts
13827
+ init_esbuild_shims();
13828
+ import {
13829
+ createServer
13830
+ } from "node:http";
13831
+ import { appendFileSync, mkdirSync as mkdirSync20, writeFileSync as writeFileSync21 } from "node:fs";
13832
+ import { dirname as dirname18, resolve as resolve18 } from "node:path";
13833
+ import { pathToFileURL } from "node:url";
13834
+ function messagesText(body) {
13835
+ const msgs = Array.isArray(body?.["messages"]) ? body["messages"] : [];
13836
+ const one = /* @__PURE__ */ __name((m) => {
13837
+ const c = m?.content;
13838
+ if (typeof c === "string") return c;
13839
+ if (Array.isArray(c))
13840
+ return c.map((p) => String(p?.text ?? "")).join("");
13841
+ return "";
13842
+ }, "one");
13843
+ return msgs.map(one).join("\n");
13844
+ }
13845
+ __name(messagesText, "messagesText");
13846
+ var enc = /* @__PURE__ */ __name((o) => `data: ${JSON.stringify(o)}
13847
+
13848
+ `, "enc");
13849
+ var BODY_MAX_BYTES = 4 * 1024 * 1024;
13850
+ var RECORD_TEXT_MAX = 8 * 1024;
13851
+ function replyProblem(r) {
13852
+ if (r === null || typeof r !== "object")
13853
+ return `responder returned ${r === void 0 ? "undefined" : JSON.stringify(r)}; it must return {text} | {tool, args?} | {status, body?}`;
13854
+ const o = r;
13855
+ if ("status" in o) {
13856
+ const st = o["status"];
13857
+ if (!(typeof st === "number" && Number.isInteger(st) && st >= 100 && st <= 599))
13858
+ return `responder returned a non-HTTP status ${JSON.stringify(st)}`;
13859
+ try {
13860
+ JSON.stringify(o["body"] ?? {});
13861
+ } catch {
13862
+ return "responder returned a status body that cannot be serialised (a circular reference?)";
13863
+ }
13864
+ return null;
13865
+ }
13866
+ if ("tool" in o) {
13867
+ if (typeof o["tool"] !== "string" || o["tool"] === "")
13868
+ return `responder returned an empty or non-string tool name ${JSON.stringify(o["tool"])}`;
13869
+ try {
13870
+ JSON.stringify(o["args"] ?? {});
13871
+ } catch {
13872
+ return "responder returned tool args that cannot be serialised (a circular reference?)";
13873
+ }
13874
+ return null;
13875
+ }
13876
+ if ("text" in o)
13877
+ return typeof o["text"] === "string" ? null : `responder returned a non-string text ${JSON.stringify(o["text"])}`;
13878
+ return `responder returned an object with none of text/tool/status: ${JSON.stringify(o).slice(0, 80)}`;
13879
+ }
13880
+ __name(replyProblem, "replyProblem");
13881
+ function forRecord(v) {
13882
+ return v.length <= RECORD_TEXT_MAX ? v : `${v.slice(0, RECORD_TEXT_MAX)}\u2026[${v.length - RECORD_TEXT_MAX} more characters]`;
13883
+ }
13884
+ __name(forRecord, "forRecord");
13885
+ function forRecordReq(r) {
13886
+ return {
13887
+ method: r.method,
13888
+ path: r.path,
13889
+ wire: r.wire,
13890
+ n: r.n,
13891
+ stream: r.stream,
13892
+ text: forRecord(r.text),
13893
+ bodyKeys: r.body ? Object.keys(r.body) : []
13894
+ };
13895
+ }
13896
+ __name(forRecordReq, "forRecordReq");
13897
+ function chunk(delta, finish = null) {
13898
+ return {
13899
+ id: "chatcmpl-qwen-review-mock",
13900
+ object: "chat.completion.chunk",
13901
+ created: Math.floor(Date.now() / 1e3),
13902
+ model: "qwen-review-mock",
13903
+ choices: [{ index: 0, delta, finish_reason: finish }]
13904
+ };
13905
+ }
13906
+ __name(chunk, "chunk");
13907
+ function chunksFor(reply) {
13908
+ const out = [
13909
+ chunk({ role: "assistant", content: "" })
13910
+ ];
13911
+ if ("tool" in reply) {
13912
+ out.push(
13913
+ chunk({
13914
+ tool_calls: [
13915
+ {
13916
+ index: 0,
13917
+ id: `call_${reply.tool}_1`,
13918
+ type: "function",
13919
+ function: {
13920
+ name: reply.tool,
13921
+ arguments: JSON.stringify(reply.args ?? {})
13922
+ }
13923
+ }
13924
+ ]
13925
+ }),
13926
+ chunk({}, "tool_calls")
13927
+ );
13928
+ return out;
13929
+ }
13930
+ if ("text" in reply) {
13931
+ for (const piece of reply.text.match(/[\s\S]{1,40}/g) ?? [])
13932
+ out.push(chunk({ content: piece }));
13933
+ }
13934
+ out.push(chunk({}, "stop"));
13935
+ return out;
13936
+ }
13937
+ __name(chunksFor, "chunksFor");
13938
+ function completionFor(reply) {
13939
+ const message = "tool" in reply ? {
13940
+ role: "assistant",
13941
+ content: null,
13942
+ tool_calls: [
13943
+ {
13944
+ index: 0,
13945
+ id: `call_${reply.tool}_1`,
13946
+ type: "function",
13947
+ function: {
13948
+ name: reply.tool,
13949
+ arguments: JSON.stringify(reply.args ?? {})
13950
+ }
13951
+ }
13952
+ ]
13953
+ } : { role: "assistant", content: "text" in reply ? reply.text : "" };
13954
+ return {
13955
+ id: "chatcmpl-qwen-review-mock",
13956
+ object: "chat.completion",
13957
+ created: Math.floor(Date.now() / 1e3),
13958
+ model: "qwen-review-mock",
13959
+ choices: [
13960
+ {
13961
+ index: 0,
13962
+ message,
13963
+ finish_reason: "tool" in reply ? "tool_calls" : "stop"
13964
+ }
13965
+ ],
13966
+ // 69% of the corpus carried one, and a client that reads token counts
13967
+ // behaves differently without it — an absent usage block is a behaviour
13968
+ // difference the mock introduced, not one the diff did.
13969
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
13970
+ };
13971
+ }
13972
+ __name(completionFor, "completionFor");
13973
+ function sseEvent(type, data) {
13974
+ return `event: ${type}
13975
+ data: ${JSON.stringify({ type, ...data })}
13976
+
13977
+ `;
13978
+ }
13979
+ __name(sseEvent, "sseEvent");
13980
+ function anthropicStream(reply) {
13981
+ const usage = {
13982
+ input_tokens: 0,
13983
+ output_tokens: 0,
13984
+ cache_creation_input_tokens: 0,
13985
+ cache_read_input_tokens: 0
13986
+ };
13987
+ let out = sseEvent("message_start", {
13988
+ message: {
13989
+ id: "msg_qwen_review_mock",
13990
+ type: "message",
13991
+ role: "assistant",
13992
+ model: "qwen-review-mock",
13993
+ content: [],
13994
+ stop_reason: null,
13995
+ usage
13996
+ }
13997
+ });
13998
+ if ("tool" in reply) {
13999
+ out += sseEvent("content_block_start", {
14000
+ index: 0,
14001
+ content_block: {
14002
+ type: "tool_use",
14003
+ id: `toolu_${reply.tool}_1`,
14004
+ name: reply.tool,
14005
+ input: {}
14006
+ }
14007
+ });
14008
+ out += sseEvent("content_block_delta", {
14009
+ index: 0,
14010
+ delta: {
14011
+ type: "input_json_delta",
14012
+ partial_json: JSON.stringify(reply.args ?? {})
14013
+ }
14014
+ });
14015
+ out += sseEvent("content_block_stop", { index: 0 });
14016
+ out += sseEvent("message_delta", {
14017
+ delta: { stop_reason: "tool_use", stop_sequence: null },
14018
+ usage: { output_tokens: 0 }
14019
+ });
14020
+ } else {
14021
+ const text = "text" in reply ? reply.text : "";
14022
+ out += sseEvent("content_block_start", {
14023
+ index: 0,
14024
+ content_block: { type: "text", text: "" }
14025
+ });
14026
+ for (const piece of text.match(/[\s\S]{1,40}/g) ?? [])
14027
+ out += sseEvent("content_block_delta", {
14028
+ index: 0,
14029
+ delta: { type: "text_delta", text: piece }
14030
+ });
14031
+ out += sseEvent("content_block_stop", { index: 0 });
14032
+ out += sseEvent("message_delta", {
14033
+ delta: { stop_reason: "end_turn", stop_sequence: null },
14034
+ usage: { output_tokens: 0 }
14035
+ });
14036
+ }
14037
+ return out + sseEvent("message_stop", {});
14038
+ }
14039
+ __name(anthropicStream, "anthropicStream");
14040
+ function anthropicMessage(reply) {
14041
+ const content = "tool" in reply ? [
14042
+ {
14043
+ type: "tool_use",
14044
+ id: `toolu_${reply.tool}_1`,
14045
+ name: reply.tool,
14046
+ input: reply.args ?? {}
14047
+ }
14048
+ ] : [{ type: "text", text: "text" in reply ? reply.text : "" }];
14049
+ return {
14050
+ id: "msg_qwen_review_mock",
14051
+ type: "message",
14052
+ role: "assistant",
14053
+ model: "qwen-review-mock",
14054
+ content,
14055
+ stop_reason: "tool" in reply ? "tool_use" : "end_turn",
14056
+ stop_sequence: null,
14057
+ usage: {
14058
+ input_tokens: 0,
14059
+ output_tokens: 0,
14060
+ cache_creation_input_tokens: 0,
14061
+ cache_read_input_tokens: 0
14062
+ }
14063
+ };
14064
+ }
14065
+ __name(anthropicMessage, "anthropicMessage");
14066
+ function anthropicText(body) {
14067
+ const sys = body?.["system"];
14068
+ const sysText = typeof sys === "string" ? sys : Array.isArray(sys) ? sys.map((b) => String(b?.text ?? "")).join("") : "";
14069
+ const rest = messagesText(body);
14070
+ return sysText && rest ? `${sysText}
14071
+ ${rest}` : sysText || rest;
14072
+ }
14073
+ __name(anthropicText, "anthropicText");
14074
+ var DEFAULT_REPLY = { text: "ok" };
14075
+ async function startMockProvider(args, respondOverride) {
14076
+ let respond = respondOverride ?? (() => DEFAULT_REPLY);
14077
+ if (!respondOverride && args.responder) {
14078
+ const mod = await import(pathToFileURL(resolve18(args.responder)).href);
14079
+ const fn = mod.respond ?? mod.default;
14080
+ if (typeof fn !== "function") {
14081
+ throw new Error(
14082
+ `mock-provider: ${args.responder} exports no \`respond\` function \u2014 a responder module must export \`respond(req)\` (or a default export of the same shape)`
14083
+ );
14084
+ }
14085
+ respond = fn;
14086
+ }
14087
+ const logPath = resolve18(args.log);
14088
+ mkdirSync20(dirname18(logPath), { recursive: true });
14089
+ writeFileSync21(logPath, "");
14090
+ let n = 0;
14091
+ const record = /* @__PURE__ */ __name((entry) => appendFileSync(logPath, `${JSON.stringify(entry)}
14092
+ `), "record");
14093
+ const server = createServer((req, res) => {
14094
+ const parts = [];
14095
+ let size = 0;
14096
+ let tooBig = false;
14097
+ req.on("data", (d) => {
14098
+ size += d.length;
14099
+ if (size > BODY_MAX_BYTES) {
14100
+ tooBig = true;
14101
+ parts.length = 0;
14102
+ return;
14103
+ }
14104
+ parts.push(d);
14105
+ });
14106
+ req.on("end", () => {
14107
+ if (tooBig) {
14108
+ appendFileSync(
14109
+ logPath,
14110
+ `${JSON.stringify({ t: Date.now(), method: req.method, path: req.url, refused: "body-too-large", bytes: size })}
14111
+ `
14112
+ );
14113
+ res.writeHead(413, { "Content-Type": "application/json" });
14114
+ res.end(
14115
+ JSON.stringify({
14116
+ error: `request body exceeded ${BODY_MAX_BYTES} bytes; refused rather than truncated, because a truncated body is a different request`
14117
+ })
14118
+ );
14119
+ return;
14120
+ }
14121
+ void (async () => {
14122
+ const raw = Buffer.concat(parts).toString("utf8");
14123
+ let body = null;
14124
+ try {
14125
+ body = raw ? JSON.parse(raw) : null;
14126
+ } catch {
14127
+ body = null;
14128
+ }
14129
+ const path3 = req.url ?? "/";
14130
+ const isModels = path3.startsWith("/v1/models");
14131
+ const chat = path3.includes("/chat/completions") || path3.includes("/v1/messages");
14132
+ const willAsk = !isModels && chat && (req.method ?? "GET") === "POST" && body !== null;
14133
+ const wire = path3.includes("/v1/messages") ? "anthropic" : "openai";
14134
+ if (willAsk) n += 1;
14135
+ const mreq = {
14136
+ method: req.method ?? "GET",
14137
+ path: path3,
14138
+ body,
14139
+ wire,
14140
+ text: wire === "anthropic" ? anthropicText(body) : messagesText(body),
14141
+ stream: body?.["stream"] === true,
14142
+ // Null for anything the responder was never asked about, so the
14143
+ // record cannot be read as "the responder saw this".
14144
+ n: willAsk ? n : null
14145
+ };
14146
+ if (!isModels) {
14147
+ const why = !chat ? `this mock serves /v1/chat/completions and /v1/messages only; ${path3} is not one of them` : mreq.method !== "POST" ? `${mreq.method} is not how either chat endpoint is called; use POST` : body === null ? "the request body was empty or not JSON, so there is nothing to answer" : null;
14148
+ if (why) {
14149
+ record({ t: Date.now(), ...forRecordReq(mreq), refused: why });
14150
+ res.writeHead(400, { "Content-Type": "application/json" });
14151
+ res.end(JSON.stringify({ error: why }));
14152
+ return;
14153
+ }
14154
+ }
14155
+ if (isModels) {
14156
+ record({ t: Date.now(), ...forRecordReq(mreq), reply: "models" });
14157
+ res.writeHead(200, { "Content-Type": "application/json" });
14158
+ res.end(
14159
+ JSON.stringify({
14160
+ object: "list",
14161
+ data: [{ id: "qwen-review-mock", object: "model" }]
14162
+ })
14163
+ );
14164
+ return;
14165
+ }
14166
+ let reply;
14167
+ try {
14168
+ reply = await respond(mreq);
14169
+ const problem = replyProblem(reply);
14170
+ if (problem) throw new Error(problem);
14171
+ } catch (err) {
14172
+ reply = {
14173
+ status: 500,
14174
+ body: { error: String(err.message) }
14175
+ };
14176
+ }
14177
+ record({ t: Date.now(), ...forRecordReq(mreq), reply });
14178
+ if ("status" in reply) {
14179
+ res.writeHead(reply.status, { "Content-Type": "application/json" });
14180
+ res.end(JSON.stringify(reply.body ?? {}));
14181
+ return;
14182
+ }
14183
+ if (!mreq.stream) {
14184
+ res.writeHead(200, { "Content-Type": "application/json" });
14185
+ res.end(
14186
+ JSON.stringify(
14187
+ wire === "anthropic" ? anthropicMessage(reply) : completionFor(reply)
14188
+ )
14189
+ );
14190
+ return;
14191
+ }
14192
+ res.writeHead(200, {
14193
+ "Content-Type": "text/event-stream",
14194
+ "Cache-Control": "no-cache",
14195
+ Connection: "keep-alive"
14196
+ });
14197
+ if (wire === "anthropic") {
14198
+ res.write(anthropicStream(reply));
14199
+ } else {
14200
+ for (const c of chunksFor(reply)) res.write(enc(c));
14201
+ res.write("data: [DONE]\n\n");
14202
+ }
14203
+ res.end();
14204
+ })();
14205
+ });
14206
+ });
14207
+ await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
14208
+ const addr = server.address();
14209
+ const port = typeof addr === "object" && addr ? addr.port : 0;
14210
+ return {
14211
+ report: {
14212
+ port,
14213
+ baseUrl: `http://127.0.0.1:${port}/v1`,
14214
+ logPath,
14215
+ note: `mock provider on port ${port}; every request is appended to ${logPath} as JSONL \u2014 diff those between two trees and the difference is evidence`
14216
+ },
14217
+ close: /* @__PURE__ */ __name(() => new Promise((ok) => {
14218
+ server.close(() => ok());
14219
+ }), "close")
12738
14220
  };
12739
14221
  }
12740
- __name(runTestDelta, "runTestDelta");
12741
- var testDeltaCommand = {
12742
- command: "test-delta",
12743
- describe: "Rerun the PR side's failed test commands on the base tree and report which failing files are the PR's own (net-new) vs pre-existing (shared)",
12744
- builder: /* @__PURE__ */ __name((yargs) => yargs.option("report", {
14222
+ __name(startMockProvider, "startMockProvider");
14223
+ var mockProviderCommand = {
14224
+ command: "mock-provider",
14225
+ describe: "Serve OpenAI (/v1/chat/completions) and Anthropic (/v1/messages) endpoints for the review to drive the real product against, recording every request as JSONL \u2014 the protocol is provided, the answers are yours; for any other kind of upstream, start it yourself and use `drive`",
14226
+ builder: /* @__PURE__ */ __name((yargs) => yargs.option("responder", {
12745
14227
  type: "string",
12746
- demandOption: true,
12747
- describe: "Agent 7's build-test report (its failed commands and outputs)"
12748
- }).option("baseline", {
14228
+ describe: "Module exporting `respond(req)` returning {text} | {tool,args} | {status,body}"
14229
+ }).option("log", {
12749
14230
  type: "string",
12750
14231
  demandOption: true,
12751
- describe: "The BUILT base tree from `qwen review base-tree`"
12752
- }).option("pr-worktree", {
12753
- type: "string",
12754
- describe: "The PR worktree the report's failures were produced in \u2014 its root is stripped so both sides compare as repo-relative paths"
12755
- }).option("out", { type: "string", describe: "Write the JSON report here" }).option("timeout", {
14232
+ describe: "Where to append the JSONL request record"
14233
+ }).option("ttl", {
12756
14234
  type: "number",
12757
- default: 300,
12758
- describe: "Per-command deadline in seconds, as build-test"
14235
+ default: 600,
14236
+ describe: "Seconds to serve before shutting down on its own"
14237
+ }).option("out", {
14238
+ type: "string",
14239
+ describe: "Write the JSON report here"
12759
14240
  }), "builder"),
12760
- handler: /* @__PURE__ */ __name((argv) => {
12761
- const args = argv;
12762
- const report = runTestDelta(args);
12763
- if (args.out) {
12764
- mkdirSync16(dirname15(resolve15(args.out)), { recursive: true });
12765
- writeFileSync18(resolve15(args.out), JSON.stringify(report, null, 2));
14241
+ // `async`, and the returned promise is what keeps the process alive. A
14242
+ // fire-and-forget `void (async () => …)()` here produced a command that
14243
+ // printed nothing at all and exited: yargs returned, node found no pending
14244
+ // work it could see, and the server it had just bound went with it. Measured
14245
+ // — both streams empty, exit 0, a mock that was never reachable.
14246
+ handler: /* @__PURE__ */ __name(async (argv) => {
14247
+ try {
14248
+ const args = argv;
14249
+ const { report, close } = await startMockProvider(args);
14250
+ if (args.out) {
14251
+ mkdirSync20(dirname18(resolve18(args.out)), { recursive: true });
14252
+ writeFileSync21(resolve18(args.out), JSON.stringify(report, null, 2));
14253
+ }
14254
+ writeStdoutLine(JSON.stringify(report, null, 2));
14255
+ writeStderrLine(`mock-provider: ${report.note}`);
14256
+ await new Promise((done) => {
14257
+ setTimeout(done, Math.max(1, args.ttl) * 1e3);
14258
+ });
14259
+ await close();
14260
+ } catch (err) {
14261
+ writeStderrLine(err.message);
14262
+ process.exitCode = 1;
12766
14263
  }
12767
- writeStdoutLine(JSON.stringify(report, null, 2));
12768
- writeStderrLine(`test-delta: ${report.note}`);
12769
14264
  }, "handler")
12770
14265
  };
12771
14266
 
12772
14267
  // packages/cli/src/commands/review/extract-step.ts
12773
14268
  init_esbuild_shims();
12774
14269
  var import_yaml = __toESM(require_dist(), 1);
12775
- import { mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync19, chmodSync } from "node:fs";
12776
- import { dirname as dirname16, resolve as resolve16 } from "node:path";
14270
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync22, chmodSync } from "node:fs";
14271
+ import { dirname as dirname19, resolve as resolve19 } from "node:path";
12777
14272
  function expressionsOf(...texts) {
12778
14273
  const seen = /* @__PURE__ */ new Set();
12779
14274
  for (const t2 of texts) {
@@ -12986,8 +14481,8 @@ function nearestScoped(...values) {
12986
14481
  __name(nearestScoped, "nearestScoped");
12987
14482
  function runDefaultsOf(container) {
12988
14483
  const defaults = container?.defaults;
12989
- const run3 = defaults && typeof defaults === "object" ? defaults.run : void 0;
12990
- return run3 && typeof run3 === "object" ? run3 : {};
14484
+ const run4 = defaults && typeof defaults === "object" ? defaults.run : void 0;
14485
+ return run4 && typeof run4 === "object" ? run4 : {};
12991
14486
  }
12992
14487
  __name(runDefaultsOf, "runDefaultsOf");
12993
14488
  function envValue(v) {
@@ -13017,10 +14512,10 @@ function nearestFirst(env, sources) {
13017
14512
  }
13018
14513
  __name(nearestFirst, "nearestFirst");
13019
14514
  function runExtractStep(args) {
13020
- const wfPath = resolve16(args.workflow);
14515
+ const wfPath = resolve19(args.workflow);
13021
14516
  let text;
13022
14517
  try {
13023
- text = readFileSync20(wfPath, "utf8");
14518
+ text = readFileSync23(wfPath, "utf8");
13024
14519
  } catch (err) {
13025
14520
  throw new Error(
13026
14521
  `extract-step: cannot read ${args.workflow}: ${err.message}`
@@ -13086,8 +14581,8 @@ function runExtractStep(args) {
13086
14581
  const workingDirectory = workingDir?.value;
13087
14582
  const script = step.run;
13088
14583
  const stepLabel = String(step.name ?? step.id ?? index);
13089
- const outPath = resolve16(args.out);
13090
- mkdirSync17(dirname16(outPath), { recursive: true });
14584
+ const outPath = resolve19(args.out);
14585
+ mkdirSync21(dirname19(outPath), { recursive: true });
13091
14586
  const header = [
13092
14587
  `#!/usr/bin/env ${shellCommand}`,
13093
14588
  ...commentLines(
@@ -13111,7 +14606,7 @@ function runExtractStep(args) {
13111
14606
  ),
13112
14607
  ""
13113
14608
  ].join("\n");
13114
- writeFileSync19(outPath, header + script + (script.endsWith("\n") ? "" : "\n"));
14609
+ writeFileSync22(outPath, header + script + (script.endsWith("\n") ? "" : "\n"));
13115
14610
  chmodSync(outPath, 493);
13116
14611
  return {
13117
14612
  workflow: args.workflow,
@@ -13172,7 +14667,7 @@ var extractStepCommand = {
13172
14667
 
13173
14668
  // packages/cli/src/commands/review/submit.ts
13174
14669
  init_esbuild_shims();
13175
- import { mkdirSync as mkdirSync19, readFileSync as readFileSync21 } from "node:fs";
14670
+ import { mkdirSync as mkdirSync22, readFileSync as readFileSync24 } from "node:fs";
13176
14671
 
13177
14672
  // packages/cli/src/commands/review/lib/receipt.ts
13178
14673
  init_esbuild_shims();
@@ -13190,123 +14685,11 @@ function parseReceiptIds(raw) {
13190
14685
  }
13191
14686
  __name(parseReceiptIds, "parseReceiptIds");
13192
14687
 
13193
- // packages/cli/src/services/skill-args-file.ts
13194
- init_esbuild_shims();
13195
- import {
13196
- mkdirSync as mkdirSync18,
13197
- openSync as openSync2,
13198
- writeSync,
13199
- closeSync as closeSync2,
13200
- rmSync as rmSync7,
13201
- lstatSync as lstatSync3,
13202
- existsSync as existsSync8,
13203
- constants
13204
- } from "node:fs";
13205
- import { join as join15 } from "node:path";
13206
- var debugLogger3 = createDebugLogger("SKILL_ARGS_FILE");
13207
- var SKILL_ARGS_DIR = join15(".qwen", "tmp");
13208
- function safe(s) {
13209
- return s.replace(/[^A-Za-z0-9._-]/g, "_");
13210
- }
13211
- __name(safe, "safe");
13212
- function currentSessionId() {
13213
- return sessionIdContext.getStore()?.trim() || process.env["QWEN_CODE_SESSION_ID"]?.trim() || "";
13214
- }
13215
- __name(currentSessionId, "currentSessionId");
13216
- function skillArgsDir(sessionId = currentSessionId()) {
13217
- return sessionId ? join15(SKILL_ARGS_DIR, `s-${safe(sessionId)}`) : SKILL_ARGS_DIR;
13218
- }
13219
- __name(skillArgsDir, "skillArgsDir");
13220
- function skillArgsPath(skillName, sessionId = currentSessionId()) {
13221
- return join15(
13222
- skillArgsDir(sessionId),
13223
- `qwen-skill-args-${safe(skillName)}.txt`
13224
- );
13225
- }
13226
- __name(skillArgsPath, "skillArgsPath");
13227
- function writeSkillArgs(skillName, args) {
13228
- const path3 = skillArgsPath(skillName);
13229
- try {
13230
- const dir = skillArgsDir();
13231
- mkdirSync18(dir, { recursive: true });
13232
- if (lstatSync3(dir).isSymbolicLink()) {
13233
- debugLogger3.warn(
13234
- `Skill args directory ${dir} is a symlink; refusing to write through it.`
13235
- );
13236
- return null;
13237
- }
13238
- if (lstatSync3(path3, { throwIfNoEntry: false })?.isSymbolicLink()) {
13239
- debugLogger3.warn(
13240
- `Skill args file ${path3} is a symlink; refusing to write through it.`
13241
- );
13242
- return null;
13243
- }
13244
- const fd = openSync2(
13245
- path3,
13246
- constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW,
13247
- 384
13248
- );
13249
- try {
13250
- const buf = Buffer.from(args, "utf8");
13251
- let off = 0;
13252
- while (off < buf.length) {
13253
- off += writeSync(fd, buf, off, buf.length - off);
13254
- }
13255
- } finally {
13256
- closeSync2(fd);
13257
- }
13258
- return path3;
13259
- } catch (err) {
13260
- debugLogger3.warn(
13261
- `Could not write skill args to ${path3}: ${err.message}`
13262
- );
13263
- return null;
13264
- }
13265
- }
13266
- __name(writeSkillArgs, "writeSkillArgs");
13267
- function clearSkillArgs(skillName) {
13268
- const path3 = skillArgsPath(skillName);
13269
- try {
13270
- rmSync7(path3, { force: true });
13271
- } catch (err) {
13272
- debugLogger3.warn(
13273
- `Could not clear skill args for ${skillName}: ${err.message}`
13274
- );
13275
- return false;
13276
- }
13277
- if (existsSync8(path3)) {
13278
- debugLogger3.warn(`Skill args for ${skillName} survived removal at ${path3}`);
13279
- return false;
13280
- }
13281
- return true;
13282
- }
13283
- __name(clearSkillArgs, "clearSkillArgs");
13284
- function staleArgsWarning() {
13285
- return `
13286
-
13287
- <skill-args-stale>A previous invocation's argument record could not be removed, so it is still on disk and still names whatever it named. This invocation supplied NO arguments. Do not treat that stale record as this run's authorisation: this run has none, and must not post.</skill-args-stale>
13288
- `;
13289
- }
13290
- __name(staleArgsWarning, "staleArgsWarning");
13291
- function skillArgsNote(path3, args) {
13292
- return `
13293
-
13294
- Your invocation arguments have been written verbatim to a session-private file. Its exact path is below \u2014 use it wherever these instructions say to read the args file (e.g. \`< '${path3}'\`), and do not retype the arguments, which is how they get mistyped.
13295
- <skill-args-file>${path3}</skill-args-file>
13296
- <skill-args>${args}</skill-args>
13297
- `;
13298
- }
13299
- __name(skillArgsNote, "skillArgsNote");
13300
-
13301
14688
  // packages/cli/src/commands/review/submit.ts
13302
- function defaultSkillArgsPath() {
13303
- return skillArgsPath("review");
13304
- }
13305
- __name(defaultSkillArgsPath, "defaultSkillArgsPath");
13306
14689
  var EVENTS = /* @__PURE__ */ new Set(["APPROVE", "REQUEST_CHANGES", "COMMENT"]);
13307
14690
  function readReceiptIds(receiptPath) {
13308
14691
  try {
13309
- return parseReceiptIds(readFileSync21(receiptPath, "utf8"));
14692
+ return parseReceiptIds(readFileSync24(receiptPath, "utf8"));
13310
14693
  } catch {
13311
14694
  return [];
13312
14695
  }
@@ -13317,60 +14700,20 @@ function isDiffLine(n) {
13317
14700
  }
13318
14701
  __name(isDiffLine, "isDiffLine");
13319
14702
  function authorization(args) {
13320
- if (args.userAuthorized) {
13321
- return { ok: true, why: "the user asked for this review to be published" };
13322
- }
13323
- const sessionScoped = defaultSkillArgsPath();
13324
- const path3 = currentSessionId() === "" && args.skillArgs ? args.skillArgs : sessionScoped;
13325
- let raw;
13326
- try {
13327
- raw = readFileSync21(path3, "utf8");
13328
- } catch {
13329
- return {
13330
- ok: false,
13331
- why: `no review arguments were recorded at ${path3}, so this run cannot show that \`--comment\` was requested`
13332
- };
13333
- }
13334
- const verdict = parseReviewArgs(raw);
13335
- if (!verdict.comment.effective) {
13336
- return {
13337
- ok: false,
13338
- why: `\`--comment\` was not in the review arguments (${JSON.stringify(raw.trim())})`
13339
- };
13340
- }
13341
- const t2 = verdict.target;
13342
- const authorisedPr = t2.type === "pr-number" || t2.type === "pr-url" ? t2.number : void 0;
13343
- if (authorisedPr === void 0) {
13344
- return {
13345
- ok: false,
13346
- why: `the review arguments (${JSON.stringify(raw.trim())}) do not name a pull request, so they cannot authorise posting to one`
13347
- };
13348
- }
13349
- if (authorisedPr !== args.pr) {
13350
- return {
13351
- ok: false,
13352
- why: `the review arguments authorise pull request #${authorisedPr}, but this submission targets #${args.pr}`
13353
- };
13354
- }
13355
- if (t2.type === "pr-url") {
13356
- const authorisedRepo = `${t2.owner}/${t2.repo}`;
13357
- if (authorisedRepo.toLowerCase() !== args.repo.toLowerCase()) {
13358
- return {
13359
- ok: false,
13360
- why: `the review arguments authorise ${authorisedRepo}, but this submission targets ${args.repo}`
13361
- };
13362
- }
13363
- if (args.host && t2.host.toLowerCase() !== args.host.toLowerCase()) {
13364
- return {
13365
- ok: false,
13366
- why: `the review arguments authorise ${t2.host}, but this submission targets ${args.host}`
13367
- };
13368
- }
13369
- }
13370
- return {
13371
- ok: true,
13372
- why: `\`--comment\` was in the review arguments for #${authorisedPr}`
13373
- };
14703
+ return reviewWriteAuthorization({
14704
+ userAuthorized: args.userAuthorized,
14705
+ skillArgs: args.skillArgs,
14706
+ pr: args.pr,
14707
+ repo: args.repo,
14708
+ // The EFFECTIVE host, not merely the flag: with --host absent the gh
14709
+ // child inherits an operator-exported GH_HOST, so that is where this
14710
+ // write would route — and what the gate must bind. Same resolution as
14711
+ // publish-assets.
14712
+ // `|| undefined`, not `??`: an exported-but-empty GH_HOST ("" survives
14713
+ // `??`, being non-nullish) must read as "no host", not as a host named
14714
+ // "" that fails every comparison.
14715
+ host: args.host ?? (process.env["GH_HOST"]?.trim() || void 0)
14716
+ });
13374
14717
  }
13375
14718
  __name(authorization, "authorization");
13376
14719
  function compose(payload, cliVersion) {
@@ -13481,7 +14824,7 @@ function runSubmit(args, cliVersion = "unknown") {
13481
14824
  }
13482
14825
  let payload;
13483
14826
  try {
13484
- payload = JSON.parse(readFileSync21(args.review, "utf8"));
14827
+ payload = JSON.parse(readFileSync24(args.review, "utf8"));
13485
14828
  } catch (err) {
13486
14829
  throw new Error(
13487
14830
  `Cannot read review JSON ${args.review}: ${err.message}`
@@ -13557,7 +14900,7 @@ Posting is a public, irreversible write, and this run has no authorisation for o
13557
14900
  const receiptPath = tmpFile(`pr-${args.pr}`, "submit-receipt.json");
13558
14901
  const priorIds = readReceiptIds(receiptPath);
13559
14902
  const reviewIds = [.../* @__PURE__ */ new Set([...priorIds, reviewId])];
13560
- mkdirSync19(REVIEW_TMP_DIR, { recursive: true });
14903
+ mkdirSync22(REVIEW_TMP_DIR, { recursive: true });
13561
14904
  atomicWriteFileSync(
13562
14905
  receiptPath,
13563
14906
  `${JSON.stringify({ reviewIds, event, postedAt: (/* @__PURE__ */ new Date()).toISOString() })}
@@ -13620,20 +14963,20 @@ var submitCommand = {
13620
14963
 
13621
14964
  // packages/cli/src/commands/review/test-efficacy.ts
13622
14965
  init_esbuild_shims();
13623
- import { spawnSync as spawnSync6 } from "node:child_process";
14966
+ import { spawnSync as spawnSync7 } from "node:child_process";
13624
14967
  import {
13625
- mkdirSync as mkdirSync20,
14968
+ mkdirSync as mkdirSync23,
13626
14969
  readdirSync as readdirSync5,
13627
- writeFileSync as writeFileSync20,
13628
- readFileSync as readFileSync22,
13629
- rmSync as rmSync8,
14970
+ writeFileSync as writeFileSync23,
14971
+ readFileSync as readFileSync25,
14972
+ rmSync as rmSync9,
13630
14973
  lstatSync as lstatSync4,
13631
- existsSync as existsSync9,
13632
- statSync as statSync6,
14974
+ existsSync as existsSync11,
14975
+ statSync as statSync8,
13633
14976
  symlinkSync
13634
14977
  } from "node:fs";
13635
14978
  import { createRequire } from "node:module";
13636
- import { dirname as dirname17, join as join16, isAbsolute as isAbsolute3, sep as sep3 } from "node:path";
14979
+ import { dirname as dirname20, join as join17, isAbsolute as isAbsolute3, sep as sep3 } from "node:path";
13637
14980
  var FIXTURE_DIR_RE = /(^|\/)(__fixtures__|__mocks__|__snapshots__|fixtures)\//;
13638
14981
  function planTestEfficacy(files, workspaceGlobs) {
13639
14982
  const tests = files.filter((f) => f.kind === "test").map((f) => f.path);
@@ -13988,6 +15331,73 @@ function collocatedProbe(file, testPaths) {
13988
15331
  });
13989
15332
  }
13990
15333
  __name(collocatedProbe, "collocatedProbe");
15334
+ function heldForRedCollocatedTest(kind, file, probes, greenProbes, baselinePerFile) {
15335
+ const own = collocatedProbe(file, probes);
15336
+ if (!own || greenProbes.includes(own)) return void 0;
15337
+ return collocatedNotGreenDetail(kind, own, baselinePerFile);
15338
+ }
15339
+ __name(heldForRedCollocatedTest, "heldForRedCollocatedTest");
15340
+ function runnerFailureReason(r) {
15341
+ return r.signal || r.error?.code === "ETIMEDOUT" ? "runner-died" : "not-run";
15342
+ }
15343
+ __name(runnerFailureReason, "runnerFailureReason");
15344
+ var ProbeRunFailure = class extends Error {
15345
+ constructor(message, reason) {
15346
+ super(message);
15347
+ this.reason = reason;
15348
+ this.name = "ProbeRunFailure";
15349
+ }
15350
+ static {
15351
+ __name(this, "ProbeRunFailure");
15352
+ }
15353
+ };
15354
+ var BASELINE_REASON = /* @__PURE__ */ new Set([
15355
+ "no-output",
15356
+ "not-in-results",
15357
+ "no-tests",
15358
+ "all-skipped"
15359
+ ]);
15360
+ var REASON_PHRASE = {
15361
+ // The suite ran and read green, but the control proved the runner could not
15362
+ // have reported otherwise.
15363
+ "control-failed": "it read green there, but the positive control failed, so nothing could have turned that run red",
15364
+ // Nothing ran: no worktree, the checkout failed, or the runner could not be
15365
+ // started at all — a spawn that fails with ENOENT never gets to a test.
15366
+ "not-run": "no probe suite ran for it at all there",
15367
+ // Started and did not survive. How much of it ran before that is unknown.
15368
+ "runner-died": "the probe suite was started there and did not survive (killed at the deadline, or by a signal)",
15369
+ // Nothing is known about collection here — the runner never produced output
15370
+ // to read. Saying "collected no tests" would be the same invented cause this
15371
+ // function exists to stop, one layer down: a reader sent after a compile
15372
+ // error that is not there, while the runner itself is what fell over.
15373
+ "no-output": "the runner produced no parseable output there, so nothing at all is known about it",
15374
+ // The run answered, and this file was not in the answer. A compile or import
15375
+ // error looks like this — and so does a path that failed to match, which the
15376
+ // boundary and case rules in `classifyProbeRun` exist because of. Naming one
15377
+ // would be picking between two live possibilities.
15378
+ "not-in-results": "the run produced results there but none for it \u2014 which a compile or import error looks like, and so does a path that did not match",
15379
+ "no-tests": "it collected no tests there \u2014 the shape a compile or import error in the probe tree takes",
15380
+ "all-skipped": "it collected tests there but executed none of them"
15381
+ };
15382
+ var NOT_REPORTED = "the baseline did not report it";
15383
+ function collocatedNotGreenDetail(kind, probe, baselinePerFile) {
15384
+ const entry = baselinePerFile.find((p) => p.file === probe);
15385
+ const reason = entry === void 0 ? NOT_REPORTED : entry.verdict === "inconclusive" ? (
15386
+ // Three reasons are set on the run-level `results` array and never by
15387
+ // `classifyProbeRun`, so a baseline entry cannot carry them. Reaching
15388
+ // one means the caller passed something other than the baseline —
15389
+ // say that, rather than emit "did not run green … it read green".
15390
+ BASELINE_REASON.has(entry.reason) ? REASON_PHRASE[entry.reason] : `the baseline did not classify it (${REASON_PHRASE[entry.reason]}), so this explanation does not apply to it`
15391
+ ) : entry.verdict === "gated" ? "it was RED there" : (
15392
+ // `inert` IS green, so the sentence this function builds does not
15393
+ // apply to it. Say that, rather than produce a fluent claim that
15394
+ // contradicts the measurement — the caller is what is wrong here.
15395
+ "the baseline reported it GREEN, so this explanation does not apply to it"
15396
+ );
15397
+ const what = kind === "mutant" ? "the statement" : "the hunk";
15398
+ return `this ${kind}'s collocated test ${probe} did not run green in the unmutated baseline \u2014 ${reason}, so the remaining probes passing cannot show ${what} is uncovered`;
15399
+ }
15400
+ __name(collocatedNotGreenDetail, "collocatedNotGreenDetail");
13991
15401
  function hasCollocatedNewTest(file, testPaths) {
13992
15402
  return collocatedProbe(file, testPaths) !== void 0;
13993
15403
  }
@@ -14018,6 +15428,7 @@ function classifyProbeRun(exitCode, stdout, probes, stderr = "") {
14018
15428
  return probes.map((file) => ({
14019
15429
  file,
14020
15430
  verdict: "inconclusive",
15431
+ reason: "no-output",
14021
15432
  detail: `runner produced no parseable JSON (exit ${exitCode})${why ? `: ${why}` : ""}`
14022
15433
  }));
14023
15434
  }
@@ -14032,10 +15443,19 @@ function classifyProbeRun(exitCode, stdout, probes, stderr = "") {
14032
15443
  const assertions = result?.assertionResults ?? [];
14033
15444
  const failed = assertions.filter((a) => a.status === "failed").length;
14034
15445
  const passed = assertions.filter((a) => a.status === "passed").length;
14035
- if (!result || assertions.length === 0) {
15446
+ if (!result) {
15447
+ return {
15448
+ file,
15449
+ verdict: "inconclusive",
15450
+ reason: "not-in-results",
15451
+ detail: `the run produced results but none for this file (run exit ${exitCode}) \u2014 a compile or import error looks like this, and so does a path that did not match; not evidence either way`
15452
+ };
15453
+ }
15454
+ if (assertions.length === 0) {
14036
15455
  return {
14037
15456
  file,
14038
15457
  verdict: "inconclusive",
15458
+ reason: "no-tests",
14039
15459
  detail: `collected no tests with the source reverted (run exit ${exitCode}) \u2014 likely a compile or import error, which is not evidence either way`
14040
15460
  };
14041
15461
  }
@@ -14050,6 +15470,7 @@ function classifyProbeRun(exitCode, stdout, probes, stderr = "") {
14050
15470
  return {
14051
15471
  file,
14052
15472
  verdict: "inconclusive",
15473
+ reason: "all-skipped",
14053
15474
  detail: `${assertions.length} test(s) collected but none executed with the source reverted (all skipped) \u2014 not evidence either way`
14054
15475
  };
14055
15476
  }
@@ -14062,7 +15483,7 @@ function classifyProbeRun(exitCode, stdout, probes, stderr = "") {
14062
15483
  }
14063
15484
  __name(classifyProbeRun, "classifyProbeRun");
14064
15485
  function git3(cwd, ...args) {
14065
- const r = spawnSync6("git", args, { cwd, encoding: "utf8" });
15486
+ const r = spawnSync7("git", args, { cwd, encoding: "utf8" });
14066
15487
  if (r.error) throw r.error;
14067
15488
  if (r.status !== 0) {
14068
15489
  throw new Error(`git ${args.join(" ")} failed: ${r.stderr ?? ""}`);
@@ -14070,7 +15491,7 @@ function git3(cwd, ...args) {
14070
15491
  }
14071
15492
  __name(git3, "git");
14072
15493
  function gitOut2(cwd, ...args) {
14073
- const r = spawnSync6("git", args, { cwd, encoding: "utf8" });
15494
+ const r = spawnSync7("git", args, { cwd, encoding: "utf8" });
14074
15495
  if (r.error) throw r.error;
14075
15496
  if (r.status !== 0) {
14076
15497
  throw new Error(`git ${args.join(" ")} failed: ${r.stderr ?? ""}`);
@@ -14079,7 +15500,7 @@ function gitOut2(cwd, ...args) {
14079
15500
  }
14080
15501
  __name(gitOut2, "gitOut");
14081
15502
  function findVitestBin(worktree) {
14082
- const req = createRequire(join16(worktree, "noop.js"));
15503
+ const req = createRequire(join17(worktree, "noop.js"));
14083
15504
  let pkgPath;
14084
15505
  try {
14085
15506
  pkgPath = req.resolve("vitest/package.json");
@@ -14089,16 +15510,16 @@ function findVitestBin(worktree) {
14089
15510
  }
14090
15511
  throw error;
14091
15512
  }
14092
- const pkg = JSON.parse(readFileSync22(pkgPath, "utf8"));
15513
+ const pkg = JSON.parse(readFileSync25(pkgPath, "utf8"));
14093
15514
  const bin = pkg.bin?.vitest;
14094
15515
  if (!bin) {
14095
15516
  throw new Error(`vitest package at ${pkgPath} declares no "vitest" bin`);
14096
15517
  }
14097
- return join16(dirname17(pkgPath), bin);
15518
+ return join17(dirname20(pkgPath), bin);
14098
15519
  }
14099
15520
  __name(findVitestBin, "findVitestBin");
14100
15521
  function gitCapture(cwd, ...args) {
14101
- const r = spawnSync6("git", args, {
15522
+ const r = spawnSync7("git", args, {
14102
15523
  cwd,
14103
15524
  encoding: "utf8",
14104
15525
  maxBuffer: 64 * 1024 * 1024
@@ -14111,7 +15532,7 @@ function gitCapture(cwd, ...args) {
14111
15532
  }
14112
15533
  __name(gitCapture, "gitCapture");
14113
15534
  function existsAtRev(cwd, rev, path3) {
14114
- const r = spawnSync6("git", ["cat-file", "-e", `${rev}:${path3}`], { cwd });
15535
+ const r = spawnSync7("git", ["cat-file", "-e", `${rev}:${path3}`], { cwd });
14115
15536
  if (r.error) throw r.error;
14116
15537
  return r.status === 0;
14117
15538
  }
@@ -14120,7 +15541,7 @@ function safeRmWithin(worktree, relPath) {
14120
15541
  const parts = relPath.split(/[/\\]+/).filter((s) => s && s !== ".");
14121
15542
  let cur = worktree;
14122
15543
  for (let i = 0; i < parts.length; i++) {
14123
- cur = join16(cur, parts[i]);
15544
+ cur = join17(cur, parts[i]);
14124
15545
  let st;
14125
15546
  try {
14126
15547
  st = lstatSync4(cur);
@@ -14133,7 +15554,7 @@ function safeRmWithin(worktree, relPath) {
14133
15554
  );
14134
15555
  }
14135
15556
  }
14136
- rmSync8(cur, { force: true });
15557
+ rmSync9(cur, { force: true });
14137
15558
  }
14138
15559
  __name(safeRmWithin, "safeRmWithin");
14139
15560
  var existsAtBase = /* @__PURE__ */ __name((cwd, base, path3) => existsAtRev(cwd, base, path3), "existsAtBase");
@@ -14145,15 +15566,15 @@ __name(probeCleanupFailureDetail, "probeCleanupFailureDetail");
14145
15566
  function exposeDependencies(probeTree, dependencyRoot) {
14146
15567
  const done = { linked: 0, failed: 0 };
14147
15568
  if (probeTree === dependencyRoot) return done;
14148
- const source = join16(dependencyRoot, "node_modules");
14149
- const target = join16(probeTree, "node_modules");
14150
- if (!existsSync9(source) || existsSync9(target)) return done;
14151
- mkdirSync20(target);
15569
+ const source = join17(dependencyRoot, "node_modules");
15570
+ const target = join17(probeTree, "node_modules");
15571
+ if (!existsSync11(source) || existsSync11(target)) return done;
15572
+ mkdirSync23(target);
14152
15573
  const linkType = process.platform === "win32" ? "junction" : "dir";
14153
15574
  for (const entry of readdirSync5(source, { withFileTypes: true })) {
14154
15575
  if (entry.name === ".vite" || entry.name === ".vite-temp") continue;
14155
- const sourceEntry = join16(source, entry.name);
14156
- const targetEntry = join16(target, entry.name);
15576
+ const sourceEntry = join17(source, entry.name);
15577
+ const targetEntry = join17(target, entry.name);
14157
15578
  let sourceStats;
14158
15579
  try {
14159
15580
  sourceStats = lstatSync4(sourceEntry);
@@ -14163,7 +15584,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
14163
15584
  }
14164
15585
  if (sourceStats.isSymbolicLink()) {
14165
15586
  try {
14166
- if (!statSync6(sourceEntry).isDirectory()) continue;
15587
+ if (!statSync8(sourceEntry).isDirectory()) continue;
14167
15588
  } catch {
14168
15589
  continue;
14169
15590
  }
@@ -14173,7 +15594,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
14173
15594
  if (entry.name.startsWith("@")) {
14174
15595
  let pkgs;
14175
15596
  try {
14176
- mkdirSync20(targetEntry);
15597
+ mkdirSync23(targetEntry);
14177
15598
  pkgs = readdirSync5(sourceEntry);
14178
15599
  } catch {
14179
15600
  done.failed++;
@@ -14181,7 +15602,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
14181
15602
  }
14182
15603
  for (const pkg of pkgs) {
14183
15604
  try {
14184
- symlinkSync(join16(sourceEntry, pkg), join16(targetEntry, pkg), linkType);
15605
+ symlinkSync(join17(sourceEntry, pkg), join17(targetEntry, pkg), linkType);
14185
15606
  done.linked++;
14186
15607
  } catch {
14187
15608
  done.failed++;
@@ -14203,7 +15624,7 @@ function runProbeSuite(probeTree, probes, deadlineAt, now = Date.now, dependency
14203
15624
  const started = now();
14204
15625
  const timeout = deadlineAt !== void 0 ? Math.max(1, Math.min(PROBE_RUN_TIMEOUT_MS, deadlineAt - started)) : PROBE_RUN_TIMEOUT_MS;
14205
15626
  const exposed = exposeDependencies(probeTree, dependencyRoot);
14206
- const r = spawnSync6(
15627
+ const r = spawnSync7(
14207
15628
  process.execPath,
14208
15629
  [findVitestBin(dependencyRoot), "run", "--reporter=json", ...probes],
14209
15630
  {
@@ -14216,10 +15637,12 @@ function runProbeSuite(probeTree, probes, deadlineAt, now = Date.now, dependency
14216
15637
  maxBuffer: 64 * 1024 * 1024
14217
15638
  }
14218
15639
  );
14219
- if (r.error) throw r.error;
15640
+ if (r.error)
15641
+ throw new ProbeRunFailure(r.error.message, runnerFailureReason(r));
14220
15642
  if (r.signal) {
14221
- throw new Error(
14222
- `runner killed by ${r.signal}${r.signal === "SIGTERM" ? ` (probe timed out after ${Math.round(timeout / 1e3)}s)` : ""}`
15643
+ throw new ProbeRunFailure(
15644
+ `runner killed by ${r.signal}${r.signal === "SIGTERM" ? ` (probe timed out after ${Math.round(timeout / 1e3)}s)` : ""}`,
15645
+ runnerFailureReason(r)
14223
15646
  );
14224
15647
  }
14225
15648
  return {
@@ -14325,10 +15748,10 @@ function newSideLength(header) {
14325
15748
  __name(newSideLength, "newSideLength");
14326
15749
  function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
14327
15750
  const { patch: _patch, ...meta } = hunk;
14328
- const abs = join16(probeTree, hunk.file);
15751
+ const abs = join17(probeTree, hunk.file);
14329
15752
  let original;
14330
15753
  try {
14331
- original = readFileSync22(abs, "utf8");
15754
+ original = readFileSync25(abs, "utf8");
14332
15755
  } catch (e) {
14333
15756
  return {
14334
15757
  ...meta,
@@ -14336,7 +15759,7 @@ function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
14336
15759
  detail: `the probe tree does not hold ${hunk.file}: ${e instanceof Error ? e.message : String(e)}`
14337
15760
  };
14338
15761
  }
14339
- const applied = spawnSync6("git", ["apply", "--reverse", "-"], {
15762
+ const applied = spawnSync7("git", ["apply", "--reverse", "-"], {
14340
15763
  cwd: probeTree,
14341
15764
  input: hunk.patch,
14342
15765
  encoding: "utf8"
@@ -14354,21 +15777,21 @@ function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
14354
15777
  const detail = verdict === "killed" ? "the suite went red with this hunk reverted \u2014 a test covers this change" : verdict === "survived" ? "every affected test still PASSED with this hunk reverted \u2014 no test in this diff fails when the change is undone" : "the tree with this hunk reverted produced no clean verdict (likely a compile or import error) \u2014 not evidence either way";
14355
15778
  return { ...meta, verdict, detail };
14356
15779
  } finally {
14357
- mkdirSync20(dirname17(abs), { recursive: true });
14358
- writeFileSync20(abs, original, "utf8");
15780
+ mkdirSync23(dirname20(abs), { recursive: true });
15781
+ writeFileSync23(abs, original, "utf8");
14359
15782
  }
14360
15783
  }
14361
15784
  __name(runOneHunkProbe, "runOneHunkProbe");
14362
15785
  function runControlMutant(probeTree, probeFile, deadlineAt, now = Date.now) {
14363
- const abs = join16(probeTree, probeFile);
15786
+ const abs = join17(probeTree, probeFile);
14364
15787
  let original;
14365
15788
  try {
14366
- original = readFileSync22(abs, "utf8");
15789
+ original = readFileSync25(abs, "utf8");
14367
15790
  } catch {
14368
15791
  return null;
14369
15792
  }
14370
15793
  try {
14371
- writeFileSync20(
15794
+ writeFileSync23(
14372
15795
  abs,
14373
15796
  `${original}
14374
15797
  ;import { it as __qcIt, expect as __qcExpect } from 'vitest';
@@ -14381,13 +15804,13 @@ __qcIt('QWEN-REVIEW-POSITIVE-CONTROL', () => {
14381
15804
  const { perFile } = runProbeSuite(probeTree, [probeFile], deadlineAt, now);
14382
15805
  return perFile.some((r) => r.verdict === "gated");
14383
15806
  } finally {
14384
- writeFileSync20(abs, original, "utf8");
15807
+ writeFileSync23(abs, original, "utf8");
14385
15808
  }
14386
15809
  }
14387
15810
  __name(runControlMutant, "runControlMutant");
14388
15811
  function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dependencyRoot = probeTree) {
14389
- const abs = join16(probeTree, mutant.file);
14390
- const original = readFileSync22(abs, "utf8");
15812
+ const abs = join17(probeTree, mutant.file);
15813
+ const original = readFileSync25(abs, "utf8");
14391
15814
  const lines2 = original.split("\n");
14392
15815
  if ((lines2[mutant.line - 1] ?? "").trim() !== mutant.statement) {
14393
15816
  return {
@@ -14403,7 +15826,7 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
14403
15826
  lines2.splice(mutant.line - 1, 1);
14404
15827
  }
14405
15828
  try {
14406
- writeFileSync20(abs, lines2.join("\n"), "utf8");
15829
+ writeFileSync23(abs, lines2.join("\n"), "utf8");
14407
15830
  const { perFile } = runProbeSuite(
14408
15831
  probeTree,
14409
15832
  probes,
@@ -14415,7 +15838,7 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
14415
15838
  const detail = verdict === "killed" ? `the suite went red with this statement ${what} \u2014 a test catches it` : verdict === "survived" ? `every affected test still PASSED with this statement ${what} \u2014 no test fails ${mutant.mutated === void 0 ? "when it is removed" : "when it changes"}` : "the mutated tree produced no clean verdict (likely a compile or import error) \u2014 not evidence either way";
14416
15839
  return { ...mutant, verdict, detail };
14417
15840
  } finally {
14418
- writeFileSync20(abs, original, "utf8");
15841
+ writeFileSync23(abs, original, "utf8");
14419
15842
  }
14420
15843
  }
14421
15844
  __name(runOneMutant, "runOneMutant");
@@ -14454,9 +15877,9 @@ async function runTestEfficacy(args) {
14454
15877
  const now = args.now ?? Date.now;
14455
15878
  const startedAt = now();
14456
15879
  const { report, worktree, base, out } = args;
14457
- const plan = JSON.parse(readFileSync22(report, "utf8"));
15880
+ const plan = JSON.parse(readFileSync25(report, "utf8"));
14458
15881
  const rootPkg = JSON.parse(
14459
- readFileSync22(`${worktree}/package.json`, "utf8")
15882
+ readFileSync25(`${worktree}/package.json`, "utf8")
14460
15883
  );
14461
15884
  const globs = rootPkg.workspaces ?? [];
14462
15885
  const { unreachable, probes, revert } = planTestEfficacy(
@@ -14464,8 +15887,8 @@ async function runTestEfficacy(args) {
14464
15887
  globs
14465
15888
  );
14466
15889
  for (const p of revert) {
14467
- const norm = join16(worktree, p);
14468
- const root = join16(worktree, ".");
15890
+ const norm = join17(worktree, p);
15891
+ const root = join17(worktree, ".");
14469
15892
  if (isAbsolute3(p) || norm !== root && !norm.startsWith(root + sep3)) {
14470
15893
  throw new Error(
14471
15894
  `refusing to run: revert path escapes the worktree: ${JSON.stringify(p)}`
@@ -14568,7 +15991,12 @@ async function runTestEfficacy(args) {
14568
15991
  String(sweep?.stderr ?? "")
14569
15992
  );
14570
15993
  for (const file of probes) {
14571
- results.push({ file, verdict: "inconclusive", detail });
15994
+ results.push({
15995
+ file,
15996
+ verdict: "inconclusive",
15997
+ reason: "not-run",
15998
+ detail
15999
+ });
14572
16000
  }
14573
16001
  for (const c of candidates) {
14574
16002
  mutantResults.push({ ...c, verdict: "inconclusive", detail });
@@ -14618,6 +16046,21 @@ async function runTestEfficacy(args) {
14618
16046
  );
14619
16047
  }
14620
16048
  for (const c of harnessValidated === false ? [] : candidates) {
16049
+ const heldDetail = heldForRedCollocatedTest(
16050
+ "mutant",
16051
+ c.file,
16052
+ probes,
16053
+ greenProbes,
16054
+ baseline.perFile
16055
+ );
16056
+ if (heldDetail) {
16057
+ mutantResults.push({
16058
+ ...c,
16059
+ verdict: "inconclusive",
16060
+ detail: heldDetail
16061
+ });
16062
+ continue;
16063
+ }
14621
16064
  const remaining = mutantDeadline - now();
14622
16065
  if (!fitsAnotherMutantRun(remaining, estimatedRunMs)) {
14623
16066
  mutantsSkippedForBudget = candidates.length - mutantResults.length;
@@ -14635,13 +16078,19 @@ async function runTestEfficacy(args) {
14635
16078
  );
14636
16079
  }
14637
16080
  for (const h of harnessValidated === false ? [] : hunkCandidates) {
14638
- const own = collocatedProbe(h.file, probes);
14639
- if (own && !greenProbes.includes(own)) {
16081
+ const heldDetail = heldForRedCollocatedTest(
16082
+ "hunk",
16083
+ h.file,
16084
+ probes,
16085
+ greenProbes,
16086
+ baseline.perFile
16087
+ );
16088
+ if (heldDetail) {
14640
16089
  const { patch: _patch, ...meta } = h;
14641
16090
  hunkResults.push({
14642
16091
  ...meta,
14643
16092
  verdict: "inconclusive",
14644
- detail: `this hunk's collocated test ${own} did not run green in the unmutated baseline (likely a compile or import error in the probe tree), so the remaining probes passing cannot show the hunk is uncovered`
16093
+ detail: heldDetail
14645
16094
  });
14646
16095
  continue;
14647
16096
  }
@@ -14692,11 +16141,14 @@ async function runTestEfficacy(args) {
14692
16141
  noteDependencyFarm(revertRun.exposed);
14693
16142
  results.push(...revertRun.perFile);
14694
16143
  } catch (e) {
14695
- const detail = `probe could not run: ${e instanceof Error ? e.message : String(e)}`;
16144
+ const message = e instanceof Error ? e.message : String(e);
16145
+ const detail = `probe could not run: ${message}`;
16146
+ const reason = e instanceof ProbeRunFailure ? e.reason : "not-run";
14696
16147
  results.push(
14697
16148
  ...probes.map((file) => ({
14698
16149
  file,
14699
16150
  verdict: "inconclusive",
16151
+ reason,
14700
16152
  detail
14701
16153
  }))
14702
16154
  );
@@ -14708,7 +16160,7 @@ async function runTestEfficacy(args) {
14708
16160
  } catch (e) {
14709
16161
  removeError = e;
14710
16162
  }
14711
- if (existsSync9(probeTree)) {
16163
+ if (existsSync11(probeTree)) {
14712
16164
  cleanupFailure = probeCleanupFailureDetail(
14713
16165
  probeTree,
14714
16166
  removeError,
@@ -14732,10 +16184,15 @@ async function runTestEfficacy(args) {
14732
16184
  h.detail = detail;
14733
16185
  }
14734
16186
  }
14735
- for (const r of results) {
16187
+ for (let i = 0; i < results.length; i++) {
16188
+ const r = results[i];
14736
16189
  if (r.verdict === "inert") {
14737
- r.verdict = "inconclusive";
14738
- r.detail = detail;
16190
+ results[i] = {
16191
+ file: r.file,
16192
+ verdict: "inconclusive",
16193
+ reason: "control-failed",
16194
+ detail
16195
+ };
14739
16196
  }
14740
16197
  }
14741
16198
  }
@@ -14802,8 +16259,8 @@ async function runTestEfficacy(args) {
14802
16259
  findings,
14803
16260
  cleanupFailure
14804
16261
  };
14805
- mkdirSync20(dirname17(out), { recursive: true });
14806
- writeFileSync20(out, JSON.stringify(result, null, 2), "utf8");
16262
+ mkdirSync23(dirname20(out), { recursive: true });
16263
+ writeFileSync23(out, JSON.stringify(result, null, 2), "utf8");
14807
16264
  writeStdoutLine(
14808
16265
  `Wrote test-efficacy report to ${out} (${unreachable.length} unreachable, ${results.length} probed, ${mutantResults.length} mutant(s), ${hunkResults.length} hunk probe(s), ${findings.length} finding(s))`
14809
16266
  );
@@ -14880,8 +16337,8 @@ var testEfficacyCommand = {
14880
16337
 
14881
16338
  // packages/cli/src/commands/review/test-plan.ts
14882
16339
  init_esbuild_shims();
14883
- import { existsSync as existsSync10, mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync21 } from "node:fs";
14884
- import { dirname as dirname18, join as join17, normalize, resolve as resolve17 } from "node:path";
16340
+ import { existsSync as existsSync12, mkdirSync as mkdirSync24, readFileSync as readFileSync26, writeFileSync as writeFileSync24 } from "node:fs";
16341
+ import { dirname as dirname21, join as join18, normalize, resolve as resolve20 } from "node:path";
14885
16342
  var PLAN_NAME_RE = /(test\s*plan|\btesting\b|how\s+(?:has\s+this|to)\s+(?:been\s+)?test(?:ed)?|测试计划|测试方案|测试步骤)/i;
14886
16343
  var HEADING_LINE_RE = /^(#{1,6})(?:[ \t](.*))?$/;
14887
16344
  var BOLD_LINE_RE = /^\*\*([^*\n]+)\*\*:?\s*$/;
@@ -15084,7 +16541,7 @@ function rulePath(text, worktree, changed) {
15084
16541
  };
15085
16542
  }
15086
16543
  const ignored = isGitIgnored(worktree, path3);
15087
- if (existsSync10(join17(worktree, path3))) {
16544
+ if (existsSync12(join18(worktree, path3))) {
15088
16545
  return {
15089
16546
  kind: "path",
15090
16547
  text,
@@ -15155,7 +16612,7 @@ function ruleCommand(text, worktree, buildTest) {
15155
16612
  let rootScripts = [];
15156
16613
  try {
15157
16614
  const rootPkg = JSON.parse(
15158
- readFileSync23(join17(worktree, "package.json"), "utf8")
16615
+ readFileSync26(join18(worktree, "package.json"), "utf8")
15159
16616
  );
15160
16617
  rootScripts = Object.keys(rootPkg.scripts ?? {});
15161
16618
  } catch {
@@ -15233,7 +16690,7 @@ __name(fetchPrBody, "fetchPrBody");
15233
16690
  function runTestPlan(args, fetchBody = fetchPrBody) {
15234
16691
  let plan;
15235
16692
  try {
15236
- plan = JSON.parse(readFileSync23(args.plan, "utf8"));
16693
+ plan = JSON.parse(readFileSync26(args.plan, "utf8"));
15237
16694
  } catch (err) {
15238
16695
  throw new Error(
15239
16696
  `test-plan: cannot read the plan ${args.plan}: ${err.message}`
@@ -15260,7 +16717,7 @@ function runTestPlan(args, fetchBody = fetchPrBody) {
15260
16717
  note: "the PR description has no Test Plan section"
15261
16718
  };
15262
16719
  }
15263
- const worktree = resolve17(args.worktree);
16720
+ const worktree = resolve20(args.worktree);
15264
16721
  const changed = new Set(
15265
16722
  (plan.files ?? []).map((f) => normalize(String(f.path)))
15266
16723
  );
@@ -15268,7 +16725,7 @@ function runTestPlan(args, fetchBody = fetchPrBody) {
15268
16725
  if (args.buildTest) {
15269
16726
  try {
15270
16727
  buildTest = JSON.parse(
15271
- readFileSync23(args.buildTest, "utf8")
16728
+ readFileSync26(args.buildTest, "utf8")
15272
16729
  );
15273
16730
  } catch {
15274
16731
  buildTest = null;
@@ -15325,8 +16782,8 @@ var testPlanCommand = {
15325
16782
  try {
15326
16783
  const report = runTestPlan(args);
15327
16784
  if (args.out) {
15328
- mkdirSync21(dirname18(resolve17(args.out)), { recursive: true });
15329
- writeFileSync21(resolve17(args.out), JSON.stringify(report, null, 2));
16785
+ mkdirSync24(dirname21(resolve20(args.out)), { recursive: true });
16786
+ writeFileSync24(resolve20(args.out), JSON.stringify(report, null, 2));
15330
16787
  }
15331
16788
  writeStdoutLine(JSON.stringify(report, null, 2));
15332
16789
  writeStderrLine(`test-plan: ${report.note}`);
@@ -15340,8 +16797,8 @@ var testPlanCommand = {
15340
16797
  // packages/cli/src/commands/review/cleanup.ts
15341
16798
  init_esbuild_shims();
15342
16799
  import { execFileSync as execFileSync5 } from "node:child_process";
15343
- import { existsSync as existsSync11, readFileSync as readFileSync24, readdirSync as readdirSync6, rmSync as rmSync9 } from "node:fs";
15344
- import { join as join18 } from "node:path";
16800
+ import { existsSync as existsSync13, readFileSync as readFileSync27, readdirSync as readdirSync6, rmSync as rmSync10 } from "node:fs";
16801
+ import { join as join19 } from "node:path";
15345
16802
  var AUTOMATION_MARKER = "<!-- qwen-";
15346
16803
  function isAutomationComment(body) {
15347
16804
  return (body ?? "").trimStart().startsWith(AUTOMATION_MARKER);
@@ -15372,7 +16829,7 @@ var OWNER_REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
15372
16829
  function readAuditWindow(target, expectedPrNumber) {
15373
16830
  let raw;
15374
16831
  try {
15375
- raw = readFileSync24(tmpFile(target, "fetch.json"), "utf8");
16832
+ raw = readFileSync27(tmpFile(target, "fetch.json"), "utf8");
15376
16833
  } catch (err) {
15377
16834
  const code = err.code;
15378
16835
  return code === "ENOENT" ? { skip: "no fetch report" } : {
@@ -15419,7 +16876,7 @@ function readSubmitReceipt(target) {
15419
16876
  try {
15420
16877
  return new Set(
15421
16878
  parseReceiptIds(
15422
- readFileSync24(tmpFile(target, "submit-receipt.json"), "utf8")
16879
+ readFileSync27(tmpFile(target, "submit-receipt.json"), "utf8")
15423
16880
  )
15424
16881
  );
15425
16882
  } catch {
@@ -15523,7 +16980,7 @@ function runCleanup(target) {
15523
16980
  report("probe worktree", probeWorktreePath(wt));
15524
16981
  report("base worktree", baseWorktreePath(wt));
15525
16982
  try {
15526
- rmSync9(`${baseWorktreePath(wt)}.lock`, { recursive: true, force: true });
16983
+ rmSync10(`${baseWorktreePath(wt)}.lock`, { recursive: true, force: true });
15527
16984
  } catch (err) {
15528
16985
  writeStderrLine(
15529
16986
  `note: could not remove base lock ${baseWorktreePath(wt)}.lock: ${err.message}`
@@ -15546,7 +17003,7 @@ function runCleanup(target) {
15546
17003
  const prefix = tmpPrefix(target);
15547
17004
  let tmpEntries = [];
15548
17005
  try {
15549
- tmpEntries = existsSync11(REVIEW_TMP_DIR) ? readdirSync6(REVIEW_TMP_DIR) : [];
17006
+ tmpEntries = existsSync13(REVIEW_TMP_DIR) ? readdirSync6(REVIEW_TMP_DIR) : [];
15550
17007
  } catch (err) {
15551
17008
  writeStderrLine(
15552
17009
  `Failed to read ${REVIEW_TMP_DIR}: ${err.message}`
@@ -15554,9 +17011,9 @@ function runCleanup(target) {
15554
17011
  }
15555
17012
  for (const file of tmpEntries) {
15556
17013
  if (!file.startsWith(prefix)) continue;
15557
- const full = join18(REVIEW_TMP_DIR, file);
17014
+ const full = join19(REVIEW_TMP_DIR, file);
15558
17015
  try {
15559
- rmSync9(full, { recursive: true, force: true });
17016
+ rmSync10(full, { recursive: true, force: true });
15560
17017
  writeStdoutLine(`Removed temp file: ${full}`);
15561
17018
  removedAny = true;
15562
17019
  } catch (err) {
@@ -15588,8 +17045,8 @@ var cleanupCommand = {
15588
17045
  // packages/cli/src/commands/review/run.ts
15589
17046
  init_esbuild_shims();
15590
17047
  import { spawn, execFileSync as execFileSync6 } from "node:child_process";
15591
- import { readdirSync as readdirSync7, readFileSync as readFileSync25, statSync as statSync7 } from "node:fs";
15592
- import { join as join19, resolve as resolve18 } from "node:path";
17048
+ import { readdirSync as readdirSync7, readFileSync as readFileSync28, realpathSync as realpathSync3, statSync as statSync9 } from "node:fs";
17049
+ import { dirname as dirname22, join as join20, resolve as resolve21 } from "node:path";
15593
17050
  var COMPOSED_PATTERN = /^qwen-review-.*composed\.json$/;
15594
17051
  var COMPOSED_POLL_MS = 250;
15595
17052
  var SIGNAL_EXIT_CODES = {
@@ -15623,10 +17080,10 @@ function newestArtifactSince(dir, pattern, startMs) {
15623
17080
  }
15624
17081
  for (const name of names) {
15625
17082
  if (!pattern.test(name)) continue;
15626
- const path3 = join19(dir, name);
17083
+ const path3 = join20(dir, name);
15627
17084
  let mtime;
15628
17085
  try {
15629
- mtime = statSync7(path3).mtimeMs;
17086
+ mtime = statSync9(path3).mtimeMs;
15630
17087
  } catch {
15631
17088
  continue;
15632
17089
  }
@@ -15644,7 +17101,7 @@ function exitCodeFor(completed, event, failOn) {
15644
17101
  __name(exitCodeFor, "exitCodeFor");
15645
17102
  function readComposed(path3) {
15646
17103
  try {
15647
- const parsed = JSON.parse(readFileSync25(path3, "utf8"));
17104
+ const parsed = JSON.parse(readFileSync28(path3, "utf8"));
15648
17105
  return typeof parsed.event === "string" ? parsed : null;
15649
17106
  } catch {
15650
17107
  return null;
@@ -15667,6 +17124,23 @@ function killProcessGroup(pid, signal) {
15667
17124
  }
15668
17125
  }
15669
17126
  __name(killProcessGroup, "killProcessGroup");
17127
+ function childEnv() {
17128
+ const env = { ...process.env };
17129
+ const inherited = env["QWEN_CODE_CLI"];
17130
+ const ownEntry = process.argv[1];
17131
+ if (!inherited || !ownEntry) {
17132
+ return env;
17133
+ }
17134
+ try {
17135
+ if (dirname22(realpathSync3(inherited)) === dirname22(realpathSync3(ownEntry))) {
17136
+ return env;
17137
+ }
17138
+ } catch {
17139
+ }
17140
+ env["QWEN_CODE_CLI"] = "";
17141
+ return env;
17142
+ }
17143
+ __name(childEnv, "childEnv");
15670
17144
  async function runReview(args) {
15671
17145
  const startMs = Date.now();
15672
17146
  const prompt = buildReviewPrompt(args);
@@ -15682,6 +17156,7 @@ async function runReview(args) {
15682
17156
  args.approvalMode
15683
17157
  ],
15684
17158
  {
17159
+ env: childEnv(),
15685
17160
  // stdin CLOSED, not inherited: piped input would be prepended to the
15686
17161
  // prompt and the leading `/` would no longer be the first character —
15687
17162
  // the slash command would reach the model as plain text.
@@ -15693,9 +17168,9 @@ async function runReview(args) {
15693
17168
  }
15694
17169
  );
15695
17170
  if (!args.quiet) {
15696
- const writeProgress = /* @__PURE__ */ __name((chunk) => {
17171
+ const writeProgress = /* @__PURE__ */ __name((chunk2) => {
15697
17172
  try {
15698
- process.stderr.write(chunk);
17173
+ process.stderr.write(chunk2);
15699
17174
  } catch {
15700
17175
  }
15701
17176
  }, "writeProgress");
@@ -15779,8 +17254,8 @@ async function runReview(args) {
15779
17254
  downgraded: composed?.downgraded ?? false,
15780
17255
  downgradedFrom: composed?.downgradedFrom ?? null,
15781
17256
  remediation: composed?.remediation ?? [],
15782
- composedPath: composedPath ? resolve18(composedPath) : null,
15783
- reportPath: reportPath ? resolve18(reportPath) : null,
17257
+ composedPath: composedPath ? resolve21(composedPath) : null,
17258
+ reportPath: reportPath ? resolve21(reportPath) : null,
15784
17259
  childExitCode,
15785
17260
  childSignal,
15786
17261
  timedOut,
@@ -15794,7 +17269,7 @@ async function runReview(args) {
15794
17269
  writeStdoutLine(result.verdictLine ?? `Event: ${result.event}`);
15795
17270
  if (result.reportPath) writeStdoutLine(`Report: ${result.reportPath}`);
15796
17271
  } else {
15797
- const detail = composedPath !== null ? `a composed verdict was found at ${resolve18(composedPath)} but could not be parsed` : "no composed verdict was produced";
17272
+ const detail = composedPath !== null ? `a composed verdict was found at ${resolve21(composedPath)} but could not be parsed` : "no composed verdict was produced";
15798
17273
  writeStdoutLine(
15799
17274
  timedOut ? "Review did not complete: timed out." : `Review did not complete: ${detail}${childExitCode !== null ? ` (CLI exit ${childExitCode})` : ""}${childSignal !== null ? ` (killed by ${childSignal})` : ""}.`
15800
17275
  );
@@ -15861,9 +17336,9 @@ var runCommand = {
15861
17336
  var reviewCommand = {
15862
17337
  command: "review",
15863
17338
  describe: "Run a review non-interactively (`run`), plus the internal helpers used by the /review skill (PR worktree setup, context fetch, rules loading, presubmit checks, cleanup)",
15864
- builder: /* @__PURE__ */ __name((yargs) => yargs.command(runCommand).command(parseArgsCommand).command(fetchPrCommand).command(captureLocalCommand).command(planDiffCommand).command(prContextCommand).command(commentStatusCommand).command(loadRulesCommand).command(agentPromptCommand).command(buildTestCommand).command(baseTreeCommand).command(testDeltaCommand).command(extractStepCommand).command(scriptLintCommand).command(resolveAnchorsCommand).command(checkCoverageCommand).command(presubmitCommand).command(testEfficacyCommand).command(testPlanCommand).command(findingsCommand).command(composeReviewCommand).command(submitCommand).command(cleanupCommand).demandCommand(
17339
+ builder: /* @__PURE__ */ __name((yargs) => yargs.command(runCommand).command(parseArgsCommand).command(fetchPrCommand).command(captureLocalCommand).command(planDiffCommand).command(prContextCommand).command(commentStatusCommand).command(loadRulesCommand).command(agentPromptCommand).command(buildTestCommand).command(baseTreeCommand).command(testDeltaCommand).command(driveCommand).command(mockProviderCommand).command(extractStepCommand).command(scriptLintCommand).command(resolveAnchorsCommand).command(checkCoverageCommand).command(presubmitCommand).command(testEfficacyCommand).command(testPlanCommand).command(findingsCommand).command(publishAssetsCommand).command(composeReviewCommand).command(submitCommand).command(cleanupCommand).demandCommand(
15865
17340
  1,
15866
- "Specify a subcommand: run, parse-args, fetch-pr, capture-local, plan-diff, pr-context, comment-status, load-rules, agent-prompt, build-test, base-tree, test-delta, extract-step, script-lint, resolve-anchors, check-coverage, presubmit, test-efficacy, test-plan, findings, compose-review, submit, or cleanup."
17341
+ "Specify a subcommand: run, parse-args, fetch-pr, capture-local, plan-diff, pr-context, comment-status, load-rules, agent-prompt, build-test, base-tree, test-delta, drive, mock-provider, extract-step, script-lint, resolve-anchors, check-coverage, presubmit, test-efficacy, test-plan, findings, publish-assets, compose-review, submit, or cleanup."
15867
17342
  ).version(false), "builder"),
15868
17343
  handler: /* @__PURE__ */ __name(() => {
15869
17344
  }, "handler")
@@ -16063,10 +17538,10 @@ var updateCommand3 = {
16063
17538
  stdioHelpers,
16064
17539
  { updateEventEmitter }
16065
17540
  ] = await Promise.all([
16066
- import("./settings-LI2ORVXC.js"),
16067
- import("./updateCheck-7EG4BLBH.js"),
16068
- import("./installationInfo-2LIKCNVX.js"),
16069
- import("./standalone-update-E4V5V7WT.js"),
17541
+ import("./settings-ILWAOKA4.js"),
17542
+ import("./updateCheck-42SPCLH7.js"),
17543
+ import("./installationInfo-IWMM6DPV.js"),
17544
+ import("./standalone-update-Y5JB4S2K.js"),
16070
17545
  import("./stdioHelpers-HGIYTWAY.js"),
16071
17546
  import("./updateEventEmitter-5MKC7BLQ.js")
16072
17547
  ]);
@@ -17233,7 +18708,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
17233
18708
  disabledSlashCommands: disabledSlashCommands.length > 0 ? disabledSlashCommands : void 0,
17234
18709
  disabledSkillNamesProvider: bareMode || safeMode ? void 0 : disabledSkillNamesProvider,
17235
18710
  terminalImageRenderSupportProvider: interactive ? async () => {
17236
- const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-BOQJPVLB.js");
18711
+ const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-NWC2RALG.js");
17237
18712
  return getTerminalImageRenderSupport();
17238
18713
  } : void 0,
17239
18714
  disabledSkillLevels: bareMode || safeMode || !Array.isArray(settings.skills?.disabledLevels) ? void 0 : settings.skills.disabledLevels.filter(isSkillLevel),
@@ -17387,6 +18862,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
17387
18862
  enableAutoSkill: bareMode || safeMode ? false : settings.memory?.enableAutoSkill ?? false,
17388
18863
  autoSkillConfirm: bareMode || safeMode ? false : settings.memory?.autoSkillConfirm ?? true,
17389
18864
  memoryAgentTimeoutMinutes: settings.memory?.agentTimeoutMinutes,
18865
+ memoryAgentMaxTurns: settings.memory?.agentMaxTurns,
17390
18866
  fastModel: settings.fastModel || void 0,
17391
18867
  webSearch: bareMode || safeMode ? void 0 : resolveWebSearchSettings(settings),
17392
18868
  visionModel: settings.visionModel || void 0,