@qwen-code/qwen-code 0.21.5 → 0.21.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/qc-helper/docs/configuration/model-providers.md +8 -8
- package/bundled/qc-helper/docs/configuration/settings.md +15 -15
- package/bundled/qc-helper/docs/features/channels/github.md +31 -19
- package/bundled/qc-helper/docs/features/code-review.md +5 -1
- package/bundled/qc-helper/docs/features/headless.md +31 -0
- package/bundled/qc-helper/docs/qwen-serve.md +75 -0
- package/bundled/review/SKILL.md +72 -60
- package/chunks/{MaxSizedBox-2MWU5JYB.js → MaxSizedBox-2HFKP4LI.js} +11 -10
- package/chunks/{StandaloneSessionPicker-B6MI5NTB.js → StandaloneSessionPicker-RBV72M5N.js} +26 -25
- package/chunks/{acp-startup-profiler-3RICG2YW.js → acp-startup-profiler-ORIQ2CCQ.js} +2 -2
- package/chunks/{acpAgent-MRPYN6FH.js → acpAgent-WO6JFCVQ.js} +1232 -139
- package/chunks/{agent-NAOXNW4I.js → agent-AHGLYNZ4.js} +11 -10
- package/chunks/{agent-headless-L4PPVFKW.js → agent-headless-NTXKKGRO.js} +11 -10
- package/chunks/{bridge-LHXGYNCO.js → bridge-RPAU64CA.js} +17 -15
- package/chunks/{channel-management-service-UHX56NBO.js → channel-management-service-IGEUOVRT.js} +3 -3
- package/chunks/{channel-settings-store-QB54TLYE.js → channel-settings-store-ON64MF7G.js} +18 -17
- package/chunks/{channel-worker-group-NDG4WOOZ.js → channel-worker-group-JWQK224X.js} +6 -5
- package/chunks/{channel-worker-manager-ZCW6UW34.js → channel-worker-manager-77E3OXRQ.js} +6 -5
- package/chunks/{channel-worker-supervisor-ATGFTZRN.js → channel-worker-supervisor-62YWUGP2.js} +4 -3
- package/chunks/{chunk-EXHQZBVJ.js → chunk-2POQWYM6.js} +2 -2
- package/chunks/{chunk-YMUZQ3MQ.js → chunk-3LZKY4V4.js} +3 -3
- package/chunks/{chunk-7MFQLOHZ.js → chunk-3PRJYMXC.js} +1 -1
- package/chunks/{chunk-SJMCYCUQ.js → chunk-3UOLYNTF.js} +5 -5
- package/chunks/{chunk-Q2V4AOQL.js → chunk-4JH77A2U.js} +1 -1
- package/chunks/{chunk-MISUEWB4.js → chunk-4S4OEQ6U.js} +10 -6
- package/chunks/{chunk-URJHXOSO.js → chunk-4WTAQEHV.js} +1 -1
- package/chunks/{chunk-ZLGPHA34.js → chunk-536SWZTA.js} +5 -5
- package/chunks/{chunk-WU5NBSLD.js → chunk-5MJGUQU5.js} +3 -3
- package/chunks/{chunk-3P4GP4S2.js → chunk-75QMRMO7.js} +4 -4
- package/chunks/{chunk-YLL3SC5S.js → chunk-A4VTILZT.js} +1032 -913
- package/chunks/{chunk-B3EBIZUY.js → chunk-ACZRB7AD.js} +8 -3
- package/chunks/chunk-BKHCW2BI.js +41 -0
- package/chunks/{chunk-XD2PNH3K.js → chunk-BOGOR4HB.js} +5 -5
- package/chunks/{chunk-QL4TN4HS.js → chunk-BRVWYMKV.js} +1 -1
- package/chunks/{chunk-TCOJPG2P.js → chunk-C4CBJQKY.js} +1 -1
- package/chunks/{chunk-4O6HLCOF.js → chunk-CA4UIDFU.js} +1 -1
- package/chunks/{chunk-SQTA6PAY.js → chunk-CJ5WLD5Y.js} +6 -2
- package/chunks/{chunk-7W5KOBYU.js → chunk-DIT4V3XH.js} +1 -1
- package/chunks/{chunk-OIGB253L.js → chunk-DVBEGGLG.js} +8 -8
- package/chunks/{chunk-DTAPI453.js → chunk-EMYZIABH.js} +1 -1
- package/chunks/{chunk-GBY3ODW7.js → chunk-EXCEQD22.js} +3 -3
- package/chunks/{chunk-VJPF6GF2.js → chunk-FZFP7RZI.js} +3 -3
- package/chunks/{chunk-3GOCVBYR.js → chunk-G5NNWEYE.js} +5 -5
- package/chunks/{chunk-IWYXZAGT.js → chunk-GESMMPAK.js} +33 -33
- package/chunks/{chunk-SVIEVA3L.js → chunk-GK2H5GRC.js} +29 -4
- package/chunks/{chunk-7M2RLFNX.js → chunk-GWRHVJY2.js} +11 -11
- package/chunks/{chunk-BK2UNYWD.js → chunk-HIBNSEJE.js} +12 -0
- package/chunks/{chunk-4LLUH3A7.js → chunk-HU5TUALO.js} +95 -8
- package/chunks/{chunk-J7I2BZHS.js → chunk-HY25ULVN.js} +23 -6
- package/chunks/{chunk-G2UXFO2B.js → chunk-IMOQO6YL.js} +1 -1
- package/chunks/{chunk-CAX76R46.js → chunk-JE44IPH6.js} +1 -1
- package/chunks/{chunk-IK3RYLAD.js → chunk-K2B3X3E7.js} +3 -3
- package/chunks/{chunk-OWAK7LMW.js → chunk-KGJGEEVR.js} +7 -0
- package/chunks/{chunk-K64OI7S7.js → chunk-KHUA3NQV.js} +2 -2
- package/chunks/{chunk-LEZFUYPM.js → chunk-LAAFLUQV.js} +4 -4
- package/chunks/{chunk-FHPKHXHT.js → chunk-MKVZE4ZJ.js} +5 -1
- package/chunks/{chunk-4NEBUDWS.js → chunk-N2GJU6KI.js} +9 -9
- package/chunks/{chunk-N3RERU5N.js → chunk-NGJOAKJY.js} +3 -3
- package/chunks/chunk-NKLP3DCK.js +22 -0
- package/chunks/{chunk-K6VEL5KP.js → chunk-NKSF7HYF.js} +1 -1
- package/chunks/{chunk-LMNM37OE.js → chunk-O7V7N4G7.js} +1 -1
- package/chunks/{chunk-3H5RFLTS.js → chunk-ODPXHGLL.js} +2 -2
- package/chunks/{chunk-KH2BIJ4H.js → chunk-OP2OHZ7K.js} +267 -11
- package/chunks/{chunk-VOA2UPZM.js → chunk-OWSW7EYP.js} +5 -5
- package/chunks/{chunk-B2RBYP7Z.js → chunk-OXX3FG2N.js} +2 -2
- package/chunks/{chunk-RM3F46UT.js → chunk-PPUOYL77.js} +71 -6
- package/chunks/{chunk-ZPGMDT4S.js → chunk-PX5DNLSA.js} +19 -92
- package/chunks/{chunk-FEUXV3DC.js → chunk-Q5RM32DR.js} +1 -1
- package/chunks/{chunk-AEWOKOJY.js → chunk-QZKYZCN3.js} +1 -1
- package/chunks/{chunk-UKZEXS6K.js → chunk-R574ATRM.js} +11 -0
- package/chunks/{chunk-OGAARVN7.js → chunk-R5RBXLNC.js} +3 -3
- package/chunks/{chunk-5HWAJ4BK.js → chunk-RFUHDCNF.js} +1 -1
- package/chunks/{chunk-LSYVASZS.js → chunk-RHTTKJTP.js} +1 -1
- package/chunks/{chunk-LYPXNRBX.js → chunk-RK2EE3T7.js} +1 -1
- package/chunks/{chunk-CIUUYB23.js → chunk-S7CV4KO6.js} +3 -3
- package/chunks/{chunk-4NNR24EP.js → chunk-SKNE3KQC.js} +3 -3
- package/chunks/{chunk-JDNWIXHJ.js → chunk-SLSWFHDF.js} +3 -3
- package/chunks/{chunk-UWSXFI6E.js → chunk-SQC6K5HI.js} +6 -2
- package/chunks/{chunk-P3SLTSNY.js → chunk-SQOMTWQT.js} +4 -4
- package/chunks/{chunk-D7QAZ5DF.js → chunk-SUKEUUAA.js} +1 -1
- package/chunks/{chunk-WTM5QONI.js → chunk-T4JSA7KU.js} +2 -2
- package/chunks/{chunk-I7JDGNG6.js → chunk-T5U6IM4C.js} +6 -2
- package/chunks/{chunk-2JBETW4S.js → chunk-TGGEZINK.js} +5 -5
- package/chunks/{chunk-C6ARWPCG.js → chunk-TOGZTG4X.js} +2 -2
- package/chunks/{chunk-A6SX5TAH.js → chunk-TSERKWCX.js} +6 -2
- package/chunks/{chunk-IWGMDSC3.js → chunk-U4AF3QLU.js} +355 -17
- package/chunks/{chunk-PDDYAXL2.js → chunk-UGTWD35F.js} +14 -1
- package/chunks/{chunk-BWLUCN35.js → chunk-ULPQQSDN.js} +2 -2
- package/chunks/{chunk-GEP54CHX.js → chunk-VGFPFM4U.js} +3 -3
- package/chunks/{chunk-NLQIEY6W.js → chunk-VNQCPBC2.js} +3 -3
- package/chunks/{chunk-K2ETEWG6.js → chunk-VSALQLT7.js} +1 -1
- package/chunks/{chunk-YO4JI7B4.js → chunk-W5DXUTXD.js} +12 -6
- package/chunks/{chunk-M744OFVE.js → chunk-W7U4ESW2.js} +1 -1
- package/chunks/{chunk-TJ56V3AR.js → chunk-WNU7TZTO.js} +4 -4
- package/chunks/{chunk-GTM6IHBB.js → chunk-WO2B5E5B.js} +1 -0
- package/chunks/chunk-WOJZWRAZ.js +123 -0
- package/chunks/{chunk-5XNO4AW7.js → chunk-XA4TMLFN.js} +11 -11
- package/chunks/{chunk-GP4IMBX5.js → chunk-XM2O73NC.js} +1696 -476
- package/chunks/{chunk-IZ2CMEYO.js → chunk-YMBOEAF4.js} +1 -1
- package/chunks/{chunk-QONSRAEV.js → chunk-YP5KH5Y7.js} +1 -1
- package/chunks/{chunk-ZLJQIGCH.js → chunk-YZQTA3ID.js} +28 -11
- package/chunks/{computer-use-XDOGP2KA.js → computer-use-5HXJ2P4X.js} +11 -10
- package/chunks/{config-utils-E3ZQTHGM.js → config-utils-VXD4SPVR.js} +2 -2
- package/chunks/{contextCommand-N3LQDCYJ.js → contextCommand-7BEHRX2H.js} +14 -13
- package/chunks/{core-runtime-STAFP45W.js → core-runtime-U4VJ5TIV.js} +11 -10
- package/chunks/create-sub-session-QZNQY332.js +772 -0
- package/chunks/{daemon-CCIX4BMG.js → daemon-YI5RY62O.js} +115 -3
- package/chunks/{daemon-status-provider-PPAH3ATF.js → daemon-status-provider-UJUGTDTL.js} +21 -19
- package/chunks/{daemon-trust-policy-X6XL3MB4.js → daemon-trust-policy-ZHDD5VYQ.js} +17 -16
- package/chunks/{daemon-trust-policy-monitor-M5LBL2HD.js → daemon-trust-policy-monitor-5SHYBDGA.js} +17 -16
- package/chunks/{deferred-core-runtime-O54YPTBU.js → deferred-core-runtime-QWAEBTR5.js} +11 -10
- package/chunks/discovery-XLHITE74.js +220 -0
- package/chunks/{dist-IKGVITP3.js → dist-KS4O2NKE.js} +99 -6
- package/chunks/{earlyInputCapture-CBIT2RGT.js → earlyInputCapture-5BMSGKOI.js} +11 -10
- package/chunks/{edit-KW2TOC6A.js → edit-IVQQVYYK.js} +11 -10
- package/chunks/{enter-worktree-YL34NEU5.js → enter-worktree-P3DF2N6O.js} +11 -10
- package/chunks/{enterPlanMode-JFJTDASQ.js → enterPlanMode-KKTDVLAD.js} +11 -10
- package/chunks/{environment-IATU5GXG.js → environment-W5TRDM3G.js} +14 -13
- package/chunks/{errors-SAVRDE7Q.js → errors-GJJB36IG.js} +14 -13
- package/chunks/{exit-worktree-5M43QPBB.js → exit-worktree-LV5RLOCU.js} +11 -10
- package/chunks/{exitPlanMode-W4EICZ4N.js → exitPlanMode-TUGOB2LL.js} +11 -10
- package/chunks/external-tool-guard-provider-5HTGOPGU.js +273 -0
- package/chunks/{fast-path-YQJPIKMP.js → fast-path-2FU23PAQ.js} +5 -5
- package/chunks/{gemini-FUF5SSPR.js → gemini-KDULDQPP.js} +70 -55
- package/chunks/{geminiContentGenerator-LLLZBOUG.js → geminiContentGenerator-LOCFHNVA.js} +1 -1
- package/chunks/{glob-LJFFMN4D.js → glob-Z4NQFKWS.js} +11 -10
- package/chunks/{grep-Q64EAM73.js → grep-Z67Z7EN6.js} +11 -10
- package/chunks/{handleAutoUpdate-QBMPRTSQ.js → handleAutoUpdate-DFUZE6I6.js} +16 -15
- package/chunks/{i18n-LDUW5DMN.js → i18n-R4FMZ7N2.js} +13 -12
- package/chunks/{image-gen-G7RLG3KR.js → image-gen-4CQJPQP5.js} +1 -1
- package/chunks/{initializer-74A7DMMD.js → initializer-DK672ZVE.js} +17 -16
- package/chunks/{installationInfo-OFDYLDFD.js → installationInfo-PO4Z3ES7.js} +12 -11
- package/chunks/{list-JA2EWRHJ.js → list-QXQF6C53.js} +20 -19
- package/chunks/{loadedSettingsAdapter-2GNJOQ3L.js → loadedSettingsAdapter-4D7GUGW6.js} +17 -16
- package/chunks/{loggingContentGenerator-B6B5EGB3.js → loggingContentGenerator-2P3MNACM.js} +4 -3
- package/chunks/{managed-npm-update-4YZHLUER.js → managed-npm-update-LWVJQTWO.js} +24 -14
- package/chunks/{mcp-LLK5YJVH.js → mcp-GHETAR6V.js} +17 -16
- package/chunks/{monitor-NYJPAWVP.js → monitor-4T7W4M6Q.js} +11 -10
- package/chunks/{nonInteractiveCli-HZBBBDJD.js → nonInteractiveCli-P4UKCJFM.js} +47 -45
- package/chunks/{notebook-edit-LLM6UXFX.js → notebook-edit-OC2V7U5D.js} +11 -10
- package/chunks/{openaiContentGenerator-2AEXDN55.js → openaiContentGenerator-RAJUFWQY.js} +5 -4
- package/chunks/{pidfile-3JUWX7G2.js → pidfile-HVPYVRLS.js} +11 -10
- package/chunks/{processUtils-JLLRVZHT.js → processUtils-VIQ4JHVX.js} +2 -2
- package/chunks/{qwenContentGenerator-2FESCF27.js → qwenContentGenerator-2QLCUFNZ.js} +12 -11
- package/chunks/{qwenOAuth2-2KKPYA5J.js → qwenOAuth2-J2X3EN43.js} +1 -1
- package/chunks/{read-file-5GVXLY74.js → read-file-ZNAN2B26.js} +3 -2
- package/chunks/{record-artifact-L3BAHQFN.js → record-artifact-E6AWQU2Z.js} +1 -1
- package/chunks/{resumeHistoryUtils-Z4OFBXPM.js → resumeHistoryUtils-RCY7AL5Y.js} +15 -14
- package/chunks/{ripGrep-TKOJTIAB.js → ripGrep-C3KMLZM5.js} +11 -10
- package/chunks/{run-qwen-serve-K5W6MM6M.js → run-qwen-serve-3JN75CIQ.js} +518 -75
- package/chunks/{runtime-IADXMRCY.js → runtime-VUA4WQYD.js} +20 -19
- package/chunks/{scheduler-T7AV37LD.js → scheduler-Z4IBXCOY.js} +11 -10
- package/chunks/{serve-ROEGMS3I.js → serve-67T3P7XI.js} +18 -16
- package/chunks/{server-NGXMRODI.js → server-46ZO2CLV.js} +7556 -397
- package/chunks/{session-RCDLDJA4.js → session-B4IFAFC4.js} +50 -48
- package/chunks/{settings-E6H3AURD.js → settings-AI2BTBOZ.js} +16 -15
- package/chunks/{shell-RHGGJ6BE.js → shell-BKII3B6E.js} +11 -10
- package/chunks/{skill-GQ7PBS25.js → skill-6CFOWVGA.js} +4 -3
- package/chunks/{skill-settings-E2F7OCBL.js → skill-settings-6BFQWMW4.js} +16 -15
- package/chunks/{spawnChannel-Q2IQBHME.js → spawnChannel-FJXW35U7.js} +14 -12
- package/chunks/{standalone-update-NYYZEHLD.js → standalone-update-WVINHRNS.js} +14 -13
- package/chunks/{startInteractiveUI-IX6I3Y2Z.js → startInteractiveUI-YE57CJ57.js} +140 -138
- package/chunks/{stdioHelpers-HGIYTWAY.js → stdioHelpers-7A2EE64Z.js} +5 -3
- package/chunks/{task-create-2X4UP4F3.js → task-create-Q727MXHJ.js} +2 -2
- package/chunks/{task-list-WOLJPZ64.js → task-list-G6KPQ5JT.js} +1 -1
- package/chunks/{task-update-VDUSN36E.js → task-update-YB7PXO4R.js} +2 -2
- package/chunks/{team-create-UTRVUEDA.js → team-create-GNC7NFY6.js} +11 -10
- package/chunks/{team-delete-D2VMSDOT.js → team-delete-GDO7H2T4.js} +1 -1
- package/chunks/{team-plan-approval-YZNB4U5U.js → team-plan-approval-ECALJLIO.js} +11 -10
- package/chunks/{terminal-image-renderer-QRSCS2BE.js → terminal-image-renderer-II74TFVW.js} +11 -10
- package/chunks/{theme-manager-EKZBY34I.js → theme-manager-2MGEDD22.js} +11 -10
- package/chunks/{tool-search-N2R7LVMC.js → tool-search-S6LRRACA.js} +3 -2
- package/chunks/{total-session-admission-HBIYARPO.js → total-session-admission-7WVAWQPU.js} +17 -15
- package/chunks/{trustedFolders-ZQLYRCSY.js → trustedFolders-NFS4KHO4.js} +13 -12
- package/chunks/{update-relaunch-PJWXMPCR.js → update-relaunch-Q5HLZ6UW.js} +6 -6
- package/chunks/{updateCheck-ZXRCGY66.js → updateCheck-UOVZSTP7.js} +15 -14
- package/chunks/{useAutoAcceptIndicator-ZYQW5SGJ.js → useAutoAcceptIndicator-PC6MOJCO.js} +19 -18
- package/chunks/{validateNonInterActiveAuth-M7J7OTRH.js → validateNonInterActiveAuth-YMQP3ZM7.js} +44 -42
- package/chunks/{version-2AOHMPK3.js → version-T4AQFZJH.js} +1 -1
- package/chunks/{web-fetch-DS34CJ3R.js → web-fetch-JQCJHEEX.js} +3 -2
- package/chunks/{web-search-ITFPD7V4.js → web-search-QGZ2KY4G.js} +4 -3
- package/chunks/{workflow-LZQ4PGNS.js → workflow-YIT27YWZ.js} +12 -11
- package/chunks/{workspace-providers-status-ZPTLKOBM.js → workspace-providers-status-FIDZQ2PL.js} +20 -19
- package/chunks/{workspace-registration-store-OTFKSGG7.js → workspace-registration-store-RCSPE4DU.js} +1 -1
- package/chunks/{workspace-registry-YZ3OXPHP.js → workspace-registry-5PEUCY6E.js} +17 -15
- package/chunks/{workspace-service-OCZXOOSQ.js → workspace-service-LANDVPL7.js} +21 -20
- package/chunks/{workspace-skills-status-K2ALGUCD.js → workspace-skills-status-2IQVJNIN.js} +18 -17
- package/chunks/{workspace-trust-reconciler-KMRZISJP.js → workspace-trust-reconciler-CZPZZCZC.js} +26 -24
- package/chunks/{write-file-AWBI4GJS.js → write-file-G5VNGCOG.js} +11 -10
- package/chunks/{zoom-image-NHKN6F77.js → zoom-image-6RBLD7X6.js} +3 -2
- package/cli-entry.js +14 -0
- package/cli.js +19 -15
- package/package.json +3 -3
- package/web-shell/assets/{arc-Dpg66jw7.js → arc-B-Vtkp1W.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-BJ4FIBxN.js → architectureDiagram-3BPJPVTR-qmTXIj64.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-VXmYdtYT.js → blockDiagram-GPEHLZMM-ZlnXEtdP.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-BNLDb8FE.js → c4Diagram-AAUBKEIU-Wyoirf6Z.js} +1 -1
- package/web-shell/assets/channel-DXaHnfjS.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DR5yHxr0.js → chunk-2J33WTMH-WQwbq9d8.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-Czkq8UZU.js → chunk-4BX2VUAB-DQKnAXC4.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DugUgM6s.js → chunk-55IACEB6-DIdgXNcO.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-Dw6quA1K.js → chunk-727SXJPM-CUwGOLEd.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-BoVYOTTz.js → chunk-AQP2D5EJ-CCBGZFZ4.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4--Amcse-j.js → chunk-FMBD7UC4-DMGXProk.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-ajaEYAC8.js → chunk-ND2GUHAM-yejcFqsh.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-CUNDZ7im.js → chunk-QZHKN3VN-DxCyqTf7.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BigU82A0.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BigU82A0.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BnfkD-m1.js → cose-bilkent-S5V4N54A-DOrF3cg_.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-BuUdFk7Z.js → dagre-BM42HDAG-Be6CdYQ_.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-B2ZXxoab.js → diagram-2AECGRRQ-B_Rjsft2.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-BsgOICzM.js → diagram-5GNKFQAL-BvQbKSm1.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-DTTjG6U-.js → diagram-KO2AKTUF-BsdmMdpS.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-temLf1x7.js → diagram-LMA3HP47-CrQlBaNr.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-CRFjScs2.js → diagram-OG6HWLK6-aB1MD_DL.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-D4oaY-rQ.js → erDiagram-TEJ5UH35-F-eUUn9n.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BqG8B49b.js → flowDiagram-I6XJVG4X-DY_d8lUK.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-DffQBdd5.js → ganttDiagram-6RSMTGT7-Dqu7KBPg.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CMDPfseF.js → gitGraphDiagram-PVQCEYII-2IkU_VEj.js} +1 -1
- package/web-shell/assets/{index-qTQR_smP.js → index-BuSbqggh.js} +1 -1
- package/web-shell/assets/index-DrfC2e9s.js +1768 -0
- package/web-shell/assets/index-QQlUCJGg.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-Cn3T83MA.js → infoDiagram-5YYISTIA-C4orwzPw.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-ijFs7e25.js → ishikawaDiagram-YF4QCWOH-fXUEfef_.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-CINTv8VB.js → journeyDiagram-JHISSGLW-Bs0I1_J5.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-CMGRZCxx.js → kanban-definition-UN3LZRKU-BPRj1dw_.js} +1 -1
- package/web-shell/assets/{linear-Co-2_37d.js → linear-DFS6ZDpW.js} +1 -1
- package/web-shell/assets/{mermaid.core-DijbyaN5.js → mermaid.core-CZxjWMaO.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-k_sDYDPZ.js → mindmap-definition-RKZ34NQL-Dsr3PmNs.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-CG-oz8V3.js → pieDiagram-4H26LBE5-D5Ngpz5Y.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-Baq7HIG2.js → quadrantDiagram-W4KKPZXB-WJK9I25z.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-CDM3ZvBt.js → requirementDiagram-4Y6WPE33-CmrYcwOH.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-C2dJbWKN.js → sankeyDiagram-5OEKKPKP-7SjCc4Mv.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Cf1_h59p.js → sequenceDiagram-3UESZ5HK-Dl9E0IRM.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CI5clUEc.js → stateDiagram-AJRCARHV-CRRio7yV.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-1atT7XaU.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-B2O8cAZX.js → timeline-definition-PNZ67QCA-DVyXjjfq.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BLsJ_h9s.js → vennDiagram-CIIHVFJN-Nb2Lckzq.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-BlPU4FTA.js → wardley-L42UT6IY-DuL_otIP.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-D_U-RdU4.js → wardleyDiagram-YWT4CUSO-DVI9Q4Aa.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-BynXGSTl.js → xychartDiagram-2RQKCTM6-BNjAm7KD.js} +1 -1
- package/web-shell/index.html +2 -2
- package/bundled/review/DESIGN.md +0 -724
- package/chunks/create-sub-session-2B3JENWD.js +0 -400
- package/web-shell/assets/channel-Bd9wOnaA.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-XhCT8FE3.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-XhCT8FE3.js +0 -1
- package/web-shell/assets/index-2F4RsNzd.js +0 -1756
- package/web-shell/assets/index-CaucmEO_.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BDc8asKD.js +0 -1
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
import {
|
|
4
4
|
serveCommand
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GK2H5GRC.js";
|
|
6
6
|
import {
|
|
7
7
|
listCommand
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-536SWZTA.js";
|
|
9
9
|
import {
|
|
10
10
|
getErrorMessage as getErrorMessage2
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-FZFP7RZI.js";
|
|
12
12
|
import {
|
|
13
13
|
ObservedChannelContactStore
|
|
14
14
|
} from "./chunk-DYA25Y7U.js";
|
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
isLoopbackBind
|
|
26
26
|
} from "./chunk-RXFQM6FQ.js";
|
|
27
|
+
import {
|
|
28
|
+
collectProviderModelsForProtocol,
|
|
29
|
+
getAuthTypeFromEnv,
|
|
30
|
+
resolveCliGenerationConfig
|
|
31
|
+
} from "./chunk-5MJGUQU5.js";
|
|
27
32
|
import {
|
|
28
33
|
resolvePath
|
|
29
34
|
} from "./chunk-JELIWHXD.js";
|
|
@@ -46,22 +51,17 @@ import {
|
|
|
46
51
|
registerToolCallDispatch,
|
|
47
52
|
selectFirstModel,
|
|
48
53
|
sessionsPath
|
|
49
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-OWSW7EYP.js";
|
|
50
55
|
import {
|
|
51
56
|
getExtensionManager,
|
|
52
57
|
requestChoicePluginNonInteractive,
|
|
53
58
|
requestConsentNonInteractive,
|
|
54
59
|
requestConsentOrFail,
|
|
55
60
|
resolveExtensionCommandScope
|
|
56
|
-
} from "./chunk-
|
|
57
|
-
import {
|
|
58
|
-
collectProviderModelsForProtocol,
|
|
59
|
-
getAuthTypeFromEnv,
|
|
60
|
-
resolveCliGenerationConfig
|
|
61
|
-
} from "./chunk-WU5NBSLD.js";
|
|
61
|
+
} from "./chunk-G5NNWEYE.js";
|
|
62
62
|
import {
|
|
63
63
|
parseChannelConfig
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-LAAFLUQV.js";
|
|
65
65
|
import {
|
|
66
66
|
findCliEntryPath
|
|
67
67
|
} from "./chunk-V6WHFGST.js";
|
|
@@ -88,7 +88,7 @@ import {
|
|
|
88
88
|
isChannelDeliveryMessage,
|
|
89
89
|
isChannelStartupReportAckMessage,
|
|
90
90
|
isChannelWebhookTaskMessage
|
|
91
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-YP5KH5Y7.js";
|
|
92
92
|
import {
|
|
93
93
|
normalizeWorkerDiagnostic,
|
|
94
94
|
sanitizeWorkerDiagnostic
|
|
@@ -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-
|
|
134
|
+
} from "./chunk-3UOLYNTF.js";
|
|
132
135
|
import {
|
|
133
136
|
loadSettings
|
|
134
|
-
} from "./chunk-
|
|
137
|
+
} from "./chunk-PPUOYL77.js";
|
|
135
138
|
import {
|
|
136
139
|
detectSystemLanguage,
|
|
137
140
|
getCurrentLanguage,
|
|
138
141
|
initializeI18n,
|
|
139
142
|
resolveLanguageSetting,
|
|
140
143
|
t
|
|
141
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-KHUA3NQV.js";
|
|
142
145
|
import {
|
|
143
146
|
loadEnvironment
|
|
144
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-RK2EE3T7.js";
|
|
145
148
|
import {
|
|
146
149
|
getCliVersion
|
|
147
|
-
} from "./chunk-
|
|
150
|
+
} from "./chunk-YMBOEAF4.js";
|
|
148
151
|
import {
|
|
149
152
|
getPackageJson
|
|
150
153
|
} from "./chunk-VJNBRRZG.js";
|
|
151
154
|
import {
|
|
152
155
|
isWorkspaceTrusted
|
|
153
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-ACZRB7AD.js";
|
|
154
157
|
import {
|
|
155
158
|
require_strip_json_comments
|
|
156
159
|
} from "./chunk-ALGGS7UH.js";
|
|
@@ -191,29 +194,30 @@ import {
|
|
|
191
194
|
unquoteCStylePath,
|
|
192
195
|
updateChannelMemoryEntry,
|
|
193
196
|
updateSetting
|
|
194
|
-
} from "./chunk-
|
|
197
|
+
} from "./chunk-A4VTILZT.js";
|
|
195
198
|
import {
|
|
196
199
|
nextFireTime,
|
|
197
200
|
parseCron
|
|
198
201
|
} from "./chunk-IRH27ZC2.js";
|
|
199
202
|
import {
|
|
200
203
|
require_dist
|
|
201
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-4S4OEQ6U.js";
|
|
202
205
|
import {
|
|
203
206
|
parseBooleanEnvFlag,
|
|
204
207
|
resolveTelemetrySettings
|
|
205
208
|
} from "./chunk-MGSSJT27.js";
|
|
206
209
|
import {
|
|
207
210
|
getAllGeminiMdFilenames,
|
|
211
|
+
parseLineTolerant,
|
|
208
212
|
setGeminiMdFilename
|
|
209
|
-
} from "./chunk-
|
|
213
|
+
} from "./chunk-PX5DNLSA.js";
|
|
210
214
|
import {
|
|
211
215
|
SchemaValidator,
|
|
212
216
|
ToolNames
|
|
213
217
|
} from "./chunk-D7D7RXIJ.js";
|
|
214
218
|
import {
|
|
215
219
|
DEFAULT_QWEN_EMBEDDING_MODEL
|
|
216
|
-
} from "./chunk-
|
|
220
|
+
} from "./chunk-BOGOR4HB.js";
|
|
217
221
|
import {
|
|
218
222
|
isTlsVerificationDisabled
|
|
219
223
|
} from "./chunk-BFHXDAR7.js";
|
|
@@ -234,8 +238,9 @@ import {
|
|
|
234
238
|
import {
|
|
235
239
|
writeStderrLine,
|
|
236
240
|
writeStderrLineSafe,
|
|
237
|
-
writeStdoutLine
|
|
238
|
-
|
|
241
|
+
writeStdoutLine,
|
|
242
|
+
writeStdoutLineSafe
|
|
243
|
+
} from "./chunk-KGJGEEVR.js";
|
|
239
244
|
import {
|
|
240
245
|
init_esbuild_shims
|
|
241
246
|
} from "./chunk-5O2XNYP6.js";
|
|
@@ -358,9 +363,9 @@ var require_command_exists = __commonJS({
|
|
|
358
363
|
cleanInput = /* @__PURE__ */ __name(function(s) {
|
|
359
364
|
var isPathName = /[\\]/.test(s);
|
|
360
365
|
if (isPathName) {
|
|
361
|
-
var
|
|
366
|
+
var dirname25 = '"' + path3.dirname(s) + '"';
|
|
362
367
|
var basename6 = '"' + path3.basename(s) + '"';
|
|
363
|
-
return
|
|
368
|
+
return dirname25 + ":" + basename6;
|
|
364
369
|
}
|
|
365
370
|
return '"' + s + '"';
|
|
366
371
|
}, "cleanInput");
|
|
@@ -368,10 +373,10 @@ var require_command_exists = __commonJS({
|
|
|
368
373
|
module.exports = /* @__PURE__ */ __name(function commandExists2(commandName, callback) {
|
|
369
374
|
var cleanedCommandName = cleanInput(commandName);
|
|
370
375
|
if (!callback && typeof Promise !== "undefined") {
|
|
371
|
-
return new Promise(function(
|
|
376
|
+
return new Promise(function(resolve25, reject) {
|
|
372
377
|
commandExists2(commandName, function(error, output) {
|
|
373
378
|
if (output) {
|
|
374
|
-
|
|
379
|
+
resolve25(commandName);
|
|
375
380
|
} else {
|
|
376
381
|
reject(error);
|
|
377
382
|
}
|
|
@@ -700,6 +705,7 @@ import { join, resolve } from "node:path";
|
|
|
700
705
|
var REVIEW_TMP_DIR = join(".qwen", "tmp");
|
|
701
706
|
var REVIEWS_DIR = join(".qwen", "reviews");
|
|
702
707
|
var REVIEW_CACHE_DIR = join(".qwen", "review-cache");
|
|
708
|
+
var LEASE_PREFIX = "qwen-review-lease-";
|
|
703
709
|
var PARSE_ARGS_REPORT = join(
|
|
704
710
|
REVIEW_TMP_DIR,
|
|
705
711
|
"qwen-review-parse-args.json"
|
|
@@ -735,7 +741,6 @@ function tmpPrefix(target) {
|
|
|
735
741
|
__name(tmpPrefix, "tmpPrefix");
|
|
736
742
|
|
|
737
743
|
// packages/cli/src/services/review-worktree-lease.ts
|
|
738
|
-
var LEASE_PREFIX = "qwen-review-lease-";
|
|
739
744
|
var GIT_TIMEOUT_MS = 12e4;
|
|
740
745
|
var debugLogger = createDebugLogger("REVIEW_WORKTREE_LEASE");
|
|
741
746
|
function gitOptions(timeout) {
|
|
@@ -2255,8 +2260,8 @@ function createBridgeReadinessGate() {
|
|
|
2255
2260
|
current: /* @__PURE__ */ __name(() => pending, "current"),
|
|
2256
2261
|
block: /* @__PURE__ */ __name(() => {
|
|
2257
2262
|
if (pending) return;
|
|
2258
|
-
pending = new Promise((
|
|
2259
|
-
releasePending =
|
|
2263
|
+
pending = new Promise((resolve25) => {
|
|
2264
|
+
releasePending = resolve25;
|
|
2260
2265
|
});
|
|
2261
2266
|
}, "block"),
|
|
2262
2267
|
release: /* @__PURE__ */ __name(() => {
|
|
@@ -2338,7 +2343,7 @@ function createBridgeRecovery(options) {
|
|
|
2338
2343
|
writeStderrLine(
|
|
2339
2344
|
`[Channel] Bridge crashed (${recentCrashCount}/${MAX_CRASH_RESTARTS} in window). Restarting in ${RESTART_DELAY_MS / 1e3}s...`
|
|
2340
2345
|
);
|
|
2341
|
-
await new Promise((
|
|
2346
|
+
await new Promise((resolve25) => setTimeout(resolve25, RESTART_DELAY_MS));
|
|
2342
2347
|
const bridge = new AcpBridge(bridgeOpts);
|
|
2343
2348
|
setBridge(bridge);
|
|
2344
2349
|
attachDisconnectHandler(bridge);
|
|
@@ -2656,7 +2661,7 @@ var stopCommand = {
|
|
|
2656
2661
|
if (argv["daemon-url"]) {
|
|
2657
2662
|
const token = argv.token ?? process.env[QWEN_SERVER_TOKEN_ENV] ?? process.env[QWEN_DAEMON_TOKEN_ENV];
|
|
2658
2663
|
try {
|
|
2659
|
-
const sdk = await import("./daemon-
|
|
2664
|
+
const sdk = await import("./daemon-YI5RY62O.js");
|
|
2660
2665
|
const client = new sdk.DaemonClient({
|
|
2661
2666
|
baseUrl: argv["daemon-url"],
|
|
2662
2667
|
...token ? { token } : {}
|
|
@@ -2770,7 +2775,7 @@ function formatChannelStartupFailures(source, fallbackWorkspaceCwd) {
|
|
|
2770
2775
|
} catch {
|
|
2771
2776
|
return [];
|
|
2772
2777
|
}
|
|
2773
|
-
const
|
|
2778
|
+
const lines = [];
|
|
2774
2779
|
for (const raw of limitedFailures) {
|
|
2775
2780
|
try {
|
|
2776
2781
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
@@ -2802,16 +2807,16 @@ function formatChannelStartupFailures(source, fallbackWorkspaceCwd) {
|
|
|
2802
2807
|
"phase=connect",
|
|
2803
2808
|
...code ? [`code=${code}`] : []
|
|
2804
2809
|
];
|
|
2805
|
-
|
|
2810
|
+
lines.push(
|
|
2806
2811
|
`[Channel] Startup failure (${fields.join(", ")}): ${safeMessage}`
|
|
2807
2812
|
);
|
|
2808
2813
|
} catch {
|
|
2809
2814
|
}
|
|
2810
2815
|
}
|
|
2811
2816
|
if (rawTruncated === true || rawFailureCount > MAX_CHANNEL_STARTUP_FAILURES) {
|
|
2812
|
-
|
|
2817
|
+
lines.push("[Channel] Additional startup failures were truncated.");
|
|
2813
2818
|
}
|
|
2814
|
-
return
|
|
2819
|
+
return lines;
|
|
2815
2820
|
}
|
|
2816
2821
|
__name(formatChannelStartupFailures, "formatChannelStartupFailures");
|
|
2817
2822
|
|
|
@@ -2842,7 +2847,7 @@ var statusCommand = {
|
|
|
2842
2847
|
if (argv["daemon-url"]) {
|
|
2843
2848
|
const token = argv.token ?? process.env[QWEN_SERVER_TOKEN_ENV] ?? process.env[QWEN_DAEMON_TOKEN_ENV];
|
|
2844
2849
|
try {
|
|
2845
|
-
const sdk = await import("./daemon-
|
|
2850
|
+
const sdk = await import("./daemon-YI5RY62O.js");
|
|
2846
2851
|
const client = new sdk.DaemonClient({
|
|
2847
2852
|
baseUrl: argv["daemon-url"],
|
|
2848
2853
|
...token ? { token } : {}
|
|
@@ -2953,7 +2958,7 @@ var reloadCommand = {
|
|
|
2953
2958
|
const token = resolveToken(argv.token);
|
|
2954
2959
|
let sdk;
|
|
2955
2960
|
try {
|
|
2956
|
-
sdk = await import("./daemon-
|
|
2961
|
+
sdk = await import("./daemon-YI5RY62O.js");
|
|
2957
2962
|
} catch (err) {
|
|
2958
2963
|
writeStderrLine(
|
|
2959
2964
|
`[Channel] Failed to load daemon SDK: ${safeChannelCommandErrorMessage(err)}`
|
|
@@ -3033,7 +3038,7 @@ var setCommand2 = {
|
|
|
3033
3038
|
const selection = names[0] === "all" ? { mode: "all" } : { mode: "names", names };
|
|
3034
3039
|
let sdk;
|
|
3035
3040
|
try {
|
|
3036
|
-
sdk = await import("./daemon-
|
|
3041
|
+
sdk = await import("./daemon-YI5RY62O.js");
|
|
3037
3042
|
} catch (error) {
|
|
3038
3043
|
writeStderrLine(
|
|
3039
3044
|
`[Channel] Failed to load daemon SDK: ${safeChannelCommandErrorMessage(error)}`
|
|
@@ -3159,7 +3164,7 @@ function createDaemonChannelBridgeFacade(bridge, opts) {
|
|
|
3159
3164
|
}
|
|
3160
3165
|
__name(createDaemonChannelBridgeFacade, "createDaemonChannelBridgeFacade");
|
|
3161
3166
|
async function loadDaemonSdk() {
|
|
3162
|
-
return await import("./daemon-
|
|
3167
|
+
return await import("./daemon-YI5RY62O.js");
|
|
3163
3168
|
}
|
|
3164
3169
|
__name(loadDaemonSdk, "loadDaemonSdk");
|
|
3165
3170
|
function selectedChannelNames(channelsConfig, selection) {
|
|
@@ -3206,12 +3211,12 @@ async function abortableStartup(value, signal) {
|
|
|
3206
3211
|
const promise = Promise.resolve(value);
|
|
3207
3212
|
if (!signal) return await promise;
|
|
3208
3213
|
if (signal.aborted) throw startupAbortError();
|
|
3209
|
-
return await new Promise((
|
|
3214
|
+
return await new Promise((resolve25, reject) => {
|
|
3210
3215
|
const onAbort = /* @__PURE__ */ __name(() => {
|
|
3211
3216
|
reject(startupAbortError());
|
|
3212
3217
|
}, "onAbort");
|
|
3213
3218
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
3214
|
-
promise.then(
|
|
3219
|
+
promise.then(resolve25, reject).finally(() => {
|
|
3215
3220
|
signal.removeEventListener("abort", onAbort);
|
|
3216
3221
|
});
|
|
3217
3222
|
});
|
|
@@ -3585,6 +3590,7 @@ function scrubDaemonWorkerEnv() {
|
|
|
3585
3590
|
delete process.env[QWEN_DAEMON_URL_ENV];
|
|
3586
3591
|
delete process.env[QWEN_DAEMON_WORKSPACE_ENV];
|
|
3587
3592
|
delete process.env[QWEN_SERVER_TOKEN_ENV];
|
|
3593
|
+
delete process.env[EXTERNAL_TOOL_GUARD_TOKEN_ENV];
|
|
3588
3594
|
}
|
|
3589
3595
|
__name(scrubDaemonWorkerEnv, "scrubDaemonWorkerEnv");
|
|
3590
3596
|
function readDaemonWorkerEnv() {
|
|
@@ -3619,7 +3625,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
|
|
|
3619
3625
|
if (!send) {
|
|
3620
3626
|
return Promise.reject(new Error("Channel worker IPC is unavailable."));
|
|
3621
3627
|
}
|
|
3622
|
-
return new Promise((
|
|
3628
|
+
return new Promise((resolve25, reject) => {
|
|
3623
3629
|
let settled = false;
|
|
3624
3630
|
let unsubscribeMessage = /* @__PURE__ */ __name(() => {
|
|
3625
3631
|
}, "unsubscribeMessage");
|
|
@@ -3633,7 +3639,7 @@ function reportStartupToSupervisor(message, signal, subscribeMessage = (listener
|
|
|
3633
3639
|
settled = true;
|
|
3634
3640
|
cleanup();
|
|
3635
3641
|
if (error) reject(error);
|
|
3636
|
-
else
|
|
3642
|
+
else resolve25();
|
|
3637
3643
|
}, "finish");
|
|
3638
3644
|
const onMessage = /* @__PURE__ */ __name((value) => {
|
|
3639
3645
|
if (isChannelStartupReportAckMessage(value)) {
|
|
@@ -3881,8 +3887,8 @@ var daemonWorkerCommand = {
|
|
|
3881
3887
|
let shuttingDown = false;
|
|
3882
3888
|
let exitCode = 0;
|
|
3883
3889
|
let finish;
|
|
3884
|
-
const finished = new Promise((
|
|
3885
|
-
finish =
|
|
3890
|
+
const finished = new Promise((resolve25) => {
|
|
3891
|
+
finish = resolve25;
|
|
3886
3892
|
});
|
|
3887
3893
|
const shutdown = /* @__PURE__ */ __name(async (reason) => {
|
|
3888
3894
|
if (shuttingDown) {
|
|
@@ -3910,8 +3916,8 @@ var daemonWorkerCommand = {
|
|
|
3910
3916
|
...activeChannelDeliveries.values(),
|
|
3911
3917
|
...activeWebhookTasks.values()
|
|
3912
3918
|
]),
|
|
3913
|
-
new Promise((
|
|
3914
|
-
const timer = setTimeout(
|
|
3919
|
+
new Promise((resolve25) => {
|
|
3920
|
+
const timer = setTimeout(resolve25, WORKER_SHUTDOWN_DRAIN_MS);
|
|
3915
3921
|
timer.unref();
|
|
3916
3922
|
})
|
|
3917
3923
|
]);
|
|
@@ -4547,12 +4553,12 @@ var parseArgsCommand = {
|
|
|
4547
4553
|
|
|
4548
4554
|
// packages/cli/src/commands/review/compose-review.ts
|
|
4549
4555
|
init_esbuild_shims();
|
|
4550
|
-
import { mkdirSync as
|
|
4551
|
-
import { dirname as dirname4, join as
|
|
4556
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
|
|
4557
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
4552
4558
|
|
|
4553
4559
|
// packages/cli/src/commands/review/lib/coverage.ts
|
|
4554
4560
|
init_esbuild_shims();
|
|
4555
|
-
import { readFileSync as
|
|
4561
|
+
import { readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
4556
4562
|
|
|
4557
4563
|
// packages/cli/src/commands/review/lib/transcripts.ts
|
|
4558
4564
|
init_esbuild_shims();
|
|
@@ -4563,7 +4569,7 @@ var TranscriptsUnavailableError = class extends Error {
|
|
|
4563
4569
|
__name(this, "TranscriptsUnavailableError");
|
|
4564
4570
|
}
|
|
4565
4571
|
};
|
|
4566
|
-
function
|
|
4572
|
+
function transcriptPaths(env = process.env) {
|
|
4567
4573
|
const projectDir = env["QWEN_CODE_PROJECT_DIR"]?.trim();
|
|
4568
4574
|
const sessionId = env["QWEN_CODE_SESSION_ID"]?.trim();
|
|
4569
4575
|
if (!projectDir || !sessionId) {
|
|
@@ -4571,7 +4577,15 @@ function transcriptDir(env = process.env) {
|
|
|
4571
4577
|
"the CLI did not export QWEN_CODE_PROJECT_DIR / QWEN_CODE_SESSION_ID, so this run cannot find the harness's record of what its agents did"
|
|
4572
4578
|
);
|
|
4573
4579
|
}
|
|
4574
|
-
return
|
|
4580
|
+
return {
|
|
4581
|
+
projectDir,
|
|
4582
|
+
sessionId,
|
|
4583
|
+
dir: join5(projectDir, "subagents", sessionId)
|
|
4584
|
+
};
|
|
4585
|
+
}
|
|
4586
|
+
__name(transcriptPaths, "transcriptPaths");
|
|
4587
|
+
function transcriptDir(env = process.env) {
|
|
4588
|
+
return transcriptPaths(env).dir;
|
|
4575
4589
|
}
|
|
4576
4590
|
__name(transcriptDir, "transcriptDir");
|
|
4577
4591
|
function textOf(rec) {
|
|
@@ -4602,8 +4616,8 @@ function parseTranscript(file, diffPath) {
|
|
|
4602
4616
|
} catch {
|
|
4603
4617
|
return null;
|
|
4604
4618
|
}
|
|
4605
|
-
const
|
|
4606
|
-
if (
|
|
4619
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
4620
|
+
if (lines.length === 0) return null;
|
|
4607
4621
|
let agentId = "";
|
|
4608
4622
|
let agentName = "";
|
|
4609
4623
|
let launchPrompt = "";
|
|
@@ -4614,7 +4628,7 @@ function parseTranscript(file, diffPath) {
|
|
|
4614
4628
|
const successfulCallArgs = [];
|
|
4615
4629
|
const byId = /* @__PURE__ */ new Map();
|
|
4616
4630
|
const anonymous = [];
|
|
4617
|
-
for (const line of
|
|
4631
|
+
for (const line of lines) {
|
|
4618
4632
|
let rec;
|
|
4619
4633
|
try {
|
|
4620
4634
|
rec = JSON.parse(line);
|
|
@@ -4688,11 +4702,15 @@ function parseTranscript(file, diffPath) {
|
|
|
4688
4702
|
};
|
|
4689
4703
|
}
|
|
4690
4704
|
__name(parseTranscript, "parseTranscript");
|
|
4705
|
+
function listAgentTranscriptFiles(dir) {
|
|
4706
|
+
return readdirSync2(dir).filter((name) => name.endsWith(".jsonl"));
|
|
4707
|
+
}
|
|
4708
|
+
__name(listAgentTranscriptFiles, "listAgentTranscriptFiles");
|
|
4691
4709
|
function readTranscripts(since, env = process.env, diffPath) {
|
|
4692
4710
|
const dir = transcriptDir(env);
|
|
4693
4711
|
let names;
|
|
4694
4712
|
try {
|
|
4695
|
-
names =
|
|
4713
|
+
names = listAgentTranscriptFiles(dir);
|
|
4696
4714
|
} catch (err) {
|
|
4697
4715
|
throw new TranscriptsUnavailableError(
|
|
4698
4716
|
`no subagent transcripts at ${dir} (${err.message}). The harness writes one per agent; if there are none, either no agents ran or the harness could not write them.`
|
|
@@ -4700,7 +4718,6 @@ function readTranscripts(since, env = process.env, diffPath) {
|
|
|
4700
4718
|
}
|
|
4701
4719
|
const out = [];
|
|
4702
4720
|
for (const name of names) {
|
|
4703
|
-
if (!name.endsWith(".jsonl")) continue;
|
|
4704
4721
|
const rec = parseTranscript(join5(dir, name), diffPath);
|
|
4705
4722
|
if (!rec) continue;
|
|
4706
4723
|
if (since !== void 0 && rec.mtimeMs < since) continue;
|
|
@@ -4718,7 +4735,13 @@ __name(wasGivenTheDiff, "wasGivenTheDiff");
|
|
|
4718
4735
|
|
|
4719
4736
|
// packages/cli/src/commands/review/lib/prompt-record.ts
|
|
4720
4737
|
init_esbuild_shims();
|
|
4721
|
-
import {
|
|
4738
|
+
import {
|
|
4739
|
+
mkdirSync as mkdirSync3,
|
|
4740
|
+
readFileSync as readFileSync5,
|
|
4741
|
+
readdirSync as readdirSync3,
|
|
4742
|
+
statSync as statSync2,
|
|
4743
|
+
writeFileSync as writeFileSync3
|
|
4744
|
+
} from "node:fs";
|
|
4722
4745
|
import { dirname as dirname2, join as join6, basename as basename3, resolve as resolve3 } from "node:path";
|
|
4723
4746
|
function promptRecordDir(planPath) {
|
|
4724
4747
|
const p = resolve3(planPath);
|
|
@@ -4760,7 +4783,7 @@ function recordPrompt(planPath, key, prompt) {
|
|
|
4760
4783
|
}
|
|
4761
4784
|
}
|
|
4762
4785
|
__name(recordPrompt, "recordPrompt");
|
|
4763
|
-
function readRecordedPrompts(planPath) {
|
|
4786
|
+
function readRecordedPrompts(planPath, sinceMs) {
|
|
4764
4787
|
const out = /* @__PURE__ */ new Map();
|
|
4765
4788
|
const dir = promptRecordDir(planPath);
|
|
4766
4789
|
let names;
|
|
@@ -4778,7 +4801,9 @@ function readRecordedPrompts(planPath) {
|
|
|
4778
4801
|
} catch {
|
|
4779
4802
|
continue;
|
|
4780
4803
|
}
|
|
4781
|
-
|
|
4804
|
+
const file = join6(dir, name);
|
|
4805
|
+
if (sinceMs !== void 0 && statSync2(file).mtimeMs < sinceMs) continue;
|
|
4806
|
+
out.set(key, readFileSync5(file, "utf8"));
|
|
4782
4807
|
} catch {
|
|
4783
4808
|
}
|
|
4784
4809
|
}
|
|
@@ -4786,25 +4811,33 @@ function readRecordedPrompts(planPath) {
|
|
|
4786
4811
|
}
|
|
4787
4812
|
__name(readRecordedPrompts, "readRecordedPrompts");
|
|
4788
4813
|
function wasDeliveredVerbatim(launchPrompt, built) {
|
|
4814
|
+
return deliveredVerbatim(flattenPrompt(launchPrompt), built);
|
|
4815
|
+
}
|
|
4816
|
+
__name(wasDeliveredVerbatim, "wasDeliveredVerbatim");
|
|
4817
|
+
function deliveredVerbatim(flattenedLaunch, built) {
|
|
4789
4818
|
if (built.trim().length === 0) return false;
|
|
4790
|
-
|
|
4819
|
+
return deliveredVerbatimLines(flattenedLaunch, promptLines(built));
|
|
4820
|
+
}
|
|
4821
|
+
__name(deliveredVerbatim, "deliveredVerbatim");
|
|
4822
|
+
function deliveredVerbatimLines(flattenedLaunch, builtLines) {
|
|
4823
|
+
if (builtLines.length === 0) return false;
|
|
4791
4824
|
let at = 0;
|
|
4792
|
-
for (const line of
|
|
4793
|
-
const i =
|
|
4825
|
+
for (const line of builtLines) {
|
|
4826
|
+
const i = flattenedLaunch.indexOf(line, at);
|
|
4794
4827
|
if (i === -1) return false;
|
|
4795
4828
|
at = i + line.length;
|
|
4796
4829
|
}
|
|
4797
4830
|
return true;
|
|
4798
4831
|
}
|
|
4799
|
-
__name(
|
|
4800
|
-
function
|
|
4832
|
+
__name(deliveredVerbatimLines, "deliveredVerbatimLines");
|
|
4833
|
+
function flattenPrompt(s) {
|
|
4801
4834
|
return s.replace(/\s+/g, " ").trim();
|
|
4802
4835
|
}
|
|
4803
|
-
__name(
|
|
4804
|
-
function
|
|
4805
|
-
return built.split("\n").map((l) =>
|
|
4836
|
+
__name(flattenPrompt, "flattenPrompt");
|
|
4837
|
+
function promptLines(built) {
|
|
4838
|
+
return built.split("\n").map((l) => flattenPrompt(l)).filter((l) => l.length > 0);
|
|
4806
4839
|
}
|
|
4807
|
-
__name(
|
|
4840
|
+
__name(promptLines, "promptLines");
|
|
4808
4841
|
|
|
4809
4842
|
// packages/cli/src/commands/review/lib/roster.ts
|
|
4810
4843
|
init_esbuild_shims();
|
|
@@ -4908,9 +4941,9 @@ function splitHeaderPaths(rest) {
|
|
|
4908
4941
|
}
|
|
4909
4942
|
__name(splitHeaderPaths, "splitHeaderPaths");
|
|
4910
4943
|
function parseDiff(diffText) {
|
|
4911
|
-
const
|
|
4912
|
-
if (
|
|
4913
|
-
const total =
|
|
4944
|
+
const lines = diffText.split("\n");
|
|
4945
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
4946
|
+
const total = lines.length;
|
|
4914
4947
|
const files = [];
|
|
4915
4948
|
let cur = null;
|
|
4916
4949
|
let curHunk = null;
|
|
@@ -4937,7 +4970,7 @@ function parseDiff(diffText) {
|
|
|
4937
4970
|
}
|
|
4938
4971
|
}, "closeFile");
|
|
4939
4972
|
for (let i = 0; i < total; i++) {
|
|
4940
|
-
const line =
|
|
4973
|
+
const line = lines[i];
|
|
4941
4974
|
const n = i + 1;
|
|
4942
4975
|
if (line.startsWith("diff --git ")) {
|
|
4943
4976
|
closeFile(n - 1);
|
|
@@ -5016,9 +5049,9 @@ function parseDiff(diffText) {
|
|
|
5016
5049
|
}
|
|
5017
5050
|
__name(parseDiff, "parseDiff");
|
|
5018
5051
|
var MIN_SPLIT_SEGMENT = 40;
|
|
5019
|
-
function isSafeSplitPoint(
|
|
5020
|
-
const cur =
|
|
5021
|
-
const prev =
|
|
5052
|
+
function isSafeSplitPoint(lines, n) {
|
|
5053
|
+
const cur = lines[n - 1];
|
|
5054
|
+
const prev = lines[n - 2];
|
|
5022
5055
|
if (cur === void 0 || prev === void 0) return false;
|
|
5023
5056
|
const isNewSide = /* @__PURE__ */ __name((l) => l === "" || /^[+ ]/.test(l), "isNewSide");
|
|
5024
5057
|
if (!isNewSide(cur) || !isNewSide(prev)) return false;
|
|
@@ -5028,9 +5061,9 @@ function isSafeSplitPoint(lines2, n) {
|
|
|
5028
5061
|
return prev === "" || /^\s*$/.test(prev.slice(1));
|
|
5029
5062
|
}
|
|
5030
5063
|
__name(isSafeSplitPoint, "isSafeSplitPoint");
|
|
5031
|
-
function charPrefix(
|
|
5032
|
-
const p = new Array(
|
|
5033
|
-
for (let i = 0; i <
|
|
5064
|
+
function charPrefix(lines) {
|
|
5065
|
+
const p = new Array(lines.length + 1).fill(0);
|
|
5066
|
+
for (let i = 0; i < lines.length; i++) p[i + 1] = p[i] + lines[i].length + 1;
|
|
5034
5067
|
return p;
|
|
5035
5068
|
}
|
|
5036
5069
|
__name(charPrefix, "charPrefix");
|
|
@@ -5038,14 +5071,14 @@ function charsIn(prefix, s, e) {
|
|
|
5038
5071
|
return prefix[e] - prefix[s - 1];
|
|
5039
5072
|
}
|
|
5040
5073
|
__name(charsIn, "charsIn");
|
|
5041
|
-
function splitUnit(unit,
|
|
5074
|
+
function splitUnit(unit, lines, prefix, maxChunkLines, bodyStart) {
|
|
5042
5075
|
const over = /* @__PURE__ */ __name((s, e) => e - s + 1 > maxChunkLines || charsIn(prefix, s, e) > MAX_CHUNK_CHARS, "over");
|
|
5043
5076
|
const bigEnough = /* @__PURE__ */ __name((s, e) => e - s + 1 >= MIN_SPLIT_SEGMENT || charsIn(prefix, s, e) >= MAX_CHUNK_CHARS / 2, "bigEnough");
|
|
5044
5077
|
if (!over(unit.start, unit.end)) return [unit];
|
|
5045
5078
|
const newLineOf = /* @__PURE__ */ new Map();
|
|
5046
5079
|
let newLine = unit.newStart;
|
|
5047
5080
|
for (let n = bodyStart; n <= unit.end; n++) {
|
|
5048
|
-
const c =
|
|
5081
|
+
const c = lines[n - 1]?.[0];
|
|
5049
5082
|
if (c === " " || c === "+") newLineOf.set(n, newLine++);
|
|
5050
5083
|
}
|
|
5051
5084
|
const out = [];
|
|
@@ -5054,7 +5087,7 @@ function splitUnit(unit, lines2, prefix, maxChunkLines, bodyStart) {
|
|
|
5054
5087
|
const upper = Math.min(unit.end, segStart + maxChunkLines - 1);
|
|
5055
5088
|
let cut = -1;
|
|
5056
5089
|
for (let n = upper; n > bodyStart; n--) {
|
|
5057
|
-
if (!isSafeSplitPoint(
|
|
5090
|
+
if (!isSafeSplitPoint(lines, n)) continue;
|
|
5058
5091
|
if (over(segStart, n - 1)) continue;
|
|
5059
5092
|
if (!bigEnough(segStart, n - 1)) continue;
|
|
5060
5093
|
cut = n;
|
|
@@ -5062,7 +5095,7 @@ function splitUnit(unit, lines2, prefix, maxChunkLines, bodyStart) {
|
|
|
5062
5095
|
}
|
|
5063
5096
|
if (cut < 0) {
|
|
5064
5097
|
for (let n = upper + 1; n <= unit.end; n++) {
|
|
5065
|
-
if (isSafeSplitPoint(
|
|
5098
|
+
if (isSafeSplitPoint(lines, n) && bigEnough(segStart, n - 1)) {
|
|
5066
5099
|
cut = n;
|
|
5067
5100
|
break;
|
|
5068
5101
|
}
|
|
@@ -5088,10 +5121,10 @@ function splitUnit(unit, lines2, prefix, maxChunkLines, bodyStart) {
|
|
|
5088
5121
|
return out;
|
|
5089
5122
|
}
|
|
5090
5123
|
__name(splitUnit, "splitUnit");
|
|
5091
|
-
function planChunks(files,
|
|
5092
|
-
const diffLines =
|
|
5124
|
+
function planChunks(files, lines, maxChunkLines = DEFAULT_MAX_CHUNK_LINES) {
|
|
5125
|
+
const diffLines = lines.length;
|
|
5093
5126
|
if (diffLines === 0) return [];
|
|
5094
|
-
const prefix = charPrefix(
|
|
5127
|
+
const prefix = charPrefix(lines);
|
|
5095
5128
|
const units = [];
|
|
5096
5129
|
for (const f of files) {
|
|
5097
5130
|
if (f.hunks.length === 0) {
|
|
@@ -5115,7 +5148,7 @@ function planChunks(files, lines2, maxChunkLines = DEFAULT_MAX_CHUNK_LINES) {
|
|
|
5115
5148
|
newEnd: h.newEnd
|
|
5116
5149
|
};
|
|
5117
5150
|
units.push(
|
|
5118
|
-
...splitUnit(unit,
|
|
5151
|
+
...splitUnit(unit, lines, prefix, maxChunkLines, h.diffStart + 1)
|
|
5119
5152
|
);
|
|
5120
5153
|
});
|
|
5121
5154
|
}
|
|
@@ -5127,7 +5160,7 @@ function planChunks(files, lines2, maxChunkLines = DEFAULT_MAX_CHUNK_LINES) {
|
|
|
5127
5160
|
cur.chars = charsIn(prefix, cur.startLine, cur.endLine);
|
|
5128
5161
|
let widest = 0;
|
|
5129
5162
|
for (let n = cur.startLine; n <= cur.endLine; n++) {
|
|
5130
|
-
if (
|
|
5163
|
+
if (lines[n - 1].length > widest) widest = lines[n - 1].length;
|
|
5131
5164
|
}
|
|
5132
5165
|
cur.maxLineChars = widest;
|
|
5133
5166
|
chunks.push(cur);
|
|
@@ -5170,10 +5203,10 @@ function planChunks(files, lines2, maxChunkLines = DEFAULT_MAX_CHUNK_LINES) {
|
|
|
5170
5203
|
__name(planChunks, "planChunks");
|
|
5171
5204
|
function buildDiffPlan(diffText, maxChunkLines = DEFAULT_MAX_CHUNK_LINES) {
|
|
5172
5205
|
const { files, diffLines } = parseDiff(diffText);
|
|
5173
|
-
const
|
|
5174
|
-
if (
|
|
5206
|
+
const lines = diffText.split("\n");
|
|
5207
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
5175
5208
|
const linesOf = /* @__PURE__ */ __name((kind) => files.filter((f) => f.kind === kind).reduce((n, f) => n + (f.diffEnd - f.diffStart + 1), 0), "linesOf");
|
|
5176
|
-
const chunks = planChunks(files,
|
|
5209
|
+
const chunks = planChunks(files, lines, maxChunkLines);
|
|
5177
5210
|
if (!chunksCoverDiff(chunks, diffLines)) {
|
|
5178
5211
|
throw new Error(
|
|
5179
5212
|
`diff-plan: chunks do not tile the diff (${chunks.length} chunks over ${diffLines} lines). Refusing to plan a review with a coverage hole.`
|
|
@@ -5692,6 +5725,7 @@ __name(requiredAgents, "requiredAgents");
|
|
|
5692
5725
|
|
|
5693
5726
|
// packages/cli/src/commands/review/lib/agent-briefs.ts
|
|
5694
5727
|
init_esbuild_shims();
|
|
5728
|
+
var REVERSE_AUDIT_EXAMPLE_RECEIPT = "No issues found \u2014 re-walked the reconnect state machine and the two changed exports' call sites; every gap I checked was already in the list";
|
|
5695
5729
|
var BRIEFS = {
|
|
5696
5730
|
"0": {
|
|
5697
5731
|
label: "Agent 0: Issue fidelity & root-cause ownership",
|
|
@@ -5990,11 +6024,11 @@ You are undirected on purpose. Do not restrict yourself to the list.`
|
|
|
5990
6024
|
readsDiff: false,
|
|
5991
6025
|
brief: `You are **Agent 7: Build & Test Verification**. You do not review the diff \u2014 you run the project's own deterministic checks and report what they say. Your evidence is **the commands you ran and their output**; a return that names no command has not done this job.
|
|
5992
6026
|
|
|
5993
|
-
**Run \`qwen review build-test\` (the exact command, with its \`--plan\` and \`--worktree\`, is below).** It installs if needed, then builds only the workspaces the diff changes plus everything they compile against, and tests the changed ones \u2014 reading the plan for what changed and the root \`package.json\` for the workspace layout. Do **not** substitute \`npm run build\` / \`npm test\` by hand. The old brief did, with a 120-second deadline, and this repo's cold full build is 125 seconds: measured across the harness's own transcripts, that command timed out **71 times** and verified nothing. \`build-test\` scopes the build, gives it a deadline it can meet, and \u2014 this is the part a hand-run command gets wrong \u2014 reports a timeout as **infrastructure, not a finding**. A build that runs out of time is never a Critical against someone's pull request.
|
|
6027
|
+
**Run \`qwen review build-test\` (the exact command, with its \`--plan\` and \`--worktree\`, is below).** It installs if needed, then builds only the workspaces the diff changes plus everything they compile against, and tests the changed ones plus every workspace that depends on them \u2014 reading the plan for what changed and the root \`package.json\` for the workspace layout. Do **not** substitute \`npm run build\` / \`npm test\` by hand. The old brief did, with a 120-second deadline, and this repo's cold full build is 125 seconds: measured across the harness's own transcripts, that command timed out **71 times** and verified nothing. \`build-test\` scopes the build, gives it a deadline it can meet, and \u2014 this is the part a hand-run command gets wrong \u2014 reports a timeout as **infrastructure, not a finding**. A build that runs out of time is never a Critical against someone's pull request.
|
|
5994
6028
|
|
|
5995
6029
|
Read the JSON it prints:
|
|
5996
6030
|
|
|
5997
|
-
- \`toolchain: "npm"\` \u2192 use its \`build[]\` / \`test[]\` results. A failure in a file **the diff changed** is a **Critical** (\`Source: [build]\` or \`[test]\`); a failure in a file it did **not** touch is pre-existing \u2014 say so, do not file it against this PR. A non-empty \`timedOut\`, or a failed \`install\`, is environment/infrastructure \u2014 informational, never a Critical. On \`ok: true\`, name the workspaces built and the commands run; a return that names no command is a whiff.
|
|
6031
|
+
- \`toolchain: "npm"\` \u2192 use its \`build[]\` / \`test[]\` results. A failure in a file **the diff changed** is a **Critical** (\`Source: [build]\` or \`[test]\`); a failure in a file it did **not** touch is pre-existing \u2014 say so, do not file it against this PR. A non-empty \`timedOut\`, or a failed \`install\`, is environment/infrastructure \u2014 informational, never a Critical. On \`ok: true\`, name the workspaces built and the commands run; a return that names no command is a whiff. Report the TEST coverage from \`testScope\`, never from assumption. \`testScope.workspaces\` lists exactly the suites that ran \u2014 say "tests scoped to <list> \u2014 the changed workspaces and their declared dependents that define a test script". \`testScope.notRun\`, when present, names suites the whole-call budget stopped before they ran \u2014 say they did not run, never fold them into the coverage. When \`testScope.caveat\` is present, the scope may be incomplete \u2014 quote the caveat and say exactly that. A green run is a claim about those suites only \u2014 do not phrase it as the whole suite passing.
|
|
5998
6032
|
- **When any \`test[]\` command failed (exit non-zero, not a timeout), MEASURE which failures are the PR's before ruling by path.** The path rule above misclassifies in both directions \u2014 an environment-flaky test in a touched file gets filed as a Critical it did not cause, and a PR that breaks a test in an UNTOUCHED file gets waved through as pre-existing. The measurement is two commands: \`qwen review base-tree --plan <plan> --worktree <worktree> --out <plan dir>/qwen-review-pr-<n>-base-tree.json\` (builds the merge base beside the worktree). **Read \`available\` before using \`path\`** \u2014 a tree that was created but did NOT build populates \`path\` too, and a base that failed to build says nothing whatsoever about the PR, so measuring against it turns an infrastructure failure into a list of Criticals. \`available: false\` (local/lightweight review, no merge base, a base that would not compile) means the path rule stands \u2014 say so and stop here, and \`qwen review test-delta --report <the build-test report you wrote> --baseline <the base-tree report's path field> --pr-worktree <this worktree> --out <plan dir>/qwen-review-pr-<n>-test-delta.json\`. Read its verdict: a file in \`netNew\` fails on the PR side only \u2014 **that is the Critical**, whatever file the diff touches; a file in \`shared\` fails on base too \u2014 **pre-existing by measurement**, never filed, whatever file the diff touches; an \`unparsed\` entry, a timed-out base rerun, a base rerun that FAILED without naming any failing file (it did not measure the base \u2014 an unbuilt tree, a missing install, a workspace absent at base), or a command the whole-command budget could not fit attributes nothing \u2014 the report names each with its own reason; fall back to the path rule for those and say the delta could not rule. Compare failing FILE SETS, never counts: a flaky suite fails different test NAMES on two runs of the same tree, so counts are noise and the set difference is the signal.
|
|
5999
6033
|
- \`toolchain: "unsupported"\` (build-test could not scope this repo \u2014 no npm package with a build/test script) \u2192 **install dependencies first** (build-test's own install only runs on the npm path, so nothing has installed yet: \`pip install -e .\`, \`mvn -q -DskipTests package\`'s own fetch, \`cargo fetch\`, \`go mod download\`, etc.), then fall back to **one** build and **one** test command by this precedence, each with a deadline it can meet: \`pom.xml\` \u2192 \`{mvn} compile\` / \`{mvn} test -q\`; \`build.gradle\` \u2192 \`{gradle} compileJava\` / \`{gradle} test\`; \`Makefile\` \u2192 \`make build\`; \`Cargo.toml\` \u2192 \`cargo build\` / \`cargo test\`; \`go.mod\` \u2192 \`go build ./...\` / \`go test ./...\`; \`pytest.ini\` or \`pyproject.toml\` \`[tool.pytest]\` \u2192 \`pytest\`. If none match, read the CI config **from the base branch** (\`git show <base>:<path>\`), never the worktree \u2014 the PR branch is untrusted and a modified workflow or Makefile could inject arbitrary commands.
|
|
6000
6034
|
|
|
@@ -6182,10 +6216,159 @@ The asymmetry cuts both ways: confirming also requires the trace, and a finding
|
|
|
6182
6216
|
- **Report only Critical or Suggestion.** Do not report Nice to have.
|
|
6183
6217
|
- A found gap uses the standard finding format (with \`Source: [review]\`), including its failure scenario \u2014 your findings go through the same verification as any other, so they must carry the evidence a verifier can trace.
|
|
6184
6218
|
|
|
6185
|
-
If you find no new gap in your scope, say so **and name what you re-examined** \u2014
|
|
6219
|
+
If you find no new gap in your scope, say so **and name what you re-examined** \u2014 \`${REVERSE_AUDIT_EXAMPLE_RECEIPT}\`. A bare "No issues found." is indistinguishable from an agent that did nothing, and it is treated as one: it ends nothing, and it earns your scope a relaunch.`
|
|
6186
6220
|
}
|
|
6187
6221
|
};
|
|
6188
6222
|
|
|
6223
|
+
// packages/cli/src/commands/review/lib/deadline.ts
|
|
6224
|
+
init_esbuild_shims();
|
|
6225
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6226
|
+
import { join as join8 } from "node:path";
|
|
6227
|
+
var DEADLINE_ENV = "QWEN_REVIEW_DEADLINE_EPOCH";
|
|
6228
|
+
var RESERVE_ENV = "QWEN_REVIEW_DEADLINE_RESERVE_SECONDS";
|
|
6229
|
+
var DEFAULT_RESERVE_SECONDS = 4800;
|
|
6230
|
+
var DEFAULT_ROUND_SECONDS = 1800;
|
|
6231
|
+
var MIN_OBSERVED_ROUND_SECONDS = 600;
|
|
6232
|
+
var STAMPS_FILE = "budget-rounds.json";
|
|
6233
|
+
var STOP_FILE = "budget-stop.json";
|
|
6234
|
+
var RUN_EPOCH_SLACK_MS = 2e3;
|
|
6235
|
+
function runEpochMs(planPath) {
|
|
6236
|
+
try {
|
|
6237
|
+
return statSync3(planPath).mtimeMs - RUN_EPOCH_SLACK_MS;
|
|
6238
|
+
} catch {
|
|
6239
|
+
return Number.NEGATIVE_INFINITY;
|
|
6240
|
+
}
|
|
6241
|
+
}
|
|
6242
|
+
__name(runEpochMs, "runEpochMs");
|
|
6243
|
+
function readRoundStamps(planPath) {
|
|
6244
|
+
try {
|
|
6245
|
+
const raw = readFileSync7(
|
|
6246
|
+
join8(promptRecordDir(planPath), STAMPS_FILE),
|
|
6247
|
+
"utf8"
|
|
6248
|
+
);
|
|
6249
|
+
const parsed = JSON.parse(raw);
|
|
6250
|
+
if (!Array.isArray(parsed)) return [];
|
|
6251
|
+
const epoch = runEpochMs(planPath);
|
|
6252
|
+
return parsed.filter(
|
|
6253
|
+
(e) => typeof e === "object" && e !== null && typeof e.atMs === "number" && e.atMs >= epoch
|
|
6254
|
+
);
|
|
6255
|
+
} catch {
|
|
6256
|
+
return [];
|
|
6257
|
+
}
|
|
6258
|
+
}
|
|
6259
|
+
__name(readRoundStamps, "readRoundStamps");
|
|
6260
|
+
function stampRound(planPath, round, nowMs = Date.now()) {
|
|
6261
|
+
try {
|
|
6262
|
+
const stamps = readRoundStamps(planPath);
|
|
6263
|
+
if (round !== void 0 && stamps.some((s) => s.round === round)) return;
|
|
6264
|
+
stamps.push({ round: round ?? null, atMs: nowMs });
|
|
6265
|
+
const dir = promptRecordDir(planPath);
|
|
6266
|
+
mkdirSync5(dir, { recursive: true });
|
|
6267
|
+
writeFileSync5(join8(dir, STAMPS_FILE), JSON.stringify(stamps));
|
|
6268
|
+
} catch {
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6271
|
+
__name(stampRound, "stampRound");
|
|
6272
|
+
function expectedRoundSeconds(planPath, round, nowMs = Date.now()) {
|
|
6273
|
+
const stamps = readRoundStamps(planPath).filter(
|
|
6274
|
+
(s) => round === void 0 || s.round !== round
|
|
6275
|
+
);
|
|
6276
|
+
if (stamps.length === 0) return DEFAULT_ROUND_SECONDS;
|
|
6277
|
+
let maxSeconds = 0;
|
|
6278
|
+
for (let i = 0; i < stamps.length; i++) {
|
|
6279
|
+
const end = i + 1 < stamps.length ? stamps[i + 1].atMs : nowMs;
|
|
6280
|
+
maxSeconds = Math.max(
|
|
6281
|
+
maxSeconds,
|
|
6282
|
+
Math.round((end - stamps[i].atMs) / 1e3)
|
|
6283
|
+
);
|
|
6284
|
+
}
|
|
6285
|
+
return Math.max(MIN_OBSERVED_ROUND_SECONDS, maxSeconds);
|
|
6286
|
+
}
|
|
6287
|
+
__name(expectedRoundSeconds, "expectedRoundSeconds");
|
|
6288
|
+
function reverseAuditBudgetExhausted(env, roundCostSeconds, nowMs = Date.now()) {
|
|
6289
|
+
const raw = env[DEADLINE_ENV];
|
|
6290
|
+
if (raw === void 0 || raw.trim() === "") return null;
|
|
6291
|
+
const deadline = Number(raw);
|
|
6292
|
+
if (!Number.isFinite(deadline) || deadline <= 0) return null;
|
|
6293
|
+
let reserve = DEFAULT_RESERVE_SECONDS;
|
|
6294
|
+
const reserveRaw = env[RESERVE_ENV];
|
|
6295
|
+
if (reserveRaw !== void 0 && reserveRaw.trim() !== "") {
|
|
6296
|
+
const r = Number(reserveRaw);
|
|
6297
|
+
if (Number.isFinite(r) && r >= 0) reserve = r;
|
|
6298
|
+
}
|
|
6299
|
+
const remainingSeconds = Math.floor(deadline - nowMs / 1e3);
|
|
6300
|
+
if (remainingSeconds >= reserve + roundCostSeconds) return null;
|
|
6301
|
+
return {
|
|
6302
|
+
remainingSeconds,
|
|
6303
|
+
reserveSeconds: reserve,
|
|
6304
|
+
expectedRoundSeconds: roundCostSeconds
|
|
6305
|
+
};
|
|
6306
|
+
}
|
|
6307
|
+
__name(reverseAuditBudgetExhausted, "reverseAuditBudgetExhausted");
|
|
6308
|
+
var BUDGET_STOP_PHRASE = "review time budget";
|
|
6309
|
+
function budgetStopDisclosure(round) {
|
|
6310
|
+
const which = round !== void 0 ? `round ${round}` : "the next round";
|
|
6311
|
+
const whichZh = round !== void 0 ? `\u7B2C ${round} \u8F6E` : "\u4E0B\u4E00\u8F6E";
|
|
6312
|
+
return {
|
|
6313
|
+
subject: "reverse audit",
|
|
6314
|
+
reason: `stopped before ${which} by the ${BUDGET_STOP_PHRASE}`,
|
|
6315
|
+
subjectZh: "\u53CD\u5411\u5BA1\u8BA1",
|
|
6316
|
+
reasonZh: `\u8BC4\u5BA1\u65F6\u95F4\u9884\u7B97\u4E0D\u8DB3\uFF0C\u672A\u80FD\u5F00\u59CB${whichZh}`
|
|
6317
|
+
};
|
|
6318
|
+
}
|
|
6319
|
+
__name(budgetStopDisclosure, "budgetStopDisclosure");
|
|
6320
|
+
function budgetStopEntry(round) {
|
|
6321
|
+
const d = budgetStopDisclosure(round);
|
|
6322
|
+
return `${d.subject} \u2014 ${d.reason}`;
|
|
6323
|
+
}
|
|
6324
|
+
__name(budgetStopEntry, "budgetStopEntry");
|
|
6325
|
+
function budgetStopEntryZh(round) {
|
|
6326
|
+
const d = budgetStopDisclosure(round);
|
|
6327
|
+
return `${d.subjectZh}\u2014\u2014${d.reasonZh}`;
|
|
6328
|
+
}
|
|
6329
|
+
__name(budgetStopEntryZh, "budgetStopEntryZh");
|
|
6330
|
+
function writeBudgetStop(planPath, spent, round, nowMs = Date.now()) {
|
|
6331
|
+
try {
|
|
6332
|
+
const dir = promptRecordDir(planPath);
|
|
6333
|
+
mkdirSync5(dir, { recursive: true });
|
|
6334
|
+
const stop = {
|
|
6335
|
+
entry: budgetStopEntry(round),
|
|
6336
|
+
entryZh: budgetStopEntryZh(round),
|
|
6337
|
+
round: round ?? null,
|
|
6338
|
+
remainingSeconds: spent.remainingSeconds,
|
|
6339
|
+
reserveSeconds: spent.reserveSeconds,
|
|
6340
|
+
atMs: nowMs
|
|
6341
|
+
};
|
|
6342
|
+
writeFileSync5(join8(dir, STOP_FILE), JSON.stringify(stop, null, 2));
|
|
6343
|
+
} catch {
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
__name(writeBudgetStop, "writeBudgetStop");
|
|
6347
|
+
function readBudgetStop(planPath) {
|
|
6348
|
+
try {
|
|
6349
|
+
const raw = readFileSync7(
|
|
6350
|
+
join8(promptRecordDir(planPath), STOP_FILE),
|
|
6351
|
+
"utf8"
|
|
6352
|
+
);
|
|
6353
|
+
const parsed = JSON.parse(raw);
|
|
6354
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.entry !== "string" || typeof parsed.atMs !== "number" || parsed.atMs < runEpochMs(planPath)) {
|
|
6355
|
+
return null;
|
|
6356
|
+
}
|
|
6357
|
+
return parsed;
|
|
6358
|
+
} catch {
|
|
6359
|
+
return null;
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
__name(readBudgetStop, "readBudgetStop");
|
|
6363
|
+
function reverseAuditBudgetMessage(spent, round) {
|
|
6364
|
+
const minutesLeft = Math.max(0, Math.floor(spent.remainingSeconds / 60));
|
|
6365
|
+
const reserveMinutes = Math.round(spent.reserveSeconds / 60);
|
|
6366
|
+
const roundMinutes = Math.round(spent.expectedRoundSeconds / 60);
|
|
6367
|
+
const which = round !== void 0 ? `round ${round}` : "the next round";
|
|
6368
|
+
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.`;
|
|
6369
|
+
}
|
|
6370
|
+
__name(reverseAuditBudgetMessage, "reverseAuditBudgetMessage");
|
|
6371
|
+
|
|
6189
6372
|
// packages/cli/src/commands/review/lib/shell-quote.ts
|
|
6190
6373
|
init_esbuild_shims();
|
|
6191
6374
|
function shellQuotePath(p) {
|
|
@@ -6195,7 +6378,7 @@ __name(shellQuotePath, "shellQuotePath");
|
|
|
6195
6378
|
|
|
6196
6379
|
// packages/cli/src/commands/review/lib/coverage.ts
|
|
6197
6380
|
function readPlan(path3) {
|
|
6198
|
-
const plan = JSON.parse(
|
|
6381
|
+
const plan = JSON.parse(readFileSync8(path3, "utf8"));
|
|
6199
6382
|
if (typeof plan?.diffPathAbsolute !== "string" || !plan.diffPathAbsolute) {
|
|
6200
6383
|
throw new Error(`coverage: ${path3} has no diffPathAbsolute`);
|
|
6201
6384
|
}
|
|
@@ -6206,7 +6389,7 @@ function readPlan(path3) {
|
|
|
6206
6389
|
if (problem) {
|
|
6207
6390
|
throw new Error(`coverage: ${path3} has ${problem}`);
|
|
6208
6391
|
}
|
|
6209
|
-
return { plan, mtimeMs:
|
|
6392
|
+
return { plan, mtimeMs: statSync4(path3).mtimeMs };
|
|
6210
6393
|
}
|
|
6211
6394
|
__name(readPlan, "readPlan");
|
|
6212
6395
|
var CHUNK_RE = /\bchunk\s+(\d+)\s+of\s+\d+\b/i;
|
|
@@ -6552,7 +6735,13 @@ function coverageFromTranscripts(planPath, env = process.env) {
|
|
|
6552
6735
|
};
|
|
6553
6736
|
}
|
|
6554
6737
|
__name(coverageFromTranscripts, "coverageFromTranscripts");
|
|
6555
|
-
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>]
|
|
6738
|
+
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
|
|
6739
|
+
// round-less call — the label keys the record and the budget gate's
|
|
6740
|
+
// accounting), so the paste-and-run repair must not bracket it as optional:
|
|
6741
|
+
// an orchestrator honouring the bracket convention would have its first
|
|
6742
|
+
// repair attempt rejected. Verify genuinely takes it or not (only a repeat
|
|
6743
|
+
// verification round passes one), so its brackets stay.
|
|
6744
|
+
(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
|
|
6556
6745
|
// parenthetical claiming --round bakes it in would send the reader to the
|
|
6557
6746
|
// wrong flag — shards are already told apart by their findings digest.
|
|
6558
6747
|
(role === "reverse-audit" ? ` (--round bakes it in)` : ``) + `, no summary of your own, no rewording`, "rebuildFix");
|
|
@@ -6667,7 +6856,9 @@ function verificationGaps(planPath, opts, env = process.env) {
|
|
|
6667
6856
|
(k) => k === "reverse-audit" || k.startsWith("reverse-audit--")
|
|
6668
6857
|
);
|
|
6669
6858
|
const reverse = bestDelivery(reverseKeys);
|
|
6670
|
-
const
|
|
6859
|
+
const budgetStopped = readBudgetStop(planPath) !== null;
|
|
6860
|
+
const reverseByDesign = budgetStopped && reverse === "not-built";
|
|
6861
|
+
const reverseGap = !balancedMedium && !reverseByDesign && reverse !== "ok";
|
|
6671
6862
|
if (reverseGap) {
|
|
6672
6863
|
remediation.push(
|
|
6673
6864
|
`reverse audit: ${REVERSE_AUDIT_GAP[reverse].fix.replace(
|
|
@@ -6970,8 +7161,27 @@ function unmarkedComments(comments) {
|
|
|
6970
7161
|
}
|
|
6971
7162
|
__name(unmarkedComments, "unmarkedComments");
|
|
6972
7163
|
|
|
7164
|
+
// packages/cli/src/commands/review/lib/review-footer.ts
|
|
7165
|
+
init_esbuild_shims();
|
|
7166
|
+
var FOOTER_MARKER = "via Qwen Code /review";
|
|
7167
|
+
function reviewFooter(modelId, cliVersion) {
|
|
7168
|
+
return `_\u2014 ${modelId} ${FOOTER_MARKER} (v${cliVersion})_`;
|
|
7169
|
+
}
|
|
7170
|
+
__name(reviewFooter, "reviewFooter");
|
|
7171
|
+
var REVIEW_FOOTER_RE = /\s*(?:_— (?:(?! via Qwen Code \/review)[^\n])* via Qwen Code \/review(?: \(v[^\n)]*\))?_?\s*)+$/;
|
|
7172
|
+
function isFooterSafeModelId(modelId) {
|
|
7173
|
+
return !/[\n\r]/.test(modelId) && !modelId.includes(FOOTER_MARKER);
|
|
7174
|
+
}
|
|
7175
|
+
__name(isFooterSafeModelId, "isFooterSafeModelId");
|
|
7176
|
+
var FOOTER_VERSION_RE = /^[A-Za-z0-9._+-]+$/;
|
|
7177
|
+
function footerVersion(stamp) {
|
|
7178
|
+
return stamp !== void 0 && FOOTER_VERSION_RE.test(stamp) ? stamp : void 0;
|
|
7179
|
+
}
|
|
7180
|
+
__name(footerVersion, "footerVersion");
|
|
7181
|
+
|
|
6973
7182
|
// packages/cli/src/commands/review/compose-review.ts
|
|
6974
7183
|
var LOW_SIGNAL_SRC_DIFF_LINES = 100;
|
|
7184
|
+
var UNVERIFIED_FINDING_TAG_RE = /—\s*\[unverified\]/gi;
|
|
6975
7185
|
function withMarker(line) {
|
|
6976
7186
|
return line.startsWith(CRITICAL_PREFIX) ? line : `${CRITICAL_PREFIX} ${line}`;
|
|
6977
7187
|
}
|
|
@@ -6996,6 +7206,10 @@ function toStringList(value, field) {
|
|
|
6996
7206
|
return [...value];
|
|
6997
7207
|
}
|
|
6998
7208
|
__name(toStringList, "toStringList");
|
|
7209
|
+
function stripReviewFooter(entry) {
|
|
7210
|
+
return entry.replace(REVIEW_FOOTER_RE, "");
|
|
7211
|
+
}
|
|
7212
|
+
__name(stripReviewFooter, "stripReviewFooter");
|
|
6999
7213
|
function toBool(value, field) {
|
|
7000
7214
|
if (value === void 0 || value === null) return false;
|
|
7001
7215
|
if (typeof value !== "boolean") {
|
|
@@ -7017,15 +7231,15 @@ __name(composeReview, "composeReview");
|
|
|
7017
7231
|
function ledgerMarkerFor(input) {
|
|
7018
7232
|
try {
|
|
7019
7233
|
if (!input.planPath) return null;
|
|
7020
|
-
const plan = JSON.parse(
|
|
7234
|
+
const plan = JSON.parse(readFileSync9(input.planPath, "utf8"));
|
|
7021
7235
|
const pr = plan?.prNumber;
|
|
7022
7236
|
const isPr = typeof pr === "number" && Number.isInteger(pr) && pr > 0 || typeof pr === "string" && /^\d+$/.test(pr);
|
|
7023
7237
|
if (!isPr) return null;
|
|
7024
7238
|
let prevRound = 0;
|
|
7025
7239
|
try {
|
|
7026
7240
|
const prev = JSON.parse(
|
|
7027
|
-
|
|
7028
|
-
|
|
7241
|
+
readFileSync9(
|
|
7242
|
+
join9(
|
|
7029
7243
|
dirname4(input.planPath),
|
|
7030
7244
|
`qwen-review-pr-${pr}-prev-ledger.json`
|
|
7031
7245
|
),
|
|
@@ -7040,7 +7254,7 @@ function ledgerMarkerFor(input) {
|
|
|
7040
7254
|
buildLedger(
|
|
7041
7255
|
prevRound + 1,
|
|
7042
7256
|
input.draftedComments ?? [],
|
|
7043
|
-
toStringList(input.bodyCriticals, "bodyCriticals")
|
|
7257
|
+
toStringList(input.bodyCriticals, "bodyCriticals").map(stripReviewFooter).filter((entry) => entry.trim() !== "")
|
|
7044
7258
|
)
|
|
7045
7259
|
);
|
|
7046
7260
|
} catch {
|
|
@@ -7054,7 +7268,7 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7054
7268
|
input.suggestionsInline,
|
|
7055
7269
|
"suggestionsInline"
|
|
7056
7270
|
);
|
|
7057
|
-
const bodyCriticals = toStringList(input.bodyCriticals, "bodyCriticals");
|
|
7271
|
+
const bodyCriticals = toStringList(input.bodyCriticals, "bodyCriticals").map(stripReviewFooter).filter((entry) => entry.trim() !== "");
|
|
7058
7272
|
const suggestionsDiscarded = toCount(
|
|
7059
7273
|
input.suggestionsDiscarded,
|
|
7060
7274
|
"suggestionsDiscarded"
|
|
@@ -7062,7 +7276,7 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7062
7276
|
const cannotTell = toStringList(
|
|
7063
7277
|
input.cannotTellCriticals,
|
|
7064
7278
|
"cannotTellCriticals"
|
|
7065
|
-
);
|
|
7279
|
+
).map(stripReviewFooter).filter((entry) => entry.trim() !== "");
|
|
7066
7280
|
const uncoverable = toStringList(
|
|
7067
7281
|
input.uncoverableChunks,
|
|
7068
7282
|
"uncoverableChunks"
|
|
@@ -7072,6 +7286,19 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7072
7286
|
"unreviewedDimensions"
|
|
7073
7287
|
);
|
|
7074
7288
|
const coverageEntries = [];
|
|
7289
|
+
let budgetEntry;
|
|
7290
|
+
if (input.planPath) {
|
|
7291
|
+
const stop = readBudgetStop(input.planPath);
|
|
7292
|
+
if (stop !== null) {
|
|
7293
|
+
for (let i = unreviewed.length - 1; i >= 0; i--) {
|
|
7294
|
+
if (unreviewed[i].includes(BUDGET_STOP_PHRASE)) {
|
|
7295
|
+
unreviewed.splice(i, 1);
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
budgetEntry = budgetStopDisclosure(stop.round ?? void 0);
|
|
7299
|
+
coverageEntries.push(budgetEntry);
|
|
7300
|
+
}
|
|
7301
|
+
}
|
|
7075
7302
|
const remediation = [];
|
|
7076
7303
|
const planRef = input.planPath ? shellQuotePath(input.planPath) : "<plan>";
|
|
7077
7304
|
const missingReceipts = [];
|
|
@@ -7202,6 +7429,33 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7202
7429
|
criticalsUnverified = criticalsNeedingVerify >= 1;
|
|
7203
7430
|
}
|
|
7204
7431
|
}
|
|
7432
|
+
let findingsUnverifiedAtCompose = false;
|
|
7433
|
+
let findingsFileUnreadable = false;
|
|
7434
|
+
let unverifiedTagCount = 0;
|
|
7435
|
+
const findingsPath = input.findingsPath;
|
|
7436
|
+
if (findingsPath !== void 0 && findingsPath !== null) {
|
|
7437
|
+
if (typeof findingsPath !== "string" || findingsPath.trim() === "") {
|
|
7438
|
+
throw new TypeError(
|
|
7439
|
+
`compose-review: findingsPath must be a non-empty string, got ${JSON.stringify(findingsPath)}`
|
|
7440
|
+
);
|
|
7441
|
+
}
|
|
7442
|
+
try {
|
|
7443
|
+
const findingsContent = readFileSync9(findingsPath, "utf8");
|
|
7444
|
+
unverifiedTagCount = (findingsContent.match(UNVERIFIED_FINDING_TAG_RE) ?? []).length;
|
|
7445
|
+
findingsUnverifiedAtCompose = unverifiedTagCount > 0;
|
|
7446
|
+
if (findingsUnverifiedAtCompose) {
|
|
7447
|
+
remediation.push(
|
|
7448
|
+
"findings still tagged `\u2014 [unverified]`: relaunch the verifier for each tagged entry (Step 4, `--role verify` with that entry), apply its verdict in the cumulative findings file, and run compose-review again with the updated file"
|
|
7449
|
+
);
|
|
7450
|
+
}
|
|
7451
|
+
} catch {
|
|
7452
|
+
findingsFileUnreadable = true;
|
|
7453
|
+
findingsUnverifiedAtCompose = true;
|
|
7454
|
+
remediation.push(
|
|
7455
|
+
"findings file not readable: pass the cumulative reverse-audit findings file \u2014 the one every round's `--findings` received \u2014 as `findingsPath` in the state JSON, and run compose-review again"
|
|
7456
|
+
);
|
|
7457
|
+
}
|
|
7458
|
+
}
|
|
7205
7459
|
const contextUnavailable = toBool(
|
|
7206
7460
|
input.contextUnavailable,
|
|
7207
7461
|
"contextUnavailable"
|
|
@@ -7231,6 +7485,11 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7231
7485
|
"compose-review: modelId is required (the public footer names the reviewing model)"
|
|
7232
7486
|
);
|
|
7233
7487
|
}
|
|
7488
|
+
if (!isFooterSafeModelId(modelId)) {
|
|
7489
|
+
throw new TypeError(
|
|
7490
|
+
"compose-review: modelId is interpolated into the public footer verbatim \u2014 it must be a single line that does not contain the footer marker"
|
|
7491
|
+
);
|
|
7492
|
+
}
|
|
7234
7493
|
const c = criticalsInline + bodyCriticals.length;
|
|
7235
7494
|
const s = suggestionsInline + suggestionsDiscarded;
|
|
7236
7495
|
const baseEvent = c >= 1 ? "REQUEST_CHANGES" : s >= 1 ? "COMMENT" : "APPROVE";
|
|
@@ -7243,10 +7502,13 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7243
7502
|
}
|
|
7244
7503
|
if (contextUnavailable) cappedBy.push("context-unavailable");
|
|
7245
7504
|
if (criticalsUnverified) cappedBy.push("criticals-unverified");
|
|
7505
|
+
if (findingsUnverifiedAtCompose) {
|
|
7506
|
+
cappedBy.push("findings-unverified-at-compose");
|
|
7507
|
+
}
|
|
7246
7508
|
let event2 = baseEvent;
|
|
7247
7509
|
if (event2 === "APPROVE" && cappedBy.length > 0) event2 = "COMMENT";
|
|
7248
7510
|
const deterministicBodyCriticals = bodyCriticals.length - nonDeterministicBodyCriticals;
|
|
7249
|
-
if (event2 === "REQUEST_CHANGES" && criticalsUnverified && deterministicBodyCriticals === 0) {
|
|
7511
|
+
if (event2 === "REQUEST_CHANGES" && (criticalsUnverified || findingsUnverifiedAtCompose && criticalsNeedingVerify >= 1) && deterministicBodyCriticals === 0) {
|
|
7250
7512
|
event2 = "COMMENT";
|
|
7251
7513
|
}
|
|
7252
7514
|
let downgraded = false;
|
|
@@ -7264,7 +7526,7 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7264
7526
|
if (event2 === "APPROVE" && input.planPath) {
|
|
7265
7527
|
try {
|
|
7266
7528
|
const plan = JSON.parse(
|
|
7267
|
-
|
|
7529
|
+
readFileSync9(input.planPath, "utf8")
|
|
7268
7530
|
);
|
|
7269
7531
|
const src = Number(plan.srcDiffLines ?? 0);
|
|
7270
7532
|
if (src > LOW_SIGNAL_SRC_DIFF_LINES) {
|
|
@@ -7273,7 +7535,7 @@ function composeReviewBody(input, cliVersion) {
|
|
|
7273
7535
|
} catch {
|
|
7274
7536
|
}
|
|
7275
7537
|
}
|
|
7276
|
-
const footer =
|
|
7538
|
+
const footer = reviewFooter(modelId, cliVersion);
|
|
7277
7539
|
const bilingual = bilingualFromPlan(input.planPath, input.prBodyFetcher);
|
|
7278
7540
|
const render = /* @__PURE__ */ __name((parts, sep5) => {
|
|
7279
7541
|
const en = parts.map((p) => p.en).join(sep5);
|
|
@@ -7332,7 +7594,7 @@ ${footer}`;
|
|
|
7332
7594
|
if (seenCaller.has(d)) continue;
|
|
7333
7595
|
seenCaller.add(d);
|
|
7334
7596
|
const echoesCoverage = covEntries.some(
|
|
7335
|
-
(e) => d === e.subject || d.startsWith(`${e.subject} \u2014 `)
|
|
7597
|
+
(e) => d === e.subject || e !== budgetEntry && d.startsWith(`${e.subject} \u2014 `)
|
|
7336
7598
|
);
|
|
7337
7599
|
if (!echoesCoverage) callerLeft.push(d);
|
|
7338
7600
|
}
|
|
@@ -7412,11 +7674,23 @@ ${footer}`;
|
|
|
7412
7674
|
zh: `Test Plan\uFF08\u975E\u963B\u65AD\uFF09\uFF1A${testPlanNotes.join("; ")}\u3002`
|
|
7413
7675
|
}
|
|
7414
7676
|
] : [];
|
|
7677
|
+
const unverifiedTagsBlock = !findingsUnverifiedAtCompose ? [] : findingsFileUnreadable ? [
|
|
7678
|
+
{
|
|
7679
|
+
en: "\u26A0\uFE0F The reverse-audit findings file could not be read at compose time, so this run cannot show its findings were verified.",
|
|
7680
|
+
zh: "\u26A0\uFE0F \u7EC4\u5408\u8BC4\u5BA1\u65F6\u65E0\u6CD5\u8BFB\u53D6\u53CD\u5411\u5BA1\u8BA1\u53D1\u73B0\u6587\u4EF6\uFF0C\u672C\u6B21\u8FD0\u884C\u65E0\u6CD5\u8BC1\u660E\u5176\u53D1\u73B0\u5DF2\u7ECF\u8FC7\u9A8C\u8BC1\u3002"
|
|
7681
|
+
}
|
|
7682
|
+
] : [
|
|
7683
|
+
{
|
|
7684
|
+
en: `\u26A0\uFE0F ${unverifiedTagCount} finding(s) still carried the \`\u2014 [unverified]\` tag when the loop ended \u2014 the verifier never ruled on them, and they are not confirmed.`,
|
|
7685
|
+
zh: `\u26A0\uFE0F \u5FAA\u73AF\u7ED3\u675F\u65F6\u4ECD\u6709 ${unverifiedTagCount} \u6761\u53D1\u73B0\u5E26\u7740 \`\u2014 [unverified]\` \u6807\u8BB0\u2014\u2014\u9A8C\u8BC1\u8005\u4ECE\u672A\u5BF9\u5B83\u4EEC\u4F5C\u51FA\u88C1\u51B3\uFF0C\u5B83\u4EEC\u4E0D\u7B97\u5DF2\u786E\u8BA4\u3002`
|
|
7686
|
+
}
|
|
7687
|
+
];
|
|
7415
7688
|
if (event2 === "REQUEST_CHANGES") {
|
|
7416
7689
|
const parts = [
|
|
7417
7690
|
...contextUnavailable ? [contextUnavailableClause] : [],
|
|
7418
7691
|
...cannotTellBlock,
|
|
7419
7692
|
...notReviewedParts,
|
|
7693
|
+
...unverifiedTagsBlock,
|
|
7420
7694
|
...deferredBlock,
|
|
7421
7695
|
...testPlanBlock,
|
|
7422
7696
|
...bodyCriticalBlock
|
|
@@ -7465,8 +7739,9 @@ ${footer}`;
|
|
|
7465
7739
|
} else {
|
|
7466
7740
|
const canCertify = !downgraded && !downgradeApprove && !downgradeRequestChanges && c === 0 && cannotTell.length === 0 && uncoverable.length === 0 && unreviewed.length + coverageEntries.length === 0 && // A missing receipt caps the event but was left out of certification, so a
|
|
7467
7741
|
// body could open "Reviewed — no blockers." two lines above "nobody read
|
|
7468
|
-
// them." Nothing nobody read can be certified blocker-free
|
|
7469
|
-
|
|
7742
|
+
// them." Nothing nobody read can be certified blocker-free — and neither
|
|
7743
|
+
// can a loop that ended with findings no verifier ever ruled on.
|
|
7744
|
+
missingReceipts.length === 0 && !findingsUnverifiedAtCompose;
|
|
7470
7745
|
const disclosedChunkIds = /* @__PURE__ */ new Set();
|
|
7471
7746
|
for (const e of coverageEntries) {
|
|
7472
7747
|
const m = /^chunk (\d+)$/.exec(e.subject);
|
|
@@ -7491,6 +7766,7 @@ ${footer}`;
|
|
|
7491
7766
|
}
|
|
7492
7767
|
clauses.push(...cannotTellBlock);
|
|
7493
7768
|
clauses.push(...notReviewedParts);
|
|
7769
|
+
clauses.push(...unverifiedTagsBlock);
|
|
7494
7770
|
clauses.push(...deferredBlock);
|
|
7495
7771
|
clauses.push(...testPlanBlock);
|
|
7496
7772
|
if (downgradedFrom === "Request changes" || criticalsUnverified) {
|
|
@@ -7556,7 +7832,7 @@ function scriptLintGate(planPath) {
|
|
|
7556
7832
|
const disclosed = [];
|
|
7557
7833
|
let plan;
|
|
7558
7834
|
try {
|
|
7559
|
-
plan = JSON.parse(
|
|
7835
|
+
plan = JSON.parse(readFileSync9(planPath, "utf8"));
|
|
7560
7836
|
} catch {
|
|
7561
7837
|
unreviewed.push(
|
|
7562
7838
|
"the executable-script lint \u2014 could not read the plan to check the gate"
|
|
@@ -7567,13 +7843,13 @@ function scriptLintGate(planPath) {
|
|
|
7567
7843
|
return { criticals, unreviewed, disclosed };
|
|
7568
7844
|
}
|
|
7569
7845
|
const owed = hasExecutableScript(plan);
|
|
7570
|
-
const reportPath =
|
|
7846
|
+
const reportPath = join9(
|
|
7571
7847
|
dirname4(planPath),
|
|
7572
7848
|
scriptLintReportName(plan.prNumber)
|
|
7573
7849
|
);
|
|
7574
7850
|
let report;
|
|
7575
7851
|
try {
|
|
7576
|
-
report = JSON.parse(
|
|
7852
|
+
report = JSON.parse(readFileSync9(reportPath, "utf8"));
|
|
7577
7853
|
} catch {
|
|
7578
7854
|
if (owed) {
|
|
7579
7855
|
unreviewed.push(
|
|
@@ -7629,7 +7905,7 @@ function testPlanGate(planPath) {
|
|
|
7629
7905
|
const notes = [];
|
|
7630
7906
|
let plan;
|
|
7631
7907
|
try {
|
|
7632
|
-
plan = JSON.parse(
|
|
7908
|
+
plan = JSON.parse(readFileSync9(planPath, "utf8"));
|
|
7633
7909
|
} catch {
|
|
7634
7910
|
return { notes };
|
|
7635
7911
|
}
|
|
@@ -7639,8 +7915,8 @@ function testPlanGate(planPath) {
|
|
|
7639
7915
|
let report;
|
|
7640
7916
|
try {
|
|
7641
7917
|
report = JSON.parse(
|
|
7642
|
-
|
|
7643
|
-
|
|
7918
|
+
readFileSync9(
|
|
7919
|
+
join9(dirname4(planPath), `qwen-review-pr-${pr}-test-plan.json`),
|
|
7644
7920
|
"utf8"
|
|
7645
7921
|
)
|
|
7646
7922
|
);
|
|
@@ -7673,7 +7949,7 @@ function bilingualFromPlan(planPath, fetchPrBody2 = fetchPrBodyViaGh) {
|
|
|
7673
7949
|
if (!planPath) return false;
|
|
7674
7950
|
let plan;
|
|
7675
7951
|
try {
|
|
7676
|
-
plan = JSON.parse(
|
|
7952
|
+
plan = JSON.parse(readFileSync9(planPath, "utf8"));
|
|
7677
7953
|
} catch {
|
|
7678
7954
|
return false;
|
|
7679
7955
|
}
|
|
@@ -7693,7 +7969,7 @@ __name(bilingualFromPlan, "bilingualFromPlan");
|
|
|
7693
7969
|
function readDraftedComments(path3) {
|
|
7694
7970
|
let raw;
|
|
7695
7971
|
try {
|
|
7696
|
-
raw =
|
|
7972
|
+
raw = readFileSync9(path3, "utf8");
|
|
7697
7973
|
} catch (err) {
|
|
7698
7974
|
throw new Error(
|
|
7699
7975
|
`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.`
|
|
@@ -7747,7 +8023,7 @@ var composeReviewCommand = {
|
|
|
7747
8023
|
"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."
|
|
7748
8024
|
);
|
|
7749
8025
|
}
|
|
7750
|
-
const raw =
|
|
8026
|
+
const raw = readFileSync9(input ?? 0, "utf8");
|
|
7751
8027
|
const parsed = JSON.parse(raw);
|
|
7752
8028
|
delete parsed.env;
|
|
7753
8029
|
delete parsed.prBodyFetcher;
|
|
@@ -7764,7 +8040,9 @@ var composeReviewCommand = {
|
|
|
7764
8040
|
...countInlineFindings(drafted),
|
|
7765
8041
|
draftedComments: drafted
|
|
7766
8042
|
},
|
|
7767
|
-
|
|
8043
|
+
// Same pin as `submit`: the startup stamp, not a version resolved at
|
|
8044
|
+
// compose time — a shared runner can rewrite the install mid-session.
|
|
8045
|
+
footerVersion(process.env["QWEN_CODE_STARTUP_VERSION"]) ?? await getCliVersion()
|
|
7768
8046
|
);
|
|
7769
8047
|
const json = JSON.stringify(
|
|
7770
8048
|
{ ...result, verdictLine: verdictLine(result) },
|
|
@@ -7772,8 +8050,8 @@ var composeReviewCommand = {
|
|
|
7772
8050
|
2
|
|
7773
8051
|
);
|
|
7774
8052
|
if (out) {
|
|
7775
|
-
|
|
7776
|
-
|
|
8053
|
+
mkdirSync6(dirname4(out), { recursive: true });
|
|
8054
|
+
writeFileSync6(out, json, "utf8");
|
|
7777
8055
|
}
|
|
7778
8056
|
writeStdoutLine(json);
|
|
7779
8057
|
for (const fix of result.remediation) {
|
|
@@ -7851,11 +8129,12 @@ function verdictLine(r) {
|
|
|
7851
8129
|
"chunk-nobody-read": "part of the diff was never read",
|
|
7852
8130
|
"uncoverable-chunk": "part of the diff cannot be read at all",
|
|
7853
8131
|
"unreviewed-dimension": "a dimension nobody reviewed",
|
|
7854
|
-
"context-unavailable": "the PR's existing discussion could not be read"
|
|
8132
|
+
"context-unavailable": "the PR's existing discussion could not be read",
|
|
8133
|
+
"findings-unverified-at-compose": "findings were still unverified when the loop ended"
|
|
7855
8134
|
};
|
|
7856
8135
|
let line = `Verdict: ${label2[r.event]}`;
|
|
7857
|
-
if (r.baseEvent === "REQUEST_CHANGES" && r.event === "COMMENT" && r.cappedBy.includes("criticals-unverified")) {
|
|
7858
|
-
line += " \u2014 a Request changes was NOT available: its blockers were never verified (they are posted, disclosed as unverified)";
|
|
8136
|
+
if (r.baseEvent === "REQUEST_CHANGES" && r.event === "COMMENT" && (r.cappedBy.includes("criticals-unverified") || r.cappedBy.includes("findings-unverified-at-compose"))) {
|
|
8137
|
+
line += " \u2014 a Request changes was NOT available: " + (r.cappedBy.includes("criticals-unverified") ? "its blockers were never verified (they are posted, disclosed as unverified)" : "findings were still unverified when the loop ended (they are posted, disclosed)");
|
|
7859
8138
|
} else if (r.baseEvent === "APPROVE" && r.event !== "APPROVE") {
|
|
7860
8139
|
const reasons = r.cappedBy.map((c) => why[c] ?? c);
|
|
7861
8140
|
if (r.downgraded) reasons.push("a presubmit check failed");
|
|
@@ -7874,7 +8153,7 @@ __name(verdictLine, "verdictLine");
|
|
|
7874
8153
|
|
|
7875
8154
|
// packages/cli/src/commands/review/findings.ts
|
|
7876
8155
|
init_esbuild_shims();
|
|
7877
|
-
import { existsSync as existsSync3, readFileSync as
|
|
8156
|
+
import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
|
|
7878
8157
|
import { dirname as dirname5, resolve as resolve5 } from "node:path";
|
|
7879
8158
|
var SEVERITIES = ["Critical", "Suggestion", "Nice to have"];
|
|
7880
8159
|
var CONFIDENCES = ["high", "low"];
|
|
@@ -8248,7 +8527,7 @@ __name(renderFindings, "renderFindings");
|
|
|
8248
8527
|
function readJson(path3, what) {
|
|
8249
8528
|
let text;
|
|
8250
8529
|
try {
|
|
8251
|
-
text =
|
|
8530
|
+
text = readFileSync10(resolve5(path3), "utf8");
|
|
8252
8531
|
} catch (err) {
|
|
8253
8532
|
throw new Error(
|
|
8254
8533
|
`Could not read the ${what} file at ${JSON.stringify(path3)}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8319,8 +8598,8 @@ var findingsCommand = {
|
|
|
8319
8598
|
}
|
|
8320
8599
|
const report = buildReport(findings);
|
|
8321
8600
|
const target = resolve5(out);
|
|
8322
|
-
|
|
8323
|
-
|
|
8601
|
+
mkdirSync7(dirname5(target), { recursive: true });
|
|
8602
|
+
writeFileSync7(target, `${JSON.stringify(report, null, 2)}
|
|
8324
8603
|
`, "utf8");
|
|
8325
8604
|
const { bySeverity, byConfidence } = report.counts;
|
|
8326
8605
|
writeStderrLine(
|
|
@@ -8351,7 +8630,7 @@ var findingsCommand = {
|
|
|
8351
8630
|
// packages/cli/src/commands/review/fetch-pr.ts
|
|
8352
8631
|
init_esbuild_shims();
|
|
8353
8632
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
8354
|
-
import { mkdirSync as
|
|
8633
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
8355
8634
|
import { dirname as dirname6, resolve as resolve6 } from "node:path";
|
|
8356
8635
|
|
|
8357
8636
|
// packages/cli/src/commands/review/lib/git.ts
|
|
@@ -8459,11 +8738,11 @@ var LITERAL_PATHSPECS = "--literal-pathspecs";
|
|
|
8459
8738
|
|
|
8460
8739
|
// packages/cli/src/commands/review/lib/effort.ts
|
|
8461
8740
|
init_esbuild_shims();
|
|
8462
|
-
import { readFileSync as
|
|
8741
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
8463
8742
|
function resolveEffort(explicit) {
|
|
8464
8743
|
if (explicit && EFFORT_LEVELS.has(explicit)) return explicit;
|
|
8465
8744
|
try {
|
|
8466
|
-
const parsed = JSON.parse(
|
|
8745
|
+
const parsed = JSON.parse(readFileSync11(PARSE_ARGS_REPORT, "utf8"));
|
|
8467
8746
|
if (typeof parsed.effort === "string" && EFFORT_LEVELS.has(parsed.effort)) {
|
|
8468
8747
|
return parsed.effort;
|
|
8469
8748
|
}
|
|
@@ -8484,7 +8763,7 @@ __name(planEffortField, "planEffortField");
|
|
|
8484
8763
|
|
|
8485
8764
|
// packages/cli/src/commands/review/lib/report.ts
|
|
8486
8765
|
init_esbuild_shims();
|
|
8487
|
-
import { statSync as
|
|
8766
|
+
import { statSync as statSync5 } from "node:fs";
|
|
8488
8767
|
|
|
8489
8768
|
// packages/cli/src/commands/review/lib/heavy.ts
|
|
8490
8769
|
init_esbuild_shims();
|
|
@@ -8587,7 +8866,7 @@ __name(buildPlanReport, "buildPlanReport");
|
|
|
8587
8866
|
function warnOnReportSize(path3, cap) {
|
|
8588
8867
|
let size = 0;
|
|
8589
8868
|
try {
|
|
8590
|
-
size =
|
|
8869
|
+
size = statSync5(path3).size;
|
|
8591
8870
|
} catch {
|
|
8592
8871
|
return;
|
|
8593
8872
|
}
|
|
@@ -8710,7 +8989,7 @@ async function runFetchPr(args) {
|
|
|
8710
8989
|
);
|
|
8711
8990
|
}
|
|
8712
8991
|
try {
|
|
8713
|
-
|
|
8992
|
+
mkdirSync8(dirname6(wt), { recursive: true });
|
|
8714
8993
|
git("worktree", "add", wt, ref);
|
|
8715
8994
|
} catch (err) {
|
|
8716
8995
|
tryRemove(
|
|
@@ -8720,7 +8999,7 @@ async function runFetchPr(args) {
|
|
|
8720
8999
|
`Failed to create worktree at ${wt}: ${err.message}`
|
|
8721
9000
|
);
|
|
8722
9001
|
}
|
|
8723
|
-
|
|
9002
|
+
mkdirSync8(REVIEW_TMP_DIR, { recursive: true });
|
|
8724
9003
|
const { sha: mergeBaseSha, baseFetchFailed } = resolveMergeBase(
|
|
8725
9004
|
remote,
|
|
8726
9005
|
meta.baseRefName,
|
|
@@ -8744,7 +9023,7 @@ async function runFetchPr(args) {
|
|
|
8744
9023
|
...PINNED_DIFF_FLAGS,
|
|
8745
9024
|
`${mergeBaseSha}..${fetchedSha}`
|
|
8746
9025
|
);
|
|
8747
|
-
|
|
9026
|
+
writeFileSync8(diffRel, buf);
|
|
8748
9027
|
diffText = buf.toString("utf8");
|
|
8749
9028
|
diffPath = diffRel;
|
|
8750
9029
|
diffPathAbsolute = resolve6(diffRel);
|
|
@@ -8771,7 +9050,7 @@ async function runFetchPr(args) {
|
|
|
8771
9050
|
let auditSince = fetchedAt;
|
|
8772
9051
|
let prevRaw = null;
|
|
8773
9052
|
try {
|
|
8774
|
-
prevRaw =
|
|
9053
|
+
prevRaw = readFileSync12(out, "utf8");
|
|
8775
9054
|
} catch (err) {
|
|
8776
9055
|
const code = err.code;
|
|
8777
9056
|
if (code !== "ENOENT") {
|
|
@@ -8854,7 +9133,7 @@ async function runFetchPr(args) {
|
|
|
8854
9133
|
...buildPlanReport(plan, (path3) => fileLineCount(fetchedSha, path3)),
|
|
8855
9134
|
...planEffortField(args.effort)
|
|
8856
9135
|
};
|
|
8857
|
-
|
|
9136
|
+
writeFileSync8(out, stringifyPlanReport(result), "utf8");
|
|
8858
9137
|
writeStdoutLine(`Wrote fetch-pr report to ${out}`);
|
|
8859
9138
|
if (diffPath) writeStdoutLine(`Wrote review diff to ${diffPath}`);
|
|
8860
9139
|
writeStderrLine(
|
|
@@ -8938,13 +9217,13 @@ var fetchPrCommand = {
|
|
|
8938
9217
|
|
|
8939
9218
|
// packages/cli/src/commands/review/capture-local.ts
|
|
8940
9219
|
init_esbuild_shims();
|
|
8941
|
-
import { mkdirSync as
|
|
9220
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "node:fs";
|
|
8942
9221
|
import { dirname as dirname7, resolve as resolve8 } from "node:path";
|
|
8943
9222
|
|
|
8944
9223
|
// packages/cli/src/commands/review/lib/local-diff.ts
|
|
8945
9224
|
init_esbuild_shims();
|
|
8946
|
-
import { lstatSync as lstatSync2, statSync as
|
|
8947
|
-
import { join as
|
|
9225
|
+
import { lstatSync as lstatSync2, statSync as statSync6, realpathSync as realpathSync2 } from "node:fs";
|
|
9226
|
+
import { join as join10, relative as relative2, resolve as resolve7, isAbsolute as isAbsolute2, sep as sep2 } from "node:path";
|
|
8948
9227
|
var MAX_UNTRACKED_BYTES = 1e6;
|
|
8949
9228
|
var MAX_UNTRACKED_FILES = 500;
|
|
8950
9229
|
var MAX_UNTRACKED_TOTAL_BYTES = 1e7;
|
|
@@ -8965,7 +9244,7 @@ function describeUndiffable(abs, st) {
|
|
|
8965
9244
|
}
|
|
8966
9245
|
if (st.isSymbolicLink()) {
|
|
8967
9246
|
try {
|
|
8968
|
-
return
|
|
9247
|
+
return statSync6(abs).isDirectory() ? "is a symlink to a directory \u2014 git cannot diff it as a file" : null;
|
|
8969
9248
|
} catch {
|
|
8970
9249
|
return null;
|
|
8971
9250
|
}
|
|
@@ -9070,7 +9349,7 @@ function captureLocalDiff(opts) {
|
|
|
9070
9349
|
for (const path3 of candidates) {
|
|
9071
9350
|
let bytes;
|
|
9072
9351
|
try {
|
|
9073
|
-
const abs =
|
|
9352
|
+
const abs = join10(repoRoot, path3);
|
|
9074
9353
|
const st = lstatSync2(abs);
|
|
9075
9354
|
const kind = describeUndiffable(abs, st);
|
|
9076
9355
|
if (kind) {
|
|
@@ -9162,10 +9441,10 @@ function runCaptureLocal(args) {
|
|
|
9162
9441
|
includeUntracked: args.untracked
|
|
9163
9442
|
});
|
|
9164
9443
|
const diffText = capture.diff.toString("utf8");
|
|
9165
|
-
|
|
9166
|
-
|
|
9444
|
+
mkdirSync9(REVIEW_TMP_DIR, { recursive: true });
|
|
9445
|
+
mkdirSync9(dirname7(resolve8(out)), { recursive: true });
|
|
9167
9446
|
const diffPath = tmpFile(target, "diff.txt");
|
|
9168
|
-
|
|
9447
|
+
writeFileSync9(diffPath, capture.diff);
|
|
9169
9448
|
const plan = buildDiffPlan(diffText);
|
|
9170
9449
|
const result = {
|
|
9171
9450
|
diffPath,
|
|
@@ -9178,7 +9457,7 @@ function runCaptureLocal(args) {
|
|
|
9178
9457
|
skippedFiles: capture.skipped,
|
|
9179
9458
|
...planEffortField(args.effort)
|
|
9180
9459
|
};
|
|
9181
|
-
|
|
9460
|
+
writeFileSync9(out, stringifyPlanReport(result), "utf8");
|
|
9182
9461
|
writeStdoutLine(`Wrote diff to ${diffPath} and plan to ${out}`);
|
|
9183
9462
|
if (capture.unbornHead) {
|
|
9184
9463
|
writeStderrLine(
|
|
@@ -9236,13 +9515,13 @@ var captureLocalCommand = {
|
|
|
9236
9515
|
|
|
9237
9516
|
// packages/cli/src/commands/review/plan-diff.ts
|
|
9238
9517
|
init_esbuild_shims();
|
|
9239
|
-
import { readFileSync as
|
|
9518
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10 } from "node:fs";
|
|
9240
9519
|
import { resolve as resolve9 } from "node:path";
|
|
9241
9520
|
function runPlanDiff(args) {
|
|
9242
9521
|
const { diff_path: diffPath, out } = args;
|
|
9243
9522
|
let diffText;
|
|
9244
9523
|
try {
|
|
9245
|
-
diffText =
|
|
9524
|
+
diffText = readFileSync13(diffPath, "utf8");
|
|
9246
9525
|
} catch (err) {
|
|
9247
9526
|
throw new Error(
|
|
9248
9527
|
`Cannot read diff file ${diffPath}: ${err.message}`
|
|
@@ -9269,8 +9548,8 @@ function runPlanDiff(args) {
|
|
|
9269
9548
|
...buildPlanReport(plan, null),
|
|
9270
9549
|
...planEffortField(args.effort)
|
|
9271
9550
|
};
|
|
9272
|
-
|
|
9273
|
-
|
|
9551
|
+
mkdirSync10(REVIEW_TMP_DIR, { recursive: true });
|
|
9552
|
+
writeFileSync10(out, stringifyPlanReport(result), "utf8");
|
|
9274
9553
|
writeStdoutLine(`Wrote diff plan to ${out}`);
|
|
9275
9554
|
if (plan.diffLines === 0) {
|
|
9276
9555
|
writeStderrLine(
|
|
@@ -9316,8 +9595,8 @@ var planDiffCommand = {
|
|
|
9316
9595
|
|
|
9317
9596
|
// packages/cli/src/commands/review/pr-context.ts
|
|
9318
9597
|
init_esbuild_shims();
|
|
9319
|
-
import { mkdirSync as
|
|
9320
|
-
import { dirname as dirname8, join as
|
|
9598
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
9599
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
9321
9600
|
var SUMMARY_MARKER = "<!-- qwen-review-suggestion-summary -->";
|
|
9322
9601
|
function isLegacySuggestionSummary(body) {
|
|
9323
9602
|
return (body ?? "").includes(SUMMARY_MARKER);
|
|
@@ -9511,9 +9790,9 @@ function blockerSection(roots, issueBlockers, repliesByRoot, ctx) {
|
|
|
9511
9790
|
""
|
|
9512
9791
|
];
|
|
9513
9792
|
let spent = out.join("\n").length;
|
|
9514
|
-
const charge = /* @__PURE__ */ __name((
|
|
9515
|
-
spent +=
|
|
9516
|
-
return
|
|
9793
|
+
const charge = /* @__PURE__ */ __name((lines) => {
|
|
9794
|
+
spent += lines.join("\n").length;
|
|
9795
|
+
return lines;
|
|
9517
9796
|
}, "charge");
|
|
9518
9797
|
const refsLine = /* @__PURE__ */ __name((body) => {
|
|
9519
9798
|
const refs = extractCodeRefs(body);
|
|
@@ -9795,9 +10074,9 @@ async function runPrContext(args) {
|
|
|
9795
10074
|
}
|
|
9796
10075
|
if (prevLedger) {
|
|
9797
10076
|
try {
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
10077
|
+
mkdirSync11(dirname8(out), { recursive: true });
|
|
10078
|
+
writeFileSync11(
|
|
10079
|
+
join11(dirname8(out), `qwen-review-pr-${prNumber}-prev-ledger.json`),
|
|
9801
10080
|
JSON.stringify(prevLedger, null, 2)
|
|
9802
10081
|
);
|
|
9803
10082
|
} catch {
|
|
@@ -9812,8 +10091,8 @@ async function runPrContext(args) {
|
|
|
9812
10091
|
reviews,
|
|
9813
10092
|
prevLedger
|
|
9814
10093
|
);
|
|
9815
|
-
|
|
9816
|
-
|
|
10094
|
+
mkdirSync11(dirname8(out), { recursive: true });
|
|
10095
|
+
writeFileSync11(out, md, "utf8");
|
|
9817
10096
|
const meaningfulReviewCount = reviews.filter(
|
|
9818
10097
|
(r) => isReviewWorthShowing(stripLedgerMarker(r.body ?? ""))
|
|
9819
10098
|
).length;
|
|
@@ -9867,7 +10146,7 @@ var prContextCommand = {
|
|
|
9867
10146
|
|
|
9868
10147
|
// packages/cli/src/commands/review/comment-status.ts
|
|
9869
10148
|
init_esbuild_shims();
|
|
9870
|
-
import { mkdirSync as
|
|
10149
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "node:fs";
|
|
9871
10150
|
import { dirname as dirname9 } from "node:path";
|
|
9872
10151
|
var TOUCHED_BY_CAP = 10;
|
|
9873
10152
|
function buildThreadStatuses(comments, prAuthor, probe) {
|
|
@@ -10073,9 +10352,9 @@ async function runCommentStatus(args) {
|
|
|
10073
10352
|
summary,
|
|
10074
10353
|
threads
|
|
10075
10354
|
};
|
|
10076
|
-
|
|
10355
|
+
mkdirSync12(dirname9(out), { recursive: true });
|
|
10077
10356
|
const json = JSON.stringify(report, null, 2) + "\n";
|
|
10078
|
-
|
|
10357
|
+
writeFileSync12(out, json, "utf8");
|
|
10079
10358
|
writeStdoutLine(
|
|
10080
10359
|
`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)`
|
|
10081
10360
|
);
|
|
@@ -10103,8 +10382,8 @@ async function runCommentStatus(args) {
|
|
|
10103
10382
|
writeStdoutLine(
|
|
10104
10383
|
`warning: comment-status failed: ${msg}. It is an index, not the evidence \u2014 re-derive thread statuses per-comment if needed.`
|
|
10105
10384
|
);
|
|
10106
|
-
|
|
10107
|
-
|
|
10385
|
+
mkdirSync12(dirname9(out), { recursive: true });
|
|
10386
|
+
writeFileSync12(
|
|
10108
10387
|
out,
|
|
10109
10388
|
JSON.stringify(
|
|
10110
10389
|
{
|
|
@@ -10166,18 +10445,18 @@ var commentStatusCommand = {
|
|
|
10166
10445
|
|
|
10167
10446
|
// packages/cli/src/commands/review/load-rules.ts
|
|
10168
10447
|
init_esbuild_shims();
|
|
10169
|
-
import { mkdirSync as
|
|
10448
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "node:fs";
|
|
10170
10449
|
import { dirname as dirname10 } from "node:path";
|
|
10171
10450
|
function showFile(baseRef, path3) {
|
|
10172
10451
|
return gitOpt("show", `${baseRef}:${path3}`);
|
|
10173
10452
|
}
|
|
10174
10453
|
__name(showFile, "showFile");
|
|
10175
10454
|
function extractCodeReviewSection(content) {
|
|
10176
|
-
const
|
|
10455
|
+
const lines = content.split("\n");
|
|
10177
10456
|
let start = -1;
|
|
10178
|
-
let end =
|
|
10179
|
-
for (let i = 0; i <
|
|
10180
|
-
const line =
|
|
10457
|
+
let end = lines.length;
|
|
10458
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10459
|
+
const line = lines[i];
|
|
10181
10460
|
if (start < 0) {
|
|
10182
10461
|
if (/^## Code Review\s*$/i.test(line)) start = i;
|
|
10183
10462
|
} else if (/^## /.test(line)) {
|
|
@@ -10186,7 +10465,7 @@ function extractCodeReviewSection(content) {
|
|
|
10186
10465
|
}
|
|
10187
10466
|
}
|
|
10188
10467
|
if (start < 0) return null;
|
|
10189
|
-
return
|
|
10468
|
+
return lines.slice(start, end).join("\n").trim();
|
|
10190
10469
|
}
|
|
10191
10470
|
__name(extractCodeReviewSection, "extractCodeReviewSection");
|
|
10192
10471
|
function loadCombined(baseRef) {
|
|
@@ -10247,8 +10526,8 @@ __name(loadCombined, "loadCombined");
|
|
|
10247
10526
|
async function runLoadRules(args) {
|
|
10248
10527
|
const { base_ref: baseRef, out } = args;
|
|
10249
10528
|
const { combined, loaded } = loadCombined(baseRef);
|
|
10250
|
-
|
|
10251
|
-
|
|
10529
|
+
mkdirSync13(dirname10(out), { recursive: true });
|
|
10530
|
+
writeFileSync13(out, combined, "utf8");
|
|
10252
10531
|
if (loaded.length === 0) {
|
|
10253
10532
|
writeStdoutLine(
|
|
10254
10533
|
`No review rules found on ${baseRef}; wrote empty file to ${out}`
|
|
@@ -10279,7 +10558,7 @@ var loadRulesCommand = {
|
|
|
10279
10558
|
|
|
10280
10559
|
// packages/cli/src/commands/review/presubmit.ts
|
|
10281
10560
|
init_esbuild_shims();
|
|
10282
|
-
import { writeFileSync as
|
|
10561
|
+
import { writeFileSync as writeFileSync14, readFileSync as readFileSync14 } from "node:fs";
|
|
10283
10562
|
function verdictStamp(run4) {
|
|
10284
10563
|
return run4.completed_at ?? run4.started_at ?? "";
|
|
10285
10564
|
}
|
|
@@ -10315,7 +10594,7 @@ __name(isCurrentActionsRunCheck, "isCurrentActionsRunCheck");
|
|
|
10315
10594
|
function parseFindingsFile(path3) {
|
|
10316
10595
|
let parsed;
|
|
10317
10596
|
try {
|
|
10318
|
-
parsed = JSON.parse(
|
|
10597
|
+
parsed = JSON.parse(readFileSync14(path3, "utf8"));
|
|
10319
10598
|
} catch {
|
|
10320
10599
|
return null;
|
|
10321
10600
|
}
|
|
@@ -10618,7 +10897,7 @@ async function runPresubmit(args) {
|
|
|
10618
10897
|
findingsFileInvalid,
|
|
10619
10898
|
headDrift
|
|
10620
10899
|
};
|
|
10621
|
-
|
|
10900
|
+
writeFileSync14(outPath, JSON.stringify(result, null, 2) + "\n", "utf8");
|
|
10622
10901
|
writeStdoutLine(`Wrote presubmit report to ${outPath}`);
|
|
10623
10902
|
}
|
|
10624
10903
|
__name(runPresubmit, "runPresubmit");
|
|
@@ -10657,17 +10936,17 @@ var presubmitCommand = {
|
|
|
10657
10936
|
// packages/cli/src/commands/review/publish-assets.ts
|
|
10658
10937
|
init_esbuild_shims();
|
|
10659
10938
|
import { createHash as createHash2 } from "node:crypto";
|
|
10660
|
-
import { readFileSync as
|
|
10939
|
+
import { readFileSync as readFileSync16, statSync as statSync7, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15 } from "node:fs";
|
|
10661
10940
|
import { basename as basename5, dirname as dirname11, resolve as resolve10 } from "node:path";
|
|
10662
10941
|
|
|
10663
10942
|
// packages/cli/src/commands/review/lib/authorization.ts
|
|
10664
10943
|
init_esbuild_shims();
|
|
10665
|
-
import { readFileSync as
|
|
10944
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
10666
10945
|
|
|
10667
10946
|
// packages/cli/src/services/skill-args-file.ts
|
|
10668
10947
|
init_esbuild_shims();
|
|
10669
10948
|
import {
|
|
10670
|
-
mkdirSync as
|
|
10949
|
+
mkdirSync as mkdirSync14,
|
|
10671
10950
|
openSync as openSync2,
|
|
10672
10951
|
writeSync,
|
|
10673
10952
|
closeSync as closeSync2,
|
|
@@ -10676,9 +10955,9 @@ import {
|
|
|
10676
10955
|
existsSync as existsSync5,
|
|
10677
10956
|
constants
|
|
10678
10957
|
} from "node:fs";
|
|
10679
|
-
import { join as
|
|
10958
|
+
import { join as join12 } from "node:path";
|
|
10680
10959
|
var debugLogger3 = createDebugLogger("SKILL_ARGS_FILE");
|
|
10681
|
-
var SKILL_ARGS_DIR =
|
|
10960
|
+
var SKILL_ARGS_DIR = join12(".qwen", "tmp");
|
|
10682
10961
|
function safe(s) {
|
|
10683
10962
|
return s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
10684
10963
|
}
|
|
@@ -10688,11 +10967,11 @@ function currentSessionId() {
|
|
|
10688
10967
|
}
|
|
10689
10968
|
__name(currentSessionId, "currentSessionId");
|
|
10690
10969
|
function skillArgsDir(sessionId = currentSessionId()) {
|
|
10691
|
-
return sessionId ?
|
|
10970
|
+
return sessionId ? join12(SKILL_ARGS_DIR, `s-${safe(sessionId)}`) : SKILL_ARGS_DIR;
|
|
10692
10971
|
}
|
|
10693
10972
|
__name(skillArgsDir, "skillArgsDir");
|
|
10694
10973
|
function skillArgsPath(skillName, sessionId = currentSessionId()) {
|
|
10695
|
-
return
|
|
10974
|
+
return join12(
|
|
10696
10975
|
skillArgsDir(sessionId),
|
|
10697
10976
|
`qwen-skill-args-${safe(skillName)}.txt`
|
|
10698
10977
|
);
|
|
@@ -10702,7 +10981,7 @@ function writeSkillArgs(skillName, args) {
|
|
|
10702
10981
|
const path3 = skillArgsPath(skillName);
|
|
10703
10982
|
try {
|
|
10704
10983
|
const dir = skillArgsDir();
|
|
10705
|
-
|
|
10984
|
+
mkdirSync14(dir, { recursive: true });
|
|
10706
10985
|
if (lstatSync3(dir).isSymbolicLink()) {
|
|
10707
10986
|
debugLogger3.warn(
|
|
10708
10987
|
`Skill args directory ${dir} is a symlink; refusing to write through it.`
|
|
@@ -10785,7 +11064,7 @@ function reviewWriteAuthorization(req) {
|
|
|
10785
11064
|
const path3 = currentSessionId() === "" && req.skillArgs ? req.skillArgs : sessionScoped;
|
|
10786
11065
|
let raw;
|
|
10787
11066
|
try {
|
|
10788
|
-
raw =
|
|
11067
|
+
raw = readFileSync15(path3, "utf8");
|
|
10789
11068
|
} catch {
|
|
10790
11069
|
return {
|
|
10791
11070
|
ok: false,
|
|
@@ -10840,7 +11119,64 @@ __name(reviewWriteAuthorization, "reviewWriteAuthorization");
|
|
|
10840
11119
|
|
|
10841
11120
|
// packages/cli/src/commands/review/lib/assets.ts
|
|
10842
11121
|
init_esbuild_shims();
|
|
10843
|
-
var
|
|
11122
|
+
var EXTENSION_FORMAT = {
|
|
11123
|
+
png: "png",
|
|
11124
|
+
jpg: "jpeg",
|
|
11125
|
+
jpeg: "jpeg",
|
|
11126
|
+
gif: "gif",
|
|
11127
|
+
webp: "webp"
|
|
11128
|
+
};
|
|
11129
|
+
var ASSET_EXTENSIONS = new Set(Object.keys(EXTENSION_FORMAT));
|
|
11130
|
+
function claimedExtension(basename6) {
|
|
11131
|
+
return basename6.includes(".") ? basename6.slice(basename6.lastIndexOf(".") + 1).toLowerCase() : "";
|
|
11132
|
+
}
|
|
11133
|
+
__name(claimedExtension, "claimedExtension");
|
|
11134
|
+
var ASSET_HEADER_BYTES = 16;
|
|
11135
|
+
function sniffImageFormat(header) {
|
|
11136
|
+
const at = /* @__PURE__ */ __name((i) => header[i] ?? -1, "at");
|
|
11137
|
+
const ascii = /* @__PURE__ */ __name((i, s) => [...s].every((ch, k) => at(i + k) === ch.charCodeAt(0)), "ascii");
|
|
11138
|
+
if (at(0) === 137 && ascii(1, "PNG") && at(4) === 13 && at(5) === 10 && at(6) === 26 && at(7) === 10) {
|
|
11139
|
+
return "png";
|
|
11140
|
+
}
|
|
11141
|
+
if (at(0) === 255 && at(1) === 216 && at(2) === 255) {
|
|
11142
|
+
return "jpeg";
|
|
11143
|
+
}
|
|
11144
|
+
if (ascii(0, "GIF87a") || ascii(0, "GIF89a")) {
|
|
11145
|
+
return "gif";
|
|
11146
|
+
}
|
|
11147
|
+
if (ascii(0, "RIFF") && ascii(8, "WEBP") && // The container claim is not the image: a RIFF box carrying WEBP at
|
|
11148
|
+
// offset 8 could still hold anything. Every spec-conformant WebP's
|
|
11149
|
+
// first chunk is VP8, VP8L or VP8X at byte 12, and the 16-byte read
|
|
11150
|
+
// this gate sniffs always covers it.
|
|
11151
|
+
(ascii(12, "VP8 ") || ascii(12, "VP8L") || ascii(12, "VP8X"))) {
|
|
11152
|
+
return "webp";
|
|
11153
|
+
}
|
|
11154
|
+
return null;
|
|
11155
|
+
}
|
|
11156
|
+
__name(sniffImageFormat, "sniffImageFormat");
|
|
11157
|
+
function refusedExtension(basename6, ext) {
|
|
11158
|
+
return {
|
|
11159
|
+
ok: false,
|
|
11160
|
+
reason: `${basename6}: extension ${JSON.stringify(ext)} is not an allowed evidence image type (${[...ASSET_EXTENSIONS].join(", ")})`
|
|
11161
|
+
};
|
|
11162
|
+
}
|
|
11163
|
+
__name(refusedExtension, "refusedExtension");
|
|
11164
|
+
function validateAssetContent(basename6, header) {
|
|
11165
|
+
const ext = claimedExtension(basename6);
|
|
11166
|
+
const claimed = Object.hasOwn(EXTENSION_FORMAT, ext) ? EXTENSION_FORMAT[ext] : void 0;
|
|
11167
|
+
if (claimed === void 0) {
|
|
11168
|
+
return refusedExtension(basename6, ext);
|
|
11169
|
+
}
|
|
11170
|
+
const actual = sniffImageFormat(header);
|
|
11171
|
+
if (actual !== claimed) {
|
|
11172
|
+
return {
|
|
11173
|
+
ok: false,
|
|
11174
|
+
reason: `content is ${actual ?? "not a recognized image"} but the extension claims ${claimed} \u2014 evidence is admitted by content, not by name`
|
|
11175
|
+
};
|
|
11176
|
+
}
|
|
11177
|
+
return { ok: true };
|
|
11178
|
+
}
|
|
11179
|
+
__name(validateAssetContent, "validateAssetContent");
|
|
10844
11180
|
var MAX_ASSET_BYTES = 10 * 1024 * 1024;
|
|
10845
11181
|
var MAX_TOTAL_ASSET_BYTES = 40 * 1024 * 1024;
|
|
10846
11182
|
function assetsBranch(pr) {
|
|
@@ -10875,12 +11211,9 @@ function parseAssetsRepo(value) {
|
|
|
10875
11211
|
}
|
|
10876
11212
|
__name(parseAssetsRepo, "parseAssetsRepo");
|
|
10877
11213
|
function validateAssetFile(basename6, bytes) {
|
|
10878
|
-
const ext =
|
|
11214
|
+
const ext = claimedExtension(basename6);
|
|
10879
11215
|
if (!ASSET_EXTENSIONS.has(ext)) {
|
|
10880
|
-
return
|
|
10881
|
-
ok: false,
|
|
10882
|
-
reason: `${basename6}: extension ${JSON.stringify(ext)} is not an allowed evidence image type (${[...ASSET_EXTENSIONS].join(", ")})`
|
|
10883
|
-
};
|
|
11216
|
+
return refusedExtension(basename6, ext);
|
|
10884
11217
|
}
|
|
10885
11218
|
if (bytes <= 0) {
|
|
10886
11219
|
return { ok: false, reason: `${basename6}: empty file` };
|
|
@@ -11051,7 +11384,7 @@ function runPublishAssets(args) {
|
|
|
11051
11384
|
const fileList = [...args.files ?? []];
|
|
11052
11385
|
if (args.findings) {
|
|
11053
11386
|
try {
|
|
11054
|
-
const artifact = JSON.parse(
|
|
11387
|
+
const artifact = JSON.parse(readFileSync16(resolve10(args.findings), "utf8"));
|
|
11055
11388
|
findings = validateFindings(
|
|
11056
11389
|
Array.isArray(artifact) ? artifact : artifact.findings
|
|
11057
11390
|
);
|
|
@@ -11081,7 +11414,7 @@ function runPublishAssets(args) {
|
|
|
11081
11414
|
for (const file of unique) {
|
|
11082
11415
|
const abs = resolve10(file);
|
|
11083
11416
|
try {
|
|
11084
|
-
const st =
|
|
11417
|
+
const st = statSync7(abs);
|
|
11085
11418
|
if (!st.isFile()) throw new Error("not a regular file");
|
|
11086
11419
|
stats.push({ file, abs, basename: basename5(abs), bytes: st.size });
|
|
11087
11420
|
} catch (err) {
|
|
@@ -11100,13 +11433,21 @@ function runPublishAssets(args) {
|
|
|
11100
11433
|
for (const st of stats) {
|
|
11101
11434
|
let content;
|
|
11102
11435
|
try {
|
|
11103
|
-
content =
|
|
11436
|
+
content = readFileSync16(st.abs);
|
|
11104
11437
|
} catch (err) {
|
|
11105
11438
|
refuse(
|
|
11106
11439
|
`cannot read ${JSON.stringify(st.file)}: ${err instanceof Error ? err.message : String(err)}`
|
|
11107
11440
|
);
|
|
11108
11441
|
return;
|
|
11109
11442
|
}
|
|
11443
|
+
const contentRuling = validateAssetContent(
|
|
11444
|
+
st.basename,
|
|
11445
|
+
content.subarray(0, ASSET_HEADER_BYTES)
|
|
11446
|
+
);
|
|
11447
|
+
if (!contentRuling.ok) {
|
|
11448
|
+
refuse(`${JSON.stringify(st.file)}: ${contentRuling.reason}`);
|
|
11449
|
+
return;
|
|
11450
|
+
}
|
|
11110
11451
|
const sha256 = createHash2("sha256").update(content).digest("hex");
|
|
11111
11452
|
prepared.push({
|
|
11112
11453
|
file: st.file,
|
|
@@ -11147,8 +11488,8 @@ function runPublishAssets(args) {
|
|
|
11147
11488
|
published
|
|
11148
11489
|
};
|
|
11149
11490
|
const outPath = resolve10(args.out);
|
|
11150
|
-
|
|
11151
|
-
|
|
11491
|
+
mkdirSync15(dirname11(outPath), { recursive: true });
|
|
11492
|
+
writeFileSync15(outPath, `${JSON.stringify(manifest, null, 2)}
|
|
11152
11493
|
`, "utf8");
|
|
11153
11494
|
if (findings && !args.findingsOut) {
|
|
11154
11495
|
writeStderrLine(
|
|
@@ -11163,8 +11504,8 @@ function runPublishAssets(args) {
|
|
|
11163
11504
|
});
|
|
11164
11505
|
const report = buildReport(updated);
|
|
11165
11506
|
const fOut = resolve10(args.findingsOut);
|
|
11166
|
-
|
|
11167
|
-
|
|
11507
|
+
mkdirSync15(dirname11(fOut), { recursive: true });
|
|
11508
|
+
writeFileSync15(fOut, `${JSON.stringify(report, null, 2)}
|
|
11168
11509
|
`, "utf8");
|
|
11169
11510
|
}
|
|
11170
11511
|
writeStderrLine(
|
|
@@ -11226,19 +11567,19 @@ var publishAssetsCommand = {
|
|
|
11226
11567
|
|
|
11227
11568
|
// packages/cli/src/commands/review/resolve-anchors.ts
|
|
11228
11569
|
init_esbuild_shims();
|
|
11229
|
-
import { readFileSync as
|
|
11570
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
|
|
11230
11571
|
import { dirname as dirname12, resolve as resolve11 } from "node:path";
|
|
11231
11572
|
|
|
11232
11573
|
// packages/cli/src/commands/review/lib/anchors.ts
|
|
11233
11574
|
init_esbuild_shims();
|
|
11234
11575
|
function collectNewSideLines(diffText, file) {
|
|
11235
|
-
const
|
|
11576
|
+
const lines = diffText.split("\n");
|
|
11236
11577
|
const out = [];
|
|
11237
11578
|
for (const hunk of file.hunks) {
|
|
11238
11579
|
if (hunk.newCount === 0) continue;
|
|
11239
11580
|
let newCursor = hunk.newStart;
|
|
11240
11581
|
for (let n = hunk.diffStart + 1; n <= hunk.diffEnd; n++) {
|
|
11241
|
-
const raw =
|
|
11582
|
+
const raw = lines[n - 1];
|
|
11242
11583
|
if (raw === void 0) break;
|
|
11243
11584
|
if (raw.startsWith("+")) {
|
|
11244
11585
|
out.push({ newLine: newCursor, text: raw.slice(1), added: true });
|
|
@@ -11266,20 +11607,20 @@ function normalizeLoose(s) {
|
|
|
11266
11607
|
}
|
|
11267
11608
|
__name(normalizeLoose, "normalizeLoose");
|
|
11268
11609
|
function anchorVariants(anchor) {
|
|
11269
|
-
const
|
|
11270
|
-
while (
|
|
11271
|
-
while (
|
|
11272
|
-
if (
|
|
11273
|
-
const variants = [
|
|
11274
|
-
const meaningful =
|
|
11610
|
+
const lines = anchor.replace(/\r\n/g, "\n").split("\n");
|
|
11611
|
+
while (lines.length > 0 && lines[0].trim() === "") lines.shift();
|
|
11612
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines.pop();
|
|
11613
|
+
if (lines.length === 0) return [];
|
|
11614
|
+
const variants = [lines];
|
|
11615
|
+
const meaningful = lines.filter((l) => l.trim() !== "");
|
|
11275
11616
|
if (meaningful.length > 0 && meaningful.every((l) => /^\+/.test(l))) {
|
|
11276
|
-
variants.push(
|
|
11617
|
+
variants.push(lines.map((l) => l.startsWith("+") ? l.slice(1) : l));
|
|
11277
11618
|
}
|
|
11278
11619
|
const NO_NEWLINE = /^\$/;
|
|
11279
|
-
const marked =
|
|
11620
|
+
const marked = lines.filter((l) => l !== "" && !NO_NEWLINE.test(l));
|
|
11280
11621
|
if (marked.length > 0 && marked.every((l) => /^[+\- ]/.test(l)) && marked.some((l) => l.startsWith("+"))) {
|
|
11281
11622
|
variants.push(
|
|
11282
|
-
|
|
11623
|
+
lines.filter((l) => !l.startsWith("-") && !NO_NEWLINE.test(l)).map((l) => l.slice(1))
|
|
11283
11624
|
);
|
|
11284
11625
|
}
|
|
11285
11626
|
return variants;
|
|
@@ -11451,7 +11792,7 @@ __name(validateRequests, "validateRequests");
|
|
|
11451
11792
|
function runResolveAnchors(args) {
|
|
11452
11793
|
let diffText;
|
|
11453
11794
|
try {
|
|
11454
|
-
diffText =
|
|
11795
|
+
diffText = readFileSync17(args.diff, "utf8");
|
|
11455
11796
|
} catch (err) {
|
|
11456
11797
|
throw new Error(
|
|
11457
11798
|
`Cannot read diff file ${args.diff}: ${err.message}`
|
|
@@ -11459,7 +11800,7 @@ function runResolveAnchors(args) {
|
|
|
11459
11800
|
}
|
|
11460
11801
|
let rawText;
|
|
11461
11802
|
try {
|
|
11462
|
-
rawText =
|
|
11803
|
+
rawText = readFileSync17(args.input, "utf8");
|
|
11463
11804
|
} catch (err) {
|
|
11464
11805
|
throw new Error(
|
|
11465
11806
|
`Cannot read findings file ${args.input}: ${err.message}`
|
|
@@ -11490,8 +11831,8 @@ function runResolveAnchors(args) {
|
|
|
11490
11831
|
loose: resolved.filter((r) => r.tier?.startsWith("loose")).length
|
|
11491
11832
|
}
|
|
11492
11833
|
};
|
|
11493
|
-
|
|
11494
|
-
|
|
11834
|
+
mkdirSync16(dirname12(resolve11(args.out)), { recursive: true });
|
|
11835
|
+
writeFileSync16(args.out, JSON.stringify(report, null, 2), "utf8");
|
|
11495
11836
|
writeStdoutLine(`Wrote resolved anchors to ${args.out}`);
|
|
11496
11837
|
const s = report.stats;
|
|
11497
11838
|
writeStderrLine(
|
|
@@ -11538,7 +11879,7 @@ var resolveAnchorsCommand = {
|
|
|
11538
11879
|
|
|
11539
11880
|
// packages/cli/src/commands/review/check-coverage.ts
|
|
11540
11881
|
init_esbuild_shims();
|
|
11541
|
-
import { writeFileSync as
|
|
11882
|
+
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync17 } from "node:fs";
|
|
11542
11883
|
import { dirname as dirname13, resolve as resolve12 } from "node:path";
|
|
11543
11884
|
function runCheckCoverage(args) {
|
|
11544
11885
|
let report;
|
|
@@ -11555,8 +11896,8 @@ This is an environment problem, not a finding about the agents. Coverage cannot
|
|
|
11555
11896
|
}
|
|
11556
11897
|
throw err;
|
|
11557
11898
|
}
|
|
11558
|
-
|
|
11559
|
-
|
|
11899
|
+
mkdirSync17(dirname13(resolve12(args.out)), { recursive: true });
|
|
11900
|
+
writeFileSync17(args.out, JSON.stringify(report, null, 2));
|
|
11560
11901
|
writeStdoutLine(`Wrote coverage report to ${args.out}`);
|
|
11561
11902
|
const totalChunks = report.coveredChunks.length + report.missingChunks.length + report.uncoverableChunks.length;
|
|
11562
11903
|
const worked = report.agents - report.blindAgents.length - report.idleAgents.length;
|
|
@@ -11655,8 +11996,172 @@ var checkCoverageCommand = {
|
|
|
11655
11996
|
// packages/cli/src/commands/review/agent-prompt.ts
|
|
11656
11997
|
init_esbuild_shims();
|
|
11657
11998
|
import { createHash as createHash3 } from "node:crypto";
|
|
11658
|
-
import { readFileSync as
|
|
11659
|
-
import { dirname as dirname14, join as
|
|
11999
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
12000
|
+
import { dirname as dirname14, join as join13, resolve as resolve13 } from "node:path";
|
|
12001
|
+
|
|
12002
|
+
// packages/cli/src/commands/review/lib/retirement.ts
|
|
12003
|
+
init_esbuild_shims();
|
|
12004
|
+
import { statSync as statSync8 } from "node:fs";
|
|
12005
|
+
var REVERSE_AUDIT_MAX_ROUNDS = 5;
|
|
12006
|
+
var RECORD_KEY_RE = /^reverse-audit--chunk-(\d+)--round-(\d+)--[0-9a-f]+$/;
|
|
12007
|
+
var REVERSE_AUDIT_MARKER = "reverse-audit";
|
|
12008
|
+
function bakedRanges(prompt, diffPath) {
|
|
12009
|
+
const out = [];
|
|
12010
|
+
if (diffPath === void 0) return out;
|
|
12011
|
+
for (const m of prompt.matchAll(
|
|
12012
|
+
/read_file\(\s*file_path="([^"]*)",\s*offset=(\d+),\s*limit=(\d+)/gi
|
|
12013
|
+
)) {
|
|
12014
|
+
if (m[1] !== diffPath) continue;
|
|
12015
|
+
const offset = Number(m[2]);
|
|
12016
|
+
const limit = Number(m[3]);
|
|
12017
|
+
if (limit > 0) out.push([offset + 1, offset + limit]);
|
|
12018
|
+
}
|
|
12019
|
+
return out;
|
|
12020
|
+
}
|
|
12021
|
+
__name(bakedRanges, "bakedRanges");
|
|
12022
|
+
var FILE_LINE_RE = /\*\*File:\*\*\s*([^\n]*)/g;
|
|
12023
|
+
var SEVERITY_LINE_RE = /\*\*Severity:\*\*/;
|
|
12024
|
+
var DRY_RECEIPT_RE = new RegExp(
|
|
12025
|
+
'(?:\\bno (?:new )?(?:issues?|findings?|gaps?)[ \\w()]{0,32}?|\u672A\u53D1\u73B0(?:\u65B0\u7684?)?(?:\u95EE\u9898|\u53D1\u73B0)|\u65E0\u65B0\u7684?(?:\u95EE\u9898|\u53D1\u73B0)|\u6CA1\u6709(?:\u53D1\u73B0)?(?:\u65B0\u7684?)?\u95EE\u9898)\\s*[*_)\\]"\u201D\u2019]*\\s*(?:[\u2014\u2013]+|[:\uFF1A]|--+|-+\\s)\\s*([\\s\\S]*)',
|
|
12026
|
+
"i"
|
|
12027
|
+
);
|
|
12028
|
+
var CJK_RE = /[一-鿿]/g;
|
|
12029
|
+
var EXAMPLE_RECEIPT_CLAUSE = (DRY_RECEIPT_RE.exec(REVERSE_AUDIT_EXAMPLE_RECEIPT)?.[1] ?? "").trim();
|
|
12030
|
+
function substantiveClause(clause) {
|
|
12031
|
+
const c = clause.replace(/\s+/g, " ").trim();
|
|
12032
|
+
if (c.length === 0) return false;
|
|
12033
|
+
if (EXAMPLE_RECEIPT_CLAUSE.length > 0 && c.includes(EXAMPLE_RECEIPT_CLAUSE)) {
|
|
12034
|
+
return false;
|
|
12035
|
+
}
|
|
12036
|
+
if (/`[^`]+`/.test(c)) return true;
|
|
12037
|
+
if (/\w[\w.-]+\/[\w.$-]+\/\w/.test(c)) return true;
|
|
12038
|
+
if (/\w[\w.-]+\/[\w$-]+\.\w+/.test(c)) return true;
|
|
12039
|
+
if ((c.match(CJK_RE) ?? []).length >= 4) return true;
|
|
12040
|
+
return c.length >= 20;
|
|
12041
|
+
}
|
|
12042
|
+
__name(substantiveClause, "substantiveClause");
|
|
12043
|
+
function classifyReturn(rec, territory) {
|
|
12044
|
+
const text = rec.finalText.trim();
|
|
12045
|
+
if (SEVERITY_LINE_RE.test(text)) {
|
|
12046
|
+
for (const m of text.matchAll(FILE_LINE_RE)) {
|
|
12047
|
+
const file = (m[1] ?? "").trim();
|
|
12048
|
+
if (file === "" || /^N\/A\b/i.test(file)) continue;
|
|
12049
|
+
if (rec.launchPrompt.includes(`**File:** ${file}`)) continue;
|
|
12050
|
+
return "yielded";
|
|
12051
|
+
}
|
|
12052
|
+
}
|
|
12053
|
+
const receipt = DRY_RECEIPT_RE.exec(text);
|
|
12054
|
+
if (rec.successfulToolCalls > 0 && rec.diffToolCalls > 0 && openedTheTerritory(rec.diffReads, territory) && receipt !== null && substantiveClause(receipt[1] ?? "")) {
|
|
12055
|
+
return "dry";
|
|
12056
|
+
}
|
|
12057
|
+
return "unknown";
|
|
12058
|
+
}
|
|
12059
|
+
__name(classifyReturn, "classifyReturn");
|
|
12060
|
+
function openedTheTerritory(diffReads, territory) {
|
|
12061
|
+
if (territory.length === 0) return true;
|
|
12062
|
+
return diffReads.some(
|
|
12063
|
+
([s, e]) => territory.some(([ts, te]) => s <= te && ts <= e)
|
|
12064
|
+
);
|
|
12065
|
+
}
|
|
12066
|
+
__name(openedTheTerritory, "openedTheTerritory");
|
|
12067
|
+
function mergeOutcomes(outcomes) {
|
|
12068
|
+
if (outcomes.includes("yielded")) return "yielded";
|
|
12069
|
+
if (outcomes.includes("dry")) return "dry";
|
|
12070
|
+
return "unknown";
|
|
12071
|
+
}
|
|
12072
|
+
__name(mergeOutcomes, "mergeOutcomes");
|
|
12073
|
+
function scheduleReverseAuditRound(planPath, chunkIds, round, env = process.env, diffPath) {
|
|
12074
|
+
if (round < 3) {
|
|
12075
|
+
return {
|
|
12076
|
+
due: [...chunkIds],
|
|
12077
|
+
coldChecks: [],
|
|
12078
|
+
skipped: [],
|
|
12079
|
+
converged: false
|
|
12080
|
+
};
|
|
12081
|
+
}
|
|
12082
|
+
const since = statSync8(planPath).mtimeMs;
|
|
12083
|
+
const transcripts = readTranscripts(since, env, diffPath);
|
|
12084
|
+
const built = readRecordedPrompts(planPath, since);
|
|
12085
|
+
const records = [];
|
|
12086
|
+
for (const [key, prompt] of built) {
|
|
12087
|
+
const m = RECORD_KEY_RE.exec(key);
|
|
12088
|
+
if (!m) continue;
|
|
12089
|
+
const r = Number(m[2]);
|
|
12090
|
+
if (r >= round) continue;
|
|
12091
|
+
records.push({
|
|
12092
|
+
chunkId: Number(m[1]),
|
|
12093
|
+
round: r,
|
|
12094
|
+
// Flattened ONCE per record, beside the once-per-transcript flatten
|
|
12095
|
+
// below: the pairing walk pays neither half per (record, transcript)
|
|
12096
|
+
// pair.
|
|
12097
|
+
lines: promptLines(prompt),
|
|
12098
|
+
territory: bakedRanges(prompt, diffPath)
|
|
12099
|
+
});
|
|
12100
|
+
}
|
|
12101
|
+
const candidates = transcripts.filter((t2) => t2.launchPrompt.includes(REVERSE_AUDIT_MARKER)).map((t2) => ({ transcript: t2, flat: flattenPrompt(t2.launchPrompt) }));
|
|
12102
|
+
const matchesByRecord = records.map(
|
|
12103
|
+
(rec) => candidates.filter((c) => deliveredVerbatimLines(c.flat, rec.lines)).map((c) => c.transcript)
|
|
12104
|
+
);
|
|
12105
|
+
const recordsPerTranscript = /* @__PURE__ */ new Map();
|
|
12106
|
+
for (const matches of matchesByRecord) {
|
|
12107
|
+
for (const t2 of matches) {
|
|
12108
|
+
recordsPerTranscript.set(t2, (recordsPerTranscript.get(t2) ?? 0) + 1);
|
|
12109
|
+
}
|
|
12110
|
+
}
|
|
12111
|
+
const outcomesByRecord = matchesByRecord.map(
|
|
12112
|
+
(matches, i) => matches.filter((t2) => recordsPerTranscript.get(t2) === 1).map((t2) => classifyReturn(t2, records[i].territory))
|
|
12113
|
+
);
|
|
12114
|
+
const history = /* @__PURE__ */ new Map();
|
|
12115
|
+
records.forEach((rec, i) => {
|
|
12116
|
+
let byRound = history.get(rec.chunkId);
|
|
12117
|
+
if (!byRound) {
|
|
12118
|
+
byRound = /* @__PURE__ */ new Map();
|
|
12119
|
+
history.set(rec.chunkId, byRound);
|
|
12120
|
+
}
|
|
12121
|
+
byRound.set(rec.round, [
|
|
12122
|
+
...byRound.get(rec.round) ?? [],
|
|
12123
|
+
...outcomesByRecord[i]
|
|
12124
|
+
]);
|
|
12125
|
+
});
|
|
12126
|
+
const due = [];
|
|
12127
|
+
const coldChecks = [];
|
|
12128
|
+
const skipped = [];
|
|
12129
|
+
for (const chunkId of chunkIds) {
|
|
12130
|
+
const audits = [...history.get(chunkId)?.entries() ?? []].map(([r, outcomes]) => ({ round: r, outcome: mergeOutcomes(outcomes) })).sort((a, b) => a.round - b.round);
|
|
12131
|
+
const lastTwo = audits.slice(-2);
|
|
12132
|
+
const retired = lastTwo.length === 2 && lastTwo.every((a) => a.outcome === "dry");
|
|
12133
|
+
if (!retired) {
|
|
12134
|
+
due.push(chunkId);
|
|
12135
|
+
continue;
|
|
12136
|
+
}
|
|
12137
|
+
if (round % 2 === 0) {
|
|
12138
|
+
due.push(chunkId);
|
|
12139
|
+
coldChecks.push(chunkId);
|
|
12140
|
+
} else {
|
|
12141
|
+
skipped.push({
|
|
12142
|
+
chunkId,
|
|
12143
|
+
dryRounds: [lastTwo[0].round, lastTwo[1].round],
|
|
12144
|
+
// The next even round — this branch only runs on odd rounds, so
|
|
12145
|
+
// that is always round + 1. Whether the cap allows it is the note
|
|
12146
|
+
// composer's question, not the schedule's (see
|
|
12147
|
+
// REVERSE_AUDIT_MAX_ROUNDS).
|
|
12148
|
+
nextColdCheck: round + 1
|
|
12149
|
+
});
|
|
12150
|
+
}
|
|
12151
|
+
}
|
|
12152
|
+
return {
|
|
12153
|
+
due,
|
|
12154
|
+
coldChecks,
|
|
12155
|
+
skipped,
|
|
12156
|
+
// An empty `chunkIds` empties `due` vacuously — nothing was ever under
|
|
12157
|
+
// audit, so nothing has proven itself cold. `runAllChunks` refuses a
|
|
12158
|
+
// chunkless plan long before scheduling, but this function is exported,
|
|
12159
|
+
// and convergence is an exit-5 termination rule: it must not be
|
|
12160
|
+
// reachable from nothing.
|
|
12161
|
+
converged: chunkIds.length > 0 && due.length === 0
|
|
12162
|
+
};
|
|
12163
|
+
}
|
|
12164
|
+
__name(scheduleReverseAuditRound, "scheduleReverseAuditRound");
|
|
11660
12165
|
|
|
11661
12166
|
// packages/cli/src/commands/review/lib/path-rules.ts
|
|
11662
12167
|
init_esbuild_shims();
|
|
@@ -12108,7 +12613,7 @@ function buildRoleBrief(report, role, opts = {}) {
|
|
|
12108
12613
|
"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."
|
|
12109
12614
|
);
|
|
12110
12615
|
}
|
|
12111
|
-
const ctx = opts.planPath ?
|
|
12616
|
+
const ctx = opts.planPath ? join13(dirname14(resolve13(opts.planPath)), `qwen-review-pr-${pr}-context.md`) : null;
|
|
12112
12617
|
parts.push(
|
|
12113
12618
|
"",
|
|
12114
12619
|
`**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.`
|
|
@@ -12367,7 +12872,7 @@ The \`description\` parameter of each Agent call is the task name the user watch
|
|
|
12367
12872
|
);
|
|
12368
12873
|
}
|
|
12369
12874
|
__name(runRoster, "runRoster");
|
|
12370
|
-
function
|
|
12875
|
+
function requireAuditableChunks(report) {
|
|
12371
12876
|
if (!Array.isArray(report.chunks) || report.chunks.length === 0) {
|
|
12372
12877
|
throw new Error("agent-prompt: the plan has no `chunks[]`.");
|
|
12373
12878
|
}
|
|
@@ -12378,9 +12883,60 @@ function runAllChunks(report, planPath, role, findingsContent, rules, round) {
|
|
|
12378
12883
|
`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.`
|
|
12379
12884
|
);
|
|
12380
12885
|
}
|
|
12886
|
+
return chunks;
|
|
12887
|
+
}
|
|
12888
|
+
__name(requireAuditableChunks, "requireAuditableChunks");
|
|
12889
|
+
function admitReverseAuditRound(planPath, round) {
|
|
12890
|
+
const spent = reverseAuditBudgetExhausted(
|
|
12891
|
+
process.env,
|
|
12892
|
+
expectedRoundSeconds(planPath, round)
|
|
12893
|
+
);
|
|
12894
|
+
if (spent !== null) {
|
|
12895
|
+
writeBudgetStop(planPath, spent, round);
|
|
12896
|
+
writeStderrLine(reverseAuditBudgetMessage(spent, round));
|
|
12897
|
+
process.exitCode = 4;
|
|
12898
|
+
return false;
|
|
12899
|
+
}
|
|
12900
|
+
return true;
|
|
12901
|
+
}
|
|
12902
|
+
__name(admitReverseAuditRound, "admitReverseAuditRound");
|
|
12903
|
+
function refuseConverged() {
|
|
12904
|
+
writeStderrLine(
|
|
12905
|
+
"CONVERGED: every chunk holds two consecutive substantive dry audits; the reverse audit has converged \u2014 stop the loop and proceed to Step 6. This is a clean convergence, not a gap: no unreviewedDimensions entry is owed."
|
|
12906
|
+
);
|
|
12907
|
+
process.exitCode = 5;
|
|
12908
|
+
}
|
|
12909
|
+
__name(refuseConverged, "refuseConverged");
|
|
12910
|
+
function runAllChunks(report, planPath, role, findingsContent, rules, round) {
|
|
12911
|
+
const chunks = requireAuditableChunks(report);
|
|
12912
|
+
let schedule = null;
|
|
12913
|
+
if (role === "reverse-audit" && round !== void 0 && round >= 3) {
|
|
12914
|
+
try {
|
|
12915
|
+
schedule = scheduleReverseAuditRound(
|
|
12916
|
+
planPath,
|
|
12917
|
+
chunks.map((c) => c.id),
|
|
12918
|
+
round,
|
|
12919
|
+
process.env,
|
|
12920
|
+
typeof report.diffPathAbsolute === "string" ? report.diffPathAbsolute : void 0
|
|
12921
|
+
);
|
|
12922
|
+
} catch {
|
|
12923
|
+
schedule = null;
|
|
12924
|
+
}
|
|
12925
|
+
}
|
|
12926
|
+
if (schedule !== null && schedule.converged) {
|
|
12927
|
+
refuseConverged();
|
|
12928
|
+
return;
|
|
12929
|
+
}
|
|
12930
|
+
if (role === "reverse-audit" && !admitReverseAuditRound(planPath, round)) {
|
|
12931
|
+
return;
|
|
12932
|
+
}
|
|
12933
|
+
const dueSet = schedule === null ? null : new Set(schedule.due);
|
|
12934
|
+
const dueChunks = dueSet === null ? chunks : chunks.filter((c) => dueSet.has(c.id));
|
|
12935
|
+
const coldSet = new Set(schedule?.coldChecks ?? []);
|
|
12936
|
+
const skipped = schedule?.skipped ?? [];
|
|
12381
12937
|
const digest = findingsDigest(findingsContent, rules);
|
|
12382
12938
|
const roundPart = round !== void 0 ? `--round-${round}` : "";
|
|
12383
|
-
const blocks =
|
|
12939
|
+
const blocks = dueChunks.map((c, i) => {
|
|
12384
12940
|
const key = `${role}--chunk-${c.id}${roundPart}--${digest}`;
|
|
12385
12941
|
const { prompt } = buildLaunch(
|
|
12386
12942
|
report,
|
|
@@ -12390,17 +12946,29 @@ function runAllChunks(report, planPath, role, findingsContent, rules, round) {
|
|
|
12390
12946
|
);
|
|
12391
12947
|
const printed = foldFindings(role, findingsContent, prompt);
|
|
12392
12948
|
recordPrompt(planPath, key, printed);
|
|
12393
|
-
|
|
12949
|
+
const cold = coldSet.has(c.id) ? " (cold check)" : "";
|
|
12950
|
+
return `\u2500\u2500\u2500\u2500\u2500 auditor ${i + 1} of ${dueChunks.length} \u2014 chunk ${c.id}${cold} \u2500\u2500\u2500\u2500\u2500
|
|
12394
12951
|
|
|
12395
12952
|
` + printed;
|
|
12396
12953
|
});
|
|
12954
|
+
const scope = skipped.length === 0 ? "one per chunk" : `one per chunk still under audit (${skipped.length} retired chunk(s) skipped; the retirement note after the end-of-round line says which \u2014 relay it to the terminal)`;
|
|
12955
|
+
const retirementNote = skipped.length === 0 ? [] : [
|
|
12956
|
+
`retirement: a chunk whose two most recent audits are substantive dry receipts is cold-checked on alternating rounds instead of audited on every one; a cold check that yields returns it to every-round auditing. Skipped this round:
|
|
12957
|
+
` + skipped.map(
|
|
12958
|
+
(s) => `chunk ${s.chunkId} \u2014 retired: dry in rounds ${s.dryRounds[0]} and ${s.dryRounds[1]}, ` + (s.nextColdCheck > REVERSE_AUDIT_MAX_ROUNDS ? `certificate final \u2014 the ${REVERSE_AUDIT_MAX_ROUNDS}-round hard cap leaves the loop no round for a cold check` : `next cold check round ${s.nextColdCheck}`)
|
|
12959
|
+
).join("\n")
|
|
12960
|
+
];
|
|
12397
12961
|
writeStdoutLine(
|
|
12398
12962
|
[
|
|
12399
|
-
`${
|
|
12963
|
+
`${dueChunks.length} auditors required this round \u2014 ${scope}. Launch one agent per block below, passing its block VERBATIM \u2014 copy, do not retype, and NEVER sample this output (no \`| head\`): the text IS the deliverable, and a launch reconstructed from a sample matches no record. Blocks are numbered \`auditor k of ${dueChunks.length}\`, and the output ends with an end-of-round line \u2014 followed by the retirement note, when there is one. If either the numbering or the end-of-round line is missing, the output was truncated in transit; rebuild just the missing chunks with --chunk <id>. Write each Agent call's \`description\` (the task name the user watches) in your output language, translating the separator label \u2014 display only; the prompt stays the block VERBATIM.`,
|
|
12400
12964
|
...blocks,
|
|
12401
|
-
`\u2500\u2500\u2500\u2500\u2500 end of round \u2014 ${
|
|
12965
|
+
`\u2500\u2500\u2500\u2500\u2500 end of round \u2014 ${dueChunks.length} auditors \u2500\u2500\u2500\u2500\u2500`,
|
|
12966
|
+
...retirementNote
|
|
12402
12967
|
].join("\n\n")
|
|
12403
12968
|
);
|
|
12969
|
+
if (role === "reverse-audit") {
|
|
12970
|
+
stampRound(planPath, round);
|
|
12971
|
+
}
|
|
12404
12972
|
}
|
|
12405
12973
|
__name(runAllChunks, "runAllChunks");
|
|
12406
12974
|
function runAgentPrompt(args) {
|
|
@@ -12483,6 +13051,11 @@ function runAgentPrompt(args) {
|
|
|
12483
13051
|
);
|
|
12484
13052
|
}
|
|
12485
13053
|
}
|
|
13054
|
+
if (role === "reverse-audit" && !hasRound) {
|
|
13055
|
+
bad(
|
|
13056
|
+
"--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."
|
|
13057
|
+
);
|
|
13058
|
+
}
|
|
12486
13059
|
} else if (hasFindings) {
|
|
12487
13060
|
const findingRoles = Object.keys(BRIEFS).filter(
|
|
12488
13061
|
(r) => BRIEFS[r].acceptsFindings
|
|
@@ -12513,7 +13086,7 @@ function runAgentPrompt(args) {
|
|
|
12513
13086
|
}
|
|
12514
13087
|
let report;
|
|
12515
13088
|
try {
|
|
12516
|
-
report = JSON.parse(
|
|
13089
|
+
report = JSON.parse(readFileSync18(args.plan, "utf8"));
|
|
12517
13090
|
} catch (err) {
|
|
12518
13091
|
throw new Error(
|
|
12519
13092
|
`agent-prompt: cannot read the plan ${args.plan}: ${err.message}`
|
|
@@ -12522,7 +13095,7 @@ function runAgentPrompt(args) {
|
|
|
12522
13095
|
let rules;
|
|
12523
13096
|
if (args.rules) {
|
|
12524
13097
|
try {
|
|
12525
|
-
rules =
|
|
13098
|
+
rules = readFileSync18(args.rules, "utf8");
|
|
12526
13099
|
} catch (err) {
|
|
12527
13100
|
throw new Error(
|
|
12528
13101
|
`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.`
|
|
@@ -12537,7 +13110,7 @@ function runAgentPrompt(args) {
|
|
|
12537
13110
|
if (hasFindings && args.role) {
|
|
12538
13111
|
const role = args.role;
|
|
12539
13112
|
try {
|
|
12540
|
-
findingsContent =
|
|
13113
|
+
findingsContent = readFileSync18(args.findings, "utf8");
|
|
12541
13114
|
} catch (err) {
|
|
12542
13115
|
throw new Error(
|
|
12543
13116
|
`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).`
|
|
@@ -12549,6 +13122,30 @@ function runAgentPrompt(args) {
|
|
|
12549
13122
|
);
|
|
12550
13123
|
}
|
|
12551
13124
|
}
|
|
13125
|
+
if (args.role === "reverse-audit" && !hasChunk && !args.allChunks && !admitReverseAuditRound(args.plan, args.round)) {
|
|
13126
|
+
return;
|
|
13127
|
+
}
|
|
13128
|
+
if (args.role === "reverse-audit" && hasChunk && !readRoundStamps(args.plan).some((s) => s.round === (args.round ?? null))) {
|
|
13129
|
+
if (args.round !== void 0) {
|
|
13130
|
+
let schedule = null;
|
|
13131
|
+
try {
|
|
13132
|
+
schedule = scheduleReverseAuditRound(
|
|
13133
|
+
args.plan,
|
|
13134
|
+
(Array.isArray(report.chunks) ? report.chunks : []).map((c) => c?.id).filter((id) => typeof id === "number"),
|
|
13135
|
+
args.round,
|
|
13136
|
+
process.env,
|
|
13137
|
+
typeof report.diffPathAbsolute === "string" ? report.diffPathAbsolute : void 0
|
|
13138
|
+
);
|
|
13139
|
+
} catch {
|
|
13140
|
+
schedule = null;
|
|
13141
|
+
}
|
|
13142
|
+
if (schedule !== null && schedule.converged) {
|
|
13143
|
+
refuseConverged();
|
|
13144
|
+
return;
|
|
13145
|
+
}
|
|
13146
|
+
}
|
|
13147
|
+
if (!admitReverseAuditRound(args.plan, args.round)) return;
|
|
13148
|
+
}
|
|
12552
13149
|
if (args.allChunks && args.role && findingsContent !== void 0) {
|
|
12553
13150
|
runAllChunks(
|
|
12554
13151
|
report,
|
|
@@ -12588,11 +13185,14 @@ function runAgentPrompt(args) {
|
|
|
12588
13185
|
const printed = findingsContent !== void 0 && args.role ? foldFindings(args.role, findingsContent, prompt) : prompt;
|
|
12589
13186
|
recordPrompt(args.plan, key, printed);
|
|
12590
13187
|
writeStdoutLine(printed);
|
|
13188
|
+
if (args.role === "reverse-audit") {
|
|
13189
|
+
stampRound(args.plan, args.round);
|
|
13190
|
+
}
|
|
12591
13191
|
}
|
|
12592
13192
|
__name(runAgentPrompt, "runAgentPrompt");
|
|
12593
13193
|
var agentPromptCommand = {
|
|
12594
13194
|
command: "agent-prompt",
|
|
12595
|
-
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)",
|
|
13195
|
+
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); 5 the reverse audit CONVERGED \u2014 every chunk holds two consecutive substantive dry audits and none is due a cold check, so stop the loop and proceed to Step 6 (also a termination rule, and a clean one: no disclosure is owed); anything else is a bad call or a broken plan.",
|
|
12596
13196
|
builder: /* @__PURE__ */ __name((yargs) => yargs.option("plan", {
|
|
12597
13197
|
type: "string",
|
|
12598
13198
|
demandOption: true,
|
|
@@ -12603,13 +13203,13 @@ var agentPromptCommand = {
|
|
|
12603
13203
|
describe: "The dimension this agent owns. Builds its WHOLE prompt \u2014 the diff's line ranges, the brief, the finding format, the severity definitions and the project rules. Pass it to the agent verbatim."
|
|
12604
13204
|
}).option("chunk", {
|
|
12605
13205
|
type: "number",
|
|
12606
|
-
describe: "Which chunk id this agent owns (a Step 3B territory agent)"
|
|
13206
|
+
describe: "Which chunk id this agent owns (a Step 3B territory agent). With --role reverse-audit --round N it rebuilds ONE auditor of that round: a round already admitted is repaired without gates; a round never admitted answers to the same convergence and budget gates as --all-chunks."
|
|
12607
13207
|
}).option("file", {
|
|
12608
13208
|
type: "string",
|
|
12609
13209
|
describe: "The heavily-rewritten file an invariant agent owns (--role invariant-a|invariant-b|invariant-c)"
|
|
12610
13210
|
}).option("all-chunks", {
|
|
12611
13211
|
type: "boolean",
|
|
12612
|
-
describe: "With --role reverse-audit --findings: build one block per chunk in one call, labelled and separated (Step 5, 3B). Never sample the output; each block is pasted verbatim to its own agent."
|
|
13212
|
+
describe: "With --role reverse-audit --findings: build one block per chunk in one call, labelled and separated (Step 5, 3B). Never sample the output; each block is pasted verbatim to its own agent. From --round 3 on, a chunk whose two most recent audits were both substantive dry receipts is retired to alternating-round cold checks (the retirement note after the end-of-round line says which); a round where every chunk is retired and none is due exits 5 \u2014 the audit has converged."
|
|
12613
13213
|
}).option("roster", {
|
|
12614
13214
|
type: "boolean",
|
|
12615
13215
|
describe: "Build EVERY prompt the plan requires \u2014 chunk, dimension and invariant agents alike \u2014 in one call, each labelled and separated. The list is the same one check-coverage reads out of the plan."
|
|
@@ -12647,17 +13247,17 @@ init_esbuild_shims();
|
|
|
12647
13247
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
12648
13248
|
import {
|
|
12649
13249
|
existsSync as existsSync7,
|
|
12650
|
-
readFileSync as
|
|
13250
|
+
readFileSync as readFileSync20,
|
|
12651
13251
|
rmSync as rmSync5,
|
|
12652
13252
|
statfsSync,
|
|
12653
|
-
writeFileSync as
|
|
13253
|
+
writeFileSync as writeFileSync18
|
|
12654
13254
|
} from "node:fs";
|
|
12655
|
-
import { join as
|
|
13255
|
+
import { join as join15, resolve as resolve14 } from "node:path";
|
|
12656
13256
|
|
|
12657
13257
|
// packages/cli/src/commands/review/lib/workspaces.ts
|
|
12658
13258
|
init_esbuild_shims();
|
|
12659
|
-
import { existsSync as existsSync6, readFileSync as
|
|
12660
|
-
import { join as
|
|
13259
|
+
import { existsSync as existsSync6, readFileSync as readFileSync19, readdirSync as readdirSync4 } from "node:fs";
|
|
13260
|
+
import { join as join14 } from "node:path";
|
|
12661
13261
|
function isWorkspaceMember(filePath, workspaceGlobs) {
|
|
12662
13262
|
return workspaceDirFor(filePath, workspaceGlobs) !== null;
|
|
12663
13263
|
}
|
|
@@ -12665,9 +13265,10 @@ __name(isWorkspaceMember, "isWorkspaceMember");
|
|
|
12665
13265
|
function workspaceDirFor(filePath, workspaceGlobs) {
|
|
12666
13266
|
const norm = filePath.replace(/^\.\//, "");
|
|
12667
13267
|
let owner = null;
|
|
13268
|
+
const previous = [];
|
|
12668
13269
|
for (const glob of workspaceGlobs) {
|
|
12669
13270
|
const negated = glob.startsWith("!");
|
|
12670
|
-
const g = glob.replace(/^!/, "").replace(/\/$/, "");
|
|
13271
|
+
const g = glob.replace(/^!/, "").replace(/^\.\//, "").replace(/\/$/, "");
|
|
12671
13272
|
let dir = null;
|
|
12672
13273
|
if (g.endsWith("/*")) {
|
|
12673
13274
|
const base = g.slice(0, -2);
|
|
@@ -12679,14 +13280,27 @@ function workspaceDirFor(filePath, workspaceGlobs) {
|
|
|
12679
13280
|
dir = g;
|
|
12680
13281
|
}
|
|
12681
13282
|
if (dir === null) continue;
|
|
12682
|
-
|
|
13283
|
+
if (!negated) {
|
|
13284
|
+
if (dir !== owner) {
|
|
13285
|
+
previous.push(owner);
|
|
13286
|
+
owner = dir;
|
|
13287
|
+
}
|
|
13288
|
+
} else if (dir === owner) {
|
|
13289
|
+
owner = previous.pop() ?? null;
|
|
13290
|
+
}
|
|
12683
13291
|
}
|
|
12684
13292
|
return owner;
|
|
12685
13293
|
}
|
|
12686
13294
|
__name(workspaceDirFor, "workspaceDirFor");
|
|
13295
|
+
function isNegationExcluded(filePath, workspaceGlobs) {
|
|
13296
|
+
if (workspaceDirFor(filePath, workspaceGlobs) !== null) return false;
|
|
13297
|
+
const positives = workspaceGlobs.filter((g) => !g.startsWith("!"));
|
|
13298
|
+
return workspaceDirFor(filePath, positives) !== null;
|
|
13299
|
+
}
|
|
13300
|
+
__name(isNegationExcluded, "isNegationExcluded");
|
|
12687
13301
|
function hasUnmodeledWorkspaceGlob(globs) {
|
|
12688
13302
|
return globs.some((glob) => {
|
|
12689
|
-
const g = glob.replace(/^!/, "");
|
|
13303
|
+
const g = glob.replace(/^!/, "").replace(/^\.\//, "");
|
|
12690
13304
|
if (!g.includes("*")) return false;
|
|
12691
13305
|
return !/^[^*]+\/\*$/.test(g);
|
|
12692
13306
|
});
|
|
@@ -12695,7 +13309,7 @@ __name(hasUnmodeledWorkspaceGlob, "hasUnmodeledWorkspaceGlob");
|
|
|
12695
13309
|
function readWorkspaceGlobs(root) {
|
|
12696
13310
|
try {
|
|
12697
13311
|
const pkg = JSON.parse(
|
|
12698
|
-
|
|
13312
|
+
readFileSync19(join14(root, "package.json"), "utf8")
|
|
12699
13313
|
);
|
|
12700
13314
|
const ws = pkg.workspaces;
|
|
12701
13315
|
const globs = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
@@ -12708,31 +13322,51 @@ __name(readWorkspaceGlobs, "readWorkspaceGlobs");
|
|
|
12708
13322
|
function readRootPackage(root) {
|
|
12709
13323
|
let pkg;
|
|
12710
13324
|
try {
|
|
12711
|
-
pkg = JSON.parse(
|
|
13325
|
+
pkg = JSON.parse(readFileSync19(join14(root, "package.json"), "utf8"));
|
|
12712
13326
|
} catch {
|
|
12713
13327
|
return null;
|
|
12714
13328
|
}
|
|
13329
|
+
if (pkg === null) return null;
|
|
12715
13330
|
const scripts = Object.keys(pkg.scripts ?? {});
|
|
12716
13331
|
if (!scripts.includes("build") && !scripts.includes("test")) return null;
|
|
13332
|
+
const scriptsText = {};
|
|
13333
|
+
for (const [k, v] of Object.entries(pkg.scripts ?? {})) {
|
|
13334
|
+
if (typeof v === "string") scriptsText[k] = v;
|
|
13335
|
+
}
|
|
12717
13336
|
return {
|
|
12718
13337
|
dir: ".",
|
|
12719
13338
|
name: typeof pkg.name === "string" && pkg.name ? pkg.name : "root",
|
|
12720
13339
|
scripts,
|
|
12721
|
-
deps:
|
|
13340
|
+
deps: declaredDeps(pkg),
|
|
13341
|
+
scriptsText
|
|
12722
13342
|
};
|
|
12723
13343
|
}
|
|
12724
13344
|
__name(readRootPackage, "readRootPackage");
|
|
12725
|
-
function
|
|
12726
|
-
|
|
13345
|
+
function scriptFansOut(text) {
|
|
13346
|
+
return typeof text === "string" && /(^|\s)(--workspaces(?=\s|$)|--?ws(?=\s|$))/.test(text);
|
|
13347
|
+
}
|
|
13348
|
+
__name(scriptFansOut, "scriptFansOut");
|
|
13349
|
+
function declaredDeps(pkg) {
|
|
13350
|
+
return [
|
|
13351
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
13352
|
+
...Object.keys(pkg.devDependencies ?? {}),
|
|
13353
|
+
...Object.keys(pkg.peerDependencies ?? {}),
|
|
13354
|
+
...Object.keys(pkg.optionalDependencies ?? {})
|
|
13355
|
+
];
|
|
13356
|
+
}
|
|
13357
|
+
__name(declaredDeps, "declaredDeps");
|
|
13358
|
+
function workspaceDirCandidates(root, globs) {
|
|
12727
13359
|
const dirs = /* @__PURE__ */ new Set();
|
|
12728
13360
|
for (const glob of globs) {
|
|
12729
13361
|
if (glob.startsWith("!")) continue;
|
|
12730
|
-
const g = glob.replace(/\/$/, "");
|
|
13362
|
+
const g = glob.replace(/^\.\//, "").replace(/\/$/, "");
|
|
12731
13363
|
if (g.endsWith("/*")) {
|
|
12732
13364
|
const base = g.slice(0, -2);
|
|
12733
13365
|
let entries;
|
|
12734
13366
|
try {
|
|
12735
|
-
entries = readdirSync4(
|
|
13367
|
+
entries = readdirSync4(join14(root, base), { withFileTypes: true }).filter(
|
|
13368
|
+
(e) => e.isDirectory() || e.isSymbolicLink() && existsSync6(join14(root, base, e.name, "package.json"))
|
|
13369
|
+
).map((e) => e.name);
|
|
12736
13370
|
} catch {
|
|
12737
13371
|
continue;
|
|
12738
13372
|
}
|
|
@@ -12741,30 +13375,43 @@ function readWorkspacePackages(root) {
|
|
|
12741
13375
|
dirs.add(g);
|
|
12742
13376
|
}
|
|
12743
13377
|
}
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
13378
|
+
return [...dirs].sort();
|
|
13379
|
+
}
|
|
13380
|
+
__name(workspaceDirCandidates, "workspaceDirCandidates");
|
|
13381
|
+
function readWorkspacePackages(root) {
|
|
13382
|
+
const globs = readWorkspaceGlobs(root);
|
|
13383
|
+
const packages = [];
|
|
13384
|
+
const skipped = [];
|
|
13385
|
+
for (const dir of workspaceDirCandidates(root, globs)) {
|
|
13386
|
+
const manifest = join14(root, dir, "package.json");
|
|
12748
13387
|
if (!existsSync6(manifest)) continue;
|
|
13388
|
+
if (workspaceDirFor(`${dir}/package.json`, globs) !== dir) {
|
|
13389
|
+
const positives = globs.filter((g) => !g.startsWith("!"));
|
|
13390
|
+
if (workspaceDirFor(`${dir}/package.json`, positives) === dir) continue;
|
|
13391
|
+
skipped.push(dir);
|
|
13392
|
+
continue;
|
|
13393
|
+
}
|
|
12749
13394
|
let pkg;
|
|
12750
13395
|
try {
|
|
12751
|
-
pkg = JSON.parse(
|
|
13396
|
+
pkg = JSON.parse(readFileSync19(manifest, "utf8"));
|
|
12752
13397
|
} catch {
|
|
13398
|
+
skipped.push(dir);
|
|
13399
|
+
continue;
|
|
13400
|
+
}
|
|
13401
|
+
if (pkg === null || typeof pkg.name !== "string" || !pkg.name) {
|
|
13402
|
+
skipped.push(dir);
|
|
12753
13403
|
continue;
|
|
12754
13404
|
}
|
|
12755
|
-
|
|
12756
|
-
pkgs.push({
|
|
13405
|
+
packages.push({
|
|
12757
13406
|
dir,
|
|
12758
13407
|
name: pkg.name,
|
|
12759
13408
|
scripts: Object.keys(pkg.scripts ?? {}),
|
|
12760
|
-
deps:
|
|
12761
|
-
...Object.keys(pkg.dependencies ?? {}),
|
|
12762
|
-
...Object.keys(pkg.devDependencies ?? {}),
|
|
12763
|
-
...Object.keys(pkg.peerDependencies ?? {})
|
|
12764
|
-
]
|
|
13409
|
+
deps: declaredDeps(pkg)
|
|
12765
13410
|
});
|
|
12766
13411
|
}
|
|
12767
|
-
|
|
13412
|
+
packages.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
13413
|
+
skipped.sort();
|
|
13414
|
+
return { packages, skipped };
|
|
12768
13415
|
}
|
|
12769
13416
|
__name(readWorkspacePackages, "readWorkspacePackages");
|
|
12770
13417
|
function affectedWorkspaces(changedFiles, workspaceGlobs) {
|
|
@@ -12776,8 +13423,7 @@ function affectedWorkspaces(changedFiles, workspaceGlobs) {
|
|
|
12776
13423
|
return [...dirs].sort();
|
|
12777
13424
|
}
|
|
12778
13425
|
__name(affectedWorkspaces, "affectedWorkspaces");
|
|
12779
|
-
function
|
|
12780
|
-
const byDir = new Map(packages.map((p) => [p.dir, p]));
|
|
13426
|
+
function dependencyEdges(packages) {
|
|
12781
13427
|
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
12782
13428
|
const dependsOn = /* @__PURE__ */ new Map();
|
|
12783
13429
|
for (const p of packages) {
|
|
@@ -12786,6 +13432,12 @@ function buildSetFor(affected, packages, alsoBuild = []) {
|
|
|
12786
13432
|
p.deps.map((d) => byName.get(d)?.dir).filter((d) => !!d && d !== p.dir)
|
|
12787
13433
|
);
|
|
12788
13434
|
}
|
|
13435
|
+
return dependsOn;
|
|
13436
|
+
}
|
|
13437
|
+
__name(dependencyEdges, "dependencyEdges");
|
|
13438
|
+
function reverseDependencyClosure(affected, packages) {
|
|
13439
|
+
const byDir = new Map(packages.map((p) => [p.dir, p]));
|
|
13440
|
+
const dependsOn = dependencyEdges(packages);
|
|
12789
13441
|
const consumers = new Set(affected.filter((a) => byDir.has(a)));
|
|
12790
13442
|
let grew = true;
|
|
12791
13443
|
while (grew) {
|
|
@@ -12798,6 +13450,13 @@ function buildSetFor(affected, packages, alsoBuild = []) {
|
|
|
12798
13450
|
}
|
|
12799
13451
|
}
|
|
12800
13452
|
}
|
|
13453
|
+
return [...consumers].sort();
|
|
13454
|
+
}
|
|
13455
|
+
__name(reverseDependencyClosure, "reverseDependencyClosure");
|
|
13456
|
+
function buildSetFor(affected, packages, alsoBuild = []) {
|
|
13457
|
+
const byDir = new Map(packages.map((p) => [p.dir, p]));
|
|
13458
|
+
const dependsOn = dependencyEdges(packages);
|
|
13459
|
+
const consumers = reverseDependencyClosure(affected, packages);
|
|
12801
13460
|
const wanted = /* @__PURE__ */ new Set();
|
|
12802
13461
|
const addDeps = /* @__PURE__ */ __name((dir) => {
|
|
12803
13462
|
if (wanted.has(dir)) return;
|
|
@@ -12822,13 +13481,81 @@ function buildSetFor(affected, packages, alsoBuild = []) {
|
|
|
12822
13481
|
}
|
|
12823
13482
|
__name(buildSetFor, "buildSetFor");
|
|
12824
13483
|
|
|
13484
|
+
// packages/cli/src/commands/review/lib/workspace-scope.ts
|
|
13485
|
+
init_esbuild_shims();
|
|
13486
|
+
var LICENSE_LIKE_RE = /(^|\/)(LICEN[CS]ES?|COPYING|NOTICES?)(-[^/.]+)?(\.(md|txt|rst))?$/;
|
|
13487
|
+
function isInertLicense(path3) {
|
|
13488
|
+
return LICENSE_LIKE_RE.test(path3);
|
|
13489
|
+
}
|
|
13490
|
+
__name(isInertLicense, "isInertLicense");
|
|
13491
|
+
var INERT_OUTSIDE_RE = /^(\.github\/|CHANGELOG\.md$|\.gitignore$|\.gitattributes$|\.editorconfig$|\.idea\/|\.vscode\/)/;
|
|
13492
|
+
function resolveTestScope(input) {
|
|
13493
|
+
const { changed, globs, packages, skipped } = input;
|
|
13494
|
+
const affected = affectedWorkspaces(changed, globs);
|
|
13495
|
+
const graph = input.rootPackage ? [input.rootPackage, ...packages] : packages;
|
|
13496
|
+
const scriptsOf = new Map(graph.map((p) => [p.dir, p.scripts]));
|
|
13497
|
+
const caveats = [];
|
|
13498
|
+
if (skipped.length > 0) {
|
|
13499
|
+
caveats.push(
|
|
13500
|
+
`the workspace graph could not be fully computed: ${skipped.join(", ")} ${skipped.length === 1 ? "has" : "have"} a package.json that does not parse, has no usable \`name\`, or is shadowed by a later workspace glob, so a reverse dependency may be missing from the scoped set`
|
|
13501
|
+
);
|
|
13502
|
+
}
|
|
13503
|
+
const unmapped = affected.filter(
|
|
13504
|
+
(d) => d !== "." && !scriptsOf.has(d) && !skipped.includes(d)
|
|
13505
|
+
);
|
|
13506
|
+
const outside = changed.filter((f) => workspaceDirFor(f, globs) === null);
|
|
13507
|
+
const excluded = outside.filter((f) => isNegationExcluded(f, globs));
|
|
13508
|
+
const influential = outside.filter(
|
|
13509
|
+
(f) => !isInertLicense(f) && !f.startsWith("docs/") && !INERT_OUTSIDE_RE.test(f) && !isNegationExcluded(f, globs)
|
|
13510
|
+
);
|
|
13511
|
+
if (unmapped.length > 0) {
|
|
13512
|
+
caveats.push(
|
|
13513
|
+
`the workspace globs claim ${unmapped.join(", ")}, but no readable package.json there makes a graph member \u2014 no suite covers a change inside, and a dependent of the diff may be missing from the scoped set`
|
|
13514
|
+
);
|
|
13515
|
+
}
|
|
13516
|
+
if (influential.length > 0) {
|
|
13517
|
+
caveats.push(
|
|
13518
|
+
`${influential.length} changed file(s) sit outside every workspace and are not inert (e.g. ${influential.slice(0, 3).join(", ")}); a root script or config can affect any package's tests, and the scoped set cannot cover them`
|
|
13519
|
+
);
|
|
13520
|
+
}
|
|
13521
|
+
if (excluded.length > 0) {
|
|
13522
|
+
caveats.push(
|
|
13523
|
+
`${excluded.length} changed file(s) sit in negated workspaces (e.g. ${excluded.slice(0, 3).join(", ")}) \u2014 excluded from the npm workspace graph (a separate toolchain and lockfile); their own toolchain's suites were not run`
|
|
13524
|
+
);
|
|
13525
|
+
}
|
|
13526
|
+
const closure = reverseDependencyClosure(affected, graph);
|
|
13527
|
+
let workspaces = closure.filter((d) => scriptsOf.get(d)?.includes("test"));
|
|
13528
|
+
let fanOutCaveat;
|
|
13529
|
+
if (input.rootTestFansOut && workspaces.includes(".")) {
|
|
13530
|
+
workspaces = workspaces.filter((d) => d !== ".");
|
|
13531
|
+
fanOutCaveat = "the root's `test` script fans out over every workspace (`--workspaces`), so the root suite did not run \u2014 it cannot finish inside one command deadline; the scoped member suites are the coverage";
|
|
13532
|
+
}
|
|
13533
|
+
{
|
|
13534
|
+
const rootRuns = input.rootPackage?.scripts.includes("test") && !input.rootTestFansOut ? 1 : 0;
|
|
13535
|
+
const testable = packages.filter((p) => p.scripts.includes("test")).length + rootRuns;
|
|
13536
|
+
const scoped = workspaces.length;
|
|
13537
|
+
if (scoped * 2 > testable) {
|
|
13538
|
+
caveats.push(
|
|
13539
|
+
`the diff's reverse-dependency closure covers ${scoped} of ${testable} testable ${rootRuns ? "suites (including the root)" : "workspaces"} \u2014 more than half, so the scoped set is not a meaningful narrowing`
|
|
13540
|
+
);
|
|
13541
|
+
}
|
|
13542
|
+
}
|
|
13543
|
+
if (fanOutCaveat) caveats.push(fanOutCaveat);
|
|
13544
|
+
return caveats.length > 0 ? { workspaces, caveat: caveats.join("; ") } : { workspaces };
|
|
13545
|
+
}
|
|
13546
|
+
__name(resolveTestScope, "resolveTestScope");
|
|
13547
|
+
|
|
12825
13548
|
// packages/cli/src/commands/review/build-test.ts
|
|
13549
|
+
function shellArg(dir) {
|
|
13550
|
+
return `"${dir.replace(/[\\"$`]/g, "\\$&")}"`;
|
|
13551
|
+
}
|
|
13552
|
+
__name(shellArg, "shellArg");
|
|
12826
13553
|
function buildCommand(dir) {
|
|
12827
|
-
return dir === "." ? "npm run build" : `npm run build --workspace
|
|
13554
|
+
return dir === "." ? "npm run build" : `npm run build --workspace=${shellArg(dir)}`;
|
|
12828
13555
|
}
|
|
12829
13556
|
__name(buildCommand, "buildCommand");
|
|
12830
13557
|
function testCommand(dir) {
|
|
12831
|
-
return dir === "." ? "npm test" : `npm test --workspace
|
|
13558
|
+
return dir === "." ? "npm test" : `npm test --workspace=${shellArg(dir)}`;
|
|
12832
13559
|
}
|
|
12833
13560
|
__name(testCommand, "testCommand");
|
|
12834
13561
|
var KEEP_HEAD = 2e3;
|
|
@@ -12863,6 +13590,7 @@ ${rescued.join("\n")}
|
|
|
12863
13590
|
__name(trimOutput, "trimOutput");
|
|
12864
13591
|
var INSTALL_MIN_FREE_BYTES = 3 * 1024 ** 3;
|
|
12865
13592
|
var BUILD_MIN_FREE_BYTES = 1024 ** 3;
|
|
13593
|
+
var BUDGET_MIN_ATTEMPT_MS = 15e3;
|
|
12866
13594
|
function freeDiskBytes(dir) {
|
|
12867
13595
|
try {
|
|
12868
13596
|
const s = statfsSync(dir);
|
|
@@ -12900,7 +13628,8 @@ function run(command, cwd, timeoutMs) {
|
|
|
12900
13628
|
exitCode: r.status,
|
|
12901
13629
|
seconds: Math.round((Date.now() - started) / 1e3),
|
|
12902
13630
|
timedOut,
|
|
12903
|
-
output: trimOutput(`${r.stdout ?? ""}${r.stderr ?? ""}`)
|
|
13631
|
+
output: trimOutput(`${r.stdout ?? ""}${r.stderr ?? ""}`),
|
|
13632
|
+
deadlineMs: timeoutMs
|
|
12904
13633
|
};
|
|
12905
13634
|
}
|
|
12906
13635
|
__name(run, "run");
|
|
@@ -12921,7 +13650,7 @@ __name(unresolvedWorkspaceDeps, "unresolvedWorkspaceDeps");
|
|
|
12921
13650
|
function changedFilesFrom(planPath) {
|
|
12922
13651
|
let parsed;
|
|
12923
13652
|
try {
|
|
12924
|
-
parsed = JSON.parse(
|
|
13653
|
+
parsed = JSON.parse(readFileSync20(planPath, "utf8"));
|
|
12925
13654
|
} catch (err) {
|
|
12926
13655
|
throw new Error(
|
|
12927
13656
|
`build-test: cannot read the plan ${planPath}: ${err.message}`
|
|
@@ -12940,6 +13669,10 @@ __name(changedFilesFrom, "changedFilesFrom");
|
|
|
12940
13669
|
function runBuildTest(args) {
|
|
12941
13670
|
const root = resolve14(args.worktree);
|
|
12942
13671
|
const perCommandMs = args.timeout * 1e3;
|
|
13672
|
+
const callBudgetMs = (args.budget ?? Math.max(args.timeout, args.timeout * 2 - 30)) * 1e3;
|
|
13673
|
+
const runStarted = Date.now();
|
|
13674
|
+
const remainingMs = /* @__PURE__ */ __name(() => callBudgetMs - (Date.now() - runStarted), "remainingMs");
|
|
13675
|
+
const deadlineSecs = /* @__PURE__ */ __name((r) => Math.round((r.deadlineMs ?? perCommandMs) / 1e3), "deadlineSecs");
|
|
12943
13676
|
const exec = args.exec ?? run;
|
|
12944
13677
|
const changed = changedFilesFrom(args.plan);
|
|
12945
13678
|
const unsupportedReport = /* @__PURE__ */ __name((note) => ({
|
|
@@ -12955,15 +13688,13 @@ function runBuildTest(args) {
|
|
|
12955
13688
|
note
|
|
12956
13689
|
}), "unsupportedReport");
|
|
12957
13690
|
const globs = readWorkspaceGlobs(root);
|
|
12958
|
-
let packages = readWorkspacePackages(root);
|
|
13691
|
+
let { packages, skipped } = readWorkspacePackages(root);
|
|
13692
|
+
const rootPkg = readRootPackage(root);
|
|
12959
13693
|
let singleRoot = false;
|
|
12960
13694
|
const unmodeled = globs.length > 0 && hasUnmodeledWorkspaceGlob(globs);
|
|
12961
|
-
if (!unmodeled && globs.length === 0) {
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
packages = [rootPkg];
|
|
12965
|
-
singleRoot = true;
|
|
12966
|
-
}
|
|
13695
|
+
if (!unmodeled && globs.length === 0 && rootPkg) {
|
|
13696
|
+
packages = [rootPkg];
|
|
13697
|
+
singleRoot = true;
|
|
12967
13698
|
}
|
|
12968
13699
|
if (unmodeled || !singleRoot && (globs.length === 0 || packages.length === 0)) {
|
|
12969
13700
|
return unsupportedReport(
|
|
@@ -12971,6 +13702,15 @@ function runBuildTest(args) {
|
|
|
12971
13702
|
);
|
|
12972
13703
|
}
|
|
12973
13704
|
const affected = singleRoot ? changed.length > 0 ? ["."] : [] : affectedWorkspaces(changed, globs);
|
|
13705
|
+
let testScope = singleRoot || args.buildOnly ? void 0 : resolveTestScope({
|
|
13706
|
+
changed,
|
|
13707
|
+
globs,
|
|
13708
|
+
packages,
|
|
13709
|
+
skipped,
|
|
13710
|
+
rootPackage: rootPkg,
|
|
13711
|
+
rootTestFansOut: rootPkg?.scripts.includes("test") ? scriptFansOut(rootPkg.scriptsText["test"]) : false
|
|
13712
|
+
});
|
|
13713
|
+
const scopeGraph = !singleRoot && rootPkg ? [rootPkg, ...packages] : packages;
|
|
12974
13714
|
if (affected.length === 0) {
|
|
12975
13715
|
return {
|
|
12976
13716
|
toolchain: "npm",
|
|
@@ -12980,12 +13720,13 @@ function runBuildTest(args) {
|
|
|
12980
13720
|
install: null,
|
|
12981
13721
|
build: [],
|
|
12982
13722
|
test: [],
|
|
13723
|
+
...testScope ? { testScope } : {},
|
|
12983
13724
|
ok: true,
|
|
12984
13725
|
timedOut: [],
|
|
12985
|
-
note: `The diff changes ${changed.length} file(s), none of them inside a workspace (
|
|
13726
|
+
note: args.buildOnly ? `The diff changes ${changed.length} file(s), none of them inside a workspace. There is no package to build, and tests are out of scope for a build-only probe.` : testScope?.caveat ? `The diff changes ${changed.length} file(s), none of them inside a workspace. There is no package to build and no test to run, but the scope decision recorded a caveat: ${testScope.caveat}.` : `The diff changes ${changed.length} file(s), none of them inside a workspace (nothing the workspaces' tests can feel). There is no package to build and no test to run \u2014 this is a complete answer, not a skipped step.`
|
|
12986
13727
|
};
|
|
12987
13728
|
}
|
|
12988
|
-
const byDir = new Map(
|
|
13729
|
+
const byDir = new Map(scopeGraph.map((p) => [p.dir, p]));
|
|
12989
13730
|
const unmapped = affected.filter((d) => d !== "." && !byDir.has(d));
|
|
12990
13731
|
if (unmapped.length > 0) {
|
|
12991
13732
|
return unsupportedReport(
|
|
@@ -13004,15 +13745,15 @@ function runBuildTest(args) {
|
|
|
13004
13745
|
timedOut: [],
|
|
13005
13746
|
note: ""
|
|
13006
13747
|
};
|
|
13007
|
-
const npmLock = existsSync7(
|
|
13008
|
-
const installComplete = /* @__PURE__ */ __name(() => existsSync7(
|
|
13009
|
-
if (args.install && !npmLock && !existsSync7(
|
|
13748
|
+
const npmLock = existsSync7(join15(root, "package-lock.json"));
|
|
13749
|
+
const installComplete = /* @__PURE__ */ __name(() => existsSync7(join15(root, "node_modules", ".package-lock.json")), "installComplete");
|
|
13750
|
+
if (args.install && !npmLock && !existsSync7(join15(root, "node_modules"))) {
|
|
13010
13751
|
const altLock = [
|
|
13011
13752
|
["yarn.lock", "yarn install --frozen-lockfile"],
|
|
13012
13753
|
["pnpm-lock.yaml", "pnpm install --frozen-lockfile"],
|
|
13013
13754
|
["bun.lockb", "bun install --frozen-lockfile"],
|
|
13014
13755
|
["bun.lock", "bun install --frozen-lockfile"]
|
|
13015
|
-
].find(([f]) => existsSync7(
|
|
13756
|
+
].find(([f]) => existsSync7(join15(root, f)));
|
|
13016
13757
|
return unsupportedReport(
|
|
13017
13758
|
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."
|
|
13018
13759
|
);
|
|
@@ -13025,12 +13766,21 @@ function runBuildTest(args) {
|
|
|
13025
13766
|
results.note = `Insufficient disk space (${gib(free)}G free, need ~${gib(INSTALL_MIN_FREE_BYTES)}G): skipped \`${installCmd}\`, so nothing could be built or tested. This is an environment issue, not a code finding \u2014 report it as informational.`;
|
|
13026
13767
|
return results;
|
|
13027
13768
|
}
|
|
13028
|
-
|
|
13769
|
+
if (remainingMs() < BUDGET_MIN_ATTEMPT_MS) {
|
|
13770
|
+
results.ok = false;
|
|
13771
|
+
results.note = `The whole-call budget was spent before the install could start (${args.budget != null ? `--budget ${args.budget}s` : "default budget"}), so nothing could be built or tested. This is an infrastructure result, not a defect in the diff \u2014 report it as informational.`;
|
|
13772
|
+
return results;
|
|
13773
|
+
}
|
|
13774
|
+
const install = exec(
|
|
13775
|
+
installCmd,
|
|
13776
|
+
root,
|
|
13777
|
+
Math.min(perCommandMs, remainingMs())
|
|
13778
|
+
);
|
|
13029
13779
|
results.install = install;
|
|
13030
13780
|
if (install.timedOut) results.timedOut.push(install.command);
|
|
13031
13781
|
if (install.timedOut) {
|
|
13032
13782
|
try {
|
|
13033
|
-
rmSync5(
|
|
13783
|
+
rmSync5(join15(root, "node_modules"), {
|
|
13034
13784
|
recursive: true,
|
|
13035
13785
|
force: true,
|
|
13036
13786
|
maxRetries: 3
|
|
@@ -13040,7 +13790,7 @@ function runBuildTest(args) {
|
|
|
13040
13790
|
}
|
|
13041
13791
|
if (install.timedOut || !installComplete()) {
|
|
13042
13792
|
results.ok = false;
|
|
13043
|
-
results.note = install.timedOut ? `\`${install.command}\` ran out of time (${
|
|
13793
|
+
results.note = install.timedOut ? `\`${install.command}\` ran out of time (${deadlineSecs(install)}s) and left an incomplete \`node_modules\`, so nothing could be built or tested against it. This is an infrastructure result, not a defect in the diff \u2014 report it as informational.` : "The install failed and left no usable `node_modules`, so nothing could be built or tested. This is an environment failure, not a defect in the diff \u2014 report it as informational.";
|
|
13044
13794
|
return results;
|
|
13045
13795
|
}
|
|
13046
13796
|
}
|
|
@@ -13051,9 +13801,12 @@ function runBuildTest(args) {
|
|
|
13051
13801
|
return results;
|
|
13052
13802
|
}
|
|
13053
13803
|
const alsoBuild = [];
|
|
13054
|
-
let set = buildSetFor(affected,
|
|
13804
|
+
let set = buildSetFor(affected, scopeGraph);
|
|
13055
13805
|
const built = /* @__PURE__ */ new Set();
|
|
13056
13806
|
const widened = /* @__PURE__ */ new Set();
|
|
13807
|
+
const rootBuildRuns = !!rootPkg?.scripts.includes("build") && !scriptFansOut(rootPkg.scriptsText["build"]);
|
|
13808
|
+
const rootBuildSkipped = !singleRoot && !rootBuildRuns;
|
|
13809
|
+
const notBuilt = [];
|
|
13057
13810
|
for (let attempt = 0; attempt <= 3; attempt++) {
|
|
13058
13811
|
let failure = null;
|
|
13059
13812
|
for (const dir of set) {
|
|
@@ -13063,7 +13816,23 @@ function runBuildTest(args) {
|
|
|
13063
13816
|
built.add(dir);
|
|
13064
13817
|
continue;
|
|
13065
13818
|
}
|
|
13066
|
-
|
|
13819
|
+
if (dir === "." && rootBuildSkipped) {
|
|
13820
|
+
built.add(dir);
|
|
13821
|
+
continue;
|
|
13822
|
+
}
|
|
13823
|
+
if (remainingMs() < BUDGET_MIN_ATTEMPT_MS) {
|
|
13824
|
+
notBuilt.push(
|
|
13825
|
+
...set.filter(
|
|
13826
|
+
(d) => !built.has(d) && byDir.get(d)?.scripts.includes("build")
|
|
13827
|
+
)
|
|
13828
|
+
);
|
|
13829
|
+
break;
|
|
13830
|
+
}
|
|
13831
|
+
const r = exec(
|
|
13832
|
+
buildCommand(dir),
|
|
13833
|
+
root,
|
|
13834
|
+
Math.min(perCommandMs, remainingMs())
|
|
13835
|
+
);
|
|
13067
13836
|
results.build.push(r);
|
|
13068
13837
|
if (r.timedOut) results.timedOut.push(r.command);
|
|
13069
13838
|
if (r.exitCode !== 0) {
|
|
@@ -13079,8 +13848,8 @@ function runBuildTest(args) {
|
|
|
13079
13848
|
});
|
|
13080
13849
|
if (missing.length === 0 || failure.timedOut || attempt === 3) {
|
|
13081
13850
|
results.ok = false;
|
|
13082
|
-
results.note = failure.timedOut ? `\`${failure.command}\` ran out of time (${
|
|
13083
|
-
results.buildSet = set;
|
|
13851
|
+
results.note = failure.timedOut ? `\`${failure.command}\` ran out of time (${deadlineSecs(failure)}s). That is an infrastructure result, not a defect in the diff \u2014 report it as informational.` : `\`${failure.command}\` failed. Correlate the errors below with the diff: a compile error in a file the PR changed is a Critical; one in a file it did not touch is a pre-existing failure, and belongs in the terminal, not on the PR.`;
|
|
13852
|
+
results.buildSet = (rootBuildSkipped ? set.filter((d) => d !== ".") : set).filter((d) => !notBuilt.includes(d));
|
|
13084
13853
|
results.widenedWith = [...widened];
|
|
13085
13854
|
return results;
|
|
13086
13855
|
}
|
|
@@ -13090,31 +13859,85 @@ function runBuildTest(args) {
|
|
|
13090
13859
|
const dir = packages.find((p) => p.name === name)?.dir;
|
|
13091
13860
|
if (dir) alsoBuild.push(dir);
|
|
13092
13861
|
}
|
|
13093
|
-
set = buildSetFor(affected,
|
|
13862
|
+
set = buildSetFor(affected, scopeGraph, alsoBuild);
|
|
13094
13863
|
}
|
|
13095
|
-
results.buildSet = set;
|
|
13864
|
+
results.buildSet = (rootBuildSkipped ? set.filter((d) => d !== ".") : set).filter((d) => !notBuilt.includes(d));
|
|
13096
13865
|
results.widenedWith = [...widened];
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13866
|
+
if (notBuilt.length > 0) results.notBuilt = [...notBuilt].sort();
|
|
13867
|
+
const rootHasTest = !!rootPkg?.scripts.includes("test");
|
|
13868
|
+
const testDirs = args.buildOnly ? [] : !testScope ? affected : testScope.workspaces;
|
|
13869
|
+
const runnable = /* @__PURE__ */ __name((dir) => dir === "." ? rootHasTest : !!byDir.get(dir)?.scripts.includes("test"), "runnable");
|
|
13870
|
+
const affectedSet = new Set(affected);
|
|
13871
|
+
const runnableDirs = [
|
|
13872
|
+
...testDirs.filter((d) => affectedSet.has(d) && runnable(d)),
|
|
13873
|
+
...testDirs.filter((d) => !affectedSet.has(d) && runnable(d))
|
|
13874
|
+
];
|
|
13875
|
+
const untestable = notBuilt.length > 0 ? new Set(reverseDependencyClosure(notBuilt, scopeGraph)) : /* @__PURE__ */ new Set();
|
|
13876
|
+
const notRun = [];
|
|
13877
|
+
for (let i = 0; i < runnableDirs.length; i++) {
|
|
13878
|
+
const dir = runnableDirs[i];
|
|
13879
|
+
if (untestable.has(dir)) {
|
|
13880
|
+
notRun.push(dir);
|
|
13881
|
+
continue;
|
|
13882
|
+
}
|
|
13883
|
+
const remaining = remainingMs();
|
|
13884
|
+
if (remaining < BUDGET_MIN_ATTEMPT_MS) {
|
|
13885
|
+
notRun.push(...runnableDirs.slice(i).filter((d) => !untestable.has(d)));
|
|
13886
|
+
break;
|
|
13887
|
+
}
|
|
13888
|
+
const r = exec(testCommand(dir), root, Math.min(perCommandMs, remaining));
|
|
13101
13889
|
results.test.push(r);
|
|
13102
13890
|
if (r.timedOut) results.timedOut.push(r.command);
|
|
13103
13891
|
if (r.exitCode !== 0) results.ok = false;
|
|
13104
13892
|
}
|
|
13893
|
+
notRun.sort();
|
|
13894
|
+
const partialNote = [
|
|
13895
|
+
notBuilt.length > 0 ? `the build phase reached the whole-call budget \u2014 not built: ` + notBuilt.join(", ") : "",
|
|
13896
|
+
notRun.length > 0 ? `the whole-call budget (${Math.round(callBudgetMs / 1e3)}s) was spent with ${notRun.length} suite(s) still to run \u2014 not run: ` + notRun.join(", ") : ""
|
|
13897
|
+
].filter(Boolean).join("; ") || void 0;
|
|
13898
|
+
if (testScope && partialNote) {
|
|
13899
|
+
const ran = testScope.workspaces.filter((d) => !notRun.includes(d));
|
|
13900
|
+
testScope = {
|
|
13901
|
+
workspaces: ran,
|
|
13902
|
+
...notRun.length > 0 ? { notRun } : {},
|
|
13903
|
+
caveat: testScope.caveat ? `${testScope.caveat}; ${partialNote}` : partialNote
|
|
13904
|
+
};
|
|
13905
|
+
}
|
|
13906
|
+
if (testScope) results.testScope = testScope;
|
|
13105
13907
|
if (!results.note) {
|
|
13106
13908
|
const failed = [...results.build, ...results.test].filter(
|
|
13107
13909
|
(r) => r.exitCode !== 0
|
|
13108
13910
|
);
|
|
13109
13911
|
const realFailures = failed.filter((r) => !r.timedOut);
|
|
13110
13912
|
if (results.ok) {
|
|
13111
|
-
|
|
13913
|
+
let testsClause;
|
|
13914
|
+
if (args.buildOnly) {
|
|
13915
|
+
testsClause = ". Tests were not run (build-only).";
|
|
13916
|
+
} else if (!testScope) {
|
|
13917
|
+
testsClause = results.test.length === 0 ? ", but the package defines no test script, so no tests ran." : " and ran the tests of the changed ones. Everything passed.";
|
|
13918
|
+
} else if (testScope.workspaces.length === 0) {
|
|
13919
|
+
testsClause = testScope.notRun?.length ? ", but the whole-call budget was spent before any suite could run." : ", but no workspace in scope defines a test script, so no tests ran.";
|
|
13920
|
+
} else {
|
|
13921
|
+
testsClause = ` and ran the tests scoped to ${testScope.workspaces.join(", ")} \u2014 the changed workspaces and every workspace declared to depend on them that defines a test script. Everything passed.`;
|
|
13922
|
+
}
|
|
13923
|
+
if (testScope?.caveat) testsClause += ` Caveat: ${testScope.caveat}.`;
|
|
13924
|
+
const builtWorkspaces = results.buildSet.filter(
|
|
13925
|
+
(d) => singleRoot || d !== "."
|
|
13926
|
+
).length;
|
|
13927
|
+
const rootSuffix = !singleRoot && results.buildSet.includes(".") && !rootBuildSkipped ? " (plus the root package)" : "";
|
|
13928
|
+
results.note = `Built ${builtWorkspaces} of ${packages.length} workspaces${rootSuffix} (the ${affected.length} the diff changes, plus what they compile against${widened.size ? `, plus ${[...widened].join(", ")} the compiler asked for` : ""})${testsClause}`;
|
|
13112
13929
|
} else if (realFailures.length === 0) {
|
|
13113
|
-
results.note = `${failed.length} command(s) ran out of time (${
|
|
13930
|
+
results.note = `${failed.length} command(s) ran out of time (${deadlineSecs(failed[0])}s). A timeout is an infrastructure result, not a defect in the diff \u2014 report it as informational.`;
|
|
13114
13931
|
} else {
|
|
13115
13932
|
results.note = `${realFailures.length} command(s) failed. Correlate each error with the diff: a failure in a file the PR changed is a Critical; one in a file it did not touch is pre-existing.` + (failed.length > realFailures.length ? " (Commands that timed out are infrastructure, not findings.)" : "");
|
|
13116
13933
|
}
|
|
13117
13934
|
}
|
|
13935
|
+
if (results.testScope?.caveat && !results.note.includes("Caveat:")) {
|
|
13936
|
+
results.note += ` Caveat: ${results.testScope.caveat}.`;
|
|
13937
|
+
}
|
|
13938
|
+
if (partialNote && !results.testScope) {
|
|
13939
|
+
results.note = results.note ? `${results.note} ${partialNote}.` : partialNote;
|
|
13940
|
+
}
|
|
13118
13941
|
if (results.install && results.install.exitCode !== 0) {
|
|
13119
13942
|
results.note = `\`${results.install.command}\` exited ${results.install.exitCode} but left a usable \`node_modules\`, so the build and test below ran anyway and their results stand. The install failure is an environment/infrastructure result \u2014 report it as informational, never as a Critical, and never against this PR. ` + results.note;
|
|
13120
13943
|
}
|
|
@@ -13123,7 +13946,7 @@ function runBuildTest(args) {
|
|
|
13123
13946
|
__name(runBuildTest, "runBuildTest");
|
|
13124
13947
|
var buildTestCommand = {
|
|
13125
13948
|
command: "build-test",
|
|
13126
|
-
describe: "Build
|
|
13949
|
+
describe: "Build the workspaces the diff changes (and what they compile against), test those plus their dependents, with a deadline the commands can actually meet",
|
|
13127
13950
|
builder: /* @__PURE__ */ __name((yargs) => yargs.option("plan", {
|
|
13128
13951
|
type: "string",
|
|
13129
13952
|
demandOption: true,
|
|
@@ -13138,7 +13961,10 @@ var buildTestCommand = {
|
|
|
13138
13961
|
}).option("timeout", {
|
|
13139
13962
|
type: "number",
|
|
13140
13963
|
default: 300,
|
|
13141
|
-
describe: "Per-command deadline in seconds. Kept strictly below the 600s (600000ms) tool timeout the agent's brief welds onto the whole call, so a single hung command's own deadline fires \u2014 and build-test reports it as data \u2014 before the outer shell kill would discard the report.
|
|
13964
|
+
describe: "Per-command deadline in seconds. Kept strictly below the 600s (600000ms) tool timeout the agent's brief welds onto the whole call, so a single hung command's own deadline fires \u2014 and build-test reports it as data \u2014 before the outer shell kill would discard the report. Commands that would SUM past the whole call are stopped and disclosed instead \u2014 see --budget."
|
|
13965
|
+
}).option("budget", {
|
|
13966
|
+
type: "number",
|
|
13967
|
+
describe: "Whole-call wall-clock budget in seconds, measured from the top of the call \u2014 install and build time count against it (default: 2\xD7 --timeout minus 30s of headroom for process startup and the report write). Each suite is attempted with whatever of the budget remains \u2014 a suite killed at the boundary is a timeout, reported as infrastructure \u2014 and only suites never attempted are named notRun. A partial report survives where the outer shell kill would discard the whole one."
|
|
13142
13968
|
}).option("install", {
|
|
13143
13969
|
type: "boolean",
|
|
13144
13970
|
default: true,
|
|
@@ -13153,7 +13979,7 @@ var buildTestCommand = {
|
|
|
13153
13979
|
try {
|
|
13154
13980
|
const report = runBuildTest(args);
|
|
13155
13981
|
if (args.out) {
|
|
13156
|
-
|
|
13982
|
+
writeFileSync18(args.out, JSON.stringify(report, null, 2));
|
|
13157
13983
|
}
|
|
13158
13984
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
13159
13985
|
} catch (err) {
|
|
@@ -13168,13 +13994,13 @@ init_esbuild_shims();
|
|
|
13168
13994
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
13169
13995
|
import {
|
|
13170
13996
|
existsSync as existsSync8,
|
|
13171
|
-
mkdirSync as
|
|
13172
|
-
readFileSync as
|
|
13997
|
+
mkdirSync as mkdirSync18,
|
|
13998
|
+
readFileSync as readFileSync21,
|
|
13173
13999
|
rmSync as rmSync7,
|
|
13174
|
-
statSync as
|
|
13175
|
-
writeFileSync as
|
|
14000
|
+
statSync as statSync9,
|
|
14001
|
+
writeFileSync as writeFileSync19
|
|
13176
14002
|
} from "node:fs";
|
|
13177
|
-
import { dirname as dirname15, join as
|
|
14003
|
+
import { dirname as dirname15, join as join16, resolve as resolve15 } from "node:path";
|
|
13178
14004
|
|
|
13179
14005
|
// packages/cli/src/commands/review/lib/worktree.ts
|
|
13180
14006
|
init_esbuild_shims();
|
|
@@ -13221,7 +14047,7 @@ function runBaseTree(args) {
|
|
|
13221
14047
|
}), "unavailable");
|
|
13222
14048
|
let plan;
|
|
13223
14049
|
try {
|
|
13224
|
-
plan = JSON.parse(
|
|
14050
|
+
plan = JSON.parse(readFileSync21(args.plan, "utf8"));
|
|
13225
14051
|
} catch (err) {
|
|
13226
14052
|
return unavailable(
|
|
13227
14053
|
`cannot read the plan ${args.plan}: ${err.message}`
|
|
@@ -13243,10 +14069,10 @@ function runBaseTree(args) {
|
|
|
13243
14069
|
return unavailable(`the review worktree ${worktree} does not exist`);
|
|
13244
14070
|
}
|
|
13245
14071
|
const tree = baseWorktreePath(worktree);
|
|
13246
|
-
const marker = /* @__PURE__ */ __name(() =>
|
|
13247
|
-
const failedMarker = /* @__PURE__ */ __name(() =>
|
|
14072
|
+
const marker = /* @__PURE__ */ __name(() => join16(tree, ".qwen-review-base-ok"), "marker");
|
|
14073
|
+
const failedMarker = /* @__PURE__ */ __name(() => join16(tree, ".qwen-review-base-failed"), "failedMarker");
|
|
13248
14074
|
try {
|
|
13249
|
-
if (existsSync8(tree) &&
|
|
14075
|
+
if (existsSync8(tree) && readFileSync21(marker(), "utf8").trim() === baseSha && gitOut(tree, "rev-parse", "HEAD") === baseSha) {
|
|
13250
14076
|
return {
|
|
13251
14077
|
available: true,
|
|
13252
14078
|
path: tree,
|
|
@@ -13258,7 +14084,7 @@ function runBaseTree(args) {
|
|
|
13258
14084
|
} catch {
|
|
13259
14085
|
}
|
|
13260
14086
|
try {
|
|
13261
|
-
if (existsSync8(tree) &&
|
|
14087
|
+
if (existsSync8(tree) && readFileSync21(failedMarker(), "utf8").trim() === baseSha) {
|
|
13262
14088
|
return {
|
|
13263
14089
|
available: false,
|
|
13264
14090
|
path: tree,
|
|
@@ -13271,14 +14097,14 @@ function runBaseTree(args) {
|
|
|
13271
14097
|
}
|
|
13272
14098
|
const lock = `${tree}.lock`;
|
|
13273
14099
|
try {
|
|
13274
|
-
const age = Date.now() -
|
|
14100
|
+
const age = Date.now() - statSync9(lock).mtimeMs;
|
|
13275
14101
|
if (age > 30 * 60 * 1e3) {
|
|
13276
14102
|
rmSync7(lock, { recursive: true, force: true });
|
|
13277
14103
|
}
|
|
13278
14104
|
} catch {
|
|
13279
14105
|
}
|
|
13280
14106
|
try {
|
|
13281
|
-
|
|
14107
|
+
mkdirSync18(lock);
|
|
13282
14108
|
} catch (err) {
|
|
13283
14109
|
if (err.code !== "EEXIST") {
|
|
13284
14110
|
return unavailable(
|
|
@@ -13314,9 +14140,18 @@ function runBaseTree(args) {
|
|
|
13314
14140
|
// tree to run against.
|
|
13315
14141
|
buildOnly: true
|
|
13316
14142
|
});
|
|
14143
|
+
if ((build.notBuilt?.length ?? 0) > 0) {
|
|
14144
|
+
return {
|
|
14145
|
+
available: false,
|
|
14146
|
+
path: tree,
|
|
14147
|
+
baseSha: baseSha2,
|
|
14148
|
+
build,
|
|
14149
|
+
note: `the base tree build at ${baseSha2.slice(0, 9)} was cut short by the whole-call budget (${build.notBuilt.join(", ")} not built), so a rerun against it would manufacture pre-existing failures; an A/B is not available for this review (this is an infrastructure result, never a finding against the PR)`
|
|
14150
|
+
};
|
|
14151
|
+
}
|
|
13317
14152
|
if (!build.ok || build.toolchain !== "npm" || build.build.length === 0) {
|
|
13318
14153
|
try {
|
|
13319
|
-
|
|
14154
|
+
writeFileSync19(failedMarker(), `${baseSha2}
|
|
13320
14155
|
`);
|
|
13321
14156
|
} catch {
|
|
13322
14157
|
}
|
|
@@ -13329,7 +14164,7 @@ function runBaseTree(args) {
|
|
|
13329
14164
|
};
|
|
13330
14165
|
}
|
|
13331
14166
|
try {
|
|
13332
|
-
|
|
14167
|
+
writeFileSync19(marker(), `${baseSha2}
|
|
13333
14168
|
`);
|
|
13334
14169
|
} catch {
|
|
13335
14170
|
}
|
|
@@ -13369,8 +14204,8 @@ var baseTreeCommand = {
|
|
|
13369
14204
|
try {
|
|
13370
14205
|
const report = runBaseTree(args);
|
|
13371
14206
|
if (args.out) {
|
|
13372
|
-
|
|
13373
|
-
|
|
14207
|
+
mkdirSync18(dirname15(resolve15(args.out)), { recursive: true });
|
|
14208
|
+
writeFileSync19(resolve15(args.out), JSON.stringify(report, null, 2));
|
|
13374
14209
|
}
|
|
13375
14210
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
13376
14211
|
writeStderrLine(`base-tree: ${report.note}`);
|
|
@@ -13384,7 +14219,7 @@ var baseTreeCommand = {
|
|
|
13384
14219
|
// packages/cli/src/commands/review/test-delta.ts
|
|
13385
14220
|
init_esbuild_shims();
|
|
13386
14221
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
13387
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
14222
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync19, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "node:fs";
|
|
13388
14223
|
import { dirname as dirname16, resolve as resolve16 } from "node:path";
|
|
13389
14224
|
var ANSI_SGR_RE2 = /\x1b\[[0-9;]*m/g;
|
|
13390
14225
|
var RERUNNABLE_COMMAND_RE = /^npm test(?: --workspace="[\w@./-]+")?$/;
|
|
@@ -13458,7 +14293,7 @@ function runTestDelta(args) {
|
|
|
13458
14293
|
}), "empty");
|
|
13459
14294
|
let report;
|
|
13460
14295
|
try {
|
|
13461
|
-
report = JSON.parse(
|
|
14296
|
+
report = JSON.parse(readFileSync22(args.report, "utf8"));
|
|
13462
14297
|
} catch (err) {
|
|
13463
14298
|
return empty(
|
|
13464
14299
|
`cannot read the build-test report ${args.report}: ${err.message}`
|
|
@@ -13600,8 +14435,8 @@ var testDeltaCommand = {
|
|
|
13600
14435
|
const args = argv;
|
|
13601
14436
|
const report = runTestDelta(args);
|
|
13602
14437
|
if (args.out) {
|
|
13603
|
-
|
|
13604
|
-
|
|
14438
|
+
mkdirSync19(dirname16(resolve16(args.out)), { recursive: true });
|
|
14439
|
+
writeFileSync20(resolve16(args.out), JSON.stringify(report, null, 2));
|
|
13605
14440
|
}
|
|
13606
14441
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
13607
14442
|
writeStderrLine(`test-delta: ${report.note}`);
|
|
@@ -13612,14 +14447,14 @@ var testDeltaCommand = {
|
|
|
13612
14447
|
init_esbuild_shims();
|
|
13613
14448
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
13614
14449
|
import {
|
|
13615
|
-
mkdirSync as
|
|
13616
|
-
writeFileSync as
|
|
13617
|
-
readFileSync as
|
|
14450
|
+
mkdirSync as mkdirSync20,
|
|
14451
|
+
writeFileSync as writeFileSync21,
|
|
14452
|
+
readFileSync as readFileSync23,
|
|
13618
14453
|
existsSync as existsSync10,
|
|
13619
14454
|
rmSync as rmSync8,
|
|
13620
|
-
statSync as
|
|
14455
|
+
statSync as statSync10
|
|
13621
14456
|
} from "node:fs";
|
|
13622
|
-
import { dirname as dirname17, join as
|
|
14457
|
+
import { dirname as dirname17, join as join17, resolve as resolve17 } from "node:path";
|
|
13623
14458
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
13624
14459
|
var SERVER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
13625
14460
|
function shellQuote(v) {
|
|
@@ -13631,7 +14466,7 @@ var LOG_MAX_BYTES = 8 * 1024 * 1024;
|
|
|
13631
14466
|
var POLL_MS = 250;
|
|
13632
14467
|
function logBytes(p) {
|
|
13633
14468
|
try {
|
|
13634
|
-
return
|
|
14469
|
+
return statSync10(p).size;
|
|
13635
14470
|
} catch {
|
|
13636
14471
|
return 0;
|
|
13637
14472
|
}
|
|
@@ -13737,13 +14572,13 @@ function runDrive(args) {
|
|
|
13737
14572
|
waitMs(POLL_MS);
|
|
13738
14573
|
}
|
|
13739
14574
|
}
|
|
13740
|
-
const dir =
|
|
13741
|
-
|
|
13742
|
-
const scriptPath =
|
|
13743
|
-
const logPath = args.logPath ??
|
|
13744
|
-
const sentinelPath =
|
|
14575
|
+
const dir = join17(tmpdir2(), `qwen-review-drive-${server}`);
|
|
14576
|
+
mkdirSync20(dir, { recursive: true });
|
|
14577
|
+
const scriptPath = join17(dir, "drive.sh");
|
|
14578
|
+
const logPath = args.logPath ?? join17(dir, "drive.log");
|
|
14579
|
+
const sentinelPath = join17(dir, "drive.rc");
|
|
13745
14580
|
rmSync8(sentinelPath, { force: true });
|
|
13746
|
-
|
|
14581
|
+
writeFileSync21(scriptPath, wrapScript(args.script, sentinelPath), "utf8");
|
|
13747
14582
|
const droveFrom = Date.now();
|
|
13748
14583
|
let output = "";
|
|
13749
14584
|
let exitCode = null;
|
|
@@ -13773,8 +14608,8 @@ function runDrive(args) {
|
|
|
13773
14608
|
}
|
|
13774
14609
|
const deadline = droveFrom + args.timeout * 1e3;
|
|
13775
14610
|
for (; ; ) {
|
|
13776
|
-
output = existsSync10(logPath) ?
|
|
13777
|
-
exitCode = existsSync10(sentinelPath) ? sentinelExitCode(
|
|
14611
|
+
output = existsSync10(logPath) ? readFileSync23(logPath, "utf8") : "";
|
|
14612
|
+
exitCode = existsSync10(sentinelPath) ? sentinelExitCode(readFileSync23(sentinelPath, "utf8")) : null;
|
|
13778
14613
|
if (exitCode !== null) {
|
|
13779
14614
|
outcome = "completed";
|
|
13780
14615
|
break;
|
|
@@ -13841,8 +14676,8 @@ var driveCommand = {
|
|
|
13841
14676
|
const args = argv;
|
|
13842
14677
|
const report = runDrive(args);
|
|
13843
14678
|
if (args.out) {
|
|
13844
|
-
|
|
13845
|
-
|
|
14679
|
+
mkdirSync20(dirname17(resolve17(args.out)), { recursive: true });
|
|
14680
|
+
writeFileSync21(resolve17(args.out), JSON.stringify(report, null, 2));
|
|
13846
14681
|
}
|
|
13847
14682
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
13848
14683
|
writeStderrLine(`drive: ${report.note}`);
|
|
@@ -13859,7 +14694,7 @@ init_esbuild_shims();
|
|
|
13859
14694
|
import {
|
|
13860
14695
|
createServer
|
|
13861
14696
|
} from "node:http";
|
|
13862
|
-
import { appendFileSync, mkdirSync as
|
|
14697
|
+
import { appendFileSync, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22 } from "node:fs";
|
|
13863
14698
|
import { dirname as dirname18, resolve as resolve18 } from "node:path";
|
|
13864
14699
|
import { pathToFileURL } from "node:url";
|
|
13865
14700
|
function messagesText(body) {
|
|
@@ -14116,8 +14951,8 @@ async function startMockProvider(args, respondOverride) {
|
|
|
14116
14951
|
respond = fn;
|
|
14117
14952
|
}
|
|
14118
14953
|
const logPath = resolve18(args.log);
|
|
14119
|
-
|
|
14120
|
-
|
|
14954
|
+
mkdirSync21(dirname18(logPath), { recursive: true });
|
|
14955
|
+
writeFileSync22(logPath, "");
|
|
14121
14956
|
let n = 0;
|
|
14122
14957
|
const record = /* @__PURE__ */ __name((entry) => appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
14123
14958
|
`), "record");
|
|
@@ -14279,8 +15114,8 @@ var mockProviderCommand = {
|
|
|
14279
15114
|
const args = argv;
|
|
14280
15115
|
const { report, close } = await startMockProvider(args);
|
|
14281
15116
|
if (args.out) {
|
|
14282
|
-
|
|
14283
|
-
|
|
15117
|
+
mkdirSync21(dirname18(resolve18(args.out)), { recursive: true });
|
|
15118
|
+
writeFileSync22(resolve18(args.out), JSON.stringify(report, null, 2));
|
|
14284
15119
|
}
|
|
14285
15120
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
14286
15121
|
writeStderrLine(`mock-provider: ${report.note}`);
|
|
@@ -14298,7 +15133,7 @@ var mockProviderCommand = {
|
|
|
14298
15133
|
// packages/cli/src/commands/review/extract-step.ts
|
|
14299
15134
|
init_esbuild_shims();
|
|
14300
15135
|
var import_yaml = __toESM(require_dist(), 1);
|
|
14301
|
-
import { mkdirSync as
|
|
15136
|
+
import { mkdirSync as mkdirSync22, readFileSync as readFileSync24, writeFileSync as writeFileSync23, chmodSync } from "node:fs";
|
|
14302
15137
|
import { dirname as dirname19, resolve as resolve19 } from "node:path";
|
|
14303
15138
|
function expressionsOf(...texts) {
|
|
14304
15139
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14546,7 +15381,7 @@ function runExtractStep(args) {
|
|
|
14546
15381
|
const wfPath = resolve19(args.workflow);
|
|
14547
15382
|
let text;
|
|
14548
15383
|
try {
|
|
14549
|
-
text =
|
|
15384
|
+
text = readFileSync24(wfPath, "utf8");
|
|
14550
15385
|
} catch (err) {
|
|
14551
15386
|
throw new Error(
|
|
14552
15387
|
`extract-step: cannot read ${args.workflow}: ${err.message}`
|
|
@@ -14613,7 +15448,7 @@ function runExtractStep(args) {
|
|
|
14613
15448
|
const script = step.run;
|
|
14614
15449
|
const stepLabel = String(step.name ?? step.id ?? index);
|
|
14615
15450
|
const outPath = resolve19(args.out);
|
|
14616
|
-
|
|
15451
|
+
mkdirSync22(dirname19(outPath), { recursive: true });
|
|
14617
15452
|
const header = [
|
|
14618
15453
|
`#!/usr/bin/env ${shellCommand}`,
|
|
14619
15454
|
...commentLines(
|
|
@@ -14637,7 +15472,7 @@ function runExtractStep(args) {
|
|
|
14637
15472
|
),
|
|
14638
15473
|
""
|
|
14639
15474
|
].join("\n");
|
|
14640
|
-
|
|
15475
|
+
writeFileSync23(outPath, header + script + (script.endsWith("\n") ? "" : "\n"));
|
|
14641
15476
|
chmodSync(outPath, 493);
|
|
14642
15477
|
return {
|
|
14643
15478
|
workflow: args.workflow,
|
|
@@ -14698,7 +15533,7 @@ var extractStepCommand = {
|
|
|
14698
15533
|
|
|
14699
15534
|
// packages/cli/src/commands/review/submit.ts
|
|
14700
15535
|
init_esbuild_shims();
|
|
14701
|
-
import { mkdirSync as
|
|
15536
|
+
import { mkdirSync as mkdirSync23, readFileSync as readFileSync25 } from "node:fs";
|
|
14702
15537
|
|
|
14703
15538
|
// packages/cli/src/commands/review/lib/receipt.ts
|
|
14704
15539
|
init_esbuild_shims();
|
|
@@ -14720,7 +15555,7 @@ __name(parseReceiptIds, "parseReceiptIds");
|
|
|
14720
15555
|
var EVENTS = /* @__PURE__ */ new Set(["APPROVE", "REQUEST_CHANGES", "COMMENT"]);
|
|
14721
15556
|
function readReceiptIds(receiptPath) {
|
|
14722
15557
|
try {
|
|
14723
|
-
return parseReceiptIds(
|
|
15558
|
+
return parseReceiptIds(readFileSync25(receiptPath, "utf8"));
|
|
14724
15559
|
} catch {
|
|
14725
15560
|
return [];
|
|
14726
15561
|
}
|
|
@@ -14730,6 +15565,24 @@ function isDiffLine(n) {
|
|
|
14730
15565
|
return typeof n === "number" && Number.isSafeInteger(n) && n > 0;
|
|
14731
15566
|
}
|
|
14732
15567
|
__name(isDiffLine, "isDiffLine");
|
|
15568
|
+
function normalizeInlineComments(comments, modelId, cliVersion) {
|
|
15569
|
+
if (typeof modelId !== "string" || modelId.trim() === "") return comments;
|
|
15570
|
+
const footer = reviewFooter(modelId, cliVersion);
|
|
15571
|
+
return comments.map(
|
|
15572
|
+
(comment) => (
|
|
15573
|
+
// An empty body stays empty: this runs BEFORE the consistency check, and
|
|
15574
|
+
// a footer pasted onto '' would hide the emptiness from the refusal that
|
|
15575
|
+
// names it ('has no body — an empty comment').
|
|
15576
|
+
typeof comment.body === "string" && comment.body.trim() !== "" ? {
|
|
15577
|
+
...comment,
|
|
15578
|
+
body: `${comment.body.replace(REVIEW_FOOTER_RE, "")}
|
|
15579
|
+
|
|
15580
|
+
${footer}`
|
|
15581
|
+
} : comment
|
|
15582
|
+
)
|
|
15583
|
+
);
|
|
15584
|
+
}
|
|
15585
|
+
__name(normalizeInlineComments, "normalizeInlineComments");
|
|
14733
15586
|
function authorization(args) {
|
|
14734
15587
|
return reviewWriteAuthorization({
|
|
14735
15588
|
userAuthorized: args.userAuthorized,
|
|
@@ -14775,6 +15628,18 @@ __name(compose, "compose");
|
|
|
14775
15628
|
function structuralProblems(payload) {
|
|
14776
15629
|
const problems = [];
|
|
14777
15630
|
if (!payload.commit_id) problems.push("`commit_id` is missing");
|
|
15631
|
+
if (payload.comments !== void 0 && !Array.isArray(payload.comments)) {
|
|
15632
|
+
problems.push(
|
|
15633
|
+
"`comments` is not an array \u2014 it is the list of findings this post carries; any other shape is not a list of findings."
|
|
15634
|
+
);
|
|
15635
|
+
}
|
|
15636
|
+
if (Array.isArray(payload.comments) && payload.comments.some(
|
|
15637
|
+
(c) => c === null || typeof c !== "object"
|
|
15638
|
+
)) {
|
|
15639
|
+
problems.push(
|
|
15640
|
+
"`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."
|
|
15641
|
+
);
|
|
15642
|
+
}
|
|
14778
15643
|
if (payload.event !== void 0 || payload.body !== void 0) {
|
|
14779
15644
|
problems.push(
|
|
14780
15645
|
"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.)"
|
|
@@ -14855,7 +15720,7 @@ function runSubmit(args, cliVersion = "unknown") {
|
|
|
14855
15720
|
}
|
|
14856
15721
|
let payload;
|
|
14857
15722
|
try {
|
|
14858
|
-
payload = JSON.parse(
|
|
15723
|
+
payload = JSON.parse(readFileSync25(args.review, "utf8"));
|
|
14859
15724
|
} catch (err) {
|
|
14860
15725
|
throw new Error(
|
|
14861
15726
|
`Cannot read review JSON ${args.review}: ${err.message}`
|
|
@@ -14880,6 +15745,14 @@ Posting is a public, irreversible write, and this run has no authorisation for o
|
|
|
14880
15745
|
` + structural.map((p) => ` - ${p}`).join("\n")
|
|
14881
15746
|
);
|
|
14882
15747
|
}
|
|
15748
|
+
payload = {
|
|
15749
|
+
...payload,
|
|
15750
|
+
comments: normalizeInlineComments(
|
|
15751
|
+
payload.comments ?? [],
|
|
15752
|
+
payload.state?.modelId,
|
|
15753
|
+
cliVersion
|
|
15754
|
+
)
|
|
15755
|
+
};
|
|
14883
15756
|
let event2;
|
|
14884
15757
|
let body;
|
|
14885
15758
|
let cappedBy;
|
|
@@ -14931,7 +15804,7 @@ Posting is a public, irreversible write, and this run has no authorisation for o
|
|
|
14931
15804
|
const receiptPath = tmpFile(`pr-${args.pr}`, "submit-receipt.json");
|
|
14932
15805
|
const priorIds = readReceiptIds(receiptPath);
|
|
14933
15806
|
const reviewIds = [.../* @__PURE__ */ new Set([...priorIds, reviewId])];
|
|
14934
|
-
|
|
15807
|
+
mkdirSync23(REVIEW_TMP_DIR, { recursive: true });
|
|
14935
15808
|
atomicWriteFileSync(
|
|
14936
15809
|
receiptPath,
|
|
14937
15810
|
`${JSON.stringify({ reviewIds, event: event2, postedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
@@ -14988,7 +15861,8 @@ var submitCommand = {
|
|
|
14988
15861
|
describe: "Check authorisation and payload consistency, then stop."
|
|
14989
15862
|
}), "builder"),
|
|
14990
15863
|
handler: /* @__PURE__ */ __name(async (argv) => {
|
|
14991
|
-
|
|
15864
|
+
const cliVersion = footerVersion(process.env["QWEN_CODE_STARTUP_VERSION"]) ?? await getCliVersion();
|
|
15865
|
+
runSubmit(argv, cliVersion);
|
|
14992
15866
|
}, "handler")
|
|
14993
15867
|
};
|
|
14994
15868
|
|
|
@@ -14996,18 +15870,18 @@ var submitCommand = {
|
|
|
14996
15870
|
init_esbuild_shims();
|
|
14997
15871
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
14998
15872
|
import {
|
|
14999
|
-
mkdirSync as
|
|
15873
|
+
mkdirSync as mkdirSync24,
|
|
15000
15874
|
readdirSync as readdirSync5,
|
|
15001
|
-
writeFileSync as
|
|
15002
|
-
readFileSync as
|
|
15875
|
+
writeFileSync as writeFileSync24,
|
|
15876
|
+
readFileSync as readFileSync26,
|
|
15003
15877
|
rmSync as rmSync9,
|
|
15004
15878
|
lstatSync as lstatSync4,
|
|
15005
15879
|
existsSync as existsSync11,
|
|
15006
|
-
statSync as
|
|
15880
|
+
statSync as statSync11,
|
|
15007
15881
|
symlinkSync
|
|
15008
15882
|
} from "node:fs";
|
|
15009
15883
|
import { createRequire } from "node:module";
|
|
15010
|
-
import { dirname as dirname20, join as
|
|
15884
|
+
import { dirname as dirname20, join as join18, isAbsolute as isAbsolute3, sep as sep3 } from "node:path";
|
|
15011
15885
|
var FIXTURE_DIR_RE = /(^|\/)(__fixtures__|__mocks__|__snapshots__|fixtures)\//;
|
|
15012
15886
|
function planTestEfficacy(files, workspaceGlobs) {
|
|
15013
15887
|
const tests = files.filter((f) => f.kind === "test").map((f) => f.path);
|
|
@@ -15204,8 +16078,8 @@ function insideClassBody(codeLines, idx) {
|
|
|
15204
16078
|
return false;
|
|
15205
16079
|
}
|
|
15206
16080
|
__name(insideClassBody, "insideClassBody");
|
|
15207
|
-
function isRemovableStatement(
|
|
15208
|
-
const t2 = (
|
|
16081
|
+
function isRemovableStatement(lines, codeLines, idx) {
|
|
16082
|
+
const t2 = (lines[idx] ?? "").trim();
|
|
15209
16083
|
if (!(codeLines[idx] ?? "").endsWith(";")) return false;
|
|
15210
16084
|
if ((codeLines[idx] ?? "").slice(0, -1).includes(";")) return false;
|
|
15211
16085
|
if (!/^(?:await\s+)?[A-Za-z_$]/.test(t2)) return false;
|
|
@@ -15274,19 +16148,19 @@ function selectMutants(files, cap = MAX_MUTANTS) {
|
|
|
15274
16148
|
const replRest = [];
|
|
15275
16149
|
const derailed = [];
|
|
15276
16150
|
for (const f of files) {
|
|
15277
|
-
const
|
|
16151
|
+
const lines = f.content.split("\n");
|
|
15278
16152
|
const { inLiteral, codeLines, endState } = scanFileLines(f.content);
|
|
15279
16153
|
if (endState !== "code") {
|
|
15280
16154
|
derailed.push(f.file);
|
|
15281
16155
|
continue;
|
|
15282
16156
|
}
|
|
15283
16157
|
for (const n of [...f.addedLines].sort((a, b) => a - b)) {
|
|
15284
|
-
const raw =
|
|
16158
|
+
const raw = lines[n - 1];
|
|
15285
16159
|
if (raw === void 0) continue;
|
|
15286
16160
|
const t2 = raw.trim();
|
|
15287
16161
|
if (inLiteral[n - 1]) continue;
|
|
15288
16162
|
const code = codeLines[n - 1] ?? "";
|
|
15289
|
-
if (SAFETY_VERB_RE.test(code) && isRemovableStatement(
|
|
16163
|
+
if (SAFETY_VERB_RE.test(code) && isRemovableStatement(lines, codeLines, n - 1)) {
|
|
15290
16164
|
(f.hasNewTests ? preferred : rest).push({
|
|
15291
16165
|
file: f.file,
|
|
15292
16166
|
line: n,
|
|
@@ -15530,23 +16404,24 @@ function gitOut2(cwd, ...args) {
|
|
|
15530
16404
|
return (r.stdout ?? "").trim();
|
|
15531
16405
|
}
|
|
15532
16406
|
__name(gitOut2, "gitOut");
|
|
15533
|
-
function findVitestBin(worktree
|
|
15534
|
-
|
|
16407
|
+
function findVitestBin(worktree, resolveModule = createRequire(
|
|
16408
|
+
join18(worktree, "noop.js")
|
|
16409
|
+
).resolve) {
|
|
15535
16410
|
let pkgPath;
|
|
15536
16411
|
try {
|
|
15537
|
-
pkgPath =
|
|
16412
|
+
pkgPath = resolveModule("vitest/package.json");
|
|
15538
16413
|
} catch (error) {
|
|
15539
16414
|
if (error.code === "MODULE_NOT_FOUND") {
|
|
15540
16415
|
throw new Error(`vitest not found searching up from ${worktree}`);
|
|
15541
16416
|
}
|
|
15542
16417
|
throw error;
|
|
15543
16418
|
}
|
|
15544
|
-
const pkg = JSON.parse(
|
|
16419
|
+
const pkg = JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
15545
16420
|
const bin = pkg.bin?.vitest;
|
|
15546
16421
|
if (!bin) {
|
|
15547
16422
|
throw new Error(`vitest package at ${pkgPath} declares no "vitest" bin`);
|
|
15548
16423
|
}
|
|
15549
|
-
return
|
|
16424
|
+
return join18(dirname20(pkgPath), bin);
|
|
15550
16425
|
}
|
|
15551
16426
|
__name(findVitestBin, "findVitestBin");
|
|
15552
16427
|
function gitCapture(cwd, ...args) {
|
|
@@ -15572,7 +16447,7 @@ function safeRmWithin(worktree, relPath) {
|
|
|
15572
16447
|
const parts = relPath.split(/[/\\]+/).filter((s) => s && s !== ".");
|
|
15573
16448
|
let cur = worktree;
|
|
15574
16449
|
for (let i = 0; i < parts.length; i++) {
|
|
15575
|
-
cur =
|
|
16450
|
+
cur = join18(cur, parts[i]);
|
|
15576
16451
|
let st;
|
|
15577
16452
|
try {
|
|
15578
16453
|
st = lstatSync4(cur);
|
|
@@ -15597,15 +16472,15 @@ __name(probeCleanupFailureDetail, "probeCleanupFailureDetail");
|
|
|
15597
16472
|
function exposeDependencies(probeTree, dependencyRoot) {
|
|
15598
16473
|
const done = { linked: 0, failed: 0 };
|
|
15599
16474
|
if (probeTree === dependencyRoot) return done;
|
|
15600
|
-
const source =
|
|
15601
|
-
const target =
|
|
16475
|
+
const source = join18(dependencyRoot, "node_modules");
|
|
16476
|
+
const target = join18(probeTree, "node_modules");
|
|
15602
16477
|
if (!existsSync11(source) || existsSync11(target)) return done;
|
|
15603
|
-
|
|
16478
|
+
mkdirSync24(target);
|
|
15604
16479
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
15605
16480
|
for (const entry of readdirSync5(source, { withFileTypes: true })) {
|
|
15606
16481
|
if (entry.name === ".vite" || entry.name === ".vite-temp") continue;
|
|
15607
|
-
const sourceEntry =
|
|
15608
|
-
const targetEntry =
|
|
16482
|
+
const sourceEntry = join18(source, entry.name);
|
|
16483
|
+
const targetEntry = join18(target, entry.name);
|
|
15609
16484
|
let sourceStats;
|
|
15610
16485
|
try {
|
|
15611
16486
|
sourceStats = lstatSync4(sourceEntry);
|
|
@@ -15615,7 +16490,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
|
|
|
15615
16490
|
}
|
|
15616
16491
|
if (sourceStats.isSymbolicLink()) {
|
|
15617
16492
|
try {
|
|
15618
|
-
if (!
|
|
16493
|
+
if (!statSync11(sourceEntry).isDirectory()) continue;
|
|
15619
16494
|
} catch {
|
|
15620
16495
|
continue;
|
|
15621
16496
|
}
|
|
@@ -15625,7 +16500,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
|
|
|
15625
16500
|
if (entry.name.startsWith("@")) {
|
|
15626
16501
|
let pkgs;
|
|
15627
16502
|
try {
|
|
15628
|
-
|
|
16503
|
+
mkdirSync24(targetEntry);
|
|
15629
16504
|
pkgs = readdirSync5(sourceEntry);
|
|
15630
16505
|
} catch {
|
|
15631
16506
|
done.failed++;
|
|
@@ -15633,7 +16508,7 @@ function exposeDependencies(probeTree, dependencyRoot) {
|
|
|
15633
16508
|
}
|
|
15634
16509
|
for (const pkg of pkgs) {
|
|
15635
16510
|
try {
|
|
15636
|
-
symlinkSync(
|
|
16511
|
+
symlinkSync(join18(sourceEntry, pkg), join18(targetEntry, pkg), linkType);
|
|
15637
16512
|
done.linked++;
|
|
15638
16513
|
} catch {
|
|
15639
16514
|
done.failed++;
|
|
@@ -15689,34 +16564,34 @@ function runProbeSuite(probeTree, probes, deadlineAt, now = Date.now, dependency
|
|
|
15689
16564
|
}
|
|
15690
16565
|
__name(runProbeSuite, "runProbeSuite");
|
|
15691
16566
|
function splitDiffIntoHunks(diffText) {
|
|
15692
|
-
const
|
|
15693
|
-
const first =
|
|
16567
|
+
const lines = diffText.split("\n");
|
|
16568
|
+
const first = lines.findIndex((l) => l.startsWith("@@"));
|
|
15694
16569
|
if (first < 0) return [];
|
|
15695
16570
|
let headerStart = 0;
|
|
15696
16571
|
for (let k = first - 1; k >= 0; k--) {
|
|
15697
|
-
if (
|
|
16572
|
+
if (lines[k].startsWith("diff --git ")) {
|
|
15698
16573
|
headerStart = k;
|
|
15699
16574
|
break;
|
|
15700
16575
|
}
|
|
15701
16576
|
}
|
|
15702
|
-
let fileHeader =
|
|
16577
|
+
let fileHeader = lines.slice(headerStart, first);
|
|
15703
16578
|
const out = [];
|
|
15704
16579
|
let i = first;
|
|
15705
|
-
while (i <
|
|
15706
|
-
if (
|
|
16580
|
+
while (i < lines.length) {
|
|
16581
|
+
if (lines[i].startsWith("diff --git ")) {
|
|
15707
16582
|
const start = i;
|
|
15708
|
-
while (i <
|
|
16583
|
+
while (i < lines.length && !lines[i].startsWith("@@") && !(i > start && lines[i].startsWith("diff --git ")))
|
|
15709
16584
|
i++;
|
|
15710
|
-
fileHeader =
|
|
16585
|
+
fileHeader = lines.slice(start, i);
|
|
15711
16586
|
continue;
|
|
15712
16587
|
}
|
|
15713
|
-
if (!
|
|
16588
|
+
if (!lines[i].startsWith("@@")) {
|
|
15714
16589
|
i++;
|
|
15715
16590
|
continue;
|
|
15716
16591
|
}
|
|
15717
|
-
const header =
|
|
16592
|
+
const header = lines[i];
|
|
15718
16593
|
let j = i + 1;
|
|
15719
|
-
while (j <
|
|
16594
|
+
while (j < lines.length && !lines[j].startsWith("@@") && !lines[j].startsWith("diff --git ")) {
|
|
15720
16595
|
j++;
|
|
15721
16596
|
}
|
|
15722
16597
|
let startLine = Number(
|
|
@@ -15724,18 +16599,18 @@ function splitDiffIntoHunks(diffText) {
|
|
|
15724
16599
|
);
|
|
15725
16600
|
let offset = 0;
|
|
15726
16601
|
for (let k = i + 1; k < j; k++) {
|
|
15727
|
-
if (
|
|
16602
|
+
if (lines[k].startsWith("+")) {
|
|
15728
16603
|
startLine += offset;
|
|
15729
16604
|
break;
|
|
15730
16605
|
}
|
|
15731
|
-
if (!
|
|
16606
|
+
if (!lines[k].startsWith("-") && !lines[k].startsWith("\\")) offset++;
|
|
15732
16607
|
}
|
|
15733
16608
|
out.push({
|
|
15734
16609
|
header: header.trim(),
|
|
15735
16610
|
startLine,
|
|
15736
16611
|
// `git apply` requires the trailing newline; a patch that ends mid-line is
|
|
15737
16612
|
// rejected with `corrupt patch`.
|
|
15738
|
-
patch: `${[...fileHeader, ...
|
|
16613
|
+
patch: `${[...fileHeader, ...lines.slice(i, j)].join("\n").replace(/\n+$/, "")}
|
|
15739
16614
|
`
|
|
15740
16615
|
});
|
|
15741
16616
|
i = j;
|
|
@@ -15779,10 +16654,10 @@ function newSideLength(header) {
|
|
|
15779
16654
|
__name(newSideLength, "newSideLength");
|
|
15780
16655
|
function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
|
|
15781
16656
|
const { patch: _patch, ...meta } = hunk;
|
|
15782
|
-
const abs =
|
|
16657
|
+
const abs = join18(probeTree, hunk.file);
|
|
15783
16658
|
let original;
|
|
15784
16659
|
try {
|
|
15785
|
-
original =
|
|
16660
|
+
original = readFileSync26(abs, "utf8");
|
|
15786
16661
|
} catch (e) {
|
|
15787
16662
|
return {
|
|
15788
16663
|
...meta,
|
|
@@ -15808,21 +16683,21 @@ function runOneHunkProbe(probeTree, hunk, probes, deadlineAt, now = Date.now) {
|
|
|
15808
16683
|
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";
|
|
15809
16684
|
return { ...meta, verdict, detail };
|
|
15810
16685
|
} finally {
|
|
15811
|
-
|
|
15812
|
-
|
|
16686
|
+
mkdirSync24(dirname20(abs), { recursive: true });
|
|
16687
|
+
writeFileSync24(abs, original, "utf8");
|
|
15813
16688
|
}
|
|
15814
16689
|
}
|
|
15815
16690
|
__name(runOneHunkProbe, "runOneHunkProbe");
|
|
15816
16691
|
function runControlMutant(probeTree, probeFile, deadlineAt, now = Date.now) {
|
|
15817
|
-
const abs =
|
|
16692
|
+
const abs = join18(probeTree, probeFile);
|
|
15818
16693
|
let original;
|
|
15819
16694
|
try {
|
|
15820
|
-
original =
|
|
16695
|
+
original = readFileSync26(abs, "utf8");
|
|
15821
16696
|
} catch {
|
|
15822
16697
|
return null;
|
|
15823
16698
|
}
|
|
15824
16699
|
try {
|
|
15825
|
-
|
|
16700
|
+
writeFileSync24(
|
|
15826
16701
|
abs,
|
|
15827
16702
|
`${original}
|
|
15828
16703
|
;import { it as __qcIt, expect as __qcExpect } from 'vitest';
|
|
@@ -15835,15 +16710,15 @@ __qcIt('QWEN-REVIEW-POSITIVE-CONTROL', () => {
|
|
|
15835
16710
|
const { perFile } = runProbeSuite(probeTree, [probeFile], deadlineAt, now);
|
|
15836
16711
|
return perFile.some((r) => r.verdict === "gated");
|
|
15837
16712
|
} finally {
|
|
15838
|
-
|
|
16713
|
+
writeFileSync24(abs, original, "utf8");
|
|
15839
16714
|
}
|
|
15840
16715
|
}
|
|
15841
16716
|
__name(runControlMutant, "runControlMutant");
|
|
15842
16717
|
function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dependencyRoot = probeTree) {
|
|
15843
|
-
const abs =
|
|
15844
|
-
const original =
|
|
15845
|
-
const
|
|
15846
|
-
if ((
|
|
16718
|
+
const abs = join18(probeTree, mutant.file);
|
|
16719
|
+
const original = readFileSync26(abs, "utf8");
|
|
16720
|
+
const lines = original.split("\n");
|
|
16721
|
+
if ((lines[mutant.line - 1] ?? "").trim() !== mutant.statement) {
|
|
15847
16722
|
return {
|
|
15848
16723
|
...mutant,
|
|
15849
16724
|
verdict: "inconclusive",
|
|
@@ -15852,12 +16727,12 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
|
|
|
15852
16727
|
}
|
|
15853
16728
|
const what = mutant.operator === "coalesce" ? "with its `?? fallback` dropped" : mutant.operator === "guard-true" ? "with its guard condition replaced by `true`" : mutant.operator === "term-drop" ? "with its `+ CONSTANT` term dropped" : "deleted";
|
|
15854
16729
|
if (mutant.mutated !== void 0) {
|
|
15855
|
-
|
|
16730
|
+
lines[mutant.line - 1] = mutant.mutated;
|
|
15856
16731
|
} else {
|
|
15857
|
-
|
|
16732
|
+
lines.splice(mutant.line - 1, 1);
|
|
15858
16733
|
}
|
|
15859
16734
|
try {
|
|
15860
|
-
|
|
16735
|
+
writeFileSync24(abs, lines.join("\n"), "utf8");
|
|
15861
16736
|
const { perFile } = runProbeSuite(
|
|
15862
16737
|
probeTree,
|
|
15863
16738
|
probes,
|
|
@@ -15869,7 +16744,7 @@ function runOneMutant(probeTree, mutant, probes, deadlineAt, now = Date.now, dep
|
|
|
15869
16744
|
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";
|
|
15870
16745
|
return { ...mutant, verdict, detail };
|
|
15871
16746
|
} finally {
|
|
15872
|
-
|
|
16747
|
+
writeFileSync24(abs, original, "utf8");
|
|
15873
16748
|
}
|
|
15874
16749
|
}
|
|
15875
16750
|
__name(runOneMutant, "runOneMutant");
|
|
@@ -15908,9 +16783,9 @@ async function runTestEfficacy(args) {
|
|
|
15908
16783
|
const now = args.now ?? Date.now;
|
|
15909
16784
|
const startedAt = now();
|
|
15910
16785
|
const { report, worktree, base, out } = args;
|
|
15911
|
-
const plan = JSON.parse(
|
|
16786
|
+
const plan = JSON.parse(readFileSync26(report, "utf8"));
|
|
15912
16787
|
const rootPkg = JSON.parse(
|
|
15913
|
-
|
|
16788
|
+
readFileSync26(`${worktree}/package.json`, "utf8")
|
|
15914
16789
|
);
|
|
15915
16790
|
const globs = rootPkg.workspaces ?? [];
|
|
15916
16791
|
const { unreachable, probes, revert } = planTestEfficacy(
|
|
@@ -15918,8 +16793,8 @@ async function runTestEfficacy(args) {
|
|
|
15918
16793
|
globs
|
|
15919
16794
|
);
|
|
15920
16795
|
for (const p of revert) {
|
|
15921
|
-
const norm =
|
|
15922
|
-
const root =
|
|
16796
|
+
const norm = join18(worktree, p);
|
|
16797
|
+
const root = join18(worktree, ".");
|
|
15923
16798
|
if (isAbsolute3(p) || norm !== root && !norm.startsWith(root + sep3)) {
|
|
15924
16799
|
throw new Error(
|
|
15925
16800
|
`refusing to run: revert path escapes the worktree: ${JSON.stringify(p)}`
|
|
@@ -16290,8 +17165,8 @@ async function runTestEfficacy(args) {
|
|
|
16290
17165
|
findings,
|
|
16291
17166
|
cleanupFailure
|
|
16292
17167
|
};
|
|
16293
|
-
|
|
16294
|
-
|
|
17168
|
+
mkdirSync24(dirname20(out), { recursive: true });
|
|
17169
|
+
writeFileSync24(out, JSON.stringify(result, null, 2), "utf8");
|
|
16295
17170
|
writeStdoutLine(
|
|
16296
17171
|
`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))`
|
|
16297
17172
|
);
|
|
@@ -16368,17 +17243,17 @@ var testEfficacyCommand = {
|
|
|
16368
17243
|
|
|
16369
17244
|
// packages/cli/src/commands/review/test-plan.ts
|
|
16370
17245
|
init_esbuild_shims();
|
|
16371
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
16372
|
-
import { dirname as dirname21, join as
|
|
17246
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync25, readFileSync as readFileSync27, writeFileSync as writeFileSync25 } from "node:fs";
|
|
17247
|
+
import { dirname as dirname21, join as join19, normalize, resolve as resolve20 } from "node:path";
|
|
16373
17248
|
var PLAN_NAME_RE = /(test\s*plan|\btesting\b|how\s+(?:has\s+this|to)\s+(?:been\s+)?test(?:ed)?|测试计划|测试方案|测试步骤)/i;
|
|
16374
17249
|
var HEADING_LINE_RE = /^(#{1,6})(?:[ \t](.*))?$/;
|
|
16375
17250
|
var BOLD_LINE_RE = /^\*\*([^*\n]+)\*\*:?\s*$/;
|
|
16376
17251
|
function extractTestPlanSection(body) {
|
|
16377
|
-
const
|
|
16378
|
-
const fenced = new Array(
|
|
17252
|
+
const lines = body.split(/\r?\n/);
|
|
17253
|
+
const fenced = new Array(lines.length).fill(false);
|
|
16379
17254
|
let fenceMarker = null;
|
|
16380
|
-
for (let i = 0; i <
|
|
16381
|
-
const m = /^\s*(```|~~~)/.exec(
|
|
17255
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17256
|
+
const m = /^\s*(```|~~~)/.exec(lines[i]);
|
|
16382
17257
|
if (m) {
|
|
16383
17258
|
fenced[i] = true;
|
|
16384
17259
|
if (!fenceMarker) fenceMarker = m[1];
|
|
@@ -16387,27 +17262,27 @@ function extractTestPlanSection(body) {
|
|
|
16387
17262
|
}
|
|
16388
17263
|
fenced[i] = fenceMarker !== null;
|
|
16389
17264
|
}
|
|
16390
|
-
for (let i = 0; i <
|
|
17265
|
+
for (let i = 0; i < lines.length; i++) {
|
|
16391
17266
|
if (fenced[i]) continue;
|
|
16392
|
-
const hash = HEADING_LINE_RE.exec(
|
|
16393
|
-
const bold = BOLD_LINE_RE.exec(
|
|
17267
|
+
const hash = HEADING_LINE_RE.exec(lines[i]);
|
|
17268
|
+
const bold = BOLD_LINE_RE.exec(lines[i]);
|
|
16394
17269
|
const name = (hash?.[2] ?? bold?.[1])?.trim();
|
|
16395
17270
|
if (!name || !PLAN_NAME_RE.test(name)) continue;
|
|
16396
17271
|
const level = hash ? hash[1].length : Infinity;
|
|
16397
17272
|
const out = [];
|
|
16398
|
-
for (let j = i + 1; j <
|
|
17273
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
16399
17274
|
if (!fenced[j]) {
|
|
16400
|
-
const next = HEADING_LINE_RE.exec(
|
|
17275
|
+
const next = HEADING_LINE_RE.exec(lines[j]);
|
|
16401
17276
|
if (next && !next[2]?.trim()) {
|
|
16402
|
-
out.push(
|
|
17277
|
+
out.push(lines[j]);
|
|
16403
17278
|
continue;
|
|
16404
17279
|
}
|
|
16405
17280
|
if (next && next[1].length <= level) break;
|
|
16406
|
-
if (!hash && (next || BOLD_LINE_RE.test(
|
|
17281
|
+
if (!hash && (next || BOLD_LINE_RE.test(lines[j]))) break;
|
|
16407
17282
|
}
|
|
16408
|
-
out.push(
|
|
17283
|
+
out.push(lines[j]);
|
|
16409
17284
|
}
|
|
16410
|
-
return { heading:
|
|
17285
|
+
return { heading: lines[i].trim(), content: out.join("\n").trim() };
|
|
16411
17286
|
}
|
|
16412
17287
|
return null;
|
|
16413
17288
|
}
|
|
@@ -16572,7 +17447,7 @@ function rulePath(text, worktree, changed) {
|
|
|
16572
17447
|
};
|
|
16573
17448
|
}
|
|
16574
17449
|
const ignored = isGitIgnored(worktree, path3);
|
|
16575
|
-
if (existsSync12(
|
|
17450
|
+
if (existsSync12(join19(worktree, path3))) {
|
|
16576
17451
|
return {
|
|
16577
17452
|
kind: "path",
|
|
16578
17453
|
text,
|
|
@@ -16643,15 +17518,27 @@ function ruleCommand(text, worktree, buildTest) {
|
|
|
16643
17518
|
let rootScripts = [];
|
|
16644
17519
|
try {
|
|
16645
17520
|
const rootPkg = JSON.parse(
|
|
16646
|
-
|
|
17521
|
+
readFileSync27(join19(worktree, "package.json"), "utf8")
|
|
16647
17522
|
);
|
|
16648
17523
|
rootScripts = Object.keys(rootPkg.scripts ?? {});
|
|
16649
17524
|
} catch {
|
|
16650
17525
|
}
|
|
16651
17526
|
const defined = new Set(rootScripts);
|
|
16652
|
-
|
|
17527
|
+
const { packages, skipped } = readWorkspacePackages(worktree);
|
|
17528
|
+
for (const pkg of packages) {
|
|
16653
17529
|
for (const s of pkg.scripts) defined.add(s);
|
|
16654
17530
|
}
|
|
17531
|
+
const unreadable = [];
|
|
17532
|
+
for (const dir of skipped) {
|
|
17533
|
+
try {
|
|
17534
|
+
const pkg = JSON.parse(
|
|
17535
|
+
readFileSync27(join19(worktree, dir, "package.json"), "utf8")
|
|
17536
|
+
);
|
|
17537
|
+
for (const s of Object.keys(pkg?.scripts ?? {})) defined.add(s);
|
|
17538
|
+
} catch {
|
|
17539
|
+
unreadable.push(dir);
|
|
17540
|
+
}
|
|
17541
|
+
}
|
|
16655
17542
|
if (defined.size === 0) {
|
|
16656
17543
|
return {
|
|
16657
17544
|
kind: "command",
|
|
@@ -16660,12 +17547,31 @@ function ruleCommand(text, worktree, buildTest) {
|
|
|
16660
17547
|
note: "no package manifest could be read"
|
|
16661
17548
|
};
|
|
16662
17549
|
}
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
17550
|
+
if (defined.has(script)) {
|
|
17551
|
+
return {
|
|
17552
|
+
kind: "command",
|
|
17553
|
+
text,
|
|
17554
|
+
verdict: "reproduces",
|
|
17555
|
+
note: `\`${script}\` is a defined script`
|
|
17556
|
+
};
|
|
17557
|
+
}
|
|
17558
|
+
if (hasUnmodeledWorkspaceGlob(readWorkspaceGlobs(worktree))) {
|
|
17559
|
+
return {
|
|
17560
|
+
kind: "command",
|
|
17561
|
+
text,
|
|
17562
|
+
verdict: "unchecked",
|
|
17563
|
+
note: "the workspace globs use a shape this check does not model, so the script table may be incomplete"
|
|
17564
|
+
};
|
|
17565
|
+
}
|
|
17566
|
+
if (unreadable.length > 0) {
|
|
17567
|
+
return {
|
|
17568
|
+
kind: "command",
|
|
17569
|
+
text,
|
|
17570
|
+
verdict: "unchecked",
|
|
17571
|
+
note: `${unreadable.join(", ")} ${unreadable.length === 1 ? "has" : "have"} a package.json this check could not read, so the script table may be incomplete`
|
|
17572
|
+
};
|
|
17573
|
+
}
|
|
17574
|
+
return {
|
|
16669
17575
|
kind: "command",
|
|
16670
17576
|
text,
|
|
16671
17577
|
verdict: "contradicted",
|
|
@@ -16721,7 +17627,7 @@ __name(fetchPrBody, "fetchPrBody");
|
|
|
16721
17627
|
function runTestPlan(args, fetchBody = fetchPrBody) {
|
|
16722
17628
|
let plan;
|
|
16723
17629
|
try {
|
|
16724
|
-
plan = JSON.parse(
|
|
17630
|
+
plan = JSON.parse(readFileSync27(args.plan, "utf8"));
|
|
16725
17631
|
} catch (err) {
|
|
16726
17632
|
throw new Error(
|
|
16727
17633
|
`test-plan: cannot read the plan ${args.plan}: ${err.message}`
|
|
@@ -16756,7 +17662,7 @@ function runTestPlan(args, fetchBody = fetchPrBody) {
|
|
|
16756
17662
|
if (args.buildTest) {
|
|
16757
17663
|
try {
|
|
16758
17664
|
buildTest = JSON.parse(
|
|
16759
|
-
|
|
17665
|
+
readFileSync27(args.buildTest, "utf8")
|
|
16760
17666
|
);
|
|
16761
17667
|
} catch {
|
|
16762
17668
|
buildTest = null;
|
|
@@ -16813,8 +17719,8 @@ var testPlanCommand = {
|
|
|
16813
17719
|
try {
|
|
16814
17720
|
const report = runTestPlan(args);
|
|
16815
17721
|
if (args.out) {
|
|
16816
|
-
|
|
16817
|
-
|
|
17722
|
+
mkdirSync25(dirname21(resolve20(args.out)), { recursive: true });
|
|
17723
|
+
writeFileSync25(resolve20(args.out), JSON.stringify(report, null, 2));
|
|
16818
17724
|
}
|
|
16819
17725
|
writeStdoutLine(JSON.stringify(report, null, 2));
|
|
16820
17726
|
writeStderrLine(`test-plan: ${report.note}`);
|
|
@@ -16828,8 +17734,8 @@ var testPlanCommand = {
|
|
|
16828
17734
|
// packages/cli/src/commands/review/cleanup.ts
|
|
16829
17735
|
init_esbuild_shims();
|
|
16830
17736
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
16831
|
-
import { existsSync as existsSync13, readFileSync as
|
|
16832
|
-
import { join as
|
|
17737
|
+
import { existsSync as existsSync13, readFileSync as readFileSync28, readdirSync as readdirSync6, rmSync as rmSync10 } from "node:fs";
|
|
17738
|
+
import { join as join20 } from "node:path";
|
|
16833
17739
|
var AUTOMATION_MARKER = "<!-- qwen-";
|
|
16834
17740
|
function isAutomationComment(body) {
|
|
16835
17741
|
return (body ?? "").trimStart().startsWith(AUTOMATION_MARKER);
|
|
@@ -16860,7 +17766,7 @@ var OWNER_REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
|
16860
17766
|
function readAuditWindow(target, expectedPrNumber) {
|
|
16861
17767
|
let raw;
|
|
16862
17768
|
try {
|
|
16863
|
-
raw =
|
|
17769
|
+
raw = readFileSync28(tmpFile(target, "fetch.json"), "utf8");
|
|
16864
17770
|
} catch (err) {
|
|
16865
17771
|
const code = err.code;
|
|
16866
17772
|
return code === "ENOENT" ? { skip: "no fetch report" } : {
|
|
@@ -16907,7 +17813,7 @@ function readSubmitReceipt(target) {
|
|
|
16907
17813
|
try {
|
|
16908
17814
|
return new Set(
|
|
16909
17815
|
parseReceiptIds(
|
|
16910
|
-
|
|
17816
|
+
readFileSync28(tmpFile(target, "submit-receipt.json"), "utf8")
|
|
16911
17817
|
)
|
|
16912
17818
|
);
|
|
16913
17819
|
} catch {
|
|
@@ -17042,7 +17948,7 @@ function runCleanup(target) {
|
|
|
17042
17948
|
}
|
|
17043
17949
|
for (const file of tmpEntries) {
|
|
17044
17950
|
if (!file.startsWith(prefix)) continue;
|
|
17045
|
-
const full =
|
|
17951
|
+
const full = join20(REVIEW_TMP_DIR, file);
|
|
17046
17952
|
try {
|
|
17047
17953
|
rmSync10(full, { recursive: true, force: true });
|
|
17048
17954
|
writeStdoutLine(`Removed temp file: ${full}`);
|
|
@@ -17073,11 +17979,324 @@ var cleanupCommand = {
|
|
|
17073
17979
|
}, "handler")
|
|
17074
17980
|
};
|
|
17075
17981
|
|
|
17982
|
+
// packages/cli/src/commands/review/cost-ledger.ts
|
|
17983
|
+
init_esbuild_shims();
|
|
17984
|
+
import { mkdirSync as mkdirSync26, readFileSync as readFileSync29, statSync as statSync12, writeFileSync as writeFileSync26 } from "node:fs";
|
|
17985
|
+
import { dirname as dirname22, join as join21, resolve as resolve21 } from "node:path";
|
|
17986
|
+
function readUsage(file, floorMs) {
|
|
17987
|
+
const raw = readFileSync29(file, "utf8");
|
|
17988
|
+
const events = [];
|
|
17989
|
+
let launch = "";
|
|
17990
|
+
for (const line of raw.split("\n")) {
|
|
17991
|
+
if (line.trim() === "") continue;
|
|
17992
|
+
for (const rec of parseLineTolerant(line, file)) {
|
|
17993
|
+
if (launch === "" && rec["type"] === "user") {
|
|
17994
|
+
launch = textOf(rec);
|
|
17995
|
+
}
|
|
17996
|
+
if (rec["type"] !== "assistant") continue;
|
|
17997
|
+
const usage = rec["usageMetadata"];
|
|
17998
|
+
if (usage === null || typeof usage !== "object") continue;
|
|
17999
|
+
if (Array.isArray(usage)) continue;
|
|
18000
|
+
const u = usage;
|
|
18001
|
+
const ts = rec["timestamp"];
|
|
18002
|
+
if (typeof ts !== "string") continue;
|
|
18003
|
+
const tsMs = Date.parse(ts);
|
|
18004
|
+
if (!Number.isFinite(tsMs) || tsMs < floorMs) continue;
|
|
18005
|
+
const rawCount = /* @__PURE__ */ __name((k) => {
|
|
18006
|
+
const v = u[k];
|
|
18007
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
18008
|
+
}, "rawCount");
|
|
18009
|
+
const rawPrompt = rawCount("promptTokenCount");
|
|
18010
|
+
const rawTotal = rawCount("totalTokenCount");
|
|
18011
|
+
const prompt = rawPrompt ?? 0;
|
|
18012
|
+
const candidates = rawCount("candidatesTokenCount") ?? 0;
|
|
18013
|
+
events.push({
|
|
18014
|
+
timestampMs: tsMs,
|
|
18015
|
+
timestamp: ts,
|
|
18016
|
+
input: prompt,
|
|
18017
|
+
// Cached is part of the prompt it is reported with; a broken proxy
|
|
18018
|
+
// can report the pair inverted, and the inversion would otherwise
|
|
18019
|
+
// land in BOTH the rendered share and the archived --out JSON.
|
|
18020
|
+
cached: Math.min(rawCount("cachedContentTokenCount") ?? 0, prompt),
|
|
18021
|
+
// `total − prompt` is the output including thinking under BOTH usage
|
|
18022
|
+
// conventions — reasoning inside candidates, and thoughts disjoint
|
|
18023
|
+
// from them — the same derivation tokenEstimation uses. Candidates
|
|
18024
|
+
// alone is the fallback when the provider reported no total; it stays
|
|
18025
|
+
// correct for the providers this CLI converts, which clamp thoughts
|
|
18026
|
+
// inside candidates. Derive it only when BOTH operands survived the
|
|
18027
|
+
// provider intact: a mixed-sign record coerces prompt to 0 while
|
|
18028
|
+
// keeping a positive total, and the subtraction would bill the
|
|
18029
|
+
// call's whole total as output.
|
|
18030
|
+
output: rawPrompt !== null && rawTotal !== null && rawTotal > rawPrompt ? rawTotal - rawPrompt : candidates,
|
|
18031
|
+
thoughts: rawCount("thoughtsTokenCount") ?? 0
|
|
18032
|
+
});
|
|
18033
|
+
}
|
|
18034
|
+
}
|
|
18035
|
+
return { events, launch };
|
|
18036
|
+
}
|
|
18037
|
+
__name(readUsage, "readUsage");
|
|
18038
|
+
var AUDIT_BRIEF_RE = /read_file\(file_path="[^"]*reverse-audit--chunk-(\d+)--round-(\d+)--[0-9a-f][^"]*\.brief\.md"\)/g;
|
|
18039
|
+
function labelOf(launch, fallback) {
|
|
18040
|
+
const nl = launch.indexOf("\n");
|
|
18041
|
+
const identity = nl === -1 ? launch : launch.slice(0, nl);
|
|
18042
|
+
if (!identity.startsWith("You are review agent `")) return fallback;
|
|
18043
|
+
let auditChunk = null;
|
|
18044
|
+
for (const m of launch.matchAll(AUDIT_BRIEF_RE)) auditChunk = m;
|
|
18045
|
+
if (auditChunk) {
|
|
18046
|
+
return `audit chunk ${auditChunk[1]} (round ${auditChunk[2]})`;
|
|
18047
|
+
}
|
|
18048
|
+
const role = /^You are review agent `([^`]+)`/.exec(identity);
|
|
18049
|
+
if (!role) return fallback;
|
|
18050
|
+
const round = /\(round (\d+)\)/.exec(identity);
|
|
18051
|
+
const chunk2 = CHUNK_RE.exec(role[1]);
|
|
18052
|
+
if (chunk2) return `chunk ${chunk2[1]}`;
|
|
18053
|
+
if (round) {
|
|
18054
|
+
return `agent ${role[1]} (round ${round[1]})`;
|
|
18055
|
+
}
|
|
18056
|
+
const file = /Your file: `([^`]+)`/.exec(identity);
|
|
18057
|
+
if (file) return `agent ${role[1]} (${file[1]})`;
|
|
18058
|
+
return `agent ${role[1]}`;
|
|
18059
|
+
}
|
|
18060
|
+
__name(labelOf, "labelOf");
|
|
18061
|
+
function foldEvents(id, label2, events) {
|
|
18062
|
+
const s = {
|
|
18063
|
+
id,
|
|
18064
|
+
label: label2,
|
|
18065
|
+
calls: 0,
|
|
18066
|
+
inputTokens: 0,
|
|
18067
|
+
cachedTokens: 0,
|
|
18068
|
+
outputTokens: 0,
|
|
18069
|
+
thoughtsTokens: 0,
|
|
18070
|
+
firstAt: null,
|
|
18071
|
+
lastAt: null
|
|
18072
|
+
};
|
|
18073
|
+
let firstMs = Number.POSITIVE_INFINITY;
|
|
18074
|
+
let lastMs = Number.NEGATIVE_INFINITY;
|
|
18075
|
+
for (const e of events) {
|
|
18076
|
+
s.calls += 1;
|
|
18077
|
+
s.inputTokens += e.input;
|
|
18078
|
+
s.cachedTokens += e.cached;
|
|
18079
|
+
s.outputTokens += e.output;
|
|
18080
|
+
s.thoughtsTokens += e.thoughts;
|
|
18081
|
+
if (e.timestampMs < firstMs) {
|
|
18082
|
+
firstMs = e.timestampMs;
|
|
18083
|
+
s.firstAt = e.timestamp;
|
|
18084
|
+
}
|
|
18085
|
+
if (e.timestampMs > lastMs) {
|
|
18086
|
+
lastMs = e.timestampMs;
|
|
18087
|
+
s.lastAt = e.timestamp;
|
|
18088
|
+
}
|
|
18089
|
+
}
|
|
18090
|
+
return s;
|
|
18091
|
+
}
|
|
18092
|
+
__name(foldEvents, "foldEvents");
|
|
18093
|
+
function human(n) {
|
|
18094
|
+
if (n >= 9995e5) return `${(n / 1e9).toFixed(1)}B`;
|
|
18095
|
+
if (n >= 999500) return `${(n / 1e6).toFixed(1)}M`;
|
|
18096
|
+
if (n >= 1e3) return `${Math.round(n / 1e3)}k`;
|
|
18097
|
+
return String(n);
|
|
18098
|
+
}
|
|
18099
|
+
__name(human, "human");
|
|
18100
|
+
var plural = /* @__PURE__ */ __name((n, word) => `${n} ${word}${n === 1 ? "" : "s"}`, "plural");
|
|
18101
|
+
function planFloorMs(planPath) {
|
|
18102
|
+
let raw;
|
|
18103
|
+
let floorMs;
|
|
18104
|
+
try {
|
|
18105
|
+
raw = readFileSync29(planPath, "utf8");
|
|
18106
|
+
floorMs = statSync12(planPath).mtimeMs;
|
|
18107
|
+
} catch (err) {
|
|
18108
|
+
throw new Error(
|
|
18109
|
+
`could not read the plan report ${planPath}: ${err.message}`
|
|
18110
|
+
);
|
|
18111
|
+
}
|
|
18112
|
+
let parsed;
|
|
18113
|
+
try {
|
|
18114
|
+
parsed = JSON.parse(raw);
|
|
18115
|
+
} catch {
|
|
18116
|
+
parsed = null;
|
|
18117
|
+
}
|
|
18118
|
+
const plan = parsed !== null && typeof parsed === "object" ? parsed : void 0;
|
|
18119
|
+
if (typeof plan?.["diffLines"] !== "number" || !Array.isArray(plan?.["chunks"])) {
|
|
18120
|
+
throw new Error(`not a review plan report: ${planPath}`);
|
|
18121
|
+
}
|
|
18122
|
+
return floorMs;
|
|
18123
|
+
}
|
|
18124
|
+
__name(planFloorMs, "planFloorMs");
|
|
18125
|
+
function computeLedger(planPath, env = process.env) {
|
|
18126
|
+
const floorMs = planFloorMs(planPath);
|
|
18127
|
+
const { projectDir, sessionId, dir } = transcriptPaths(env);
|
|
18128
|
+
const chatFile = join21(projectDir, "chats", `${sessionId}.jsonl`);
|
|
18129
|
+
let mainEvents;
|
|
18130
|
+
try {
|
|
18131
|
+
mainEvents = readUsage(chatFile, floorMs).events;
|
|
18132
|
+
} catch (err) {
|
|
18133
|
+
throw new Error(
|
|
18134
|
+
`could not read the chat transcript ${chatFile}: ${err.message}`
|
|
18135
|
+
);
|
|
18136
|
+
}
|
|
18137
|
+
const main = mainEvents.length > 0 ? foldEvents("main", "main loop", mainEvents) : null;
|
|
18138
|
+
let files;
|
|
18139
|
+
try {
|
|
18140
|
+
files = listAgentTranscriptFiles(dir);
|
|
18141
|
+
} catch (err) {
|
|
18142
|
+
if (err.code === "ENOENT") {
|
|
18143
|
+
files = [];
|
|
18144
|
+
} else {
|
|
18145
|
+
throw new Error(
|
|
18146
|
+
`could not list the subagent transcripts at ${dir}: ${err.message}`
|
|
18147
|
+
);
|
|
18148
|
+
}
|
|
18149
|
+
}
|
|
18150
|
+
const agents = [];
|
|
18151
|
+
const agentEvents = [];
|
|
18152
|
+
for (const f of files) {
|
|
18153
|
+
const full = join21(dir, f);
|
|
18154
|
+
let mtimeMs;
|
|
18155
|
+
try {
|
|
18156
|
+
mtimeMs = statSync12(full).mtimeMs;
|
|
18157
|
+
} catch {
|
|
18158
|
+
continue;
|
|
18159
|
+
}
|
|
18160
|
+
if (mtimeMs < floorMs) continue;
|
|
18161
|
+
let read;
|
|
18162
|
+
try {
|
|
18163
|
+
read = readUsage(full, floorMs);
|
|
18164
|
+
} catch {
|
|
18165
|
+
continue;
|
|
18166
|
+
}
|
|
18167
|
+
if (read.events.length === 0) continue;
|
|
18168
|
+
const id = f.replace(/^agent-/, "").replace(/\.jsonl$/, "");
|
|
18169
|
+
agents.push(foldEvents(id, labelOf(read.launch, id), read.events));
|
|
18170
|
+
agentEvents.push(...read.events);
|
|
18171
|
+
}
|
|
18172
|
+
agents.sort((a, b) => b.inputTokens - a.inputTokens);
|
|
18173
|
+
if (mainEvents.length === 0) {
|
|
18174
|
+
throw new Error(
|
|
18175
|
+
`could not read the chat transcript ${chatFile}: no main-loop usage records at or after the plan`
|
|
18176
|
+
);
|
|
18177
|
+
}
|
|
18178
|
+
const totals = foldEvents("totals", "totals", [
|
|
18179
|
+
...mainEvents,
|
|
18180
|
+
...agentEvents
|
|
18181
|
+
]);
|
|
18182
|
+
const wallSeconds = totals.firstAt !== null && totals.lastAt !== null ? Math.max(
|
|
18183
|
+
0,
|
|
18184
|
+
Math.round(
|
|
18185
|
+
(Date.parse(totals.lastAt) - Date.parse(totals.firstAt)) / 1e3
|
|
18186
|
+
)
|
|
18187
|
+
) : 0;
|
|
18188
|
+
const { id: _i, label: _l, ...totalsRest } = totals;
|
|
18189
|
+
return { totals: { ...totalsRest, wallSeconds }, main, agents };
|
|
18190
|
+
}
|
|
18191
|
+
__name(computeLedger, "computeLedger");
|
|
18192
|
+
function renderLedger(ledger) {
|
|
18193
|
+
const t2 = ledger.totals;
|
|
18194
|
+
const cachedPct = t2.inputTokens > 0 ? Math.round(t2.cachedTokens / t2.inputTokens * 100) : 0;
|
|
18195
|
+
const lines = [];
|
|
18196
|
+
lines.push(
|
|
18197
|
+
`Cost ledger: ${plural(t2.calls, "model call")} \xB7 ${human(t2.inputTokens)} input (${cachedPct}% cached) \xB7 ${human(t2.outputTokens)} output (${human(t2.thoughtsTokens)} thinking) \xB7 ${Math.round(t2.wallSeconds / 60)} min wall`
|
|
18198
|
+
);
|
|
18199
|
+
if (ledger.main !== null) {
|
|
18200
|
+
const m = ledger.main;
|
|
18201
|
+
lines.push(
|
|
18202
|
+
` main loop: ${plural(m.calls, "call")} \xB7 ${human(m.inputTokens)} in \xB7 ${human(m.outputTokens)} out`
|
|
18203
|
+
);
|
|
18204
|
+
}
|
|
18205
|
+
if (ledger.agents.length > 0) {
|
|
18206
|
+
const rows = [];
|
|
18207
|
+
for (const a of ledger.agents) {
|
|
18208
|
+
const row = rows.find((r) => r.label === a.label);
|
|
18209
|
+
if (row) {
|
|
18210
|
+
row.calls += a.calls;
|
|
18211
|
+
row.inputTokens += a.inputTokens;
|
|
18212
|
+
row.outputTokens += a.outputTokens;
|
|
18213
|
+
row.count += 1;
|
|
18214
|
+
} else {
|
|
18215
|
+
rows.push({
|
|
18216
|
+
label: a.label,
|
|
18217
|
+
calls: a.calls,
|
|
18218
|
+
inputTokens: a.inputTokens,
|
|
18219
|
+
outputTokens: a.outputTokens,
|
|
18220
|
+
count: 1
|
|
18221
|
+
});
|
|
18222
|
+
}
|
|
18223
|
+
}
|
|
18224
|
+
rows.sort((a, b) => b.inputTokens - a.inputTokens);
|
|
18225
|
+
lines.push(` agent runs: ${ledger.agents.length}`);
|
|
18226
|
+
for (const r of rows.slice(0, 8)) {
|
|
18227
|
+
const label2 = r.count > 1 ? `${r.label} (\xD7${r.count})` : r.label;
|
|
18228
|
+
lines.push(
|
|
18229
|
+
` ${label2}: ${plural(r.calls, "call")} \xB7 ${human(r.inputTokens)} in \xB7 ${human(r.outputTokens)} out`
|
|
18230
|
+
);
|
|
18231
|
+
}
|
|
18232
|
+
if (rows.length > 8) {
|
|
18233
|
+
const rest = rows.slice(8);
|
|
18234
|
+
const restIn = rest.reduce((n, r) => n + r.inputTokens, 0);
|
|
18235
|
+
const restAgents = rest.reduce((n, r) => n + r.count, 0);
|
|
18236
|
+
lines.push(
|
|
18237
|
+
` \u2026and ${plural(restAgents, "more agent")} \xB7 ${human(restIn)} in combined`
|
|
18238
|
+
);
|
|
18239
|
+
}
|
|
18240
|
+
}
|
|
18241
|
+
return lines.join("\n");
|
|
18242
|
+
}
|
|
18243
|
+
__name(renderLedger, "renderLedger");
|
|
18244
|
+
function runCostLedger(args) {
|
|
18245
|
+
const stdoutErrorHandler = /* @__PURE__ */ __name((err) => {
|
|
18246
|
+
if (err.code === "EPIPE") process.stdout.destroy();
|
|
18247
|
+
}, "stdoutErrorHandler");
|
|
18248
|
+
const stderrErrorHandler = /* @__PURE__ */ __name((err) => {
|
|
18249
|
+
if (err.code === "EPIPE") process.stderr.destroy();
|
|
18250
|
+
}, "stderrErrorHandler");
|
|
18251
|
+
process.stdout.on("error", stdoutErrorHandler);
|
|
18252
|
+
process.stderr.on("error", stderrErrorHandler);
|
|
18253
|
+
try {
|
|
18254
|
+
let ledger;
|
|
18255
|
+
try {
|
|
18256
|
+
ledger = computeLedger(args.plan, process.env);
|
|
18257
|
+
} catch (err) {
|
|
18258
|
+
const why = err instanceof TranscriptsUnavailableError ? err.message : err.message;
|
|
18259
|
+
writeStderrLineSafe(`cost-ledger unavailable \u2014 ${why}`);
|
|
18260
|
+
return;
|
|
18261
|
+
}
|
|
18262
|
+
if (args.out !== void 0 && args.out.length > 0) {
|
|
18263
|
+
try {
|
|
18264
|
+
mkdirSync26(dirname22(resolve21(args.out)), { recursive: true });
|
|
18265
|
+
writeFileSync26(args.out, JSON.stringify(ledger, null, 2));
|
|
18266
|
+
} catch (err) {
|
|
18267
|
+
writeStderrLineSafe(
|
|
18268
|
+
`cost-ledger: could not write ${args.out} \u2014 ${err.message}`
|
|
18269
|
+
);
|
|
18270
|
+
}
|
|
18271
|
+
}
|
|
18272
|
+
writeStdoutLineSafe(renderLedger(ledger));
|
|
18273
|
+
} finally {
|
|
18274
|
+
process.stdout.removeListener("error", stdoutErrorHandler);
|
|
18275
|
+
process.stderr.removeListener("error", stderrErrorHandler);
|
|
18276
|
+
}
|
|
18277
|
+
}
|
|
18278
|
+
__name(runCostLedger, "runCostLedger");
|
|
18279
|
+
var costLedgerCommand = {
|
|
18280
|
+
command: "cost-ledger",
|
|
18281
|
+
describe: "Aggregate this review's model-call cost from the harness's usage records",
|
|
18282
|
+
builder: /* @__PURE__ */ __name((yargs) => yargs.option("plan", {
|
|
18283
|
+
type: "string",
|
|
18284
|
+
demandOption: true,
|
|
18285
|
+
describe: "The plan report from Step 1 \u2014 its mtime marks the review start"
|
|
18286
|
+
}).option("out", {
|
|
18287
|
+
type: "string",
|
|
18288
|
+
describe: "Also write the full ledger as JSON to this path"
|
|
18289
|
+
}), "builder"),
|
|
18290
|
+
handler: /* @__PURE__ */ __name((args) => {
|
|
18291
|
+
runCostLedger(args);
|
|
18292
|
+
}, "handler")
|
|
18293
|
+
};
|
|
18294
|
+
|
|
17076
18295
|
// packages/cli/src/commands/review/run.ts
|
|
17077
18296
|
init_esbuild_shims();
|
|
17078
18297
|
import { spawn, execFileSync as execFileSync6 } from "node:child_process";
|
|
17079
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
17080
|
-
import { dirname as
|
|
18298
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync30, realpathSync as realpathSync3, statSync as statSync13 } from "node:fs";
|
|
18299
|
+
import { dirname as dirname23, join as join22, resolve as resolve22 } from "node:path";
|
|
17081
18300
|
var COMPOSED_PATTERN = /^qwen-review-.*composed\.json$/;
|
|
17082
18301
|
var COMPOSED_POLL_MS = 250;
|
|
17083
18302
|
var SIGNAL_EXIT_CODES = {
|
|
@@ -17111,10 +18330,10 @@ function newestArtifactSince(dir, pattern, startMs) {
|
|
|
17111
18330
|
}
|
|
17112
18331
|
for (const name of names) {
|
|
17113
18332
|
if (!pattern.test(name)) continue;
|
|
17114
|
-
const path3 =
|
|
18333
|
+
const path3 = join22(dir, name);
|
|
17115
18334
|
let mtime;
|
|
17116
18335
|
try {
|
|
17117
|
-
mtime =
|
|
18336
|
+
mtime = statSync13(path3).mtimeMs;
|
|
17118
18337
|
} catch {
|
|
17119
18338
|
continue;
|
|
17120
18339
|
}
|
|
@@ -17132,7 +18351,7 @@ function exitCodeFor(completed, event2, failOn) {
|
|
|
17132
18351
|
__name(exitCodeFor, "exitCodeFor");
|
|
17133
18352
|
function readComposed(path3) {
|
|
17134
18353
|
try {
|
|
17135
|
-
const parsed = JSON.parse(
|
|
18354
|
+
const parsed = JSON.parse(readFileSync30(path3, "utf8"));
|
|
17136
18355
|
return typeof parsed.event === "string" ? parsed : null;
|
|
17137
18356
|
} catch {
|
|
17138
18357
|
return null;
|
|
@@ -17163,7 +18382,7 @@ function childEnv() {
|
|
|
17163
18382
|
return env;
|
|
17164
18383
|
}
|
|
17165
18384
|
try {
|
|
17166
|
-
if (
|
|
18385
|
+
if (dirname23(realpathSync3(inherited)) === dirname23(realpathSync3(ownEntry))) {
|
|
17167
18386
|
return env;
|
|
17168
18387
|
}
|
|
17169
18388
|
} catch {
|
|
@@ -17285,8 +18504,8 @@ async function runReview(args) {
|
|
|
17285
18504
|
downgraded: composed?.downgraded ?? false,
|
|
17286
18505
|
downgradedFrom: composed?.downgradedFrom ?? null,
|
|
17287
18506
|
remediation: composed?.remediation ?? [],
|
|
17288
|
-
composedPath: composedPath ?
|
|
17289
|
-
reportPath: reportPath ?
|
|
18507
|
+
composedPath: composedPath ? resolve22(composedPath) : null,
|
|
18508
|
+
reportPath: reportPath ? resolve22(reportPath) : null,
|
|
17290
18509
|
childExitCode,
|
|
17291
18510
|
childSignal,
|
|
17292
18511
|
timedOut,
|
|
@@ -17300,7 +18519,7 @@ async function runReview(args) {
|
|
|
17300
18519
|
writeStdoutLine(result.verdictLine ?? `Event: ${result.event}`);
|
|
17301
18520
|
if (result.reportPath) writeStdoutLine(`Report: ${result.reportPath}`);
|
|
17302
18521
|
} else {
|
|
17303
|
-
const detail = composedPath !== null ? `a composed verdict was found at ${
|
|
18522
|
+
const detail = composedPath !== null ? `a composed verdict was found at ${resolve22(composedPath)} but could not be parsed` : "no composed verdict was produced";
|
|
17304
18523
|
writeStdoutLine(
|
|
17305
18524
|
timedOut ? "Review did not complete: timed out." : `Review did not complete: ${detail}${childExitCode !== null ? ` (CLI exit ${childExitCode})` : ""}${childSignal !== null ? ` (killed by ${childSignal})` : ""}.`
|
|
17306
18525
|
);
|
|
@@ -17368,22 +18587,22 @@ init_esbuild_shims();
|
|
|
17368
18587
|
import {
|
|
17369
18588
|
existsSync as existsSync14,
|
|
17370
18589
|
lstatSync as lstatSync5,
|
|
17371
|
-
mkdirSync as
|
|
17372
|
-
readFileSync as
|
|
17373
|
-
statSync as
|
|
18590
|
+
mkdirSync as mkdirSync27,
|
|
18591
|
+
readFileSync as readFileSync31,
|
|
18592
|
+
statSync as statSync14
|
|
17374
18593
|
} from "node:fs";
|
|
17375
18594
|
import {
|
|
17376
|
-
dirname as
|
|
18595
|
+
dirname as dirname24,
|
|
17377
18596
|
isAbsolute as isAbsolute4,
|
|
17378
|
-
join as
|
|
18597
|
+
join as join23,
|
|
17379
18598
|
relative as relative3,
|
|
17380
|
-
resolve as
|
|
18599
|
+
resolve as resolve23,
|
|
17381
18600
|
sep as sep4,
|
|
17382
18601
|
toNamespacedPath
|
|
17383
18602
|
} from "node:path";
|
|
17384
18603
|
import { isDeepStrictEqual } from "node:util";
|
|
17385
18604
|
function workspaceRoot() {
|
|
17386
|
-
return
|
|
18605
|
+
return resolve23(process.env["QWEN_CODE_PROJECT_DIR"] ?? process.cwd());
|
|
17387
18606
|
}
|
|
17388
18607
|
__name(workspaceRoot, "workspaceRoot");
|
|
17389
18608
|
function isWithin(parent, child) {
|
|
@@ -17392,7 +18611,7 @@ function isWithin(parent, child) {
|
|
|
17392
18611
|
}
|
|
17393
18612
|
__name(isWithin, "isWithin");
|
|
17394
18613
|
function workspacePath(root, value, label2) {
|
|
17395
|
-
const absolute =
|
|
18614
|
+
const absolute = resolve23(root, value);
|
|
17396
18615
|
if (!isWithin(root, absolute)) {
|
|
17397
18616
|
throw new Error(
|
|
17398
18617
|
`${label2} must be inside the workspace: ${JSON.stringify(value)}.`
|
|
@@ -17405,7 +18624,7 @@ function rejectSymlinkPath(root, path3, label2) {
|
|
|
17405
18624
|
const rel = relative3(root, path3);
|
|
17406
18625
|
let current = root;
|
|
17407
18626
|
for (const segment of rel.split(sep4)) {
|
|
17408
|
-
current =
|
|
18627
|
+
current = join23(current, segment);
|
|
17409
18628
|
if (existsSync14(current) && lstatSync5(current).isSymbolicLink()) {
|
|
17410
18629
|
throw new Error(`${label2} must not traverse a symbolic link.`);
|
|
17411
18630
|
}
|
|
@@ -17415,14 +18634,14 @@ __name(rejectSymlinkPath, "rejectSymlinkPath");
|
|
|
17415
18634
|
function sameFile(left, right) {
|
|
17416
18635
|
if (left === right) return true;
|
|
17417
18636
|
if (!existsSync14(left) || !existsSync14(right)) return false;
|
|
17418
|
-
const leftStat =
|
|
17419
|
-
const rightStat =
|
|
18637
|
+
const leftStat = statSync14(left);
|
|
18638
|
+
const rightStat = statSync14(right);
|
|
17420
18639
|
return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
17421
18640
|
}
|
|
17422
18641
|
__name(sameFile, "sameFile");
|
|
17423
18642
|
function readText(path3, label2) {
|
|
17424
18643
|
try {
|
|
17425
|
-
return
|
|
18644
|
+
return readFileSync31(path3, "utf8");
|
|
17426
18645
|
} catch (error) {
|
|
17427
18646
|
throw new Error(
|
|
17428
18647
|
`Could not read the ${label2} file at ${JSON.stringify(path3)}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -17548,7 +18767,7 @@ function saveReviewArtifact(args) {
|
|
|
17548
18767
|
const composedPath = workspacePath(root, args.composed, "Composed input");
|
|
17549
18768
|
const reportPath = workspacePath(root, args.report, "Markdown report");
|
|
17550
18769
|
const outputPath = workspacePath(root, args.out, "Output");
|
|
17551
|
-
const reviewsRoot =
|
|
18770
|
+
const reviewsRoot = resolve23(root, REVIEWS_DIR);
|
|
17552
18771
|
if (!isWithin(reviewsRoot, reportPath) || reportPath === reviewsRoot) {
|
|
17553
18772
|
throw new Error(
|
|
17554
18773
|
`Markdown report must be a file under ${JSON.stringify(REVIEWS_DIR)}: ${JSON.stringify(args.report)}.`
|
|
@@ -17589,7 +18808,7 @@ function saveReviewArtifact(args) {
|
|
|
17589
18808
|
const verdict = validateVerdict(readJson2(composedPath, "composed verdict"));
|
|
17590
18809
|
let reportIsFile;
|
|
17591
18810
|
try {
|
|
17592
|
-
reportIsFile =
|
|
18811
|
+
reportIsFile = statSync14(reportPath).isFile();
|
|
17593
18812
|
} catch {
|
|
17594
18813
|
}
|
|
17595
18814
|
if (reportIsFile === false) {
|
|
@@ -17609,7 +18828,7 @@ function saveReviewArtifact(args) {
|
|
|
17609
18828
|
outcomesRecorded: findings.outcomesRecorded,
|
|
17610
18829
|
markdownReportPath
|
|
17611
18830
|
};
|
|
17612
|
-
|
|
18831
|
+
mkdirSync27(dirname24(outputPath), { recursive: true });
|
|
17613
18832
|
atomicWriteFileSync(
|
|
17614
18833
|
toNamespacedPath(outputPath),
|
|
17615
18834
|
`${JSON.stringify(document, null, 2)}
|
|
@@ -17662,9 +18881,9 @@ var saveArtifactCommand = {
|
|
|
17662
18881
|
var reviewCommand = {
|
|
17663
18882
|
command: "review",
|
|
17664
18883
|
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)",
|
|
17665
|
-
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(
|
|
18884
|
+
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(costLedgerCommand).command(presubmitCommand).command(testEfficacyCommand).command(testPlanCommand).command(findingsCommand).command(publishAssetsCommand).command(composeReviewCommand).command(saveArtifactCommand).command(submitCommand).command(cleanupCommand).demandCommand(
|
|
17666
18885
|
1,
|
|
17667
|
-
"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."
|
|
18886
|
+
"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, cost-ledger, presubmit, test-efficacy, test-plan, findings, publish-assets, compose-review, save-artifact, submit, or cleanup."
|
|
17668
18887
|
).version(false), "builder"),
|
|
17669
18888
|
handler: /* @__PURE__ */ __name(() => {
|
|
17670
18889
|
}, "handler")
|
|
@@ -17864,11 +19083,11 @@ var updateCommand3 = {
|
|
|
17864
19083
|
stdioHelpers,
|
|
17865
19084
|
{ updateEventEmitter }
|
|
17866
19085
|
] = await Promise.all([
|
|
17867
|
-
import("./settings-
|
|
17868
|
-
import("./updateCheck-
|
|
17869
|
-
import("./installationInfo-
|
|
17870
|
-
import("./standalone-update-
|
|
17871
|
-
import("./stdioHelpers-
|
|
19086
|
+
import("./settings-AI2BTBOZ.js"),
|
|
19087
|
+
import("./updateCheck-UOVZSTP7.js"),
|
|
19088
|
+
import("./installationInfo-PO4Z3ES7.js"),
|
|
19089
|
+
import("./standalone-update-WVINHRNS.js"),
|
|
19090
|
+
import("./stdioHelpers-7A2EE64Z.js"),
|
|
17872
19091
|
import("./updateEventEmitter-5MKC7BLQ.js")
|
|
17873
19092
|
]);
|
|
17874
19093
|
const { formatUpdateInstructions, getInstallationInfo } = installationInfoModule;
|
|
@@ -18713,7 +19932,7 @@ var SessionIdConflictError = class extends Error {
|
|
|
18713
19932
|
this.sessionId = sessionId;
|
|
18714
19933
|
}
|
|
18715
19934
|
};
|
|
18716
|
-
async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtensions, hooksConfig, disabledSkillNamesProvider, sessionMcpServers, settingsWatcher, throwOnSessionIdConflict = false) {
|
|
19935
|
+
async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtensions, hooksConfig, disabledSkillNamesProvider, sessionMcpServers, settingsWatcher, throwOnSessionIdConflict = false, hostPolicy) {
|
|
18717
19936
|
const debugMode = isDebugMode(argv);
|
|
18718
19937
|
if (debugMode && process.env["QWEN_DEBUG_LOG_FILE"] === void 0) {
|
|
18719
19938
|
process.env["QWEN_DEBUG_LOG_FILE"] = "1";
|
|
@@ -19034,7 +20253,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
|
|
|
19034
20253
|
disabledSlashCommands: disabledSlashCommands.length > 0 ? disabledSlashCommands : void 0,
|
|
19035
20254
|
disabledSkillNamesProvider: bareMode || safeMode ? void 0 : disabledSkillNamesProvider,
|
|
19036
20255
|
terminalImageRenderSupportProvider: interactive ? async () => {
|
|
19037
|
-
const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-
|
|
20256
|
+
const { getTerminalImageRenderSupport } = await import("./terminal-image-renderer-II74TFVW.js");
|
|
19038
20257
|
return getTerminalImageRenderSupport();
|
|
19039
20258
|
} : void 0,
|
|
19040
20259
|
disabledSkillLevels: bareMode || safeMode || !Array.isArray(settings.skills?.disabledLevels) ? void 0 : settings.skills.disabledLevels.filter(isSkillLevel),
|
|
@@ -19051,6 +20270,7 @@ async function loadCliConfig(settings, argv, cwd = process.cwd(), overrideExtens
|
|
|
19051
20270
|
deny: mergedDeny.length > 0 ? mergedDeny : void 0,
|
|
19052
20271
|
autoMode: bareMode || safeMode ? void 0 : settings.permissions?.autoMode
|
|
19053
20272
|
},
|
|
20273
|
+
toolInvocationGuard: hostPolicy?.toolInvocationGuard,
|
|
19054
20274
|
// Permission rule persistence callback (writes to settings files).
|
|
19055
20275
|
onPersistPermissionRule: /* @__PURE__ */ __name(async (scope, ruleType, rule) => {
|
|
19056
20276
|
const currentSettings = loadSettings(cwd);
|