@qwen-code/qwen-code 0.21.4 → 0.21.5-nightly.20260805.32e274157

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 (260) hide show
  1. package/bundled/qc-helper/docs/configuration/model-providers.md +8 -7
  2. package/bundled/qc-helper/docs/configuration/settings.md +16 -16
  3. package/bundled/qc-helper/docs/features/code-review.md +4 -0
  4. package/bundled/qc-helper/docs/features/headless.md +31 -0
  5. package/bundled/qc-helper/docs/features/mcp.md +11 -3
  6. package/bundled/qc-helper/docs/qwen-serve.md +75 -0
  7. package/bundled/review/SKILL.md +90 -52
  8. package/chunks/{MaxSizedBox-5XDFQHTQ.js → MaxSizedBox-H44SGRP7.js} +17 -16
  9. package/chunks/{StandaloneSessionPicker-DLADA54K.js → StandaloneSessionPicker-T5FQMYD3.js} +31 -30
  10. package/chunks/{acpAgent-MVEFZSBS.js → acpAgent-J474KXVM.js} +849 -313
  11. package/chunks/{agent-RB6LCRZX.js → agent-SC23IV5L.js} +17 -16
  12. package/chunks/{agent-headless-272ZACJV.js → agent-headless-2CHR5WJP.js} +17 -16
  13. package/chunks/{artifact-tool-TBOYEKTY.js → artifact-tool-RROCCJDA.js} +1 -1
  14. package/chunks/{askUserQuestion-23Y5WZFJ.js → askUserQuestion-2EPPBPUZ.js} +1 -1
  15. package/chunks/{bridge-GXUXFSHK.js → bridge-BLHYPCCR.js} +21 -19
  16. package/chunks/{channel-settings-store-5UA2SABE.js → channel-settings-store-CDOZ7GCP.js} +22 -21
  17. package/chunks/{channel-worker-group-NDG4WOOZ.js → channel-worker-group-SVGPDH2C.js} +3 -2
  18. package/chunks/{channel-worker-manager-ZCW6UW34.js → channel-worker-manager-72ZK2GTS.js} +3 -2
  19. package/chunks/{channel-worker-supervisor-ATGFTZRN.js → channel-worker-supervisor-NUEDVHX4.js} +2 -1
  20. package/chunks/{chunk-PWCQRLM5.js → chunk-2AGOQ7QO.js} +1 -1
  21. package/chunks/{chunk-AFJSRJTO.js → chunk-2B6FBVWE.js} +1 -1
  22. package/chunks/{chunk-XPAY4EMH.js → chunk-2OSE7Q5O.js} +3 -3
  23. package/chunks/{chunk-DHNFB7NC.js → chunk-2QOLRESU.js} +5 -5
  24. package/chunks/{chunk-UWSXFI6E.js → chunk-2U7GPHA6.js} +6 -2
  25. package/chunks/{chunk-CIUUYB23.js → chunk-3DTM3RPU.js} +1 -1
  26. package/chunks/{chunk-X6QNZOTC.js → chunk-3PRJYMXC.js} +2 -2
  27. package/chunks/{chunk-6KQUOXT5.js → chunk-3XDH6PZM.js} +7 -7
  28. package/chunks/{chunk-DJA7YBLJ.js → chunk-56MC5OM7.js} +2 -2
  29. package/chunks/{chunk-B3EBXISV.js → chunk-5DVP76WL.js} +90 -7
  30. package/chunks/{chunk-2T3RYG4L.js → chunk-5KDLX7ER.js} +919 -323
  31. package/chunks/{chunk-O2ZYHG33.js → chunk-5L6CQ4ZU.js} +131 -67
  32. package/chunks/{chunk-E2SYUEIA.js → chunk-5OAMVWFL.js} +874 -375
  33. package/chunks/{chunk-LPLMUGIM.js → chunk-5X76FZLI.js} +5 -5
  34. package/chunks/{chunk-M2L4TMH3.js → chunk-6HTEECQY.js} +3 -3
  35. package/chunks/{chunk-KHSJSMFS.js → chunk-6I6WUVYI.js} +1 -1
  36. package/chunks/{chunk-LLMGP5IC.js → chunk-6WBWIPBF.js} +2 -2
  37. package/chunks/{chunk-PQ5JFCNL.js → chunk-762UTZQZ.js} +3 -3
  38. package/chunks/{chunk-HBCKZF2A.js → chunk-7EW7CYLA.js} +1 -1
  39. package/chunks/{chunk-4PZ7SPD5.js → chunk-7FKLLD4X.js} +1 -1
  40. package/chunks/{chunk-XSOAW7FC.js → chunk-AE7AKP3E.js} +6 -6
  41. package/chunks/{chunk-UATJZOAJ.js → chunk-ASS3UQB5.js} +3 -3
  42. package/chunks/{chunk-XGA6XHH3.js → chunk-AXOFMDZC.js} +3 -3
  43. package/chunks/chunk-BKHCW2BI.js +41 -0
  44. package/chunks/{chunk-DZPOYHBT.js → chunk-D6FISK43.js} +1 -1
  45. package/chunks/{chunk-CFDR3GNL.js → chunk-D7D7RXIJ.js} +11 -0
  46. package/chunks/{chunk-FPPVCCAH.js → chunk-DNPP267F.js} +1 -1
  47. package/chunks/{chunk-I32URQI7.js → chunk-EA2CKB6L.js} +5 -5
  48. package/chunks/{chunk-C5YFS6VR.js → chunk-EL4W7MGQ.js} +4 -4
  49. package/chunks/{chunk-QXTGHZCX.js → chunk-FDLXWNCE.js} +3 -3
  50. package/chunks/{chunk-PCVMRBLN.js → chunk-FFCSRU3F.js} +1 -1
  51. package/chunks/{chunk-SV5X2EUV.js → chunk-FMU2QVA5.js} +1 -1
  52. package/chunks/{chunk-2ON2O4MD.js → chunk-FV2JYSWS.js} +28 -3
  53. package/chunks/{chunk-KB6VSG2U.js → chunk-GXP44JOS.js} +7 -7
  54. package/chunks/{chunk-BC4LTKEG.js → chunk-HP6MKLWU.js} +1 -1
  55. package/chunks/{chunk-YXEVDA66.js → chunk-HU5TUALO.js} +110 -22
  56. package/chunks/{chunk-2JN5V3I7.js → chunk-ITZK3L4S.js} +1 -1
  57. package/chunks/{chunk-5XGI4IPK.js → chunk-JEVHOCCA.js} +4 -4
  58. package/chunks/{chunk-6ME3PVMM.js → chunk-JNFWMBG6.js} +5 -5
  59. package/chunks/{chunk-3PHUGFPH.js → chunk-JXPOBM4B.js} +1 -1
  60. package/chunks/{chunk-TXZDRD36.js → chunk-LCCBAGRT.js} +5 -5
  61. package/chunks/{chunk-G7XXKQYS.js → chunk-LKRMNRSN.js} +27 -1
  62. package/chunks/{chunk-DOCBJIBL.js → chunk-MB3ILUB7.js} +1 -1
  63. package/chunks/{chunk-HFVW54NL.js → chunk-MBEMTXQT.js} +9 -2
  64. package/chunks/{chunk-RNKGMZEW.js → chunk-MFTOMYE2.js} +1 -1
  65. package/chunks/{chunk-RPYYTL7E.js → chunk-MGSSJT27.js} +2 -2
  66. package/chunks/{chunk-URVJ4AZC.js → chunk-MKMUBBEX.js} +1 -1
  67. package/chunks/{chunk-RL2NPV5E.js → chunk-N5QPOGRQ.js} +1 -1
  68. package/chunks/{chunk-2CCUHZP5.js → chunk-N7PF4KZR.js} +2 -2
  69. package/chunks/{chunk-JDJ7SC4E.js → chunk-NMBDPO63.js} +255 -197
  70. package/chunks/{chunk-M6RNMBUO.js → chunk-NQZ5IVLH.js} +4 -4
  71. package/chunks/{chunk-FV4TUUDE.js → chunk-O4WOWHMZ.js} +1 -1
  72. package/chunks/{chunk-GDREW3U5.js → chunk-OIVG6IHL.js} +2 -2
  73. package/chunks/{chunk-IMGNGOGC.js → chunk-PQ2J56BJ.js} +3 -3
  74. package/chunks/{chunk-EJXATV5R.js → chunk-PWFDZA5A.js} +2 -2
  75. package/chunks/{chunk-OOPFAIWP.js → chunk-Q7EXR3W2.js} +1 -1
  76. package/chunks/{chunk-4CTV3FZH.js → chunk-QJL2AQZI.js} +23 -19
  77. package/chunks/{chunk-SBBHZ3YT.js → chunk-QKWPXGW4.js} +1 -1
  78. package/chunks/{chunk-HI4YGRJD.js → chunk-RDICVTQC.js} +3 -3
  79. package/chunks/{chunk-ZRQY5BBS.js → chunk-REAWLQAU.js} +10 -10
  80. package/chunks/{chunk-BOWKLLWA.js → chunk-SOBA6GIW.js} +3 -3
  81. package/chunks/{chunk-WDLQGFWP.js → chunk-TYWI5NXY.js} +27 -27
  82. package/chunks/{chunk-A7PONO5G.js → chunk-URJHXOSO.js} +2 -2
  83. package/chunks/{chunk-C6FU76AA.js → chunk-V3BNVUFI.js} +1 -1
  84. package/chunks/{chunk-G6KI6B5L.js → chunk-VF6WPN4R.js} +1 -1
  85. package/chunks/{chunk-AWTLFUVN.js → chunk-VT27B2XS.js} +13 -13
  86. package/chunks/{chunk-O4IWFYUW.js → chunk-VY4TEQQ6.js} +4 -4
  87. package/chunks/{chunk-EIAPOG76.js → chunk-WECYW5EE.js} +4 -4
  88. package/chunks/{chunk-PDDYAXL2.js → chunk-WIRAPZ62.js} +7 -0
  89. package/chunks/chunk-WOJZWRAZ.js +123 -0
  90. package/chunks/{chunk-7UXI6P72.js → chunk-WVAESIM6.js} +2 -2
  91. package/chunks/{chunk-P5UJMCVS.js → chunk-XIDYVC4P.js} +4 -4
  92. package/chunks/{chunk-THLV7YGY.js → chunk-XIKKANF4.js} +2 -2
  93. package/chunks/{chunk-FHPKHXHT.js → chunk-XNZBXQCG.js} +4 -0
  94. package/chunks/{chunk-ZLJQIGCH.js → chunk-XZCMQUOF.js} +8 -0
  95. package/chunks/{chunk-PQEIKQXN.js → chunk-YJMFRTLV.js} +24 -6
  96. package/chunks/{chunk-QZQ72YF6.js → chunk-Z26UXKZE.js} +1 -1
  97. package/chunks/{chunk-VA5NLVPT.js → chunk-Z7JILXGX.js} +1 -1
  98. package/chunks/{chunk-UEJESCS7.js → chunk-ZPPP6VCL.js} +1 -1
  99. package/chunks/{computer-use-MDOMIVTL.js → computer-use-ENYMKQAI.js} +17 -16
  100. package/chunks/{config-utils-E3ZQTHGM.js → config-utils-LIRO7NSU.js} +2 -2
  101. package/chunks/{contextCommand-FCK3CB37.js → contextCommand-E7XMHU6M.js} +19 -18
  102. package/chunks/{core-runtime-UHRHHU2O.js → core-runtime-KDIRQ63A.js} +17 -16
  103. package/chunks/{create-sub-session-J32WC64U.js → create-sub-session-6EWLAZD5.js} +1 -1
  104. package/chunks/{create-sub-session-MIXUVLWW.js → create-sub-session-VVEL77UF.js} +17 -16
  105. package/chunks/{cron-create-HMFSHIAR.js → cron-create-CCRIULJE.js} +1 -1
  106. package/chunks/{cron-delete-ZJFHOX57.js → cron-delete-FCFW6BGP.js} +1 -1
  107. package/chunks/{cron-list-ZEX2NBMN.js → cron-list-QDBVCHRF.js} +1 -1
  108. package/chunks/{daemon-status-provider-6LC7LUNR.js → daemon-status-provider-4LMCSLZ4.js} +25 -23
  109. package/chunks/{daemon-trust-policy-Y36HGRAM.js → daemon-trust-policy-XLONWB2C.js} +22 -21
  110. package/chunks/{daemon-trust-policy-monitor-ROTBBDOH.js → daemon-trust-policy-monitor-FHFZ3NNG.js} +22 -21
  111. package/chunks/{deferred-core-runtime-ZQ4JIAJD.js → deferred-core-runtime-QNEBJ3DP.js} +17 -16
  112. package/chunks/{display-image-2WOG27DY.js → display-image-GUJA352B.js} +2 -2
  113. package/chunks/{earlyInputCapture-GPRUNXTP.js → earlyInputCapture-OG5IUF4R.js} +17 -16
  114. package/chunks/{edit-YK3UP6D2.js → edit-E2WNOITJ.js} +18 -17
  115. package/chunks/{enter-worktree-QMVQW7UG.js → enter-worktree-RMCBAAA2.js} +17 -16
  116. package/chunks/{enterPlanMode-TZAYQ5MP.js → enterPlanMode-GISFJA3O.js} +17 -16
  117. package/chunks/{environment-3UQURCZG.js → environment-JXTQGYDN.js} +19 -18
  118. package/chunks/{errors-VBIMTNNI.js → errors-2AYTYCQ3.js} +19 -18
  119. package/chunks/{exit-worktree-4EC7LBBC.js → exit-worktree-HORS3V7Z.js} +17 -16
  120. package/chunks/{exitPlanMode-K5Y3REU2.js → exitPlanMode-V75Y36HJ.js} +17 -16
  121. package/chunks/external-tool-guard-provider-5HTGOPGU.js +273 -0
  122. package/chunks/{fast-path-KS6IE7YR.js → fast-path-HURC6JUT.js} +5 -5
  123. package/chunks/{gemini-LTUCUOJH.js → gemini-WHMD4RGY.js} +70 -55
  124. package/chunks/{geminiContentGenerator-M3HJTHKR.js → geminiContentGenerator-DZB3GPNQ.js} +1 -1
  125. package/chunks/{glob-SKUYGTF2.js → glob-IGTV723M.js} +17 -16
  126. package/chunks/{goal-tools-SPQ5M6D3.js → goal-tools-3AZKX5WQ.js} +2 -18
  127. package/chunks/{grep-SQ7RD7SV.js → grep-SJ2NYUCD.js} +17 -16
  128. package/chunks/{handleAutoUpdate-OK7OX23A.js → handleAutoUpdate-NPBKROGA.js} +21 -20
  129. package/chunks/{i18n-P77YBKY4.js → i18n-CBSMJZ3Z.js} +18 -17
  130. package/chunks/{image-gen-2ZWPS42A.js → image-gen-KNKDTUEN.js} +2 -2
  131. package/chunks/{initializer-PIOYXVW7.js → initializer-JT5P3IQF.js} +22 -21
  132. package/chunks/{installationInfo-IWMM6DPV.js → installationInfo-CZVCKPPZ.js} +18 -17
  133. package/chunks/{list-3DCR4TRI.js → list-MT7T6JVH.js} +25 -24
  134. package/chunks/{list-agents-TWEGRJWO.js → list-agents-GC4BRET7.js} +1 -1
  135. package/chunks/{loadedSettingsAdapter-EJC6CU5N.js → loadedSettingsAdapter-D4JFGYYQ.js} +22 -21
  136. package/chunks/{loggingContentGenerator-2JEFU25T.js → loggingContentGenerator-SFT5CMPU.js} +9 -8
  137. package/chunks/{loop-wakeup-6LLEOQYV.js → loop-wakeup-SQQSBMEY.js} +2 -2
  138. package/chunks/{ls-H5WX22VK.js → ls-63CC3NNX.js} +1 -1
  139. package/chunks/{lsp-G6FXYEIS.js → lsp-P6NVJVNT.js} +1 -1
  140. package/chunks/{managed-npm-update-EGVQPSBR.js → managed-npm-update-YHJW6SGD.js} +18 -17
  141. package/chunks/{mcp-KFFH2MBV.js → mcp-53X2M7XL.js} +22 -21
  142. package/chunks/{monitor-6Y3NDMTI.js → monitor-I43P3MNV.js} +17 -16
  143. package/chunks/{nonInteractiveCli-426IQDPE.js → nonInteractiveCli-UADCDERF.js} +50 -48
  144. package/chunks/{notebook-edit-373IQQLE.js → notebook-edit-WKZUONWV.js} +18 -17
  145. package/chunks/{openaiContentGenerator-PGFSOMIQ.js → openaiContentGenerator-YHF3FIYI.js} +9 -8
  146. package/chunks/{pidfile-Y4INDMHG.js → pidfile-VU37FJS4.js} +17 -16
  147. package/chunks/{processUtils-6BCPCJZW.js → processUtils-VIQVTDWH.js} +2 -2
  148. package/chunks/{qwenContentGenerator-QC3KSHSX.js → qwenContentGenerator-46476NL4.js} +18 -17
  149. package/chunks/{qwenOAuth2-IOAXKJ3L.js → qwenOAuth2-D62OJZFU.js} +1 -1
  150. package/chunks/{read-file-2T54FYZV.js → read-file-6PSNROKB.js} +7 -6
  151. package/chunks/{read-mcp-resource-DRZSI3HE.js → read-mcp-resource-RTSSEER5.js} +1 -1
  152. package/chunks/{record-artifact-IPIRWMFY.js → record-artifact-E6AWQU2Z.js} +2 -2
  153. package/chunks/{resumeHistoryUtils-O62RDCZ3.js → resumeHistoryUtils-QFD6LB7N.js} +20 -19
  154. package/chunks/{ripGrep-3H72KY7S.js → ripGrep-4WZJEOGB.js} +17 -16
  155. package/chunks/{run-qwen-serve-3AIDNNHO.js → run-qwen-serve-SEL3TISE.js} +72 -36
  156. package/chunks/{runtime-AZ4EHHFV.js → runtime-T6JAS7GL.js} +25 -24
  157. package/chunks/{scheduler-L4UUCU4Z.js → scheduler-ZCTDMJJV.js} +17 -16
  158. package/chunks/{sdk-exporters-http-IKNWQJNV.js → sdk-exporters-http-IL2346MJ.js} +6 -2
  159. package/chunks/{sdk-impl-7CUUMQDB.js → sdk-impl-SYKGVWOZ.js} +2 -2
  160. package/chunks/{send-message-LHJLVWFD.js → send-message-TL6FIMMN.js} +3 -3
  161. package/chunks/{serve-CLXGSYOE.js → serve-A7B6VWK7.js} +23 -21
  162. package/chunks/{server-MEAE3WMM.js → server-6CCTTLO4.js} +170 -63
  163. package/chunks/{session-OSLD2LT5.js → session-3YYXW6MZ.js} +51 -49
  164. package/chunks/{settings-ILWAOKA4.js → settings-KWYWA3BS.js} +21 -20
  165. package/chunks/{shell-RT5DBH6B.js → shell-6SUYJURL.js} +17 -16
  166. package/chunks/{skill-LVGA3ATZ.js → skill-ZIQFS6YD.js} +8 -7
  167. package/chunks/{skill-settings-WTZE7EPE.js → skill-settings-23IFV7S3.js} +21 -20
  168. package/chunks/{spawnChannel-BOBS7DVM.js → spawnChannel-AI7QA35U.js} +20 -18
  169. package/chunks/{standalone-update-Y5JB4S2K.js → standalone-update-WNXRFLEA.js} +19 -18
  170. package/chunks/{startInteractiveUI-ZV25CD2S.js → startInteractiveUI-NPZU6YXV.js} +76 -71
  171. package/chunks/{syntheticOutput-EYL7DWO7.js → syntheticOutput-BN7E626U.js} +2 -2
  172. package/chunks/{task-create-4ALZMGP5.js → task-create-2X4UP4F3.js} +4 -4
  173. package/chunks/{task-list-6CVQPORX.js → task-list-WOLJPZ64.js} +1 -1
  174. package/chunks/{task-stop-Y5ZTFVRX.js → task-stop-TNXRNAL2.js} +1 -1
  175. package/chunks/{task-update-IKCEEWJ6.js → task-update-VDUSN36E.js} +4 -4
  176. package/chunks/{team-create-ZVPQIXCP.js → team-create-CMF2RRF2.js} +17 -16
  177. package/chunks/{team-delete-CE7DC53R.js → team-delete-D2VMSDOT.js} +1 -1
  178. package/chunks/{team-plan-approval-5UHUBY3W.js → team-plan-approval-5EET4S3I.js} +17 -16
  179. package/chunks/{terminal-image-renderer-NWC2RALG.js → terminal-image-renderer-TCEAHDFE.js} +18 -17
  180. package/chunks/{theme-manager-5KICSBOB.js → theme-manager-RPYRQJLX.js} +17 -16
  181. package/chunks/{todoWrite-FTZWQRZH.js → todoWrite-MHWHO5UO.js} +1 -1
  182. package/chunks/{tool-search-2AOU77H4.js → tool-search-HJN53LCN.js} +9 -8
  183. package/chunks/{total-session-admission-FR7RMHVL.js → total-session-admission-4FZK4PCR.js} +21 -19
  184. package/chunks/{trustedFolders-V7UUPQXO.js → trustedFolders-RIYUQQJC.js} +18 -17
  185. package/chunks/{update-relaunch-TBWRMQD6.js → update-relaunch-3HVSJU2T.js} +5 -5
  186. package/chunks/{updateCheck-42SPCLH7.js → updateCheck-EE2XXQNJ.js} +20 -19
  187. package/chunks/{useAutoAcceptIndicator-4C2KM345.js → useAutoAcceptIndicator-RRV7MW47.js} +23 -22
  188. package/chunks/{validateNonInterActiveAuth-UVFRXV3Z.js → validateNonInterActiveAuth-3QRII2BS.js} +47 -45
  189. package/chunks/{version-HKTV6TPN.js → version-4KUHPDAC.js} +1 -1
  190. package/chunks/{web-fetch-ZRXSV5AN.js → web-fetch-MXA6HQ42.js} +7 -6
  191. package/chunks/{web-search-WOLV3L6T.js → web-search-YENHOBMC.js} +5 -4
  192. package/chunks/{workflow-F63ES3OB.js → workflow-RYEVBM4L.js} +18 -17
  193. package/chunks/{workspace-providers-status-PJMGMYMC.js → workspace-providers-status-SBTU5TWO.js} +25 -24
  194. package/chunks/{workspace-registration-store-LNBOX5TJ.js → workspace-registration-store-VUMOTODR.js} +1 -1
  195. package/chunks/{workspace-registry-4PR7VDQ3.js → workspace-registry-OZ5ZUBJ6.js} +21 -19
  196. package/chunks/{workspace-service-PVI3VUNQ.js → workspace-service-Y3IGAN4K.js} +26 -25
  197. package/chunks/{workspace-skills-status-2Q5ZISO6.js → workspace-skills-status-FXOIDGOS.js} +23 -22
  198. package/chunks/{workspace-trust-reconciler-NOILUH3C.js → workspace-trust-reconciler-DQI7MYJS.js} +26 -24
  199. package/chunks/{write-file-MOM4JDHG.js → write-file-NK4C3GFC.js} +17 -16
  200. package/chunks/{zoom-image-E3VHRCFU.js → zoom-image-4Y6Y6DAX.js} +7 -6
  201. package/cli-entry.js +14 -0
  202. package/cli.js +16 -12
  203. package/package.json +3 -3
  204. package/web-shell/assets/{arc-GpdmhIxr.js → arc-DtI1rZcQ.js} +1 -1
  205. package/web-shell/assets/{architectureDiagram-3BPJPVTR-C-LhCgH_.js → architectureDiagram-3BPJPVTR-BPZwSVct.js} +1 -1
  206. package/web-shell/assets/{blockDiagram-GPEHLZMM-CzNdTCm-.js → blockDiagram-GPEHLZMM-BIQ03j4C.js} +1 -1
  207. package/web-shell/assets/{c4Diagram-AAUBKEIU-Ce7hbrRM.js → c4Diagram-AAUBKEIU-CB0RyFGm.js} +1 -1
  208. package/web-shell/assets/channel-vfbVFuM1.js +1 -0
  209. package/web-shell/assets/{chunk-2J33WTMH-BBSEn1PK.js → chunk-2J33WTMH-B3WoS-4H.js} +1 -1
  210. package/web-shell/assets/{chunk-4BX2VUAB-_llgjfe5.js → chunk-4BX2VUAB-C98xYZ1X.js} +1 -1
  211. package/web-shell/assets/{chunk-55IACEB6-9fJZ_oZE.js → chunk-55IACEB6-CbTIWRwQ.js} +1 -1
  212. package/web-shell/assets/{chunk-727SXJPM-DgEzQWOu.js → chunk-727SXJPM-DWGnKTPu.js} +1 -1
  213. package/web-shell/assets/{chunk-AQP2D5EJ-B4RthUTc.js → chunk-AQP2D5EJ-4__QyCEX.js} +1 -1
  214. package/web-shell/assets/{chunk-FMBD7UC4-CDI-emMW.js → chunk-FMBD7UC4-DFtG1YCW.js} +1 -1
  215. package/web-shell/assets/{chunk-ND2GUHAM-jM2-yOd8.js → chunk-ND2GUHAM-rvAhW51N.js} +1 -1
  216. package/web-shell/assets/{chunk-QZHKN3VN-XGZHlKPF.js → chunk-QZHKN3VN-DfiU9pwZ.js} +1 -1
  217. package/web-shell/assets/classDiagram-4FO5ZUOK-Cvr8atq3.js +1 -0
  218. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-Cvr8atq3.js +1 -0
  219. package/web-shell/assets/{cose-bilkent-S5V4N54A-90nbW1xQ.js → cose-bilkent-S5V4N54A-Cc4EIdxu.js} +1 -1
  220. package/web-shell/assets/{dagre-BM42HDAG-BtzlyqZ9.js → dagre-BM42HDAG-CZNj4dGW.js} +1 -1
  221. package/web-shell/assets/{diagram-2AECGRRQ-DAaqGWaH.js → diagram-2AECGRRQ-D5tiqQCr.js} +1 -1
  222. package/web-shell/assets/{diagram-5GNKFQAL-CxlzSIFa.js → diagram-5GNKFQAL-pwzw0jVO.js} +1 -1
  223. package/web-shell/assets/{diagram-KO2AKTUF-8kj-1H6u.js → diagram-KO2AKTUF-4CWiM00x.js} +1 -1
  224. package/web-shell/assets/{diagram-LMA3HP47-6NhjFwRF.js → diagram-LMA3HP47-Dd7rc7Qf.js} +1 -1
  225. package/web-shell/assets/{diagram-OG6HWLK6-Bn9kvInz.js → diagram-OG6HWLK6-_vzV52ml.js} +1 -1
  226. package/web-shell/assets/{erDiagram-TEJ5UH35-CaRoGsAt.js → erDiagram-TEJ5UH35-CqbE2bx_.js} +1 -1
  227. package/web-shell/assets/{flowDiagram-I6XJVG4X-TkLweHdf.js → flowDiagram-I6XJVG4X-CaxNr7w4.js} +1 -1
  228. package/web-shell/assets/{ganttDiagram-6RSMTGT7-ruwhCKBl.js → ganttDiagram-6RSMTGT7-AGjrtfUA.js} +1 -1
  229. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DNI_PxoU.js → gitGraphDiagram-PVQCEYII-ADG2zSkc.js} +1 -1
  230. package/web-shell/assets/{index-p1gz-sxE.js → index-BOryKCqt.js} +1 -1
  231. package/web-shell/assets/index-Cb1LjsCv.css +5 -0
  232. package/web-shell/assets/{index-T4KluHvu.js → index-CvJDaIy8.js} +329 -329
  233. package/web-shell/assets/{infoDiagram-5YYISTIA-DarXq1-A.js → infoDiagram-5YYISTIA-CE3B_IoZ.js} +1 -1
  234. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-GxDaDlT0.js → ishikawaDiagram-YF4QCWOH-CN-Frhoi.js} +1 -1
  235. package/web-shell/assets/{journeyDiagram-JHISSGLW-mDEoGBzY.js → journeyDiagram-JHISSGLW-CGgezS93.js} +1 -1
  236. package/web-shell/assets/{kanban-definition-UN3LZRKU-Ds0t-Mup.js → kanban-definition-UN3LZRKU-BqWijky5.js} +1 -1
  237. package/web-shell/assets/{linear-05nJ0xZ7.js → linear-D8sfpZf6.js} +1 -1
  238. package/web-shell/assets/{mermaid.core-Bua24ThM.js → mermaid.core-3PA4QrzM.js} +5 -5
  239. package/web-shell/assets/{mindmap-definition-RKZ34NQL-D_zRdvUS.js → mindmap-definition-RKZ34NQL-D9QxwiCY.js} +1 -1
  240. package/web-shell/assets/{pieDiagram-4H26LBE5-DZHr1x16.js → pieDiagram-4H26LBE5-Bioaf6uE.js} +1 -1
  241. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-HA06Wf0h.js → quadrantDiagram-W4KKPZXB-DBb2AS73.js} +1 -1
  242. package/web-shell/assets/{requirementDiagram-4Y6WPE33-DH3urmJf.js → requirementDiagram-4Y6WPE33-DXOvZ3KB.js} +1 -1
  243. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-C4xgz0TT.js → sankeyDiagram-5OEKKPKP-D9qg09Dm.js} +1 -1
  244. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DAUoZWgv.js → sequenceDiagram-3UESZ5HK-DSAUIfpw.js} +1 -1
  245. package/web-shell/assets/{stateDiagram-AJRCARHV-v7NzXy4l.js → stateDiagram-AJRCARHV-BPZkzfQ3.js} +1 -1
  246. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DInOqnhZ.js +1 -0
  247. package/web-shell/assets/{timeline-definition-PNZ67QCA-B3GTYR2W.js → timeline-definition-PNZ67QCA-UDaQp9sP.js} +1 -1
  248. package/web-shell/assets/{vennDiagram-CIIHVFJN-BWHjSPAq.js → vennDiagram-CIIHVFJN-2ouhAnHO.js} +1 -1
  249. package/web-shell/assets/{wardley-L42UT6IY-m9r6IUBC.js → wardley-L42UT6IY-Cq_WEvTK.js} +1 -1
  250. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-kT0FkVfK.js → wardleyDiagram-YWT4CUSO-Dt24aXOM.js} +1 -1
  251. package/web-shell/assets/{xychartDiagram-2RQKCTM6-CYRm10z5.js → xychartDiagram-2RQKCTM6-A5Xk2WCH.js} +1 -1
  252. package/web-shell/index.html +2 -2
  253. package/bundled/review/DESIGN.md +0 -724
  254. package/web-shell/assets/channel-95y6Rcp3.js +0 -1
  255. package/web-shell/assets/classDiagram-4FO5ZUOK-pfzVkmjQ.js +0 -1
  256. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-pfzVkmjQ.js +0 -1
  257. package/web-shell/assets/index-WxSvj2tI.css +0 -5
  258. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CtdFexEf.js +0 -1
  259. package/chunks/{channel-management-service-UHX56NBO.js → channel-management-service-5XPPTWUQ.js} +3 -3
  260. package/chunks/{chunk-LEZFUYPM.js → chunk-7GYP5COR.js} +3 -3
@@ -2,13 +2,13 @@
2
2
  "use strict";
3
3
  import {
4
4
  serveCommand
5
- } from "./chunk-2ON2O4MD.js";
5
+ } from "./chunk-FV2JYSWS.js";
6
6
  import {
7
7
  listCommand
8
- } from "./chunk-P5UJMCVS.js";
8
+ } from "./chunk-XIDYVC4P.js";
9
9
  import {
10
10
  getErrorMessage as getErrorMessage2
11
- } from "./chunk-EJXATV5R.js";
11
+ } from "./chunk-PWFDZA5A.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-5XGI4IPK.js";
49
+ } from "./chunk-JEVHOCCA.js";
50
50
  import {
51
51
  getExtensionManager,
52
52
  requestChoicePluginNonInteractive,
53
53
  requestConsentNonInteractive,
54
54
  requestConsentOrFail,
55
55
  resolveExtensionCommandScope
56
- } from "./chunk-O4IWFYUW.js";
56
+ } from "./chunk-VY4TEQQ6.js";
57
57
  import {
58
58
  collectProviderModelsForProtocol,
59
59
  getAuthTypeFromEnv,
60
60
  resolveCliGenerationConfig
61
- } from "./chunk-IMGNGOGC.js";
61
+ } from "./chunk-PQ2J56BJ.js";
62
62
  import {
63
63
  parseChannelConfig
64
- } from "./chunk-LEZFUYPM.js";
64
+ } from "./chunk-7GYP5COR.js";
65
65
  import {
66
66
  findCliEntryPath
67
67
  } from "./chunk-V6WHFGST.js";
@@ -96,6 +96,9 @@ import {
96
96
  import {
97
97
  normalizeServeChannelSelection
98
98
  } from "./chunk-ALMR3E6Q.js";
99
+ import {
100
+ EXTERNAL_TOOL_GUARD_TOKEN_ENV
101
+ } from "./chunk-BKHCW2BI.js";
99
102
  import {
100
103
  redactLogCredentials
101
104
  } from "./chunk-M5GB774H.js";
@@ -128,29 +131,29 @@ import {
128
131
  assembleMcpServers,
129
132
  getPendingGatedMcpServers,
130
133
  mcpCommand
131
- } from "./chunk-6ME3PVMM.js";
134
+ } from "./chunk-JNFWMBG6.js";
132
135
  import {
133
136
  loadSettings
134
- } from "./chunk-KB6VSG2U.js";
137
+ } from "./chunk-GXP44JOS.js";
135
138
  import {
136
139
  detectSystemLanguage,
137
140
  getCurrentLanguage,
138
141
  initializeI18n,
139
142
  resolveLanguageSetting,
140
143
  t
141
- } from "./chunk-FV4TUUDE.js";
144
+ } from "./chunk-O4WOWHMZ.js";
142
145
  import {
143
146
  loadEnvironment
144
- } from "./chunk-4PZ7SPD5.js";
147
+ } from "./chunk-7FKLLD4X.js";
145
148
  import {
146
149
  getCliVersion
147
- } from "./chunk-HBCKZF2A.js";
150
+ } from "./chunk-7EW7CYLA.js";
148
151
  import {
149
152
  getPackageJson
150
153
  } from "./chunk-VJNBRRZG.js";
151
154
  import {
152
155
  isWorkspaceTrusted
153
- } from "./chunk-URVJ4AZC.js";
156
+ } from "./chunk-MKMUBBEX.js";
154
157
  import {
155
158
  require_strip_json_comments
156
159
  } from "./chunk-ALGGS7UH.js";
@@ -191,7 +194,7 @@ import {
191
194
  unquoteCStylePath,
192
195
  updateChannelMemoryEntry,
193
196
  updateSetting
194
- } from "./chunk-E2SYUEIA.js";
197
+ } from "./chunk-5OAMVWFL.js";
195
198
  import {
196
199
  nextFireTime,
197
200
  parseCron
@@ -202,18 +205,18 @@ import {
202
205
  import {
203
206
  parseBooleanEnvFlag,
204
207
  resolveTelemetrySettings
205
- } from "./chunk-RPYYTL7E.js";
208
+ } from "./chunk-MGSSJT27.js";
206
209
  import {
207
210
  getAllGeminiMdFilenames,
208
211
  setGeminiMdFilename
209
- } from "./chunk-JDJ7SC4E.js";
212
+ } from "./chunk-NMBDPO63.js";
210
213
  import {
211
214
  SchemaValidator,
212
215
  ToolNames
213
- } from "./chunk-CFDR3GNL.js";
216
+ } from "./chunk-D7D7RXIJ.js";
214
217
  import {
215
218
  DEFAULT_QWEN_EMBEDDING_MODEL
216
- } from "./chunk-TXZDRD36.js";
219
+ } from "./chunk-LCCBAGRT.js";
217
220
  import {
218
221
  isTlsVerificationDisabled
219
222
  } from "./chunk-BFHXDAR7.js";
@@ -222,7 +225,7 @@ import {
222
225
  } from "./chunk-UH7Z7Y45.js";
223
226
  import {
224
227
  recordChannelMemoryRecallMetrics
225
- } from "./chunk-O2ZYHG33.js";
228
+ } from "./chunk-5L6CQ4ZU.js";
226
229
  import {
227
230
  FatalConfigError,
228
231
  FatalSandboxError,
@@ -358,9 +361,9 @@ var require_command_exists = __commonJS({
358
361
  cleanInput = /* @__PURE__ */ __name(function(s) {
359
362
  var isPathName = /[\\]/.test(s);
360
363
  if (isPathName) {
361
- var dirname23 = '"' + path3.dirname(s) + '"';
364
+ var dirname24 = '"' + path3.dirname(s) + '"';
362
365
  var basename6 = '"' + path3.basename(s) + '"';
363
- return dirname23 + ":" + basename6;
366
+ return dirname24 + ":" + basename6;
364
367
  }
365
368
  return '"' + s + '"';
366
369
  }, "cleanInput");
@@ -368,10 +371,10 @@ var require_command_exists = __commonJS({
368
371
  module.exports = /* @__PURE__ */ __name(function commandExists2(commandName, callback) {
369
372
  var cleanedCommandName = cleanInput(commandName);
370
373
  if (!callback && typeof Promise !== "undefined") {
371
- return new Promise(function(resolve23, reject) {
374
+ return new Promise(function(resolve24, reject) {
372
375
  commandExists2(commandName, function(error, output) {
373
376
  if (output) {
374
- resolve23(commandName);
377
+ resolve24(commandName);
375
378
  } else {
376
379
  reject(error);
377
380
  }
@@ -2255,8 +2258,8 @@ function createBridgeReadinessGate() {
2255
2258
  current: /* @__PURE__ */ __name(() => pending, "current"),
2256
2259
  block: /* @__PURE__ */ __name(() => {
2257
2260
  if (pending) return;
2258
- pending = new Promise((resolve23) => {
2259
- releasePending = resolve23;
2261
+ pending = new Promise((resolve24) => {
2262
+ releasePending = resolve24;
2260
2263
  });
2261
2264
  }, "block"),
2262
2265
  release: /* @__PURE__ */ __name(() => {
@@ -2338,7 +2341,7 @@ function createBridgeRecovery(options) {
2338
2341
  writeStderrLine(
2339
2342
  `[Channel] Bridge crashed (${recentCrashCount}/${MAX_CRASH_RESTARTS} in window). Restarting in ${RESTART_DELAY_MS / 1e3}s...`
2340
2343
  );
2341
- await new Promise((resolve23) => setTimeout(resolve23, RESTART_DELAY_MS));
2344
+ await new Promise((resolve24) => setTimeout(resolve24, RESTART_DELAY_MS));
2342
2345
  const bridge = new AcpBridge(bridgeOpts);
2343
2346
  setBridge(bridge);
2344
2347
  attachDisconnectHandler(bridge);
@@ -3206,12 +3209,12 @@ async function abortableStartup(value, signal) {
3206
3209
  const promise = Promise.resolve(value);
3207
3210
  if (!signal) return await promise;
3208
3211
  if (signal.aborted) throw startupAbortError();
3209
- return await new Promise((resolve23, reject) => {
3212
+ return await new Promise((resolve24, reject) => {
3210
3213
  const onAbort = /* @__PURE__ */ __name(() => {
3211
3214
  reject(startupAbortError());
3212
3215
  }, "onAbort");
3213
3216
  signal.addEventListener("abort", onAbort, { once: true });
3214
- promise.then(resolve23, reject).finally(() => {
3217
+ promise.then(resolve24, reject).finally(() => {
3215
3218
  signal.removeEventListener("abort", onAbort);
3216
3219
  });
3217
3220
  });
@@ -3585,6 +3588,7 @@ function scrubDaemonWorkerEnv() {
3585
3588
  delete process.env[QWEN_DAEMON_URL_ENV];
3586
3589
  delete process.env[QWEN_DAEMON_WORKSPACE_ENV];
3587
3590
  delete process.env[QWEN_SERVER_TOKEN_ENV];
3591
+ delete process.env[EXTERNAL_TOOL_GUARD_TOKEN_ENV];
3588
3592
  }
3589
3593
  __name(scrubDaemonWorkerEnv, "scrubDaemonWorkerEnv");
3590
3594
  function readDaemonWorkerEnv() {
@@ -3619,7 +3623,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
3619
3623
  if (!send) {
3620
3624
  return Promise.reject(new Error("Channel worker IPC is unavailable."));
3621
3625
  }
3622
- return new Promise((resolve23, reject) => {
3626
+ return new Promise((resolve24, reject) => {
3623
3627
  let settled = false;
3624
3628
  let unsubscribeMessage = /* @__PURE__ */ __name(() => {
3625
3629
  }, "unsubscribeMessage");
@@ -3633,7 +3637,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
3633
3637
  settled = true;
3634
3638
  cleanup();
3635
3639
  if (error) reject(error);
3636
- else resolve23();
3640
+ else resolve24();
3637
3641
  }, "finish");
3638
3642
  const onMessage = /* @__PURE__ */ __name((value) => {
3639
3643
  if (isChannelStartupReportAckMessage(value)) {
@@ -3881,8 +3885,8 @@ var daemonWorkerCommand = {
3881
3885
  let shuttingDown = false;
3882
3886
  let exitCode = 0;
3883
3887
  let finish;
3884
- const finished = new Promise((resolve23) => {
3885
- finish = resolve23;
3888
+ const finished = new Promise((resolve24) => {
3889
+ finish = resolve24;
3886
3890
  });
3887
3891
  const shutdown = /* @__PURE__ */ __name(async (reason) => {
3888
3892
  if (shuttingDown) {
@@ -3910,8 +3914,8 @@ var daemonWorkerCommand = {
3910
3914
  ...activeChannelDeliveries.values(),
3911
3915
  ...activeWebhookTasks.values()
3912
3916
  ]),
3913
- new Promise((resolve23) => {
3914
- const timer = setTimeout(resolve23, WORKER_SHUTDOWN_DRAIN_MS);
3917
+ new Promise((resolve24) => {
3918
+ const timer = setTimeout(resolve24, WORKER_SHUTDOWN_DRAIN_MS);
3915
3919
  timer.unref();
3916
3920
  })
3917
3921
  ]);
@@ -4547,12 +4551,12 @@ var parseArgsCommand = {
4547
4551
 
4548
4552
  // packages/cli/src/commands/review/compose-review.ts
4549
4553
  init_esbuild_shims();
4550
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
4551
- import { dirname as dirname4, join as join8 } from "node:path";
4554
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
4555
+ import { dirname as dirname4, join as join9 } from "node:path";
4552
4556
 
4553
4557
  // packages/cli/src/commands/review/lib/coverage.ts
4554
4558
  init_esbuild_shims();
4555
- import { readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
4559
+ import { readFileSync as readFileSync8, statSync as statSync3 } from "node:fs";
4556
4560
 
4557
4561
  // packages/cli/src/commands/review/lib/transcripts.ts
4558
4562
  init_esbuild_shims();
@@ -6186,6 +6190,150 @@ If you find no new gap in your scope, say so **and name what you re-examined** \
6186
6190
  }
6187
6191
  };
6188
6192
 
6193
+ // packages/cli/src/commands/review/lib/deadline.ts
6194
+ init_esbuild_shims();
6195
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync7, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
6196
+ import { join as join8 } from "node:path";
6197
+ var DEADLINE_ENV = "QWEN_REVIEW_DEADLINE_EPOCH";
6198
+ var RESERVE_ENV = "QWEN_REVIEW_DEADLINE_RESERVE_SECONDS";
6199
+ var DEFAULT_RESERVE_SECONDS = 3600;
6200
+ var DEFAULT_ROUND_SECONDS = 1800;
6201
+ var MIN_OBSERVED_ROUND_SECONDS = 600;
6202
+ var STAMPS_FILE = "budget-rounds.json";
6203
+ var STOP_FILE = "budget-stop.json";
6204
+ var RUN_EPOCH_SLACK_MS = 2e3;
6205
+ function runEpochMs(planPath) {
6206
+ try {
6207
+ return statSync2(planPath).mtimeMs - RUN_EPOCH_SLACK_MS;
6208
+ } catch {
6209
+ return Number.NEGATIVE_INFINITY;
6210
+ }
6211
+ }
6212
+ __name(runEpochMs, "runEpochMs");
6213
+ function readRoundStamps(planPath) {
6214
+ try {
6215
+ const raw = readFileSync7(
6216
+ join8(promptRecordDir(planPath), STAMPS_FILE),
6217
+ "utf8"
6218
+ );
6219
+ const parsed = JSON.parse(raw);
6220
+ if (!Array.isArray(parsed)) return [];
6221
+ const epoch = runEpochMs(planPath);
6222
+ return parsed.filter(
6223
+ (e) => typeof e === "object" && e !== null && typeof e.atMs === "number" && e.atMs >= epoch
6224
+ );
6225
+ } catch {
6226
+ return [];
6227
+ }
6228
+ }
6229
+ __name(readRoundStamps, "readRoundStamps");
6230
+ function stampRound(planPath, round, nowMs = Date.now()) {
6231
+ try {
6232
+ const stamps = readRoundStamps(planPath);
6233
+ if (round !== void 0 && stamps.some((s) => s.round === round)) return;
6234
+ stamps.push({ round: round ?? null, atMs: nowMs });
6235
+ const dir = promptRecordDir(planPath);
6236
+ mkdirSync5(dir, { recursive: true });
6237
+ writeFileSync5(join8(dir, STAMPS_FILE), JSON.stringify(stamps));
6238
+ } catch {
6239
+ }
6240
+ }
6241
+ __name(stampRound, "stampRound");
6242
+ function expectedRoundSeconds(planPath, round, nowMs = Date.now()) {
6243
+ const stamps = readRoundStamps(planPath);
6244
+ for (let i = stamps.length - 1; i >= 0; i--) {
6245
+ const s = stamps[i];
6246
+ if (round !== void 0 && s.round === round) continue;
6247
+ const observed = Math.round((nowMs - s.atMs) / 1e3);
6248
+ return Math.max(MIN_OBSERVED_ROUND_SECONDS, observed);
6249
+ }
6250
+ return DEFAULT_ROUND_SECONDS;
6251
+ }
6252
+ __name(expectedRoundSeconds, "expectedRoundSeconds");
6253
+ function reverseAuditBudgetExhausted(env, roundCostSeconds, nowMs = Date.now()) {
6254
+ const raw = env[DEADLINE_ENV];
6255
+ if (raw === void 0 || raw.trim() === "") return null;
6256
+ const deadline = Number(raw);
6257
+ if (!Number.isFinite(deadline) || deadline <= 0) return null;
6258
+ let reserve = DEFAULT_RESERVE_SECONDS;
6259
+ const reserveRaw = env[RESERVE_ENV];
6260
+ if (reserveRaw !== void 0 && reserveRaw.trim() !== "") {
6261
+ const r = Number(reserveRaw);
6262
+ if (Number.isFinite(r) && r >= 0) reserve = r;
6263
+ }
6264
+ const remainingSeconds = Math.floor(deadline - nowMs / 1e3);
6265
+ if (remainingSeconds >= reserve + roundCostSeconds) return null;
6266
+ return {
6267
+ remainingSeconds,
6268
+ reserveSeconds: reserve,
6269
+ expectedRoundSeconds: roundCostSeconds
6270
+ };
6271
+ }
6272
+ __name(reverseAuditBudgetExhausted, "reverseAuditBudgetExhausted");
6273
+ var BUDGET_STOP_PHRASE = "review time budget";
6274
+ function budgetStopDisclosure(round) {
6275
+ const which = round !== void 0 ? `round ${round}` : "the next round";
6276
+ const whichZh = round !== void 0 ? `\u7B2C ${round} \u8F6E` : "\u4E0B\u4E00\u8F6E";
6277
+ return {
6278
+ subject: "reverse audit",
6279
+ reason: `stopped before ${which} by the ${BUDGET_STOP_PHRASE}`,
6280
+ subjectZh: "\u53CD\u5411\u5BA1\u8BA1",
6281
+ reasonZh: `\u8BC4\u5BA1\u65F6\u95F4\u9884\u7B97\u4E0D\u8DB3\uFF0C\u672A\u80FD\u5F00\u59CB${whichZh}`
6282
+ };
6283
+ }
6284
+ __name(budgetStopDisclosure, "budgetStopDisclosure");
6285
+ function budgetStopEntry(round) {
6286
+ const d = budgetStopDisclosure(round);
6287
+ return `${d.subject} \u2014 ${d.reason}`;
6288
+ }
6289
+ __name(budgetStopEntry, "budgetStopEntry");
6290
+ function budgetStopEntryZh(round) {
6291
+ const d = budgetStopDisclosure(round);
6292
+ return `${d.subjectZh}\u2014\u2014${d.reasonZh}`;
6293
+ }
6294
+ __name(budgetStopEntryZh, "budgetStopEntryZh");
6295
+ function writeBudgetStop(planPath, spent, round, nowMs = Date.now()) {
6296
+ try {
6297
+ const dir = promptRecordDir(planPath);
6298
+ mkdirSync5(dir, { recursive: true });
6299
+ const stop = {
6300
+ entry: budgetStopEntry(round),
6301
+ entryZh: budgetStopEntryZh(round),
6302
+ round: round ?? null,
6303
+ remainingSeconds: spent.remainingSeconds,
6304
+ reserveSeconds: spent.reserveSeconds,
6305
+ atMs: nowMs
6306
+ };
6307
+ writeFileSync5(join8(dir, STOP_FILE), JSON.stringify(stop, null, 2));
6308
+ } catch {
6309
+ }
6310
+ }
6311
+ __name(writeBudgetStop, "writeBudgetStop");
6312
+ function readBudgetStop(planPath) {
6313
+ try {
6314
+ const raw = readFileSync7(
6315
+ join8(promptRecordDir(planPath), STOP_FILE),
6316
+ "utf8"
6317
+ );
6318
+ const parsed = JSON.parse(raw);
6319
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.entry !== "string" || typeof parsed.atMs !== "number" || parsed.atMs < runEpochMs(planPath)) {
6320
+ return null;
6321
+ }
6322
+ return parsed;
6323
+ } catch {
6324
+ return null;
6325
+ }
6326
+ }
6327
+ __name(readBudgetStop, "readBudgetStop");
6328
+ function reverseAuditBudgetMessage(spent, round) {
6329
+ const minutesLeft = Math.max(0, Math.floor(spent.remainingSeconds / 60));
6330
+ const reserveMinutes = Math.round(spent.reserveSeconds / 60);
6331
+ const roundMinutes = Math.round(spent.expectedRoundSeconds / 60);
6332
+ const which = round !== void 0 ? `round ${round}` : "the next round";
6333
+ return `BUDGET: ${minutesLeft} minute(s) remain before this review's deadline \u2014 not enough for the ~${roundMinutes}-minute round being asked for plus the ${reserveMinutes}-minute reserve kept for its verification, compose-review and submission \u2014 so no further reverse-audit round will be built. This is the loop's termination rule, not an error: do not rebuild ${which} and do not relaunch auditors. A budget-stop marker has been recorded and compose-review will disclose it and cap the verdict itself; also add exactly this entry to unreviewedDimensions so the terminal report says it too \u2014 \`${budgetStopEntry(round)}\` \u2014 and proceed to Step 6 with the findings already confirmed. Spend what time remains only on verifying findings already in hand, composing, and submitting. A review that stops here still reports everything it proved; a review that runs past its deadline is killed holding all of it.`;
6334
+ }
6335
+ __name(reverseAuditBudgetMessage, "reverseAuditBudgetMessage");
6336
+
6189
6337
  // packages/cli/src/commands/review/lib/shell-quote.ts
6190
6338
  init_esbuild_shims();
6191
6339
  function shellQuotePath(p) {
@@ -6195,7 +6343,7 @@ __name(shellQuotePath, "shellQuotePath");
6195
6343
 
6196
6344
  // packages/cli/src/commands/review/lib/coverage.ts
6197
6345
  function readPlan(path3) {
6198
- const plan = JSON.parse(readFileSync7(path3, "utf8"));
6346
+ const plan = JSON.parse(readFileSync8(path3, "utf8"));
6199
6347
  if (typeof plan?.diffPathAbsolute !== "string" || !plan.diffPathAbsolute) {
6200
6348
  throw new Error(`coverage: ${path3} has no diffPathAbsolute`);
6201
6349
  }
@@ -6206,7 +6354,7 @@ function readPlan(path3) {
6206
6354
  if (problem) {
6207
6355
  throw new Error(`coverage: ${path3} has ${problem}`);
6208
6356
  }
6209
- return { plan, mtimeMs: statSync2(path3).mtimeMs };
6357
+ return { plan, mtimeMs: statSync3(path3).mtimeMs };
6210
6358
  }
6211
6359
  __name(readPlan, "readPlan");
6212
6360
  var CHUNK_RE = /\bchunk\s+(\d+)\s+of\s+\d+\b/i;
@@ -6283,6 +6431,11 @@ function coverageFromTranscripts(planPath, env = process.env) {
6283
6431
  const idleAgents = [];
6284
6432
  const unopenedAgents = [];
6285
6433
  const rewrittenPrompts = [];
6434
+ const driftedLaunches = [];
6435
+ const openedBriefOf = /* @__PURE__ */ __name((rec, key) => {
6436
+ const needle = JSON.stringify(briefPath(planPath, key));
6437
+ return rec.successfulCallArgs.some((a) => a.includes(needle));
6438
+ }, "openedBriefOf");
6286
6439
  const disclosures = [];
6287
6440
  const disclose = /* @__PURE__ */ __name((subject, reason, pub) => {
6288
6441
  disclosures.push({
@@ -6355,15 +6508,23 @@ function coverageFromTranscripts(planPath, env = process.env) {
6355
6508
  );
6356
6509
  }
6357
6510
  } else if (!wasDeliveredVerbatim(rec.launchPrompt, b)) {
6358
- rewrittenThisRecord = true;
6359
- if (!superseded(rec, chunk2)) {
6360
- rewrittenPrompts.push(
6361
- disclose(
6362
- name,
6363
- "launched with a prompt that is not the one the CLI built",
6364
- { reasonZh: "\u542F\u52A8\u65F6\u4F7F\u7528\u7684 prompt \u4E0D\u662F CLI \u6784\u5EFA\u7684\u90A3\u4E00\u4EFD" }
6365
- )
6366
- );
6511
+ if (openedBriefOf(rec, `chunk-${chunk2}`) && rec.diffToolCalls > 0) {
6512
+ if (!superseded(rec, chunk2)) {
6513
+ driftedLaunches.push(
6514
+ `${name} \u2014 launched with a near-verbatim prompt; its brief was opened and the diff was read, so the delivery stands`
6515
+ );
6516
+ }
6517
+ } else {
6518
+ rewrittenThisRecord = true;
6519
+ if (!superseded(rec, chunk2)) {
6520
+ rewrittenPrompts.push(
6521
+ disclose(
6522
+ name,
6523
+ "launched with a prompt that is not the one the CLI built",
6524
+ { reasonZh: "\u542F\u52A8\u65F6\u4F7F\u7528\u7684 prompt \u4E0D\u662F CLI \u6784\u5EFA\u7684\u90A3\u4E00\u4EFD" }
6525
+ )
6526
+ );
6527
+ }
6367
6528
  }
6368
6529
  }
6369
6530
  }
@@ -6407,16 +6568,12 @@ function coverageFromTranscripts(planPath, env = process.env) {
6407
6568
  );
6408
6569
  }
6409
6570
  const buildable = roster.filter((r) => builtOf(r.key) !== void 0);
6410
- const openedBrief = /* @__PURE__ */ __name((rec, key) => {
6411
- const needle = JSON.stringify(briefPath(planPath, key));
6412
- return rec.successfulCallArgs.some((a) => a.includes(needle));
6413
- }, "openedBrief");
6414
6571
  const candidatesOf = buildable.map((req) => {
6415
6572
  const b = builtOf(req.key);
6416
6573
  return records.filter((r) => wasDeliveredVerbatim(r.launchPrompt, b));
6417
6574
  });
6418
6575
  const openedOfReq = buildable.map(
6419
- (req, i) => candidatesOf[i].filter((r) => openedBrief(r, req.key))
6576
+ (req, i) => candidatesOf[i].filter((r) => openedBriefOf(r, req.key))
6420
6577
  );
6421
6578
  const matchedRec = /* @__PURE__ */ new Map();
6422
6579
  const augment = /* @__PURE__ */ __name((i, edges, seen) => {
@@ -6441,6 +6598,7 @@ function coverageFromTranscripts(planPath, env = process.env) {
6441
6598
  }
6442
6599
  const assignment = /* @__PURE__ */ new Map();
6443
6600
  for (const [rec, i] of matchedRec) assignment.set(i, rec);
6601
+ const rescued = /* @__PURE__ */ new Set();
6444
6602
  let buildableIdx = -1;
6445
6603
  for (const req of roster) {
6446
6604
  const b = builtOf(req.key);
@@ -6465,6 +6623,21 @@ function coverageFromTranscripts(planPath, env = process.env) {
6465
6623
  const pick2 = assignment.get(buildableIdx);
6466
6624
  if (pick2 === void 0) {
6467
6625
  const anyMatch = candidatesOf[buildableIdx].length > 0;
6626
+ if (!anyMatch) {
6627
+ const needsDiff = req.role === "chunk" || BRIEFS[req.role].readsDiff;
6628
+ const rescue = records.find(
6629
+ (r) => !matchedRec.has(r) && !rescued.has(r) && r.successfulToolCalls > 0 && (!needsDiff || r.diffToolCalls > 0) && openedBriefOf(r, req.key)
6630
+ );
6631
+ if (rescue !== void 0) {
6632
+ rescued.add(rescue);
6633
+ if (req.role !== "chunk") {
6634
+ driftedLaunches.push(
6635
+ `${roleLabel(req)} \u2014 no launch matched its block verbatim, but an agent opened this role's brief and did the work, so the delivery stands`
6636
+ );
6637
+ }
6638
+ continue;
6639
+ }
6640
+ }
6468
6641
  missingRoles.push(
6469
6642
  disclose(
6470
6643
  roleLabel(req),
@@ -6512,6 +6685,7 @@ function coverageFromTranscripts(planPath, env = process.env) {
6512
6685
  idleAgents,
6513
6686
  unopenedAgents,
6514
6687
  rewrittenPrompts,
6688
+ driftedLaunches,
6515
6689
  missingRoles,
6516
6690
  missingRoleSelectors,
6517
6691
  disclosures,
@@ -6526,7 +6700,13 @@ function coverageFromTranscripts(planPath, env = process.env) {
6526
6700
  };
6527
6701
  }
6528
6702
  __name(coverageFromTranscripts, "coverageFromTranscripts");
6529
- var rebuildFix = /* @__PURE__ */ __name((role, noun) => `build the prompt with \`"\${QWEN_CODE_CLI:-qwen}" review agent-prompt --plan <plan> --role ${role} --findings <file> [--rules <rules file>] [--round <k>]\` ` + (role === "reverse-audit" ? `(an early round with nothing confirmed passes an empty file; ` : `(pass the shard's findings, never an empty file \u2014 a verifier that sees no findings verifies nothing; `) + `pass --rules whenever the review loaded any, or the rebuilt brief silently drops the project rules) and launch an agent with EXACTLY what it prints \u2014 no hand-added ${noun} number` + // --round bakes in a ROUND number. Verify's noun is "shard", and a
6703
+ var rebuildFix = /* @__PURE__ */ __name((role, noun) => `build the prompt with \`"\${QWEN_CODE_CLI:-qwen}" review agent-prompt --plan <plan> --role ${role} --findings <file> [--rules <rules file>] ` + // --round is MANDATORY for a reverse-audit build (`agent-prompt` refuses a
6704
+ // round-less call — the label keys the record and the budget gate's
6705
+ // accounting), so the paste-and-run repair must not bracket it as optional:
6706
+ // an orchestrator honouring the bracket convention would have its first
6707
+ // repair attempt rejected. Verify genuinely takes it or not (only a repeat
6708
+ // verification round passes one), so its brackets stay.
6709
+ (role === "reverse-audit" ? `--round <k>\` ` : `[--round <k>]\` `) + (role === "reverse-audit" ? `(an early round with nothing confirmed passes an empty file; ` : `(pass the shard's findings, never an empty file \u2014 a verifier that sees no findings verifies nothing; `) + `pass --rules whenever the review loaded any, or the rebuilt brief silently drops the project rules) and launch an agent with EXACTLY what it prints \u2014 no hand-added ${noun} number` + // --round bakes in a ROUND number. Verify's noun is "shard", and a
6530
6710
  // parenthetical claiming --round bakes it in would send the reader to the
6531
6711
  // wrong flag — shards are already told apart by their findings digest.
6532
6712
  (role === "reverse-audit" ? ` (--round bakes it in)` : ``) + `, no summary of your own, no rewording`, "rebuildFix");
@@ -6641,7 +6821,9 @@ function verificationGaps(planPath, opts, env = process.env) {
6641
6821
  (k) => k === "reverse-audit" || k.startsWith("reverse-audit--")
6642
6822
  );
6643
6823
  const reverse = bestDelivery(reverseKeys);
6644
- const reverseGap = !balancedMedium && reverse !== "ok";
6824
+ const budgetStopped = readBudgetStop(planPath) !== null;
6825
+ const reverseByDesign = budgetStopped && reverse === "not-built";
6826
+ const reverseGap = !balancedMedium && !reverseByDesign && reverse !== "ok";
6645
6827
  if (reverseGap) {
6646
6828
  remediation.push(
6647
6829
  `reverse audit: ${REVERSE_AUDIT_GAP[reverse].fix.replace(
@@ -6944,6 +7126,24 @@ function unmarkedComments(comments) {
6944
7126
  }
6945
7127
  __name(unmarkedComments, "unmarkedComments");
6946
7128
 
7129
+ // packages/cli/src/commands/review/lib/review-footer.ts
7130
+ init_esbuild_shims();
7131
+ var FOOTER_MARKER = "via Qwen Code /review";
7132
+ function reviewFooter(modelId, cliVersion) {
7133
+ return `_\u2014 ${modelId} ${FOOTER_MARKER} (v${cliVersion})_`;
7134
+ }
7135
+ __name(reviewFooter, "reviewFooter");
7136
+ var REVIEW_FOOTER_RE = /\s*(?:_— (?:(?! via Qwen Code \/review)[^\n])* via Qwen Code \/review(?: \(v[^\n)]*\))?_?\s*)+$/;
7137
+ function isFooterSafeModelId(modelId) {
7138
+ return !/[\n\r]/.test(modelId) && !modelId.includes(FOOTER_MARKER);
7139
+ }
7140
+ __name(isFooterSafeModelId, "isFooterSafeModelId");
7141
+ var FOOTER_VERSION_RE = /^[A-Za-z0-9._+-]+$/;
7142
+ function footerVersion(stamp) {
7143
+ return stamp !== void 0 && FOOTER_VERSION_RE.test(stamp) ? stamp : void 0;
7144
+ }
7145
+ __name(footerVersion, "footerVersion");
7146
+
6947
7147
  // packages/cli/src/commands/review/compose-review.ts
6948
7148
  var LOW_SIGNAL_SRC_DIFF_LINES = 100;
6949
7149
  function withMarker(line) {
@@ -6970,6 +7170,10 @@ function toStringList(value, field) {
6970
7170
  return [...value];
6971
7171
  }
6972
7172
  __name(toStringList, "toStringList");
7173
+ function stripReviewFooter(entry) {
7174
+ return entry.replace(REVIEW_FOOTER_RE, "");
7175
+ }
7176
+ __name(stripReviewFooter, "stripReviewFooter");
6973
7177
  function toBool(value, field) {
6974
7178
  if (value === void 0 || value === null) return false;
6975
7179
  if (typeof value !== "boolean") {
@@ -6991,15 +7195,15 @@ __name(composeReview, "composeReview");
6991
7195
  function ledgerMarkerFor(input) {
6992
7196
  try {
6993
7197
  if (!input.planPath) return null;
6994
- const plan = JSON.parse(readFileSync8(input.planPath, "utf8"));
7198
+ const plan = JSON.parse(readFileSync9(input.planPath, "utf8"));
6995
7199
  const pr = plan?.prNumber;
6996
7200
  const isPr = typeof pr === "number" && Number.isInteger(pr) && pr > 0 || typeof pr === "string" && /^\d+$/.test(pr);
6997
7201
  if (!isPr) return null;
6998
7202
  let prevRound = 0;
6999
7203
  try {
7000
7204
  const prev = JSON.parse(
7001
- readFileSync8(
7002
- join8(
7205
+ readFileSync9(
7206
+ join9(
7003
7207
  dirname4(input.planPath),
7004
7208
  `qwen-review-pr-${pr}-prev-ledger.json`
7005
7209
  ),
@@ -7014,7 +7218,7 @@ function ledgerMarkerFor(input) {
7014
7218
  buildLedger(
7015
7219
  prevRound + 1,
7016
7220
  input.draftedComments ?? [],
7017
- toStringList(input.bodyCriticals, "bodyCriticals")
7221
+ toStringList(input.bodyCriticals, "bodyCriticals").map(stripReviewFooter).filter((entry) => entry.trim() !== "")
7018
7222
  )
7019
7223
  );
7020
7224
  } catch {
@@ -7028,7 +7232,7 @@ function composeReviewBody(input, cliVersion) {
7028
7232
  input.suggestionsInline,
7029
7233
  "suggestionsInline"
7030
7234
  );
7031
- const bodyCriticals = toStringList(input.bodyCriticals, "bodyCriticals");
7235
+ const bodyCriticals = toStringList(input.bodyCriticals, "bodyCriticals").map(stripReviewFooter).filter((entry) => entry.trim() !== "");
7032
7236
  const suggestionsDiscarded = toCount(
7033
7237
  input.suggestionsDiscarded,
7034
7238
  "suggestionsDiscarded"
@@ -7036,7 +7240,7 @@ function composeReviewBody(input, cliVersion) {
7036
7240
  const cannotTell = toStringList(
7037
7241
  input.cannotTellCriticals,
7038
7242
  "cannotTellCriticals"
7039
- );
7243
+ ).map(stripReviewFooter).filter((entry) => entry.trim() !== "");
7040
7244
  const uncoverable = toStringList(
7041
7245
  input.uncoverableChunks,
7042
7246
  "uncoverableChunks"
@@ -7046,6 +7250,19 @@ function composeReviewBody(input, cliVersion) {
7046
7250
  "unreviewedDimensions"
7047
7251
  );
7048
7252
  const coverageEntries = [];
7253
+ let budgetEntry;
7254
+ if (input.planPath) {
7255
+ const stop = readBudgetStop(input.planPath);
7256
+ if (stop !== null) {
7257
+ for (let i = unreviewed.length - 1; i >= 0; i--) {
7258
+ if (unreviewed[i].includes(BUDGET_STOP_PHRASE)) {
7259
+ unreviewed.splice(i, 1);
7260
+ }
7261
+ }
7262
+ budgetEntry = budgetStopDisclosure(stop.round ?? void 0);
7263
+ coverageEntries.push(budgetEntry);
7264
+ }
7265
+ }
7049
7266
  const remediation = [];
7050
7267
  const planRef = input.planPath ? shellQuotePath(input.planPath) : "<plan>";
7051
7268
  const missingReceipts = [];
@@ -7205,6 +7422,11 @@ function composeReviewBody(input, cliVersion) {
7205
7422
  "compose-review: modelId is required (the public footer names the reviewing model)"
7206
7423
  );
7207
7424
  }
7425
+ if (!isFooterSafeModelId(modelId)) {
7426
+ throw new TypeError(
7427
+ "compose-review: modelId is interpolated into the public footer verbatim \u2014 it must be a single line that does not contain the footer marker"
7428
+ );
7429
+ }
7208
7430
  const c = criticalsInline + bodyCriticals.length;
7209
7431
  const s = suggestionsInline + suggestionsDiscarded;
7210
7432
  const baseEvent = c >= 1 ? "REQUEST_CHANGES" : s >= 1 ? "COMMENT" : "APPROVE";
@@ -7217,28 +7439,28 @@ function composeReviewBody(input, cliVersion) {
7217
7439
  }
7218
7440
  if (contextUnavailable) cappedBy.push("context-unavailable");
7219
7441
  if (criticalsUnverified) cappedBy.push("criticals-unverified");
7220
- let event = baseEvent;
7221
- if (event === "APPROVE" && cappedBy.length > 0) event = "COMMENT";
7442
+ let event2 = baseEvent;
7443
+ if (event2 === "APPROVE" && cappedBy.length > 0) event2 = "COMMENT";
7222
7444
  const deterministicBodyCriticals = bodyCriticals.length - nonDeterministicBodyCriticals;
7223
- if (event === "REQUEST_CHANGES" && criticalsUnverified && deterministicBodyCriticals === 0) {
7224
- event = "COMMENT";
7445
+ if (event2 === "REQUEST_CHANGES" && criticalsUnverified && deterministicBodyCriticals === 0) {
7446
+ event2 = "COMMENT";
7225
7447
  }
7226
7448
  let downgraded = false;
7227
7449
  let downgradedFrom = null;
7228
- if (event === "APPROVE" && downgradeApprove) {
7229
- event = "COMMENT";
7450
+ if (event2 === "APPROVE" && downgradeApprove) {
7451
+ event2 = "COMMENT";
7230
7452
  downgraded = true;
7231
7453
  downgradedFrom = "Approve";
7232
- } else if ((event === "REQUEST_CHANGES" || baseEvent === "REQUEST_CHANGES" && criticalsUnverified) && downgradeRequestChanges) {
7233
- event = "COMMENT";
7454
+ } else if ((event2 === "REQUEST_CHANGES" || baseEvent === "REQUEST_CHANGES" && criticalsUnverified) && downgradeRequestChanges) {
7455
+ event2 = "COMMENT";
7234
7456
  downgraded = true;
7235
7457
  downgradedFrom = "Request changes";
7236
7458
  }
7237
7459
  let lowSignal = null;
7238
- if (event === "APPROVE" && input.planPath) {
7460
+ if (event2 === "APPROVE" && input.planPath) {
7239
7461
  try {
7240
7462
  const plan = JSON.parse(
7241
- readFileSync8(input.planPath, "utf8")
7463
+ readFileSync9(input.planPath, "utf8")
7242
7464
  );
7243
7465
  const src = Number(plan.srcDiffLines ?? 0);
7244
7466
  if (src > LOW_SIGNAL_SRC_DIFF_LINES) {
@@ -7247,12 +7469,12 @@ function composeReviewBody(input, cliVersion) {
7247
7469
  } catch {
7248
7470
  }
7249
7471
  }
7250
- const footer = `_\u2014 ${modelId} via Qwen Code /review (v${cliVersion})_`;
7472
+ const footer = reviewFooter(modelId, cliVersion);
7251
7473
  const bilingual = bilingualFromPlan(input.planPath, input.prBodyFetcher);
7252
- const render = /* @__PURE__ */ __name((parts, sep4) => {
7253
- const en = parts.map((p) => p.en).join(sep4);
7474
+ const render = /* @__PURE__ */ __name((parts, sep5) => {
7475
+ const en = parts.map((p) => p.en).join(sep5);
7254
7476
  if (en === "") return "";
7255
- const zh = parts.map((p) => p.zh).join(sep4);
7477
+ const zh = parts.map((p) => p.zh).join(sep5);
7256
7478
  const text = bilingual && zh !== en ? `${en}
7257
7479
 
7258
7480
  <details>
@@ -7306,7 +7528,7 @@ ${footer}`;
7306
7528
  if (seenCaller.has(d)) continue;
7307
7529
  seenCaller.add(d);
7308
7530
  const echoesCoverage = covEntries.some(
7309
- (e) => d === e.subject || d.startsWith(`${e.subject} \u2014 `)
7531
+ (e) => d === e.subject || e !== budgetEntry && d.startsWith(`${e.subject} \u2014 `)
7310
7532
  );
7311
7533
  if (!echoesCoverage) callerLeft.push(d);
7312
7534
  }
@@ -7386,7 +7608,7 @@ ${footer}`;
7386
7608
  zh: `Test Plan\uFF08\u975E\u963B\u65AD\uFF09\uFF1A${testPlanNotes.join("; ")}\u3002`
7387
7609
  }
7388
7610
  ] : [];
7389
- if (event === "REQUEST_CHANGES") {
7611
+ if (event2 === "REQUEST_CHANGES") {
7390
7612
  const parts = [
7391
7613
  ...contextUnavailable ? [contextUnavailableClause] : [],
7392
7614
  ...cannotTellBlock,
@@ -7396,7 +7618,7 @@ ${footer}`;
7396
7618
  ...bodyCriticalBlock
7397
7619
  ];
7398
7620
  return {
7399
- event,
7621
+ event: event2,
7400
7622
  body: render(parts, "\n\n"),
7401
7623
  baseEvent,
7402
7624
  cappedBy,
@@ -7406,9 +7628,9 @@ ${footer}`;
7406
7628
  lowSignal
7407
7629
  };
7408
7630
  }
7409
- if (event === "APPROVE") {
7631
+ if (event2 === "APPROVE") {
7410
7632
  return {
7411
- event,
7633
+ event: event2,
7412
7634
  body: render(
7413
7635
  [
7414
7636
  { en: "No issues found. LGTM! \u2705", zh: "\u672A\u53D1\u73B0\u95EE\u9898\u3002LGTM\uFF01\u2705" },
@@ -7471,7 +7693,7 @@ ${footer}`;
7471
7693
  clauses.push(...bodyCriticalBlock);
7472
7694
  }
7473
7695
  return {
7474
- event,
7696
+ event: event2,
7475
7697
  body: render(clauses, " "),
7476
7698
  baseEvent,
7477
7699
  cappedBy,
@@ -7530,7 +7752,7 @@ function scriptLintGate(planPath) {
7530
7752
  const disclosed = [];
7531
7753
  let plan;
7532
7754
  try {
7533
- plan = JSON.parse(readFileSync8(planPath, "utf8"));
7755
+ plan = JSON.parse(readFileSync9(planPath, "utf8"));
7534
7756
  } catch {
7535
7757
  unreviewed.push(
7536
7758
  "the executable-script lint \u2014 could not read the plan to check the gate"
@@ -7541,13 +7763,13 @@ function scriptLintGate(planPath) {
7541
7763
  return { criticals, unreviewed, disclosed };
7542
7764
  }
7543
7765
  const owed = hasExecutableScript(plan);
7544
- const reportPath = join8(
7766
+ const reportPath = join9(
7545
7767
  dirname4(planPath),
7546
7768
  scriptLintReportName(plan.prNumber)
7547
7769
  );
7548
7770
  let report;
7549
7771
  try {
7550
- report = JSON.parse(readFileSync8(reportPath, "utf8"));
7772
+ report = JSON.parse(readFileSync9(reportPath, "utf8"));
7551
7773
  } catch {
7552
7774
  if (owed) {
7553
7775
  unreviewed.push(
@@ -7603,7 +7825,7 @@ function testPlanGate(planPath) {
7603
7825
  const notes = [];
7604
7826
  let plan;
7605
7827
  try {
7606
- plan = JSON.parse(readFileSync8(planPath, "utf8"));
7828
+ plan = JSON.parse(readFileSync9(planPath, "utf8"));
7607
7829
  } catch {
7608
7830
  return { notes };
7609
7831
  }
@@ -7613,8 +7835,8 @@ function testPlanGate(planPath) {
7613
7835
  let report;
7614
7836
  try {
7615
7837
  report = JSON.parse(
7616
- readFileSync8(
7617
- join8(dirname4(planPath), `qwen-review-pr-${pr}-test-plan.json`),
7838
+ readFileSync9(
7839
+ join9(dirname4(planPath), `qwen-review-pr-${pr}-test-plan.json`),
7618
7840
  "utf8"
7619
7841
  )
7620
7842
  );
@@ -7647,7 +7869,7 @@ function bilingualFromPlan(planPath, fetchPrBody2 = fetchPrBodyViaGh) {
7647
7869
  if (!planPath) return false;
7648
7870
  let plan;
7649
7871
  try {
7650
- plan = JSON.parse(readFileSync8(planPath, "utf8"));
7872
+ plan = JSON.parse(readFileSync9(planPath, "utf8"));
7651
7873
  } catch {
7652
7874
  return false;
7653
7875
  }
@@ -7667,7 +7889,7 @@ __name(bilingualFromPlan, "bilingualFromPlan");
7667
7889
  function readDraftedComments(path3) {
7668
7890
  let raw;
7669
7891
  try {
7670
- raw = readFileSync8(path3, "utf8");
7892
+ raw = readFileSync9(path3, "utf8");
7671
7893
  } catch (err) {
7672
7894
  throw new Error(
7673
7895
  `compose-review: cannot read the comments file ${path3}: ${err.message}. Pass the drafted inline comments \u2014 the same array the review payload will carry \u2014 or a file containing [] when nothing anchors inline.`
@@ -7721,7 +7943,7 @@ var composeReviewCommand = {
7721
7943
  "compose-review: --comments is required \u2014 the inline counts are counted from the drafted comments file, never typed. Pass a file containing [] when nothing anchors inline."
7722
7944
  );
7723
7945
  }
7724
- const raw = readFileSync8(input ?? 0, "utf8");
7946
+ const raw = readFileSync9(input ?? 0, "utf8");
7725
7947
  const parsed = JSON.parse(raw);
7726
7948
  delete parsed.env;
7727
7949
  delete parsed.prBodyFetcher;
@@ -7738,7 +7960,9 @@ var composeReviewCommand = {
7738
7960
  ...countInlineFindings(drafted),
7739
7961
  draftedComments: drafted
7740
7962
  },
7741
- await getCliVersion()
7963
+ // Same pin as `submit`: the startup stamp, not a version resolved at
7964
+ // compose time — a shared runner can rewrite the install mid-session.
7965
+ footerVersion(process.env["QWEN_CODE_STARTUP_VERSION"]) ?? await getCliVersion()
7742
7966
  );
7743
7967
  const json = JSON.stringify(
7744
7968
  { ...result, verdictLine: verdictLine(result) },
@@ -7746,8 +7970,8 @@ var composeReviewCommand = {
7746
7970
  2
7747
7971
  );
7748
7972
  if (out) {
7749
- mkdirSync5(dirname4(out), { recursive: true });
7750
- writeFileSync5(out, json, "utf8");
7973
+ mkdirSync6(dirname4(out), { recursive: true });
7974
+ writeFileSync6(out, json, "utf8");
7751
7975
  }
7752
7976
  writeStdoutLine(json);
7753
7977
  for (const fix of result.remediation) {
@@ -7848,7 +8072,7 @@ __name(verdictLine, "verdictLine");
7848
8072
 
7849
8073
  // packages/cli/src/commands/review/findings.ts
7850
8074
  init_esbuild_shims();
7851
- import { existsSync as existsSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "node:fs";
8075
+ import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
7852
8076
  import { dirname as dirname5, resolve as resolve5 } from "node:path";
7853
8077
  var SEVERITIES = ["Critical", "Suggestion", "Nice to have"];
7854
8078
  var CONFIDENCES = ["high", "low"];
@@ -7901,8 +8125,8 @@ function parseLocations(o, index) {
7901
8125
  const lo = l;
7902
8126
  const file2 = asString(lo, "file");
7903
8127
  if (!file2) fail(index, `location ${j} is missing a non-empty "file".`);
7904
- if (lo["line"] !== void 0 && typeof lo["line"] !== "number") {
7905
- fail(index, `location ${j} has a non-numeric "line".`);
8128
+ if (lo["line"] !== void 0 && (typeof lo["line"] !== "number" || !Number.isSafeInteger(lo["line"]) || lo["line"] <= 0)) {
8129
+ fail(index, `location ${j} has an invalid "line".`);
7906
8130
  }
7907
8131
  return {
7908
8132
  file: file2,
@@ -7918,8 +8142,8 @@ function parseLocations(o, index) {
7918
8142
  'needs a non-empty "file" (or a "locations" array for a pattern aggregate).'
7919
8143
  );
7920
8144
  }
7921
- if (o["line"] !== void 0 && typeof o["line"] !== "number") {
7922
- fail(index, 'has a non-numeric "line".');
8145
+ if (o["line"] !== void 0 && (typeof o["line"] !== "number" || !Number.isSafeInteger(o["line"]) || o["line"] <= 0)) {
8146
+ fail(index, 'has an invalid "line".');
7923
8147
  }
7924
8148
  return [
7925
8149
  {
@@ -8222,7 +8446,7 @@ __name(renderFindings, "renderFindings");
8222
8446
  function readJson(path3, what) {
8223
8447
  let text;
8224
8448
  try {
8225
- text = readFileSync9(resolve5(path3), "utf8");
8449
+ text = readFileSync10(resolve5(path3), "utf8");
8226
8450
  } catch (err) {
8227
8451
  throw new Error(
8228
8452
  `Could not read the ${what} file at ${JSON.stringify(path3)}: ${err instanceof Error ? err.message : String(err)}`
@@ -8293,8 +8517,8 @@ var findingsCommand = {
8293
8517
  }
8294
8518
  const report = buildReport(findings);
8295
8519
  const target = resolve5(out);
8296
- mkdirSync6(dirname5(target), { recursive: true });
8297
- writeFileSync6(target, `${JSON.stringify(report, null, 2)}
8520
+ mkdirSync7(dirname5(target), { recursive: true });
8521
+ writeFileSync7(target, `${JSON.stringify(report, null, 2)}
8298
8522
  `, "utf8");
8299
8523
  const { bySeverity, byConfidence } = report.counts;
8300
8524
  writeStderrLine(
@@ -8325,7 +8549,7 @@ var findingsCommand = {
8325
8549
  // packages/cli/src/commands/review/fetch-pr.ts
8326
8550
  init_esbuild_shims();
8327
8551
  import { execFileSync as execFileSync4 } from "node:child_process";
8328
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
8552
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
8329
8553
  import { dirname as dirname6, resolve as resolve6 } from "node:path";
8330
8554
 
8331
8555
  // packages/cli/src/commands/review/lib/git.ts
@@ -8433,11 +8657,11 @@ var LITERAL_PATHSPECS = "--literal-pathspecs";
8433
8657
 
8434
8658
  // packages/cli/src/commands/review/lib/effort.ts
8435
8659
  init_esbuild_shims();
8436
- import { readFileSync as readFileSync10 } from "node:fs";
8660
+ import { readFileSync as readFileSync11 } from "node:fs";
8437
8661
  function resolveEffort(explicit) {
8438
8662
  if (explicit && EFFORT_LEVELS.has(explicit)) return explicit;
8439
8663
  try {
8440
- const parsed = JSON.parse(readFileSync10(PARSE_ARGS_REPORT, "utf8"));
8664
+ const parsed = JSON.parse(readFileSync11(PARSE_ARGS_REPORT, "utf8"));
8441
8665
  if (typeof parsed.effort === "string" && EFFORT_LEVELS.has(parsed.effort)) {
8442
8666
  return parsed.effort;
8443
8667
  }
@@ -8458,7 +8682,7 @@ __name(planEffortField, "planEffortField");
8458
8682
 
8459
8683
  // packages/cli/src/commands/review/lib/report.ts
8460
8684
  init_esbuild_shims();
8461
- import { statSync as statSync3 } from "node:fs";
8685
+ import { statSync as statSync4 } from "node:fs";
8462
8686
 
8463
8687
  // packages/cli/src/commands/review/lib/heavy.ts
8464
8688
  init_esbuild_shims();
@@ -8561,7 +8785,7 @@ __name(buildPlanReport, "buildPlanReport");
8561
8785
  function warnOnReportSize(path3, cap) {
8562
8786
  let size = 0;
8563
8787
  try {
8564
- size = statSync3(path3).size;
8788
+ size = statSync4(path3).size;
8565
8789
  } catch {
8566
8790
  return;
8567
8791
  }
@@ -8684,7 +8908,7 @@ async function runFetchPr(args) {
8684
8908
  );
8685
8909
  }
8686
8910
  try {
8687
- mkdirSync7(dirname6(wt), { recursive: true });
8911
+ mkdirSync8(dirname6(wt), { recursive: true });
8688
8912
  git("worktree", "add", wt, ref);
8689
8913
  } catch (err) {
8690
8914
  tryRemove(
@@ -8694,7 +8918,7 @@ async function runFetchPr(args) {
8694
8918
  `Failed to create worktree at ${wt}: ${err.message}`
8695
8919
  );
8696
8920
  }
8697
- mkdirSync7(REVIEW_TMP_DIR, { recursive: true });
8921
+ mkdirSync8(REVIEW_TMP_DIR, { recursive: true });
8698
8922
  const { sha: mergeBaseSha, baseFetchFailed } = resolveMergeBase(
8699
8923
  remote,
8700
8924
  meta.baseRefName,
@@ -8718,7 +8942,7 @@ async function runFetchPr(args) {
8718
8942
  ...PINNED_DIFF_FLAGS,
8719
8943
  `${mergeBaseSha}..${fetchedSha}`
8720
8944
  );
8721
- writeFileSync7(diffRel, buf);
8945
+ writeFileSync8(diffRel, buf);
8722
8946
  diffText = buf.toString("utf8");
8723
8947
  diffPath = diffRel;
8724
8948
  diffPathAbsolute = resolve6(diffRel);
@@ -8745,7 +8969,7 @@ async function runFetchPr(args) {
8745
8969
  let auditSince = fetchedAt;
8746
8970
  let prevRaw = null;
8747
8971
  try {
8748
- prevRaw = readFileSync11(out, "utf8");
8972
+ prevRaw = readFileSync12(out, "utf8");
8749
8973
  } catch (err) {
8750
8974
  const code = err.code;
8751
8975
  if (code !== "ENOENT") {
@@ -8828,7 +9052,7 @@ async function runFetchPr(args) {
8828
9052
  ...buildPlanReport(plan, (path3) => fileLineCount(fetchedSha, path3)),
8829
9053
  ...planEffortField(args.effort)
8830
9054
  };
8831
- writeFileSync7(out, stringifyPlanReport(result), "utf8");
9055
+ writeFileSync8(out, stringifyPlanReport(result), "utf8");
8832
9056
  writeStdoutLine(`Wrote fetch-pr report to ${out}`);
8833
9057
  if (diffPath) writeStdoutLine(`Wrote review diff to ${diffPath}`);
8834
9058
  writeStderrLine(
@@ -8912,13 +9136,13 @@ var fetchPrCommand = {
8912
9136
 
8913
9137
  // packages/cli/src/commands/review/capture-local.ts
8914
9138
  init_esbuild_shims();
8915
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
9139
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "node:fs";
8916
9140
  import { dirname as dirname7, resolve as resolve8 } from "node:path";
8917
9141
 
8918
9142
  // packages/cli/src/commands/review/lib/local-diff.ts
8919
9143
  init_esbuild_shims();
8920
- import { lstatSync as lstatSync2, statSync as statSync4, realpathSync as realpathSync2 } from "node:fs";
8921
- import { join as join9, relative as relative2, resolve as resolve7, isAbsolute as isAbsolute2, sep as sep2 } from "node:path";
9144
+ import { lstatSync as lstatSync2, statSync as statSync5, realpathSync as realpathSync2 } from "node:fs";
9145
+ import { join as join10, relative as relative2, resolve as resolve7, isAbsolute as isAbsolute2, sep as sep2 } from "node:path";
8922
9146
  var MAX_UNTRACKED_BYTES = 1e6;
8923
9147
  var MAX_UNTRACKED_FILES = 500;
8924
9148
  var MAX_UNTRACKED_TOTAL_BYTES = 1e7;
@@ -8939,7 +9163,7 @@ function describeUndiffable(abs, st) {
8939
9163
  }
8940
9164
  if (st.isSymbolicLink()) {
8941
9165
  try {
8942
- return statSync4(abs).isDirectory() ? "is a symlink to a directory \u2014 git cannot diff it as a file" : null;
9166
+ return statSync5(abs).isDirectory() ? "is a symlink to a directory \u2014 git cannot diff it as a file" : null;
8943
9167
  } catch {
8944
9168
  return null;
8945
9169
  }
@@ -9044,7 +9268,7 @@ function captureLocalDiff(opts) {
9044
9268
  for (const path3 of candidates) {
9045
9269
  let bytes;
9046
9270
  try {
9047
- const abs = join9(repoRoot, path3);
9271
+ const abs = join10(repoRoot, path3);
9048
9272
  const st = lstatSync2(abs);
9049
9273
  const kind = describeUndiffable(abs, st);
9050
9274
  if (kind) {
@@ -9136,10 +9360,10 @@ function runCaptureLocal(args) {
9136
9360
  includeUntracked: args.untracked
9137
9361
  });
9138
9362
  const diffText = capture.diff.toString("utf8");
9139
- mkdirSync8(REVIEW_TMP_DIR, { recursive: true });
9140
- mkdirSync8(dirname7(resolve8(out)), { recursive: true });
9363
+ mkdirSync9(REVIEW_TMP_DIR, { recursive: true });
9364
+ mkdirSync9(dirname7(resolve8(out)), { recursive: true });
9141
9365
  const diffPath = tmpFile(target, "diff.txt");
9142
- writeFileSync8(diffPath, capture.diff);
9366
+ writeFileSync9(diffPath, capture.diff);
9143
9367
  const plan = buildDiffPlan(diffText);
9144
9368
  const result = {
9145
9369
  diffPath,
@@ -9152,7 +9376,7 @@ function runCaptureLocal(args) {
9152
9376
  skippedFiles: capture.skipped,
9153
9377
  ...planEffortField(args.effort)
9154
9378
  };
9155
- writeFileSync8(out, stringifyPlanReport(result), "utf8");
9379
+ writeFileSync9(out, stringifyPlanReport(result), "utf8");
9156
9380
  writeStdoutLine(`Wrote diff to ${diffPath} and plan to ${out}`);
9157
9381
  if (capture.unbornHead) {
9158
9382
  writeStderrLine(
@@ -9210,13 +9434,13 @@ var captureLocalCommand = {
9210
9434
 
9211
9435
  // packages/cli/src/commands/review/plan-diff.ts
9212
9436
  init_esbuild_shims();
9213
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9 } from "node:fs";
9437
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10 } from "node:fs";
9214
9438
  import { resolve as resolve9 } from "node:path";
9215
9439
  function runPlanDiff(args) {
9216
9440
  const { diff_path: diffPath, out } = args;
9217
9441
  let diffText;
9218
9442
  try {
9219
- diffText = readFileSync12(diffPath, "utf8");
9443
+ diffText = readFileSync13(diffPath, "utf8");
9220
9444
  } catch (err) {
9221
9445
  throw new Error(
9222
9446
  `Cannot read diff file ${diffPath}: ${err.message}`
@@ -9243,8 +9467,8 @@ function runPlanDiff(args) {
9243
9467
  ...buildPlanReport(plan, null),
9244
9468
  ...planEffortField(args.effort)
9245
9469
  };
9246
- mkdirSync9(REVIEW_TMP_DIR, { recursive: true });
9247
- writeFileSync9(out, stringifyPlanReport(result), "utf8");
9470
+ mkdirSync10(REVIEW_TMP_DIR, { recursive: true });
9471
+ writeFileSync10(out, stringifyPlanReport(result), "utf8");
9248
9472
  writeStdoutLine(`Wrote diff plan to ${out}`);
9249
9473
  if (plan.diffLines === 0) {
9250
9474
  writeStderrLine(
@@ -9290,8 +9514,8 @@ var planDiffCommand = {
9290
9514
 
9291
9515
  // packages/cli/src/commands/review/pr-context.ts
9292
9516
  init_esbuild_shims();
9293
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
9294
- import { dirname as dirname8, join as join10 } from "node:path";
9517
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
9518
+ import { dirname as dirname8, join as join11 } from "node:path";
9295
9519
  var SUMMARY_MARKER = "<!-- qwen-review-suggestion-summary -->";
9296
9520
  function isLegacySuggestionSummary(body) {
9297
9521
  return (body ?? "").includes(SUMMARY_MARKER);
@@ -9769,9 +9993,9 @@ async function runPrContext(args) {
9769
9993
  }
9770
9994
  if (prevLedger) {
9771
9995
  try {
9772
- mkdirSync10(dirname8(out), { recursive: true });
9773
- writeFileSync10(
9774
- join10(dirname8(out), `qwen-review-pr-${prNumber}-prev-ledger.json`),
9996
+ mkdirSync11(dirname8(out), { recursive: true });
9997
+ writeFileSync11(
9998
+ join11(dirname8(out), `qwen-review-pr-${prNumber}-prev-ledger.json`),
9775
9999
  JSON.stringify(prevLedger, null, 2)
9776
10000
  );
9777
10001
  } catch {
@@ -9786,8 +10010,8 @@ async function runPrContext(args) {
9786
10010
  reviews,
9787
10011
  prevLedger
9788
10012
  );
9789
- mkdirSync10(dirname8(out), { recursive: true });
9790
- writeFileSync10(out, md, "utf8");
10013
+ mkdirSync11(dirname8(out), { recursive: true });
10014
+ writeFileSync11(out, md, "utf8");
9791
10015
  const meaningfulReviewCount = reviews.filter(
9792
10016
  (r) => isReviewWorthShowing(stripLedgerMarker(r.body ?? ""))
9793
10017
  ).length;
@@ -9841,7 +10065,7 @@ var prContextCommand = {
9841
10065
 
9842
10066
  // packages/cli/src/commands/review/comment-status.ts
9843
10067
  init_esbuild_shims();
9844
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
10068
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "node:fs";
9845
10069
  import { dirname as dirname9 } from "node:path";
9846
10070
  var TOUCHED_BY_CAP = 10;
9847
10071
  function buildThreadStatuses(comments, prAuthor, probe) {
@@ -10047,9 +10271,9 @@ async function runCommentStatus(args) {
10047
10271
  summary,
10048
10272
  threads
10049
10273
  };
10050
- mkdirSync11(dirname9(out), { recursive: true });
10274
+ mkdirSync12(dirname9(out), { recursive: true });
10051
10275
  const json = JSON.stringify(report, null, 2) + "\n";
10052
- writeFileSync11(out, json, "utf8");
10276
+ writeFileSync12(out, json, "utf8");
10053
10277
  writeStdoutLine(
10054
10278
  `Wrote comment-status report to ${out} (${comments.length} inline comments in ${summary.threads} threads: ${summary.outdated} outdated, ${summary.blockers} blocker(s), ${summary.changedSinceComment} on files changed since their comment, ${summary.withReplies} with replies, ${summary.authorReplied} answered by the PR author)`
10055
10279
  );
@@ -10077,8 +10301,8 @@ async function runCommentStatus(args) {
10077
10301
  writeStdoutLine(
10078
10302
  `warning: comment-status failed: ${msg}. It is an index, not the evidence \u2014 re-derive thread statuses per-comment if needed.`
10079
10303
  );
10080
- mkdirSync11(dirname9(out), { recursive: true });
10081
- writeFileSync11(
10304
+ mkdirSync12(dirname9(out), { recursive: true });
10305
+ writeFileSync12(
10082
10306
  out,
10083
10307
  JSON.stringify(
10084
10308
  {
@@ -10140,7 +10364,7 @@ var commentStatusCommand = {
10140
10364
 
10141
10365
  // packages/cli/src/commands/review/load-rules.ts
10142
10366
  init_esbuild_shims();
10143
- import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "node:fs";
10367
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "node:fs";
10144
10368
  import { dirname as dirname10 } from "node:path";
10145
10369
  function showFile(baseRef, path3) {
10146
10370
  return gitOpt("show", `${baseRef}:${path3}`);
@@ -10221,8 +10445,8 @@ __name(loadCombined, "loadCombined");
10221
10445
  async function runLoadRules(args) {
10222
10446
  const { base_ref: baseRef, out } = args;
10223
10447
  const { combined, loaded } = loadCombined(baseRef);
10224
- mkdirSync12(dirname10(out), { recursive: true });
10225
- writeFileSync12(out, combined, "utf8");
10448
+ mkdirSync13(dirname10(out), { recursive: true });
10449
+ writeFileSync13(out, combined, "utf8");
10226
10450
  if (loaded.length === 0) {
10227
10451
  writeStdoutLine(
10228
10452
  `No review rules found on ${baseRef}; wrote empty file to ${out}`
@@ -10253,7 +10477,7 @@ var loadRulesCommand = {
10253
10477
 
10254
10478
  // packages/cli/src/commands/review/presubmit.ts
10255
10479
  init_esbuild_shims();
10256
- import { writeFileSync as writeFileSync13, readFileSync as readFileSync13 } from "node:fs";
10480
+ import { writeFileSync as writeFileSync14, readFileSync as readFileSync14 } from "node:fs";
10257
10481
  function verdictStamp(run4) {
10258
10482
  return run4.completed_at ?? run4.started_at ?? "";
10259
10483
  }
@@ -10289,7 +10513,7 @@ __name(isCurrentActionsRunCheck, "isCurrentActionsRunCheck");
10289
10513
  function parseFindingsFile(path3) {
10290
10514
  let parsed;
10291
10515
  try {
10292
- parsed = JSON.parse(readFileSync13(path3, "utf8"));
10516
+ parsed = JSON.parse(readFileSync14(path3, "utf8"));
10293
10517
  } catch {
10294
10518
  return null;
10295
10519
  }
@@ -10592,7 +10816,7 @@ async function runPresubmit(args) {
10592
10816
  findingsFileInvalid,
10593
10817
  headDrift
10594
10818
  };
10595
- writeFileSync13(outPath, JSON.stringify(result, null, 2) + "\n", "utf8");
10819
+ writeFileSync14(outPath, JSON.stringify(result, null, 2) + "\n", "utf8");
10596
10820
  writeStdoutLine(`Wrote presubmit report to ${outPath}`);
10597
10821
  }
10598
10822
  __name(runPresubmit, "runPresubmit");
@@ -10631,17 +10855,17 @@ var presubmitCommand = {
10631
10855
  // packages/cli/src/commands/review/publish-assets.ts
10632
10856
  init_esbuild_shims();
10633
10857
  import { createHash as createHash2 } from "node:crypto";
10634
- import { readFileSync as readFileSync15, statSync as statSync5, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14 } from "node:fs";
10858
+ import { readFileSync as readFileSync16, statSync as statSync6, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15 } from "node:fs";
10635
10859
  import { basename as basename5, dirname as dirname11, resolve as resolve10 } from "node:path";
10636
10860
 
10637
10861
  // packages/cli/src/commands/review/lib/authorization.ts
10638
10862
  init_esbuild_shims();
10639
- import { readFileSync as readFileSync14 } from "node:fs";
10863
+ import { readFileSync as readFileSync15 } from "node:fs";
10640
10864
 
10641
10865
  // packages/cli/src/services/skill-args-file.ts
10642
10866
  init_esbuild_shims();
10643
10867
  import {
10644
- mkdirSync as mkdirSync13,
10868
+ mkdirSync as mkdirSync14,
10645
10869
  openSync as openSync2,
10646
10870
  writeSync,
10647
10871
  closeSync as closeSync2,
@@ -10650,9 +10874,9 @@ import {
10650
10874
  existsSync as existsSync5,
10651
10875
  constants
10652
10876
  } from "node:fs";
10653
- import { join as join11 } from "node:path";
10877
+ import { join as join12 } from "node:path";
10654
10878
  var debugLogger3 = createDebugLogger("SKILL_ARGS_FILE");
10655
- var SKILL_ARGS_DIR = join11(".qwen", "tmp");
10879
+ var SKILL_ARGS_DIR = join12(".qwen", "tmp");
10656
10880
  function safe(s) {
10657
10881
  return s.replace(/[^A-Za-z0-9._-]/g, "_");
10658
10882
  }
@@ -10662,11 +10886,11 @@ function currentSessionId() {
10662
10886
  }
10663
10887
  __name(currentSessionId, "currentSessionId");
10664
10888
  function skillArgsDir(sessionId = currentSessionId()) {
10665
- return sessionId ? join11(SKILL_ARGS_DIR, `s-${safe(sessionId)}`) : SKILL_ARGS_DIR;
10889
+ return sessionId ? join12(SKILL_ARGS_DIR, `s-${safe(sessionId)}`) : SKILL_ARGS_DIR;
10666
10890
  }
10667
10891
  __name(skillArgsDir, "skillArgsDir");
10668
10892
  function skillArgsPath(skillName, sessionId = currentSessionId()) {
10669
- return join11(
10893
+ return join12(
10670
10894
  skillArgsDir(sessionId),
10671
10895
  `qwen-skill-args-${safe(skillName)}.txt`
10672
10896
  );
@@ -10676,7 +10900,7 @@ function writeSkillArgs(skillName, args) {
10676
10900
  const path3 = skillArgsPath(skillName);
10677
10901
  try {
10678
10902
  const dir = skillArgsDir();
10679
- mkdirSync13(dir, { recursive: true });
10903
+ mkdirSync14(dir, { recursive: true });
10680
10904
  if (lstatSync3(dir).isSymbolicLink()) {
10681
10905
  debugLogger3.warn(
10682
10906
  `Skill args directory ${dir} is a symlink; refusing to write through it.`
@@ -10759,7 +10983,7 @@ function reviewWriteAuthorization(req) {
10759
10983
  const path3 = currentSessionId() === "" && req.skillArgs ? req.skillArgs : sessionScoped;
10760
10984
  let raw;
10761
10985
  try {
10762
- raw = readFileSync14(path3, "utf8");
10986
+ raw = readFileSync15(path3, "utf8");
10763
10987
  } catch {
10764
10988
  return {
10765
10989
  ok: false,
@@ -11025,7 +11249,7 @@ function runPublishAssets(args) {
11025
11249
  const fileList = [...args.files ?? []];
11026
11250
  if (args.findings) {
11027
11251
  try {
11028
- const artifact = JSON.parse(readFileSync15(resolve10(args.findings), "utf8"));
11252
+ const artifact = JSON.parse(readFileSync16(resolve10(args.findings), "utf8"));
11029
11253
  findings = validateFindings(
11030
11254
  Array.isArray(artifact) ? artifact : artifact.findings
11031
11255
  );
@@ -11055,7 +11279,7 @@ function runPublishAssets(args) {
11055
11279
  for (const file of unique) {
11056
11280
  const abs = resolve10(file);
11057
11281
  try {
11058
- const st = statSync5(abs);
11282
+ const st = statSync6(abs);
11059
11283
  if (!st.isFile()) throw new Error("not a regular file");
11060
11284
  stats.push({ file, abs, basename: basename5(abs), bytes: st.size });
11061
11285
  } catch (err) {
@@ -11074,7 +11298,7 @@ function runPublishAssets(args) {
11074
11298
  for (const st of stats) {
11075
11299
  let content;
11076
11300
  try {
11077
- content = readFileSync15(st.abs);
11301
+ content = readFileSync16(st.abs);
11078
11302
  } catch (err) {
11079
11303
  refuse(
11080
11304
  `cannot read ${JSON.stringify(st.file)}: ${err instanceof Error ? err.message : String(err)}`
@@ -11121,8 +11345,8 @@ function runPublishAssets(args) {
11121
11345
  published
11122
11346
  };
11123
11347
  const outPath = resolve10(args.out);
11124
- mkdirSync14(dirname11(outPath), { recursive: true });
11125
- writeFileSync14(outPath, `${JSON.stringify(manifest, null, 2)}
11348
+ mkdirSync15(dirname11(outPath), { recursive: true });
11349
+ writeFileSync15(outPath, `${JSON.stringify(manifest, null, 2)}
11126
11350
  `, "utf8");
11127
11351
  if (findings && !args.findingsOut) {
11128
11352
  writeStderrLine(
@@ -11137,8 +11361,8 @@ function runPublishAssets(args) {
11137
11361
  });
11138
11362
  const report = buildReport(updated);
11139
11363
  const fOut = resolve10(args.findingsOut);
11140
- mkdirSync14(dirname11(fOut), { recursive: true });
11141
- writeFileSync14(fOut, `${JSON.stringify(report, null, 2)}
11364
+ mkdirSync15(dirname11(fOut), { recursive: true });
11365
+ writeFileSync15(fOut, `${JSON.stringify(report, null, 2)}
11142
11366
  `, "utf8");
11143
11367
  }
11144
11368
  writeStderrLine(
@@ -11200,7 +11424,7 @@ var publishAssetsCommand = {
11200
11424
 
11201
11425
  // packages/cli/src/commands/review/resolve-anchors.ts
11202
11426
  init_esbuild_shims();
11203
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15 } from "node:fs";
11427
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
11204
11428
  import { dirname as dirname12, resolve as resolve11 } from "node:path";
11205
11429
 
11206
11430
  // packages/cli/src/commands/review/lib/anchors.ts
@@ -11398,8 +11622,8 @@ function validateRequests(raw) {
11398
11622
  );
11399
11623
  }
11400
11624
  }
11401
- if (o["line"] !== void 0 && typeof o["line"] !== "number") {
11402
- throw new Error(`Finding "${o["id"]}" has a non-numeric "line".`);
11625
+ if (o["line"] !== void 0 && (typeof o["line"] !== "number" || !Number.isSafeInteger(o["line"]) || o["line"] <= 0)) {
11626
+ throw new Error(`Finding "${o["id"]}" has an invalid "line".`);
11403
11627
  }
11404
11628
  return {
11405
11629
  id: o["id"],
@@ -11425,7 +11649,7 @@ __name(validateRequests, "validateRequests");
11425
11649
  function runResolveAnchors(args) {
11426
11650
  let diffText;
11427
11651
  try {
11428
- diffText = readFileSync16(args.diff, "utf8");
11652
+ diffText = readFileSync17(args.diff, "utf8");
11429
11653
  } catch (err) {
11430
11654
  throw new Error(
11431
11655
  `Cannot read diff file ${args.diff}: ${err.message}`
@@ -11433,7 +11657,7 @@ function runResolveAnchors(args) {
11433
11657
  }
11434
11658
  let rawText;
11435
11659
  try {
11436
- rawText = readFileSync16(args.input, "utf8");
11660
+ rawText = readFileSync17(args.input, "utf8");
11437
11661
  } catch (err) {
11438
11662
  throw new Error(
11439
11663
  `Cannot read findings file ${args.input}: ${err.message}`
@@ -11464,8 +11688,8 @@ function runResolveAnchors(args) {
11464
11688
  loose: resolved.filter((r) => r.tier?.startsWith("loose")).length
11465
11689
  }
11466
11690
  };
11467
- mkdirSync15(dirname12(resolve11(args.out)), { recursive: true });
11468
- writeFileSync15(args.out, JSON.stringify(report, null, 2), "utf8");
11691
+ mkdirSync16(dirname12(resolve11(args.out)), { recursive: true });
11692
+ writeFileSync16(args.out, JSON.stringify(report, null, 2), "utf8");
11469
11693
  writeStdoutLine(`Wrote resolved anchors to ${args.out}`);
11470
11694
  const s = report.stats;
11471
11695
  writeStderrLine(
@@ -11512,7 +11736,7 @@ var resolveAnchorsCommand = {
11512
11736
 
11513
11737
  // packages/cli/src/commands/review/check-coverage.ts
11514
11738
  init_esbuild_shims();
11515
- import { writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
11739
+ import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync17 } from "node:fs";
11516
11740
  import { dirname as dirname13, resolve as resolve12 } from "node:path";
11517
11741
  function runCheckCoverage(args) {
11518
11742
  let report;
@@ -11529,8 +11753,8 @@ This is an environment problem, not a finding about the agents. Coverage cannot
11529
11753
  }
11530
11754
  throw err;
11531
11755
  }
11532
- mkdirSync16(dirname13(resolve12(args.out)), { recursive: true });
11533
- writeFileSync16(args.out, JSON.stringify(report, null, 2));
11756
+ mkdirSync17(dirname13(resolve12(args.out)), { recursive: true });
11757
+ writeFileSync17(args.out, JSON.stringify(report, null, 2));
11534
11758
  writeStdoutLine(`Wrote coverage report to ${args.out}`);
11535
11759
  const totalChunks = report.coveredChunks.length + report.missingChunks.length + report.uncoverableChunks.length;
11536
11760
  const worked = report.agents - report.blindAgents.length - report.idleAgents.length;
@@ -11547,6 +11771,11 @@ This is an environment problem, not a finding about the agents. Coverage cannot
11547
11771
  `ERROR: ${report.rewrittenPrompts.length} agent(s) were not launched with the prompt this CLI built for them \u2014 ${report.rewrittenPrompts.join("; ")}. \`agent-prompt\` prints a prompt to be passed VERBATIM; a summary of it is not it. The last run to paraphrase one dropped the rule against reciting a stock sentence and replaced the project's review rules with three sentences of its own. Re-run \`"\${QWEN_CODE_CLI:-qwen}" review agent-prompt\` and pass its output unedited \u2014 copy it, do not retype it.`
11548
11772
  );
11549
11773
  }
11774
+ if (report.driftedLaunches.length > 0) {
11775
+ writeStderrLine(
11776
+ `NOTE: ${report.driftedLaunches.length} agent(s) launched with a near-verbatim prompt \u2014 ${report.driftedLaunches.join("; ")}. Their briefs were opened and the work is on record, so the delivery stands and NO relaunch is owed. Copy future blocks exactly \u2014 one edited word is what turns a clean pass into this note.`
11777
+ );
11778
+ }
11550
11779
  if (report.missingRoles.length > 0) {
11551
11780
  writeStderrLine(
11552
11781
  // No count: when no role was briefed at all, `missingRoles` collapses to one
@@ -11624,8 +11853,8 @@ var checkCoverageCommand = {
11624
11853
  // packages/cli/src/commands/review/agent-prompt.ts
11625
11854
  init_esbuild_shims();
11626
11855
  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";
11856
+ import { readFileSync as readFileSync18 } from "node:fs";
11857
+ import { dirname as dirname14, join as join13, resolve as resolve13 } from "node:path";
11629
11858
 
11630
11859
  // packages/cli/src/commands/review/lib/path-rules.ts
11631
11860
  init_esbuild_shims();
@@ -12077,7 +12306,7 @@ function buildRoleBrief(report, role, opts = {}) {
12077
12306
  "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."
12078
12307
  );
12079
12308
  }
12080
- const ctx = opts.planPath ? join12(dirname14(resolve13(opts.planPath)), `qwen-review-pr-${pr}-context.md`) : null;
12309
+ const ctx = opts.planPath ? join13(dirname14(resolve13(opts.planPath)), `qwen-review-pr-${pr}-context.md`) : null;
12081
12310
  parts.push(
12082
12311
  "",
12083
12312
  `**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.`
@@ -12336,7 +12565,7 @@ The \`description\` parameter of each Agent call is the task name the user watch
12336
12565
  );
12337
12566
  }
12338
12567
  __name(runRoster, "runRoster");
12339
- function runAllChunks(report, planPath, role, findingsContent, rules, round) {
12568
+ function requireAuditableChunks(report) {
12340
12569
  if (!Array.isArray(report.chunks) || report.chunks.length === 0) {
12341
12570
  throw new Error("agent-prompt: the plan has no `chunks[]`.");
12342
12571
  }
@@ -12347,6 +12576,11 @@ function runAllChunks(report, planPath, role, findingsContent, rules, round) {
12347
12576
  `agent-prompt: the plan has ${problem} \u2014 a round built from what remains would look complete while a territory goes unaudited. Re-run the Step 1 capture; do not hand-edit the plan.`
12348
12577
  );
12349
12578
  }
12579
+ return chunks;
12580
+ }
12581
+ __name(requireAuditableChunks, "requireAuditableChunks");
12582
+ function runAllChunks(report, planPath, role, findingsContent, rules, round) {
12583
+ const chunks = requireAuditableChunks(report);
12350
12584
  const digest = findingsDigest(findingsContent, rules);
12351
12585
  const roundPart = round !== void 0 ? `--round-${round}` : "";
12352
12586
  const blocks = chunks.map((c, i) => {
@@ -12452,6 +12686,11 @@ function runAgentPrompt(args) {
12452
12686
  );
12453
12687
  }
12454
12688
  }
12689
+ if (role === "reverse-audit" && !hasRound) {
12690
+ bad(
12691
+ "--role reverse-audit builds one round of the Step 5 loop and requires --round <k>: the label keys the record and the budget gate's per-round accounting."
12692
+ );
12693
+ }
12455
12694
  } else if (hasFindings) {
12456
12695
  const findingRoles = Object.keys(BRIEFS).filter(
12457
12696
  (r) => BRIEFS[r].acceptsFindings
@@ -12482,7 +12721,7 @@ function runAgentPrompt(args) {
12482
12721
  }
12483
12722
  let report;
12484
12723
  try {
12485
- report = JSON.parse(readFileSync17(args.plan, "utf8"));
12724
+ report = JSON.parse(readFileSync18(args.plan, "utf8"));
12486
12725
  } catch (err) {
12487
12726
  throw new Error(
12488
12727
  `agent-prompt: cannot read the plan ${args.plan}: ${err.message}`
@@ -12491,7 +12730,7 @@ function runAgentPrompt(args) {
12491
12730
  let rules;
12492
12731
  if (args.rules) {
12493
12732
  try {
12494
- rules = readFileSync17(args.rules, "utf8");
12733
+ rules = readFileSync18(args.rules, "utf8");
12495
12734
  } catch (err) {
12496
12735
  throw new Error(
12497
12736
  `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.`
@@ -12506,7 +12745,7 @@ function runAgentPrompt(args) {
12506
12745
  if (hasFindings && args.role) {
12507
12746
  const role = args.role;
12508
12747
  try {
12509
- findingsContent = readFileSync17(args.findings, "utf8");
12748
+ findingsContent = readFileSync18(args.findings, "utf8");
12510
12749
  } catch (err) {
12511
12750
  throw new Error(
12512
12751
  `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).`
@@ -12518,6 +12757,21 @@ function runAgentPrompt(args) {
12518
12757
  );
12519
12758
  }
12520
12759
  }
12760
+ if (args.allChunks && args.role && findingsContent !== void 0) {
12761
+ requireAuditableChunks(report);
12762
+ }
12763
+ if (args.role === "reverse-audit") {
12764
+ const spent = reverseAuditBudgetExhausted(
12765
+ process.env,
12766
+ expectedRoundSeconds(args.plan, args.round)
12767
+ );
12768
+ if (spent !== null) {
12769
+ writeBudgetStop(args.plan, spent, args.round);
12770
+ writeStderrLine(reverseAuditBudgetMessage(spent, args.round));
12771
+ process.exitCode = 4;
12772
+ return;
12773
+ }
12774
+ }
12521
12775
  if (args.allChunks && args.role && findingsContent !== void 0) {
12522
12776
  runAllChunks(
12523
12777
  report,
@@ -12527,6 +12781,9 @@ function runAgentPrompt(args) {
12527
12781
  rules,
12528
12782
  args.round
12529
12783
  );
12784
+ if (args.role === "reverse-audit") {
12785
+ stampRound(args.plan, args.round);
12786
+ }
12530
12787
  return;
12531
12788
  }
12532
12789
  let prompt;
@@ -12557,11 +12814,14 @@ function runAgentPrompt(args) {
12557
12814
  const printed = findingsContent !== void 0 && args.role ? foldFindings(args.role, findingsContent, prompt) : prompt;
12558
12815
  recordPrompt(args.plan, key, printed);
12559
12816
  writeStdoutLine(printed);
12817
+ if (args.role === "reverse-audit") {
12818
+ stampRound(args.plan, args.round);
12819
+ }
12560
12820
  }
12561
12821
  __name(runAgentPrompt, "runAgentPrompt");
12562
12822
  var agentPromptCommand = {
12563
12823
  command: "agent-prompt",
12564
- describe: "Build a review agent's launch prompt from the plan (the diff path, its line ranges and the agent's own brief are welded in, not left to the caller to remember)",
12824
+ describe: "Build a review agent's launch prompt from the plan (the diff path, its line ranges and the agent's own brief are welded in, not left to the caller to remember). Exit codes: 0 built; 4 the review time budget refused another reverse-audit round (a termination rule, not an error \u2014 see the BUDGET line on stderr); anything else is a bad call or a broken plan.",
12565
12825
  builder: /* @__PURE__ */ __name((yargs) => yargs.option("plan", {
12566
12826
  type: "string",
12567
12827
  demandOption: true,
@@ -12616,17 +12876,17 @@ init_esbuild_shims();
12616
12876
  import { spawnSync as spawnSync2 } from "node:child_process";
12617
12877
  import {
12618
12878
  existsSync as existsSync7,
12619
- readFileSync as readFileSync19,
12879
+ readFileSync as readFileSync20,
12620
12880
  rmSync as rmSync5,
12621
12881
  statfsSync,
12622
- writeFileSync as writeFileSync17
12882
+ writeFileSync as writeFileSync18
12623
12883
  } from "node:fs";
12624
- import { join as join14, resolve as resolve14 } from "node:path";
12884
+ import { join as join15, resolve as resolve14 } from "node:path";
12625
12885
 
12626
12886
  // packages/cli/src/commands/review/lib/workspaces.ts
12627
12887
  init_esbuild_shims();
12628
- import { existsSync as existsSync6, readFileSync as readFileSync18, readdirSync as readdirSync4 } from "node:fs";
12629
- import { join as join13 } from "node:path";
12888
+ import { existsSync as existsSync6, readFileSync as readFileSync19, readdirSync as readdirSync4 } from "node:fs";
12889
+ import { join as join14 } from "node:path";
12630
12890
  function isWorkspaceMember(filePath, workspaceGlobs) {
12631
12891
  return workspaceDirFor(filePath, workspaceGlobs) !== null;
12632
12892
  }
@@ -12664,7 +12924,7 @@ __name(hasUnmodeledWorkspaceGlob, "hasUnmodeledWorkspaceGlob");
12664
12924
  function readWorkspaceGlobs(root) {
12665
12925
  try {
12666
12926
  const pkg = JSON.parse(
12667
- readFileSync18(join13(root, "package.json"), "utf8")
12927
+ readFileSync19(join14(root, "package.json"), "utf8")
12668
12928
  );
12669
12929
  const ws = pkg.workspaces;
12670
12930
  const globs = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
@@ -12677,7 +12937,7 @@ __name(readWorkspaceGlobs, "readWorkspaceGlobs");
12677
12937
  function readRootPackage(root) {
12678
12938
  let pkg;
12679
12939
  try {
12680
- pkg = JSON.parse(readFileSync18(join13(root, "package.json"), "utf8"));
12940
+ pkg = JSON.parse(readFileSync19(join14(root, "package.json"), "utf8"));
12681
12941
  } catch {
12682
12942
  return null;
12683
12943
  }
@@ -12701,7 +12961,7 @@ function readWorkspacePackages(root) {
12701
12961
  const base = g.slice(0, -2);
12702
12962
  let entries;
12703
12963
  try {
12704
- entries = readdirSync4(join13(root, base), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12964
+ entries = readdirSync4(join14(root, base), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12705
12965
  } catch {
12706
12966
  continue;
12707
12967
  }
@@ -12713,11 +12973,11 @@ function readWorkspacePackages(root) {
12713
12973
  const pkgs = [];
12714
12974
  for (const dir of dirs) {
12715
12975
  if (workspaceDirFor(`${dir}/package.json`, globs) !== dir) continue;
12716
- const manifest = join13(root, dir, "package.json");
12976
+ const manifest = join14(root, dir, "package.json");
12717
12977
  if (!existsSync6(manifest)) continue;
12718
12978
  let pkg;
12719
12979
  try {
12720
- pkg = JSON.parse(readFileSync18(manifest, "utf8"));
12980
+ pkg = JSON.parse(readFileSync19(manifest, "utf8"));
12721
12981
  } catch {
12722
12982
  continue;
12723
12983
  }
@@ -12890,7 +13150,7 @@ __name(unresolvedWorkspaceDeps, "unresolvedWorkspaceDeps");
12890
13150
  function changedFilesFrom(planPath) {
12891
13151
  let parsed;
12892
13152
  try {
12893
- parsed = JSON.parse(readFileSync19(planPath, "utf8"));
13153
+ parsed = JSON.parse(readFileSync20(planPath, "utf8"));
12894
13154
  } catch (err) {
12895
13155
  throw new Error(
12896
13156
  `build-test: cannot read the plan ${planPath}: ${err.message}`
@@ -12973,15 +13233,15 @@ function runBuildTest(args) {
12973
13233
  timedOut: [],
12974
13234
  note: ""
12975
13235
  };
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"))) {
13236
+ const npmLock = existsSync7(join15(root, "package-lock.json"));
13237
+ const installComplete = /* @__PURE__ */ __name(() => existsSync7(join15(root, "node_modules", ".package-lock.json")), "installComplete");
13238
+ if (args.install && !npmLock && !existsSync7(join15(root, "node_modules"))) {
12979
13239
  const altLock = [
12980
13240
  ["yarn.lock", "yarn install --frozen-lockfile"],
12981
13241
  ["pnpm-lock.yaml", "pnpm install --frozen-lockfile"],
12982
13242
  ["bun.lockb", "bun install --frozen-lockfile"],
12983
13243
  ["bun.lock", "bun install --frozen-lockfile"]
12984
- ].find(([f]) => existsSync7(join14(root, f)));
13244
+ ].find(([f]) => existsSync7(join15(root, f)));
12985
13245
  return unsupportedReport(
12986
13246
  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."
12987
13247
  );
@@ -12999,7 +13259,7 @@ function runBuildTest(args) {
12999
13259
  if (install.timedOut) results.timedOut.push(install.command);
13000
13260
  if (install.timedOut) {
13001
13261
  try {
13002
- rmSync5(join14(root, "node_modules"), {
13262
+ rmSync5(join15(root, "node_modules"), {
13003
13263
  recursive: true,
13004
13264
  force: true,
13005
13265
  maxRetries: 3
@@ -13122,7 +13382,7 @@ var buildTestCommand = {
13122
13382
  try {
13123
13383
  const report = runBuildTest(args);
13124
13384
  if (args.out) {
13125
- writeFileSync17(args.out, JSON.stringify(report, null, 2));
13385
+ writeFileSync18(args.out, JSON.stringify(report, null, 2));
13126
13386
  }
13127
13387
  writeStdoutLine(JSON.stringify(report, null, 2));
13128
13388
  } catch (err) {
@@ -13137,13 +13397,13 @@ init_esbuild_shims();
13137
13397
  import { spawnSync as spawnSync4 } from "node:child_process";
13138
13398
  import {
13139
13399
  existsSync as existsSync8,
13140
- mkdirSync as mkdirSync17,
13141
- readFileSync as readFileSync20,
13400
+ mkdirSync as mkdirSync18,
13401
+ readFileSync as readFileSync21,
13142
13402
  rmSync as rmSync7,
13143
- statSync as statSync6,
13144
- writeFileSync as writeFileSync18
13403
+ statSync as statSync7,
13404
+ writeFileSync as writeFileSync19
13145
13405
  } from "node:fs";
13146
- import { dirname as dirname15, join as join15, resolve as resolve15 } from "node:path";
13406
+ import { dirname as dirname15, join as join16, resolve as resolve15 } from "node:path";
13147
13407
 
13148
13408
  // packages/cli/src/commands/review/lib/worktree.ts
13149
13409
  init_esbuild_shims();
@@ -13190,7 +13450,7 @@ function runBaseTree(args) {
13190
13450
  }), "unavailable");
13191
13451
  let plan;
13192
13452
  try {
13193
- plan = JSON.parse(readFileSync20(args.plan, "utf8"));
13453
+ plan = JSON.parse(readFileSync21(args.plan, "utf8"));
13194
13454
  } catch (err) {
13195
13455
  return unavailable(
13196
13456
  `cannot read the plan ${args.plan}: ${err.message}`
@@ -13212,10 +13472,10 @@ function runBaseTree(args) {
13212
13472
  return unavailable(`the review worktree ${worktree} does not exist`);
13213
13473
  }
13214
13474
  const tree = baseWorktreePath(worktree);
13215
- const marker = /* @__PURE__ */ __name(() => join15(tree, ".qwen-review-base-ok"), "marker");
13216
- const failedMarker = /* @__PURE__ */ __name(() => join15(tree, ".qwen-review-base-failed"), "failedMarker");
13475
+ const marker = /* @__PURE__ */ __name(() => join16(tree, ".qwen-review-base-ok"), "marker");
13476
+ const failedMarker = /* @__PURE__ */ __name(() => join16(tree, ".qwen-review-base-failed"), "failedMarker");
13217
13477
  try {
13218
- if (existsSync8(tree) && readFileSync20(marker(), "utf8").trim() === baseSha && gitOut(tree, "rev-parse", "HEAD") === baseSha) {
13478
+ if (existsSync8(tree) && readFileSync21(marker(), "utf8").trim() === baseSha && gitOut(tree, "rev-parse", "HEAD") === baseSha) {
13219
13479
  return {
13220
13480
  available: true,
13221
13481
  path: tree,
@@ -13227,7 +13487,7 @@ function runBaseTree(args) {
13227
13487
  } catch {
13228
13488
  }
13229
13489
  try {
13230
- if (existsSync8(tree) && readFileSync20(failedMarker(), "utf8").trim() === baseSha) {
13490
+ if (existsSync8(tree) && readFileSync21(failedMarker(), "utf8").trim() === baseSha) {
13231
13491
  return {
13232
13492
  available: false,
13233
13493
  path: tree,
@@ -13240,14 +13500,14 @@ function runBaseTree(args) {
13240
13500
  }
13241
13501
  const lock = `${tree}.lock`;
13242
13502
  try {
13243
- const age = Date.now() - statSync6(lock).mtimeMs;
13503
+ const age = Date.now() - statSync7(lock).mtimeMs;
13244
13504
  if (age > 30 * 60 * 1e3) {
13245
13505
  rmSync7(lock, { recursive: true, force: true });
13246
13506
  }
13247
13507
  } catch {
13248
13508
  }
13249
13509
  try {
13250
- mkdirSync17(lock);
13510
+ mkdirSync18(lock);
13251
13511
  } catch (err) {
13252
13512
  if (err.code !== "EEXIST") {
13253
13513
  return unavailable(
@@ -13285,7 +13545,7 @@ function runBaseTree(args) {
13285
13545
  });
13286
13546
  if (!build.ok || build.toolchain !== "npm" || build.build.length === 0) {
13287
13547
  try {
13288
- writeFileSync18(failedMarker(), `${baseSha2}
13548
+ writeFileSync19(failedMarker(), `${baseSha2}
13289
13549
  `);
13290
13550
  } catch {
13291
13551
  }
@@ -13298,7 +13558,7 @@ function runBaseTree(args) {
13298
13558
  };
13299
13559
  }
13300
13560
  try {
13301
- writeFileSync18(marker(), `${baseSha2}
13561
+ writeFileSync19(marker(), `${baseSha2}
13302
13562
  `);
13303
13563
  } catch {
13304
13564
  }
@@ -13338,8 +13598,8 @@ var baseTreeCommand = {
13338
13598
  try {
13339
13599
  const report = runBaseTree(args);
13340
13600
  if (args.out) {
13341
- mkdirSync17(dirname15(resolve15(args.out)), { recursive: true });
13342
- writeFileSync18(resolve15(args.out), JSON.stringify(report, null, 2));
13601
+ mkdirSync18(dirname15(resolve15(args.out)), { recursive: true });
13602
+ writeFileSync19(resolve15(args.out), JSON.stringify(report, null, 2));
13343
13603
  }
13344
13604
  writeStdoutLine(JSON.stringify(report, null, 2));
13345
13605
  writeStderrLine(`base-tree: ${report.note}`);
@@ -13353,7 +13613,7 @@ var baseTreeCommand = {
13353
13613
  // packages/cli/src/commands/review/test-delta.ts
13354
13614
  init_esbuild_shims();
13355
13615
  import { spawnSync as spawnSync5 } from "node:child_process";
13356
- import { existsSync as existsSync9, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
13616
+ import { existsSync as existsSync9, mkdirSync as mkdirSync19, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "node:fs";
13357
13617
  import { dirname as dirname16, resolve as resolve16 } from "node:path";
13358
13618
  var ANSI_SGR_RE2 = /\x1b\[[0-9;]*m/g;
13359
13619
  var RERUNNABLE_COMMAND_RE = /^npm test(?: --workspace="[\w@./-]+")?$/;
@@ -13427,7 +13687,7 @@ function runTestDelta(args) {
13427
13687
  }), "empty");
13428
13688
  let report;
13429
13689
  try {
13430
- report = JSON.parse(readFileSync21(args.report, "utf8"));
13690
+ report = JSON.parse(readFileSync22(args.report, "utf8"));
13431
13691
  } catch (err) {
13432
13692
  return empty(
13433
13693
  `cannot read the build-test report ${args.report}: ${err.message}`
@@ -13569,8 +13829,8 @@ var testDeltaCommand = {
13569
13829
  const args = argv;
13570
13830
  const report = runTestDelta(args);
13571
13831
  if (args.out) {
13572
- mkdirSync18(dirname16(resolve16(args.out)), { recursive: true });
13573
- writeFileSync19(resolve16(args.out), JSON.stringify(report, null, 2));
13832
+ mkdirSync19(dirname16(resolve16(args.out)), { recursive: true });
13833
+ writeFileSync20(resolve16(args.out), JSON.stringify(report, null, 2));
13574
13834
  }
13575
13835
  writeStdoutLine(JSON.stringify(report, null, 2));
13576
13836
  writeStderrLine(`test-delta: ${report.note}`);
@@ -13581,14 +13841,14 @@ var testDeltaCommand = {
13581
13841
  init_esbuild_shims();
13582
13842
  import { spawnSync as spawnSync6 } from "node:child_process";
13583
13843
  import {
13584
- mkdirSync as mkdirSync19,
13585
- writeFileSync as writeFileSync20,
13586
- readFileSync as readFileSync22,
13844
+ mkdirSync as mkdirSync20,
13845
+ writeFileSync as writeFileSync21,
13846
+ readFileSync as readFileSync23,
13587
13847
  existsSync as existsSync10,
13588
13848
  rmSync as rmSync8,
13589
- statSync as statSync7
13849
+ statSync as statSync8
13590
13850
  } from "node:fs";
13591
- import { dirname as dirname17, join as join16, resolve as resolve17 } from "node:path";
13851
+ import { dirname as dirname17, join as join17, resolve as resolve17 } from "node:path";
13592
13852
  import { tmpdir as tmpdir2 } from "node:os";
13593
13853
  var SERVER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
13594
13854
  function shellQuote(v) {
@@ -13600,7 +13860,7 @@ var LOG_MAX_BYTES = 8 * 1024 * 1024;
13600
13860
  var POLL_MS = 250;
13601
13861
  function logBytes(p) {
13602
13862
  try {
13603
- return statSync7(p).size;
13863
+ return statSync8(p).size;
13604
13864
  } catch {
13605
13865
  return 0;
13606
13866
  }
@@ -13706,13 +13966,13 @@ function runDrive(args) {
13706
13966
  waitMs(POLL_MS);
13707
13967
  }
13708
13968
  }
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");
13969
+ const dir = join17(tmpdir2(), `qwen-review-drive-${server}`);
13970
+ mkdirSync20(dir, { recursive: true });
13971
+ const scriptPath = join17(dir, "drive.sh");
13972
+ const logPath = args.logPath ?? join17(dir, "drive.log");
13973
+ const sentinelPath = join17(dir, "drive.rc");
13714
13974
  rmSync8(sentinelPath, { force: true });
13715
- writeFileSync20(scriptPath, wrapScript(args.script, sentinelPath), "utf8");
13975
+ writeFileSync21(scriptPath, wrapScript(args.script, sentinelPath), "utf8");
13716
13976
  const droveFrom = Date.now();
13717
13977
  let output = "";
13718
13978
  let exitCode = null;
@@ -13742,8 +14002,8 @@ function runDrive(args) {
13742
14002
  }
13743
14003
  const deadline = droveFrom + args.timeout * 1e3;
13744
14004
  for (; ; ) {
13745
- output = existsSync10(logPath) ? readFileSync22(logPath, "utf8") : "";
13746
- exitCode = existsSync10(sentinelPath) ? sentinelExitCode(readFileSync22(sentinelPath, "utf8")) : null;
14005
+ output = existsSync10(logPath) ? readFileSync23(logPath, "utf8") : "";
14006
+ exitCode = existsSync10(sentinelPath) ? sentinelExitCode(readFileSync23(sentinelPath, "utf8")) : null;
13747
14007
  if (exitCode !== null) {
13748
14008
  outcome = "completed";
13749
14009
  break;
@@ -13810,8 +14070,8 @@ var driveCommand = {
13810
14070
  const args = argv;
13811
14071
  const report = runDrive(args);
13812
14072
  if (args.out) {
13813
- mkdirSync19(dirname17(resolve17(args.out)), { recursive: true });
13814
- writeFileSync20(resolve17(args.out), JSON.stringify(report, null, 2));
14073
+ mkdirSync20(dirname17(resolve17(args.out)), { recursive: true });
14074
+ writeFileSync21(resolve17(args.out), JSON.stringify(report, null, 2));
13815
14075
  }
13816
14076
  writeStdoutLine(JSON.stringify(report, null, 2));
13817
14077
  writeStderrLine(`drive: ${report.note}`);
@@ -13828,7 +14088,7 @@ init_esbuild_shims();
13828
14088
  import {
13829
14089
  createServer
13830
14090
  } from "node:http";
13831
- import { appendFileSync, mkdirSync as mkdirSync20, writeFileSync as writeFileSync21 } from "node:fs";
14091
+ import { appendFileSync, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22 } from "node:fs";
13832
14092
  import { dirname as dirname18, resolve as resolve18 } from "node:path";
13833
14093
  import { pathToFileURL } from "node:url";
13834
14094
  function messagesText(body) {
@@ -14085,8 +14345,8 @@ async function startMockProvider(args, respondOverride) {
14085
14345
  respond = fn;
14086
14346
  }
14087
14347
  const logPath = resolve18(args.log);
14088
- mkdirSync20(dirname18(logPath), { recursive: true });
14089
- writeFileSync21(logPath, "");
14348
+ mkdirSync21(dirname18(logPath), { recursive: true });
14349
+ writeFileSync22(logPath, "");
14090
14350
  let n = 0;
14091
14351
  const record = /* @__PURE__ */ __name((entry) => appendFileSync(logPath, `${JSON.stringify(entry)}
14092
14352
  `), "record");
@@ -14248,8 +14508,8 @@ var mockProviderCommand = {
14248
14508
  const args = argv;
14249
14509
  const { report, close } = await startMockProvider(args);
14250
14510
  if (args.out) {
14251
- mkdirSync20(dirname18(resolve18(args.out)), { recursive: true });
14252
- writeFileSync21(resolve18(args.out), JSON.stringify(report, null, 2));
14511
+ mkdirSync21(dirname18(resolve18(args.out)), { recursive: true });
14512
+ writeFileSync22(resolve18(args.out), JSON.stringify(report, null, 2));
14253
14513
  }
14254
14514
  writeStdoutLine(JSON.stringify(report, null, 2));
14255
14515
  writeStderrLine(`mock-provider: ${report.note}`);
@@ -14267,7 +14527,7 @@ var mockProviderCommand = {
14267
14527
  // packages/cli/src/commands/review/extract-step.ts
14268
14528
  init_esbuild_shims();
14269
14529
  var import_yaml = __toESM(require_dist(), 1);
14270
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync22, chmodSync } from "node:fs";
14530
+ import { mkdirSync as mkdirSync22, readFileSync as readFileSync24, writeFileSync as writeFileSync23, chmodSync } from "node:fs";
14271
14531
  import { dirname as dirname19, resolve as resolve19 } from "node:path";
14272
14532
  function expressionsOf(...texts) {
14273
14533
  const seen = /* @__PURE__ */ new Set();
@@ -14515,7 +14775,7 @@ function runExtractStep(args) {
14515
14775
  const wfPath = resolve19(args.workflow);
14516
14776
  let text;
14517
14777
  try {
14518
- text = readFileSync23(wfPath, "utf8");
14778
+ text = readFileSync24(wfPath, "utf8");
14519
14779
  } catch (err) {
14520
14780
  throw new Error(
14521
14781
  `extract-step: cannot read ${args.workflow}: ${err.message}`
@@ -14582,7 +14842,7 @@ function runExtractStep(args) {
14582
14842
  const script = step.run;
14583
14843
  const stepLabel = String(step.name ?? step.id ?? index);
14584
14844
  const outPath = resolve19(args.out);
14585
- mkdirSync21(dirname19(outPath), { recursive: true });
14845
+ mkdirSync22(dirname19(outPath), { recursive: true });
14586
14846
  const header = [
14587
14847
  `#!/usr/bin/env ${shellCommand}`,
14588
14848
  ...commentLines(
@@ -14606,7 +14866,7 @@ function runExtractStep(args) {
14606
14866
  ),
14607
14867
  ""
14608
14868
  ].join("\n");
14609
- writeFileSync22(outPath, header + script + (script.endsWith("\n") ? "" : "\n"));
14869
+ writeFileSync23(outPath, header + script + (script.endsWith("\n") ? "" : "\n"));
14610
14870
  chmodSync(outPath, 493);
14611
14871
  return {
14612
14872
  workflow: args.workflow,
@@ -14667,7 +14927,7 @@ var extractStepCommand = {
14667
14927
 
14668
14928
  // packages/cli/src/commands/review/submit.ts
14669
14929
  init_esbuild_shims();
14670
- import { mkdirSync as mkdirSync22, readFileSync as readFileSync24 } from "node:fs";
14930
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync25 } from "node:fs";
14671
14931
 
14672
14932
  // packages/cli/src/commands/review/lib/receipt.ts
14673
14933
  init_esbuild_shims();
@@ -14689,7 +14949,7 @@ __name(parseReceiptIds, "parseReceiptIds");
14689
14949
  var EVENTS = /* @__PURE__ */ new Set(["APPROVE", "REQUEST_CHANGES", "COMMENT"]);
14690
14950
  function readReceiptIds(receiptPath) {
14691
14951
  try {
14692
- return parseReceiptIds(readFileSync24(receiptPath, "utf8"));
14952
+ return parseReceiptIds(readFileSync25(receiptPath, "utf8"));
14693
14953
  } catch {
14694
14954
  return [];
14695
14955
  }
@@ -14699,6 +14959,24 @@ function isDiffLine(n) {
14699
14959
  return typeof n === "number" && Number.isSafeInteger(n) && n > 0;
14700
14960
  }
14701
14961
  __name(isDiffLine, "isDiffLine");
14962
+ function normalizeInlineComments(comments, modelId, cliVersion) {
14963
+ if (typeof modelId !== "string" || modelId.trim() === "") return comments;
14964
+ const footer = reviewFooter(modelId, cliVersion);
14965
+ return comments.map(
14966
+ (comment) => (
14967
+ // An empty body stays empty: this runs BEFORE the consistency check, and
14968
+ // a footer pasted onto '' would hide the emptiness from the refusal that
14969
+ // names it ('has no body — an empty comment').
14970
+ typeof comment.body === "string" && comment.body.trim() !== "" ? {
14971
+ ...comment,
14972
+ body: `${comment.body.replace(REVIEW_FOOTER_RE, "")}
14973
+
14974
+ ${footer}`
14975
+ } : comment
14976
+ )
14977
+ );
14978
+ }
14979
+ __name(normalizeInlineComments, "normalizeInlineComments");
14702
14980
  function authorization(args) {
14703
14981
  return reviewWriteAuthorization({
14704
14982
  userAuthorized: args.userAuthorized,
@@ -14744,6 +15022,18 @@ __name(compose, "compose");
14744
15022
  function structuralProblems(payload) {
14745
15023
  const problems = [];
14746
15024
  if (!payload.commit_id) problems.push("`commit_id` is missing");
15025
+ if (payload.comments !== void 0 && !Array.isArray(payload.comments)) {
15026
+ problems.push(
15027
+ "`comments` is not an array \u2014 it is the list of findings this post carries; any other shape is not a list of findings."
15028
+ );
15029
+ }
15030
+ if (Array.isArray(payload.comments) && payload.comments.some(
15031
+ (c) => c === null || typeof c !== "object"
15032
+ )) {
15033
+ problems.push(
15034
+ "`comments` entries must each be an object \u2014 a finding is a path, a line and a body; any other shape is not a finding."
15035
+ );
15036
+ }
14747
15037
  if (payload.event !== void 0 || payload.body !== void 0) {
14748
15038
  problems.push(
14749
15039
  "the payload carries `event`/`body`. Those are computed here, from `state` and from the comments you attached \u2014 they are not inputs. Remove them. (A run that skipped `compose-review` and typed its own Approve is exactly what this refuses.)"
@@ -14762,12 +15052,12 @@ function structuralProblems(payload) {
14762
15052
  return problems;
14763
15053
  }
14764
15054
  __name(structuralProblems, "structuralProblems");
14765
- function inconsistencies(payload, event) {
15055
+ function inconsistencies(payload, event2) {
14766
15056
  const problems = [];
14767
15057
  const comments = payload.comments ?? [];
14768
- if (!EVENTS.has(event)) {
15058
+ if (!EVENTS.has(event2)) {
14769
15059
  problems.push(
14770
- `computed \`event\` is ${JSON.stringify(event)}; GitHub accepts only ${[...EVENTS].join(", ")}`
15060
+ `computed \`event\` is ${JSON.stringify(event2)}; GitHub accepts only ${[...EVENTS].join(", ")}`
14771
15061
  );
14772
15062
  }
14773
15063
  comments.forEach((c, i) => {
@@ -14824,7 +15114,7 @@ function runSubmit(args, cliVersion = "unknown") {
14824
15114
  }
14825
15115
  let payload;
14826
15116
  try {
14827
- payload = JSON.parse(readFileSync24(args.review, "utf8"));
15117
+ payload = JSON.parse(readFileSync25(args.review, "utf8"));
14828
15118
  } catch (err) {
14829
15119
  throw new Error(
14830
15120
  `Cannot read review JSON ${args.review}: ${err.message}`
@@ -14849,18 +15139,26 @@ Posting is a public, irreversible write, and this run has no authorisation for o
14849
15139
  ` + structural.map((p) => ` - ${p}`).join("\n")
14850
15140
  );
14851
15141
  }
14852
- let event;
15142
+ payload = {
15143
+ ...payload,
15144
+ comments: normalizeInlineComments(
15145
+ payload.comments ?? [],
15146
+ payload.state?.modelId,
15147
+ cliVersion
15148
+ )
15149
+ };
15150
+ let event2;
14853
15151
  let body;
14854
15152
  let cappedBy;
14855
15153
  try {
14856
- ({ event, body, cappedBy } = compose(payload, cliVersion));
15154
+ ({ event: event2, body, cappedBy } = compose(payload, cliVersion));
14857
15155
  } catch (err) {
14858
15156
  throw new Error(
14859
15157
  `The review state does not compose into a verdict; refusing to post:
14860
15158
  - ${err.message}`
14861
15159
  );
14862
15160
  }
14863
- const problems = inconsistencies(payload, event);
15161
+ const problems = inconsistencies(payload, event2);
14864
15162
  if (problems.length > 0) {
14865
15163
  throw new Error(
14866
15164
  `The review payload contradicts itself; refusing to post it:
@@ -14869,7 +15167,7 @@ Posting is a public, irreversible write, and this run has no authorisation for o
14869
15167
  }
14870
15168
  const post = {
14871
15169
  commit_id: payload.commit_id,
14872
- event,
15170
+ event: event2,
14873
15171
  body,
14874
15172
  comments: payload.comments ?? []
14875
15173
  };
@@ -14880,7 +15178,7 @@ Posting is a public, irreversible write, and this run has no authorisation for o
14880
15178
  );
14881
15179
  writeStdoutLine(
14882
15180
  JSON.stringify(
14883
- { posted: false, wouldPost: true, target, event, cappedBy },
15181
+ { posted: false, wouldPost: true, target, event: event2, cappedBy },
14884
15182
  null,
14885
15183
  2
14886
15184
  )
@@ -14900,23 +15198,23 @@ Posting is a public, irreversible write, and this run has no authorisation for o
14900
15198
  const receiptPath = tmpFile(`pr-${args.pr}`, "submit-receipt.json");
14901
15199
  const priorIds = readReceiptIds(receiptPath);
14902
15200
  const reviewIds = [.../* @__PURE__ */ new Set([...priorIds, reviewId])];
14903
- mkdirSync22(REVIEW_TMP_DIR, { recursive: true });
15201
+ mkdirSync23(REVIEW_TMP_DIR, { recursive: true });
14904
15202
  atomicWriteFileSync(
14905
15203
  receiptPath,
14906
- `${JSON.stringify({ reviewIds, event, postedAt: (/* @__PURE__ */ new Date()).toISOString() })}
15204
+ `${JSON.stringify({ reviewIds, event: event2, postedAt: (/* @__PURE__ */ new Date()).toISOString() })}
14907
15205
  `
14908
15206
  );
14909
15207
  }
14910
15208
  } catch {
14911
15209
  }
14912
15210
  writeStderrLine(
14913
- `Posted ${event} to ${args.repo}#${args.pr} \u2014 ${auth.why}` + (cappedBy.length ? ` (capped by ${cappedBy.join(", ")})` : "") + "."
15211
+ `Posted ${event2} to ${args.repo}#${args.pr} \u2014 ${auth.why}` + (cappedBy.length ? ` (capped by ${cappedBy.join(", ")})` : "") + "."
14914
15212
  );
14915
15213
  writeStdoutLine(
14916
15214
  JSON.stringify(
14917
15215
  {
14918
15216
  posted: true,
14919
- event,
15217
+ event: event2,
14920
15218
  cappedBy,
14921
15219
  inlineComments: post.comments.length
14922
15220
  },
@@ -14957,7 +15255,8 @@ var submitCommand = {
14957
15255
  describe: "Check authorisation and payload consistency, then stop."
14958
15256
  }), "builder"),
14959
15257
  handler: /* @__PURE__ */ __name(async (argv) => {
14960
- runSubmit(argv, await getCliVersion());
15258
+ const cliVersion = footerVersion(process.env["QWEN_CODE_STARTUP_VERSION"]) ?? await getCliVersion();
15259
+ runSubmit(argv, cliVersion);
14961
15260
  }, "handler")
14962
15261
  };
14963
15262
 
@@ -14965,18 +15264,18 @@ var submitCommand = {
14965
15264
  init_esbuild_shims();
14966
15265
  import { spawnSync as spawnSync7 } from "node:child_process";
14967
15266
  import {
14968
- mkdirSync as mkdirSync23,
15267
+ mkdirSync as mkdirSync24,
14969
15268
  readdirSync as readdirSync5,
14970
- writeFileSync as writeFileSync23,
14971
- readFileSync as readFileSync25,
15269
+ writeFileSync as writeFileSync24,
15270
+ readFileSync as readFileSync26,
14972
15271
  rmSync as rmSync9,
14973
15272
  lstatSync as lstatSync4,
14974
15273
  existsSync as existsSync11,
14975
- statSync as statSync8,
15274
+ statSync as statSync9,
14976
15275
  symlinkSync
14977
15276
  } from "node:fs";
14978
15277
  import { createRequire } from "node:module";
14979
- import { dirname as dirname20, join as join17, isAbsolute as isAbsolute3, sep as sep3 } from "node:path";
15278
+ import { dirname as dirname20, join as join18, isAbsolute as isAbsolute3, sep as sep3 } from "node:path";
14980
15279
  var FIXTURE_DIR_RE = /(^|\/)(__fixtures__|__mocks__|__snapshots__|fixtures)\//;
14981
15280
  function planTestEfficacy(files, workspaceGlobs) {
14982
15281
  const tests = files.filter((f) => f.kind === "test").map((f) => f.path);
@@ -15499,23 +15798,24 @@ function gitOut2(cwd, ...args) {
15499
15798
  return (r.stdout ?? "").trim();
15500
15799
  }
15501
15800
  __name(gitOut2, "gitOut");
15502
- function findVitestBin(worktree) {
15503
- const req = createRequire(join17(worktree, "noop.js"));
15801
+ function findVitestBin(worktree, resolveModule = createRequire(
15802
+ join18(worktree, "noop.js")
15803
+ ).resolve) {
15504
15804
  let pkgPath;
15505
15805
  try {
15506
- pkgPath = req.resolve("vitest/package.json");
15806
+ pkgPath = resolveModule("vitest/package.json");
15507
15807
  } catch (error) {
15508
15808
  if (error.code === "MODULE_NOT_FOUND") {
15509
15809
  throw new Error(`vitest not found searching up from ${worktree}`);
15510
15810
  }
15511
15811
  throw error;
15512
15812
  }
15513
- const pkg = JSON.parse(readFileSync25(pkgPath, "utf8"));
15813
+ const pkg = JSON.parse(readFileSync26(pkgPath, "utf8"));
15514
15814
  const bin = pkg.bin?.vitest;
15515
15815
  if (!bin) {
15516
15816
  throw new Error(`vitest package at ${pkgPath} declares no "vitest" bin`);
15517
15817
  }
15518
- return join17(dirname20(pkgPath), bin);
15818
+ return join18(dirname20(pkgPath), bin);
15519
15819
  }
15520
15820
  __name(findVitestBin, "findVitestBin");
15521
15821
  function gitCapture(cwd, ...args) {
@@ -15541,7 +15841,7 @@ function safeRmWithin(worktree, relPath) {
15541
15841
  const parts = relPath.split(/[/\\]+/).filter((s) => s && s !== ".");
15542
15842
  let cur = worktree;
15543
15843
  for (let i = 0; i < parts.length; i++) {
15544
- cur = join17(cur, parts[i]);
15844
+ cur = join18(cur, parts[i]);
15545
15845
  let st;
15546
15846
  try {
15547
15847
  st = lstatSync4(cur);
@@ -15566,15 +15866,15 @@ __name(probeCleanupFailureDetail, "probeCleanupFailureDetail");
15566
15866
  function exposeDependencies(probeTree, dependencyRoot) {
15567
15867
  const done = { linked: 0, failed: 0 };
15568
15868
  if (probeTree === dependencyRoot) return done;
15569
- const source = join17(dependencyRoot, "node_modules");
15570
- const target = join17(probeTree, "node_modules");
15869
+ const source = join18(dependencyRoot, "node_modules");
15870
+ const target = join18(probeTree, "node_modules");
15571
15871
  if (!existsSync11(source) || existsSync11(target)) return done;
15572
- mkdirSync23(target);
15872
+ mkdirSync24(target);
15573
15873
  const linkType = process.platform === "win32" ? "junction" : "dir";
15574
15874
  for (const entry of readdirSync5(source, { withFileTypes: true })) {
15575
15875
  if (entry.name === ".vite" || entry.name === ".vite-temp") continue;
15576
- const sourceEntry = join17(source, entry.name);
15577
- const targetEntry = join17(target, entry.name);
15876
+ const sourceEntry = join18(source, entry.name);
15877
+ const targetEntry = join18(target, entry.name);
15578
15878
  let sourceStats;
15579
15879
  try {
15580
15880
  sourceStats = lstatSync4(sourceEntry);
@@ -15584,7 +15884,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
15584
15884
  }
15585
15885
  if (sourceStats.isSymbolicLink()) {
15586
15886
  try {
15587
- if (!statSync8(sourceEntry).isDirectory()) continue;
15887
+ if (!statSync9(sourceEntry).isDirectory()) continue;
15588
15888
  } catch {
15589
15889
  continue;
15590
15890
  }
@@ -15594,7 +15894,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
15594
15894
  if (entry.name.startsWith("@")) {
15595
15895
  let pkgs;
15596
15896
  try {
15597
- mkdirSync23(targetEntry);
15897
+ mkdirSync24(targetEntry);
15598
15898
  pkgs = readdirSync5(sourceEntry);
15599
15899
  } catch {
15600
15900
  done.failed++;
@@ -15602,7 +15902,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
15602
15902
  }
15603
15903
  for (const pkg of pkgs) {
15604
15904
  try {
15605
- symlinkSync(join17(sourceEntry, pkg), join17(targetEntry, pkg), linkType);
15905
+ symlinkSync(join18(sourceEntry, pkg), join18(targetEntry, pkg), linkType);
15606
15906
  done.linked++;
15607
15907
  } catch {
15608
15908
  done.failed++;
@@ -15748,10 +16048,10 @@ function newSideLength(header) {
15748
16048
  __name(newSideLength, "newSideLength");
15749
16049
  function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
15750
16050
  const { patch: _patch, ...meta } = hunk;
15751
- const abs = join17(probeTree, hunk.file);
16051
+ const abs = join18(probeTree, hunk.file);
15752
16052
  let original;
15753
16053
  try {
15754
- original = readFileSync25(abs, "utf8");
16054
+ original = readFileSync26(abs, "utf8");
15755
16055
  } catch (e) {
15756
16056
  return {
15757
16057
  ...meta,
@@ -15777,21 +16077,21 @@ function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
15777
16077
  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";
15778
16078
  return { ...meta, verdict, detail };
15779
16079
  } finally {
15780
- mkdirSync23(dirname20(abs), { recursive: true });
15781
- writeFileSync23(abs, original, "utf8");
16080
+ mkdirSync24(dirname20(abs), { recursive: true });
16081
+ writeFileSync24(abs, original, "utf8");
15782
16082
  }
15783
16083
  }
15784
16084
  __name(runOneHunkProbe, "runOneHunkProbe");
15785
16085
  function runControlMutant(probeTree, probeFile, deadlineAt, now = Date.now) {
15786
- const abs = join17(probeTree, probeFile);
16086
+ const abs = join18(probeTree, probeFile);
15787
16087
  let original;
15788
16088
  try {
15789
- original = readFileSync25(abs, "utf8");
16089
+ original = readFileSync26(abs, "utf8");
15790
16090
  } catch {
15791
16091
  return null;
15792
16092
  }
15793
16093
  try {
15794
- writeFileSync23(
16094
+ writeFileSync24(
15795
16095
  abs,
15796
16096
  `${original}
15797
16097
  ;import { it as __qcIt, expect as __qcExpect } from 'vitest';
@@ -15804,13 +16104,13 @@ __qcIt('QWEN-REVIEW-POSITIVE-CONTROL', () => {
15804
16104
  const { perFile } = runProbeSuite(probeTree, [probeFile], deadlineAt, now);
15805
16105
  return perFile.some((r) => r.verdict === "gated");
15806
16106
  } finally {
15807
- writeFileSync23(abs, original, "utf8");
16107
+ writeFileSync24(abs, original, "utf8");
15808
16108
  }
15809
16109
  }
15810
16110
  __name(runControlMutant, "runControlMutant");
15811
16111
  function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dependencyRoot = probeTree) {
15812
- const abs = join17(probeTree, mutant.file);
15813
- const original = readFileSync25(abs, "utf8");
16112
+ const abs = join18(probeTree, mutant.file);
16113
+ const original = readFileSync26(abs, "utf8");
15814
16114
  const lines2 = original.split("\n");
15815
16115
  if ((lines2[mutant.line - 1] ?? "").trim() !== mutant.statement) {
15816
16116
  return {
@@ -15826,7 +16126,7 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
15826
16126
  lines2.splice(mutant.line - 1, 1);
15827
16127
  }
15828
16128
  try {
15829
- writeFileSync23(abs, lines2.join("\n"), "utf8");
16129
+ writeFileSync24(abs, lines2.join("\n"), "utf8");
15830
16130
  const { perFile } = runProbeSuite(
15831
16131
  probeTree,
15832
16132
  probes,
@@ -15838,7 +16138,7 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
15838
16138
  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";
15839
16139
  return { ...mutant, verdict, detail };
15840
16140
  } finally {
15841
- writeFileSync23(abs, original, "utf8");
16141
+ writeFileSync24(abs, original, "utf8");
15842
16142
  }
15843
16143
  }
15844
16144
  __name(runOneMutant, "runOneMutant");
@@ -15877,9 +16177,9 @@ async function runTestEfficacy(args) {
15877
16177
  const now = args.now ?? Date.now;
15878
16178
  const startedAt = now();
15879
16179
  const { report, worktree, base, out } = args;
15880
- const plan = JSON.parse(readFileSync25(report, "utf8"));
16180
+ const plan = JSON.parse(readFileSync26(report, "utf8"));
15881
16181
  const rootPkg = JSON.parse(
15882
- readFileSync25(`${worktree}/package.json`, "utf8")
16182
+ readFileSync26(`${worktree}/package.json`, "utf8")
15883
16183
  );
15884
16184
  const globs = rootPkg.workspaces ?? [];
15885
16185
  const { unreachable, probes, revert } = planTestEfficacy(
@@ -15887,8 +16187,8 @@ async function runTestEfficacy(args) {
15887
16187
  globs
15888
16188
  );
15889
16189
  for (const p of revert) {
15890
- const norm = join17(worktree, p);
15891
- const root = join17(worktree, ".");
16190
+ const norm = join18(worktree, p);
16191
+ const root = join18(worktree, ".");
15892
16192
  if (isAbsolute3(p) || norm !== root && !norm.startsWith(root + sep3)) {
15893
16193
  throw new Error(
15894
16194
  `refusing to run: revert path escapes the worktree: ${JSON.stringify(p)}`
@@ -16259,8 +16559,8 @@ async function runTestEfficacy(args) {
16259
16559
  findings,
16260
16560
  cleanupFailure
16261
16561
  };
16262
- mkdirSync23(dirname20(out), { recursive: true });
16263
- writeFileSync23(out, JSON.stringify(result, null, 2), "utf8");
16562
+ mkdirSync24(dirname20(out), { recursive: true });
16563
+ writeFileSync24(out, JSON.stringify(result, null, 2), "utf8");
16264
16564
  writeStdoutLine(
16265
16565
  `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))`
16266
16566
  );
@@ -16337,8 +16637,8 @@ var testEfficacyCommand = {
16337
16637
 
16338
16638
  // packages/cli/src/commands/review/test-plan.ts
16339
16639
  init_esbuild_shims();
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";
16640
+ import { existsSync as existsSync12, mkdirSync as mkdirSync25, readFileSync as readFileSync27, writeFileSync as writeFileSync25 } from "node:fs";
16641
+ import { dirname as dirname21, join as join19, normalize, resolve as resolve20 } from "node:path";
16342
16642
  var PLAN_NAME_RE = /(test\s*plan|\btesting\b|how\s+(?:has\s+this|to)\s+(?:been\s+)?test(?:ed)?|测试计划|测试方案|测试步骤)/i;
16343
16643
  var HEADING_LINE_RE = /^(#{1,6})(?:[ \t](.*))?$/;
16344
16644
  var BOLD_LINE_RE = /^\*\*([^*\n]+)\*\*:?\s*$/;
@@ -16541,7 +16841,7 @@ function rulePath(text, worktree, changed) {
16541
16841
  };
16542
16842
  }
16543
16843
  const ignored = isGitIgnored(worktree, path3);
16544
- if (existsSync12(join18(worktree, path3))) {
16844
+ if (existsSync12(join19(worktree, path3))) {
16545
16845
  return {
16546
16846
  kind: "path",
16547
16847
  text,
@@ -16612,7 +16912,7 @@ function ruleCommand(text, worktree, buildTest) {
16612
16912
  let rootScripts = [];
16613
16913
  try {
16614
16914
  const rootPkg = JSON.parse(
16615
- readFileSync26(join18(worktree, "package.json"), "utf8")
16915
+ readFileSync27(join19(worktree, "package.json"), "utf8")
16616
16916
  );
16617
16917
  rootScripts = Object.keys(rootPkg.scripts ?? {});
16618
16918
  } catch {
@@ -16690,7 +16990,7 @@ __name(fetchPrBody, "fetchPrBody");
16690
16990
  function runTestPlan(args, fetchBody = fetchPrBody) {
16691
16991
  let plan;
16692
16992
  try {
16693
- plan = JSON.parse(readFileSync26(args.plan, "utf8"));
16993
+ plan = JSON.parse(readFileSync27(args.plan, "utf8"));
16694
16994
  } catch (err) {
16695
16995
  throw new Error(
16696
16996
  `test-plan: cannot read the plan ${args.plan}: ${err.message}`
@@ -16725,7 +17025,7 @@ function runTestPlan(args, fetchBody = fetchPrBody) {
16725
17025
  if (args.buildTest) {
16726
17026
  try {
16727
17027
  buildTest = JSON.parse(
16728
- readFileSync26(args.buildTest, "utf8")
17028
+ readFileSync27(args.buildTest, "utf8")
16729
17029
  );
16730
17030
  } catch {
16731
17031
  buildTest = null;
@@ -16782,8 +17082,8 @@ var testPlanCommand = {
16782
17082
  try {
16783
17083
  const report = runTestPlan(args);
16784
17084
  if (args.out) {
16785
- mkdirSync24(dirname21(resolve20(args.out)), { recursive: true });
16786
- writeFileSync24(resolve20(args.out), JSON.stringify(report, null, 2));
17085
+ mkdirSync25(dirname21(resolve20(args.out)), { recursive: true });
17086
+ writeFileSync25(resolve20(args.out), JSON.stringify(report, null, 2));
16787
17087
  }
16788
17088
  writeStdoutLine(JSON.stringify(report, null, 2));
16789
17089
  writeStderrLine(`test-plan: ${report.note}`);
@@ -16797,8 +17097,8 @@ var testPlanCommand = {
16797
17097
  // packages/cli/src/commands/review/cleanup.ts
16798
17098
  init_esbuild_shims();
16799
17099
  import { execFileSync as execFileSync5 } from "node:child_process";
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";
17100
+ import { existsSync as existsSync13, readFileSync as readFileSync28, readdirSync as readdirSync6, rmSync as rmSync10 } from "node:fs";
17101
+ import { join as join20 } from "node:path";
16802
17102
  var AUTOMATION_MARKER = "<!-- qwen-";
16803
17103
  function isAutomationComment(body) {
16804
17104
  return (body ?? "").trimStart().startsWith(AUTOMATION_MARKER);
@@ -16829,7 +17129,7 @@ var OWNER_REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
16829
17129
  function readAuditWindow(target, expectedPrNumber) {
16830
17130
  let raw;
16831
17131
  try {
16832
- raw = readFileSync27(tmpFile(target, "fetch.json"), "utf8");
17132
+ raw = readFileSync28(tmpFile(target, "fetch.json"), "utf8");
16833
17133
  } catch (err) {
16834
17134
  const code = err.code;
16835
17135
  return code === "ENOENT" ? { skip: "no fetch report" } : {
@@ -16876,7 +17176,7 @@ function readSubmitReceipt(target) {
16876
17176
  try {
16877
17177
  return new Set(
16878
17178
  parseReceiptIds(
16879
- readFileSync27(tmpFile(target, "submit-receipt.json"), "utf8")
17179
+ readFileSync28(tmpFile(target, "submit-receipt.json"), "utf8")
16880
17180
  )
16881
17181
  );
16882
17182
  } catch {
@@ -17011,7 +17311,7 @@ function runCleanup(target) {
17011
17311
  }
17012
17312
  for (const file of tmpEntries) {
17013
17313
  if (!file.startsWith(prefix)) continue;
17014
- const full = join19(REVIEW_TMP_DIR, file);
17314
+ const full = join20(REVIEW_TMP_DIR, file);
17015
17315
  try {
17016
17316
  rmSync10(full, { recursive: true, force: true });
17017
17317
  writeStdoutLine(`Removed temp file: ${full}`);
@@ -17045,8 +17345,8 @@ var cleanupCommand = {
17045
17345
  // packages/cli/src/commands/review/run.ts
17046
17346
  init_esbuild_shims();
17047
17347
  import { spawn, execFileSync as execFileSync6 } from "node:child_process";
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";
17348
+ import { readdirSync as readdirSync7, readFileSync as readFileSync29, realpathSync as realpathSync3, statSync as statSync10 } from "node:fs";
17349
+ import { dirname as dirname22, join as join21, resolve as resolve21 } from "node:path";
17050
17350
  var COMPOSED_PATTERN = /^qwen-review-.*composed\.json$/;
17051
17351
  var COMPOSED_POLL_MS = 250;
17052
17352
  var SIGNAL_EXIT_CODES = {
@@ -17080,10 +17380,10 @@ function newestArtifactSince(dir, pattern, startMs) {
17080
17380
  }
17081
17381
  for (const name of names) {
17082
17382
  if (!pattern.test(name)) continue;
17083
- const path3 = join20(dir, name);
17383
+ const path3 = join21(dir, name);
17084
17384
  let mtime;
17085
17385
  try {
17086
- mtime = statSync9(path3).mtimeMs;
17386
+ mtime = statSync10(path3).mtimeMs;
17087
17387
  } catch {
17088
17388
  continue;
17089
17389
  }
@@ -17093,15 +17393,15 @@ function newestArtifactSince(dir, pattern, startMs) {
17093
17393
  return best ? best.path : null;
17094
17394
  }
17095
17395
  __name(newestArtifactSince, "newestArtifactSince");
17096
- function exitCodeFor(completed, event, failOn) {
17396
+ function exitCodeFor(completed, event2, failOn) {
17097
17397
  if (!completed) return 1;
17098
- if (failOn === "request-changes" && event === "REQUEST_CHANGES") return 3;
17398
+ if (failOn === "request-changes" && event2 === "REQUEST_CHANGES") return 3;
17099
17399
  return 0;
17100
17400
  }
17101
17401
  __name(exitCodeFor, "exitCodeFor");
17102
17402
  function readComposed(path3) {
17103
17403
  try {
17104
- const parsed = JSON.parse(readFileSync28(path3, "utf8"));
17404
+ const parsed = JSON.parse(readFileSync29(path3, "utf8"));
17105
17405
  return typeof parsed.event === "string" ? parsed : null;
17106
17406
  } catch {
17107
17407
  return null;
@@ -17332,13 +17632,308 @@ var runCommand = {
17332
17632
  }, "handler")
17333
17633
  };
17334
17634
 
17635
+ // packages/cli/src/commands/review/save-artifact.ts
17636
+ init_esbuild_shims();
17637
+ import {
17638
+ existsSync as existsSync14,
17639
+ lstatSync as lstatSync5,
17640
+ mkdirSync as mkdirSync26,
17641
+ readFileSync as readFileSync30,
17642
+ statSync as statSync11
17643
+ } from "node:fs";
17644
+ import {
17645
+ dirname as dirname23,
17646
+ isAbsolute as isAbsolute4,
17647
+ join as join22,
17648
+ relative as relative3,
17649
+ resolve as resolve22,
17650
+ sep as sep4,
17651
+ toNamespacedPath
17652
+ } from "node:path";
17653
+ import { isDeepStrictEqual } from "node:util";
17654
+ function workspaceRoot() {
17655
+ return resolve22(process.env["QWEN_CODE_PROJECT_DIR"] ?? process.cwd());
17656
+ }
17657
+ __name(workspaceRoot, "workspaceRoot");
17658
+ function isWithin(parent, child) {
17659
+ const rel = relative3(parent, child);
17660
+ return rel === "" || !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep4}`);
17661
+ }
17662
+ __name(isWithin, "isWithin");
17663
+ function workspacePath(root, value, label2) {
17664
+ const absolute = resolve22(root, value);
17665
+ if (!isWithin(root, absolute)) {
17666
+ throw new Error(
17667
+ `${label2} must be inside the workspace: ${JSON.stringify(value)}.`
17668
+ );
17669
+ }
17670
+ return absolute;
17671
+ }
17672
+ __name(workspacePath, "workspacePath");
17673
+ function rejectSymlinkPath(root, path3, label2) {
17674
+ const rel = relative3(root, path3);
17675
+ let current = root;
17676
+ for (const segment of rel.split(sep4)) {
17677
+ current = join22(current, segment);
17678
+ if (existsSync14(current) && lstatSync5(current).isSymbolicLink()) {
17679
+ throw new Error(`${label2} must not traverse a symbolic link.`);
17680
+ }
17681
+ }
17682
+ }
17683
+ __name(rejectSymlinkPath, "rejectSymlinkPath");
17684
+ function sameFile(left, right) {
17685
+ if (left === right) return true;
17686
+ if (!existsSync14(left) || !existsSync14(right)) return false;
17687
+ const leftStat = statSync11(left);
17688
+ const rightStat = statSync11(right);
17689
+ return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
17690
+ }
17691
+ __name(sameFile, "sameFile");
17692
+ function readText(path3, label2) {
17693
+ try {
17694
+ return readFileSync30(path3, "utf8");
17695
+ } catch (error) {
17696
+ throw new Error(
17697
+ `Could not read the ${label2} file at ${JSON.stringify(path3)}: ${error instanceof Error ? error.message : String(error)}`
17698
+ );
17699
+ }
17700
+ }
17701
+ __name(readText, "readText");
17702
+ function readJson2(path3, label2) {
17703
+ const text = readText(path3, label2);
17704
+ try {
17705
+ return JSON.parse(text);
17706
+ } catch (error) {
17707
+ throw new Error(
17708
+ `The ${label2} file at ${JSON.stringify(path3)} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
17709
+ );
17710
+ }
17711
+ }
17712
+ __name(readJson2, "readJson");
17713
+ function object(value, label2) {
17714
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
17715
+ throw new Error(`${label2} must be an object.`);
17716
+ }
17717
+ return value;
17718
+ }
17719
+ __name(object, "object");
17720
+ function string(value, label2) {
17721
+ if (typeof value !== "string") {
17722
+ throw new Error(`${label2} must be a string.`);
17723
+ }
17724
+ return value;
17725
+ }
17726
+ __name(string, "string");
17727
+ function nonEmptyString(value, label2) {
17728
+ const text = string(value, label2);
17729
+ if (text.trim() === "") {
17730
+ throw new Error(`${label2} must be a non-empty string.`);
17731
+ }
17732
+ return text;
17733
+ }
17734
+ __name(nonEmptyString, "nonEmptyString");
17735
+ function stringArray2(value, label2) {
17736
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
17737
+ throw new Error(`${label2} must be an array of strings.`);
17738
+ }
17739
+ return value;
17740
+ }
17741
+ __name(stringArray2, "stringArray");
17742
+ function event(value, label2) {
17743
+ if (value !== "APPROVE" && value !== "COMMENT" && value !== "REQUEST_CHANGES") {
17744
+ throw new Error(`${label2} has an unsupported value.`);
17745
+ }
17746
+ return value;
17747
+ }
17748
+ __name(event, "event");
17749
+ function validateVerdict(value) {
17750
+ const verdict = object(value, "Composed verdict");
17751
+ const downgradedFrom = verdict["downgradedFrom"];
17752
+ if (downgradedFrom !== null && downgradedFrom !== "Approve" && downgradedFrom !== "Request changes") {
17753
+ throw new Error(
17754
+ "Composed verdict.downgradedFrom has an unsupported value."
17755
+ );
17756
+ }
17757
+ if (typeof verdict["downgraded"] !== "boolean") {
17758
+ throw new Error("Composed verdict.downgraded must be a boolean.");
17759
+ }
17760
+ const lowSignal = verdict["lowSignal"];
17761
+ if (lowSignal !== null) {
17762
+ const signal = object(lowSignal, "Composed verdict.lowSignal");
17763
+ for (const key of ["agents", "srcDiffLines"]) {
17764
+ if (typeof signal[key] !== "number" || !Number.isInteger(signal[key]) || signal[key] < 0) {
17765
+ throw new Error(
17766
+ `Composed verdict.lowSignal.${key} must be a non-negative integer.`
17767
+ );
17768
+ }
17769
+ }
17770
+ }
17771
+ return {
17772
+ event: event(verdict["event"], "Composed verdict.event"),
17773
+ body: string(verdict["body"], "Composed verdict.body"),
17774
+ baseEvent: event(verdict["baseEvent"], "Composed verdict.baseEvent"),
17775
+ cappedBy: stringArray2(verdict["cappedBy"], "Composed verdict.cappedBy"),
17776
+ downgraded: verdict["downgraded"],
17777
+ downgradedFrom,
17778
+ remediation: stringArray2(
17779
+ verdict["remediation"],
17780
+ "Composed verdict.remediation"
17781
+ ),
17782
+ lowSignal: lowSignal === null ? null : {
17783
+ agents: lowSignal["agents"],
17784
+ srcDiffLines: lowSignal["srcDiffLines"]
17785
+ },
17786
+ verdictLine: nonEmptyString(
17787
+ verdict["verdictLine"],
17788
+ "Composed verdict.verdictLine"
17789
+ )
17790
+ };
17791
+ }
17792
+ __name(validateVerdict, "validateVerdict");
17793
+ function validateFindingsReport(value) {
17794
+ const report = object(value, "Canonical findings report");
17795
+ if (typeof report["outcomesRecorded"] !== "boolean") {
17796
+ throw new Error(
17797
+ "Canonical findings report.outcomesRecorded must be a boolean."
17798
+ );
17799
+ }
17800
+ const canonical = buildReport(validateFindings(report["findings"]));
17801
+ const supplied = {
17802
+ findings: report["findings"],
17803
+ counts: report["counts"],
17804
+ outcomesRecorded: report["outcomesRecorded"]
17805
+ };
17806
+ if (!isDeepStrictEqual(supplied, canonical)) {
17807
+ throw new Error(
17808
+ "Canonical findings report is malformed or inconsistent with its findings."
17809
+ );
17810
+ }
17811
+ return supplied;
17812
+ }
17813
+ __name(validateFindingsReport, "validateFindingsReport");
17814
+ function saveReviewArtifact(args) {
17815
+ const root = workspaceRoot();
17816
+ const findingsPath = workspacePath(root, args.findings, "Findings input");
17817
+ const composedPath = workspacePath(root, args.composed, "Composed input");
17818
+ const reportPath = workspacePath(root, args.report, "Markdown report");
17819
+ const outputPath = workspacePath(root, args.out, "Output");
17820
+ const reviewsRoot = resolve22(root, REVIEWS_DIR);
17821
+ if (!isWithin(reviewsRoot, reportPath) || reportPath === reviewsRoot) {
17822
+ throw new Error(
17823
+ `Markdown report must be a file under ${JSON.stringify(REVIEWS_DIR)}: ${JSON.stringify(args.report)}.`
17824
+ );
17825
+ }
17826
+ if (!isWithin(reviewsRoot, outputPath) || outputPath === reviewsRoot) {
17827
+ throw new Error(
17828
+ `Output must be a file under ${JSON.stringify(REVIEWS_DIR)}: ${JSON.stringify(args.out)}.`
17829
+ );
17830
+ }
17831
+ for (const [label2, inputPath] of [
17832
+ ["findings input", findingsPath],
17833
+ ["composed input", composedPath],
17834
+ ["Markdown report", reportPath]
17835
+ ]) {
17836
+ if (sameFile(outputPath, inputPath)) {
17837
+ throw new Error(`Output must not overwrite the ${label2}.`);
17838
+ }
17839
+ }
17840
+ rejectSymlinkPath(root, findingsPath, "Findings input");
17841
+ rejectSymlinkPath(root, composedPath, "Composed input");
17842
+ rejectSymlinkPath(root, reportPath, "Markdown report");
17843
+ rejectSymlinkPath(root, outputPath, "Output");
17844
+ if (!EFFORT_LEVELS.has(args.effort)) {
17845
+ throw new Error(
17846
+ `Unsupported review effort: ${JSON.stringify(args.effort)}.`
17847
+ );
17848
+ }
17849
+ if (args.effort === "low") {
17850
+ throw new Error(
17851
+ "save-artifact does not support low-effort reviews: low has no canonical composed verdict to persist."
17852
+ );
17853
+ }
17854
+ nonEmptyString(args.target, "Target");
17855
+ const findings = validateFindingsReport(
17856
+ readJson2(findingsPath, "canonical findings")
17857
+ );
17858
+ const verdict = validateVerdict(readJson2(composedPath, "composed verdict"));
17859
+ let reportIsFile;
17860
+ try {
17861
+ reportIsFile = statSync11(reportPath).isFile();
17862
+ } catch {
17863
+ }
17864
+ if (reportIsFile === false) {
17865
+ throw new Error(
17866
+ `Markdown report is not a file: ${JSON.stringify(args.report)}.`
17867
+ );
17868
+ }
17869
+ readText(reportPath, "Markdown report");
17870
+ const markdownReportPath = relative3(root, reportPath).split(sep4).join("/");
17871
+ const document = {
17872
+ schemaVersion: 1,
17873
+ target: args.target,
17874
+ effort: args.effort,
17875
+ verdict,
17876
+ findings: findings.findings,
17877
+ counts: findings.counts,
17878
+ outcomesRecorded: findings.outcomesRecorded,
17879
+ markdownReportPath
17880
+ };
17881
+ mkdirSync26(dirname23(outputPath), { recursive: true });
17882
+ atomicWriteFileSync(
17883
+ toNamespacedPath(outputPath),
17884
+ `${JSON.stringify(document, null, 2)}
17885
+ `,
17886
+ { encoding: "utf8", noFollow: true }
17887
+ );
17888
+ return {
17889
+ path: outputPath,
17890
+ workspacePath: relative3(root, outputPath).split(sep4).join("/")
17891
+ };
17892
+ }
17893
+ __name(saveReviewArtifact, "saveReviewArtifact");
17894
+ var saveArtifactCommand = {
17895
+ command: "save-artifact",
17896
+ describe: "Create a durable versioned code-review JSON document from canonical review artifacts",
17897
+ builder: /* @__PURE__ */ __name((yargs) => yargs.option("findings", {
17898
+ type: "string",
17899
+ demandOption: true,
17900
+ describe: "Canonical findings JSON"
17901
+ }).option("composed", {
17902
+ type: "string",
17903
+ demandOption: true,
17904
+ describe: "Composed verdict JSON"
17905
+ }).option("report", {
17906
+ type: "string",
17907
+ demandOption: true,
17908
+ describe: "Existing durable Markdown report"
17909
+ }).option("target", {
17910
+ type: "string",
17911
+ demandOption: true,
17912
+ describe: "Review target label"
17913
+ }).option("effort", {
17914
+ type: "string",
17915
+ choices: [...EFFORT_LEVELS].filter((effort) => effort !== "low"),
17916
+ demandOption: true,
17917
+ describe: "Resolved review effort"
17918
+ }).option("out", {
17919
+ type: "string",
17920
+ demandOption: true,
17921
+ describe: "Output path under .qwen/reviews/"
17922
+ }), "builder"),
17923
+ handler: /* @__PURE__ */ __name((argv) => {
17924
+ const saved = saveReviewArtifact(argv);
17925
+ writeStdoutLine(JSON.stringify(saved));
17926
+ writeStderrLine(`save-artifact: wrote ${saved.path}`);
17927
+ }, "handler")
17928
+ };
17929
+
17335
17930
  // packages/cli/src/commands/review.ts
17336
17931
  var reviewCommand = {
17337
17932
  command: "review",
17338
17933
  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)",
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(
17934
+ 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(saveArtifactCommand).command(submitCommand).command(cleanupCommand).demandCommand(
17340
17935
  1,
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."
17936
+ "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, save-artifact, submit, or cleanup."
17342
17937
  ).version(false), "builder"),
17343
17938
  handler: /* @__PURE__ */ __name(() => {
17344
17939
  }, "handler")
@@ -17538,10 +18133,10 @@ var updateCommand3 = {
17538
18133
  stdioHelpers,
17539
18134
  { updateEventEmitter }
17540
18135
  ] = await Promise.all([
17541
- import("./settings-ILWAOKA4.js"),
17542
- import("./updateCheck-42SPCLH7.js"),
17543
- import("./installationInfo-IWMM6DPV.js"),
17544
- import("./standalone-update-Y5JB4S2K.js"),
18136
+ import("./settings-KWYWA3BS.js"),
18137
+ import("./updateCheck-EE2XXQNJ.js"),
18138
+ import("./installationInfo-CZVCKPPZ.js"),
18139
+ import("./standalone-update-WNXRFLEA.js"),
17545
18140
  import("./stdioHelpers-HGIYTWAY.js"),
17546
18141
  import("./updateEventEmitter-5MKC7BLQ.js")
17547
18142
  ]);
@@ -18387,7 +18982,7 @@ var SessionIdConflictError = class extends Error {
18387
18982
  this.sessionId = sessionId;
18388
18983
  }
18389
18984
  };
18390
- async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtensions, hooksConfig, disabledSkillNamesProvider, sessionMcpServers, settingsWatcher, throwOnSessionIdConflict = false) {
18985
+ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtensions, hooksConfig, disabledSkillNamesProvider, sessionMcpServers, settingsWatcher, throwOnSessionIdConflict = false, hostPolicy) {
18391
18986
  const debugMode = isDebugMode(argv);
18392
18987
  if (debugMode && process.env["QWEN_DEBUG_LOG_FILE"] === void 0) {
18393
18988
  process.env["QWEN_DEBUG_LOG_FILE"] = "1";
@@ -18708,7 +19303,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
18708
19303
  disabledSlashCommands: disabledSlashCommands.length > 0 ? disabledSlashCommands : void 0,
18709
19304
  disabledSkillNamesProvider: bareMode || safeMode ? void 0 : disabledSkillNamesProvider,
18710
19305
  terminalImageRenderSupportProvider: interactive ? async () => {
18711
- const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-NWC2RALG.js");
19306
+ const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-TCEAHDFE.js");
18712
19307
  return getTerminalImageRenderSupport();
18713
19308
  } : void 0,
18714
19309
  disabledSkillLevels: bareMode || safeMode || !Array.isArray(settings.skills?.disabledLevels) ? void 0 : settings.skills.disabledLevels.filter(isSkillLevel),
@@ -18725,6 +19320,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
18725
19320
  deny: mergedDeny.length > 0 ? mergedDeny : void 0,
18726
19321
  autoMode: bareMode || safeMode ? void 0 : settings.permissions?.autoMode
18727
19322
  },
19323
+ toolInvocationGuard: hostPolicy?.toolInvocationGuard,
18728
19324
  // Permission rule persistence callback (writes to settings files).
18729
19325
  onPersistPermissionRule: /* @__PURE__ */ __name(async (scope, ruleType, rule) => {
18730
19326
  const currentSettings = loadSettings(cwd);
@@ -18984,11 +19580,11 @@ var ExtensionRefreshState = class {
18984
19580
  contentChangedDuringReload = false;
18985
19581
  suppressionDepth = 0;
18986
19582
  suppressUntil = 0;
18987
- on(event, listener) {
18988
- this.events.on(event, listener);
19583
+ on(event2, listener) {
19584
+ this.events.on(event2, listener);
18989
19585
  }
18990
- off(event, listener) {
18991
- this.events.off(event, listener);
19586
+ off(event2, listener) {
19587
+ this.events.off(event2, listener);
18992
19588
  }
18993
19589
  markExtensionsChanged(reason) {
18994
19590
  if (this.isSuppressed()) {