@ebrindley/build 0.0.0-stage → 1.42.1-rc28
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/.build-project/providers/catalog.json +13951 -0
- package/.build-project/providers/pricing.json +1978 -0
- package/.build-project/providers/registry.json +4103 -0
- package/.github/SECURITY.md +75 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +661 -0
- package/CODE_OF_CONDUCT.md +44 -0
- package/CONTRIBUTING.md +24 -0
- package/INSTALL.md +623 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/THIRD_PARTY_NOTICES.txt +26 -0
- package/dist/ai-judge-UE5UOYOO.js +191 -0
- package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
- package/dist/allowlist-utils-MZZKKXRD.js +31 -0
- package/dist/apply-completion-mode-EM37UZHB.js +110 -0
- package/dist/artifact-migrator-WPACNSEP.js +276 -0
- package/dist/ask-2OZ4JFQR.js +176 -0
- package/dist/auth-6UKBTJUQ.js +185 -0
- package/dist/auth-H5KJL6HL.js +165 -0
- package/dist/auth-LVJRJDMU.js +12 -0
- package/dist/auth-NHZOCNRR.js +94 -0
- package/dist/auth-PBXHWXGW.js +95 -0
- package/dist/auth-T7Y5DCAH.js +91 -0
- package/dist/auth-TVVKCHQW.js +13 -0
- package/dist/auth-UHB2UCSS.js +97 -0
- package/dist/auth-VACA3VUL.js +98 -0
- package/dist/auth-VVLC77S3.js +44 -0
- package/dist/auth-liveness-XO4AGDDD.js +53 -0
- package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
- package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
- package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
- package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
- package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
- package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
- package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
- package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
- package/dist/autoloop-success-C32QVWOP.js +93 -0
- package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
- package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
- package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
- package/dist/backend-YUTFDZXZ.js +30 -0
- package/dist/background-executor-Z3ODTMD7.js +260 -0
- package/dist/backlog-75QPEAMW.js +403 -0
- package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
- package/dist/backup-active-store-XHWLN34M.js +16 -0
- package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
- package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
- package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
- package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
- package/dist/build-gate-7NKWPY62.js +68 -0
- package/dist/build-root-S7DINODY.js +18 -0
- package/dist/change-summary-R6CB6VXJ.js +50 -0
- package/dist/chunk-24MNKKOM.js +173 -0
- package/dist/chunk-2H6VZFIO.js +235 -0
- package/dist/chunk-2JIX3EKV.js +39 -0
- package/dist/chunk-2KO7HVZM.js +106 -0
- package/dist/chunk-2L36U25O.js +100 -0
- package/dist/chunk-2NMNFUQA.js +475 -0
- package/dist/chunk-2OUAEH67.js +83 -0
- package/dist/chunk-2VLJUXJU.js +115 -0
- package/dist/chunk-2VUEDIUM.js +77 -0
- package/dist/chunk-2Y2IIHPI.js +430 -0
- package/dist/chunk-2YRG3LHZ.js +220 -0
- package/dist/chunk-2Z5Q3SUY.js +65 -0
- package/dist/chunk-32IYXIE5.js +873 -0
- package/dist/chunk-33IGPPUI.js +292 -0
- package/dist/chunk-352PCJLK.js +283 -0
- package/dist/chunk-35J4NE6S.js +226 -0
- package/dist/chunk-3C7WBIVK.js +1016 -0
- package/dist/chunk-3H3EY6U7.js +1225 -0
- package/dist/chunk-3H4MWYIC.js +198 -0
- package/dist/chunk-3HHWQR3N.js +2479 -0
- package/dist/chunk-3JKNRYEE.js +14 -0
- package/dist/chunk-3LECCYZY.js +128 -0
- package/dist/chunk-3LOY6YZI.js +852 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3NOTLVLG.js +3803 -0
- package/dist/chunk-3OPP2OZB.js +1480 -0
- package/dist/chunk-3P5IN5E7.js +9 -0
- package/dist/chunk-3PL22IYZ.js +20 -0
- package/dist/chunk-3REDAJUJ.js +14 -0
- package/dist/chunk-3S2R4XJI.js +203 -0
- package/dist/chunk-3UBZI5DW.js +61 -0
- package/dist/chunk-3VV5OBGG.js +308 -0
- package/dist/chunk-3WY5TU2O.js +823 -0
- package/dist/chunk-43OKHTW4.js +82 -0
- package/dist/chunk-45KUJBDI.js +112 -0
- package/dist/chunk-47X5LM7B.js +224 -0
- package/dist/chunk-4B6FQJ77.js +268 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4DJL2VXA.js +24 -0
- package/dist/chunk-4EBSXZ73.js +48 -0
- package/dist/chunk-4QKP6I4C.js +294 -0
- package/dist/chunk-4U6XK2DG.js +300 -0
- package/dist/chunk-4UMRUFBD.js +132 -0
- package/dist/chunk-4US4SYJ4.js +569 -0
- package/dist/chunk-4ZPLWEZO.js +154 -0
- package/dist/chunk-56WPYDJH.js +624 -0
- package/dist/chunk-57WRCJJW.js +50 -0
- package/dist/chunk-5AW4RMNG.js +24 -0
- package/dist/chunk-5EQN56OZ.js +155 -0
- package/dist/chunk-5F43TUIN.js +38 -0
- package/dist/chunk-5FKHIKTS.js +115 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5KQETTNS.js +152 -0
- package/dist/chunk-5PDVN2KO.js +498 -0
- package/dist/chunk-5UBIFVRD.js +509 -0
- package/dist/chunk-5YDE3Q62.js +46 -0
- package/dist/chunk-626AJVMU.js +302 -0
- package/dist/chunk-633XZ7UF.js +3583 -0
- package/dist/chunk-63DHNHGA.js +236 -0
- package/dist/chunk-666656AR.js +568 -0
- package/dist/chunk-67YRJPJO.js +19 -0
- package/dist/chunk-6CAU7VBA.js +20 -0
- package/dist/chunk-6CB3KJRY.js +21 -0
- package/dist/chunk-6DBMALXL.js +29 -0
- package/dist/chunk-6E25C4IA.js +269 -0
- package/dist/chunk-6FIBNIQU.js +73 -0
- package/dist/chunk-6G57FYBV.js +189 -0
- package/dist/chunk-6KZKRTG3.js +4885 -0
- package/dist/chunk-6LAH2UV2.js +525 -0
- package/dist/chunk-6PZY2TTU.js +921 -0
- package/dist/chunk-6T6NHXK4.js +230 -0
- package/dist/chunk-6TBEX4R4.js +432 -0
- package/dist/chunk-6TRD7TEE.js +911 -0
- package/dist/chunk-6XCP2RAI.js +47 -0
- package/dist/chunk-726G6WDL.js +351 -0
- package/dist/chunk-75EMT7BF.js +616 -0
- package/dist/chunk-762ACDZJ.js +4696 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DJ6SQH3.js +510 -0
- package/dist/chunk-7HPCIOMA.js +1383 -0
- package/dist/chunk-7HYBSRPZ.js +17503 -0
- package/dist/chunk-7KBEOPVB.js +73 -0
- package/dist/chunk-7LHJDJB3.js +2222 -0
- package/dist/chunk-7NVJMH52.js +306 -0
- package/dist/chunk-7RMARDVH.js +33 -0
- package/dist/chunk-7SEWU4QG.js +36 -0
- package/dist/chunk-7TUTZTI2.js +911 -0
- package/dist/chunk-7VBDFPUM.js +340 -0
- package/dist/chunk-7VRJHTGZ.js +6406 -0
- package/dist/chunk-7YAOUUKK.js +493 -0
- package/dist/chunk-AAAODV76.js +860 -0
- package/dist/chunk-AB4HUJVQ.js +339 -0
- package/dist/chunk-AEQWV746.js +611 -0
- package/dist/chunk-AHU3FKPA.js +11639 -0
- package/dist/chunk-AISTV5XU.js +226 -0
- package/dist/chunk-AJBTKGJZ.js +48 -0
- package/dist/chunk-AJJNW7XT.js +248 -0
- package/dist/chunk-AJN5Y5YZ.js +160 -0
- package/dist/chunk-ALDTFNZ3.js +150 -0
- package/dist/chunk-ARARVIZK.js +281 -0
- package/dist/chunk-ATBM6AW5.js +53 -0
- package/dist/chunk-ATKEKM3E.js +206 -0
- package/dist/chunk-AUJLW6JZ.js +92 -0
- package/dist/chunk-AY3MHEFM.js +24 -0
- package/dist/chunk-B7PYTV72.js +236 -0
- package/dist/chunk-B7YRSULE.js +1423 -0
- package/dist/chunk-BAWJNMWF.js +339 -0
- package/dist/chunk-BBPIILO2.js +88 -0
- package/dist/chunk-BD3PGW6A.js +957 -0
- package/dist/chunk-BDPYFAB4.js +44 -0
- package/dist/chunk-BIUV23HH.js +729 -0
- package/dist/chunk-BQ3VAGXH.js +243 -0
- package/dist/chunk-BQJOGAWT.js +31 -0
- package/dist/chunk-BT7EG7CO.js +68 -0
- package/dist/chunk-BV3XQQLU.js +27 -0
- package/dist/chunk-BY6ODHNZ.js +78 -0
- package/dist/chunk-BZRLSZNV.js +79 -0
- package/dist/chunk-C3DH3NCY.js +2773 -0
- package/dist/chunk-C3KCMAJP.js +756 -0
- package/dist/chunk-C7AGUJ5K.js +267 -0
- package/dist/chunk-CBSWKQCX.js +342 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFCVYHSW.js +373 -0
- package/dist/chunk-CHA2HDE5.js +126 -0
- package/dist/chunk-CIHWPGS5.js +146 -0
- package/dist/chunk-CJBSZX3F.js +127 -0
- package/dist/chunk-CLA6QIJM.js +43 -0
- package/dist/chunk-CPSVQR2I.js +365 -0
- package/dist/chunk-CRPRVNQ4.js +254 -0
- package/dist/chunk-CSIE4Y7C.js +1093 -0
- package/dist/chunk-CSOPSYDY.js +142 -0
- package/dist/chunk-CSXLLEGB.js +205 -0
- package/dist/chunk-CTDLERWU.js +293 -0
- package/dist/chunk-CULBXMSK.js +110 -0
- package/dist/chunk-D2M2Y5ID.js +21 -0
- package/dist/chunk-D37WY7SH.js +1060 -0
- package/dist/chunk-D4VJFCGZ.js +89 -0
- package/dist/chunk-D6WTZMZB.js +48 -0
- package/dist/chunk-DBHOWDUH.js +976 -0
- package/dist/chunk-DCACX7BV.js +19364 -0
- package/dist/chunk-DFHX4XYA.js +128 -0
- package/dist/chunk-DIKBRRKE.js +176 -0
- package/dist/chunk-DJG5RIOR.js +9 -0
- package/dist/chunk-DNIOIISW.js +616 -0
- package/dist/chunk-DOA5G7IA.js +315 -0
- package/dist/chunk-DP7FKP6W.js +855 -0
- package/dist/chunk-DS2JZ677.js +79 -0
- package/dist/chunk-DSRLVYBS.js +82 -0
- package/dist/chunk-EAEVKNJA.js +209 -0
- package/dist/chunk-EEUESN3T.js +575 -0
- package/dist/chunk-EJ4LUU5Y.js +73 -0
- package/dist/chunk-EJIWTPSZ.js +1063 -0
- package/dist/chunk-EJVCDTAN.js +116 -0
- package/dist/chunk-EKLUBDBQ.js +627 -0
- package/dist/chunk-EN62WLFD.js +849 -0
- package/dist/chunk-ENYRCL43.js +26 -0
- package/dist/chunk-EP22ZMFO.js +273 -0
- package/dist/chunk-EP4SVAVI.js +87 -0
- package/dist/chunk-EPAMVGI3.js +1562 -0
- package/dist/chunk-EPRMH6HO.js +24 -0
- package/dist/chunk-ES6NWJ36.js +4057 -0
- package/dist/chunk-ETA2K4KI.js +30 -0
- package/dist/chunk-ETAD4NKD.js +31 -0
- package/dist/chunk-EUWIIDQ7.js +93 -0
- package/dist/chunk-EVE3OD2W.js +141 -0
- package/dist/chunk-EXA26FFC.js +459 -0
- package/dist/chunk-EXSPAA35.js +244 -0
- package/dist/chunk-F55YABKV.js +258 -0
- package/dist/chunk-F5DNGU5W.js +27 -0
- package/dist/chunk-F622MJYV.js +103 -0
- package/dist/chunk-F6NIQD74.js +5111 -0
- package/dist/chunk-FAWJTC34.js +43 -0
- package/dist/chunk-FAZBRHGG.js +47 -0
- package/dist/chunk-FIEUSGNJ.js +39 -0
- package/dist/chunk-FKCG7K6H.js +1146 -0
- package/dist/chunk-FOVVXV45.js +229 -0
- package/dist/chunk-FRBPKBFE.js +30 -0
- package/dist/chunk-FSA5BPFI.js +268 -0
- package/dist/chunk-FSJ5TTZ3.js +29 -0
- package/dist/chunk-FT7SGLXT.js +193 -0
- package/dist/chunk-FUUVZZUB.js +62 -0
- package/dist/chunk-FYMRVUNZ.js +1697 -0
- package/dist/chunk-FYTAA5FG.js +162 -0
- package/dist/chunk-G3VB2UCZ.js +3892 -0
- package/dist/chunk-G6OIMVXH.js +405 -0
- package/dist/chunk-G7PQCXVH.js +228 -0
- package/dist/chunk-GAB6UW52.js +1087 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GC4CRMTS.js +642 -0
- package/dist/chunk-GFHSIWHL.js +5297 -0
- package/dist/chunk-GI5M67YA.js +735 -0
- package/dist/chunk-GIJWHUWF.js +191 -0
- package/dist/chunk-GO6DRECI.js +1 -0
- package/dist/chunk-GOFVBTMN.js +314 -0
- package/dist/chunk-GQIP72ZJ.js +13 -0
- package/dist/chunk-GQXLXRFA.js +28 -0
- package/dist/chunk-GT6HFKJB.js +48 -0
- package/dist/chunk-GVJGSSKM.js +174 -0
- package/dist/chunk-GWHGTG37.js +36 -0
- package/dist/chunk-GZAJHLKO.js +18 -0
- package/dist/chunk-GZK62I3N.js +314 -0
- package/dist/chunk-H3HGMISG.js +483 -0
- package/dist/chunk-H3ISZJMJ.js +403 -0
- package/dist/chunk-H4HXVWEC.js +9 -0
- package/dist/chunk-H6MJGKAW.js +120 -0
- package/dist/chunk-HBTTGVR5.js +353 -0
- package/dist/chunk-HBZ322WZ.js +35 -0
- package/dist/chunk-HEP3ILP4.js +34 -0
- package/dist/chunk-HGFXODKO.js +106 -0
- package/dist/chunk-HHWDTB6S.js +59 -0
- package/dist/chunk-HKO45NLZ.js +286 -0
- package/dist/chunk-HLVTEA6A.js +89 -0
- package/dist/chunk-HS3SY6HN.js +160 -0
- package/dist/chunk-HT5OCGKN.js +899 -0
- package/dist/chunk-HXIIEMGF.js +78 -0
- package/dist/chunk-I56TH7VM.js +46 -0
- package/dist/chunk-I6FN3R5D.js +352 -0
- package/dist/chunk-I7SCBHJN.js +655 -0
- package/dist/chunk-IJM4GKAT.js +529 -0
- package/dist/chunk-IMEYU36R.js +344 -0
- package/dist/chunk-INJ3SS6A.js +936 -0
- package/dist/chunk-IUKOPCCE.js +441 -0
- package/dist/chunk-IVEPZQJO.js +33 -0
- package/dist/chunk-IVZEONFC.js +43 -0
- package/dist/chunk-IWBHRSG7.js +936 -0
- package/dist/chunk-IYY3HQAD.js +484 -0
- package/dist/chunk-J2ICMYDA.js +48 -0
- package/dist/chunk-J2QCFLUN.js +505 -0
- package/dist/chunk-J5HM6V5P.js +1039 -0
- package/dist/chunk-J6NZX6QH.js +398 -0
- package/dist/chunk-JBBB2Y47.js +432 -0
- package/dist/chunk-JDCZQZU7.js +42 -0
- package/dist/chunk-JEDNXGSS.js +169 -0
- package/dist/chunk-JHP7OT6K.js +89 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JNMWYMC6.js +38 -0
- package/dist/chunk-JNSTWNNW.js +25 -0
- package/dist/chunk-JOSK6GEU.js +9564 -0
- package/dist/chunk-JQUMSIGC.js +100 -0
- package/dist/chunk-JVHGMURQ.js +1106 -0
- package/dist/chunk-JYXS47FQ.js +951 -0
- package/dist/chunk-K2ZMW2Z5.js +2147 -0
- package/dist/chunk-K7FAFORM.js +14 -0
- package/dist/chunk-KBJXX3ON.js +333 -0
- package/dist/chunk-KHFKVQWY.js +486 -0
- package/dist/chunk-KHMEI3DQ.js +173 -0
- package/dist/chunk-KHW3UD7B.js +69 -0
- package/dist/chunk-KKQKWAMF.js +365 -0
- package/dist/chunk-KOKXJV2U.js +455 -0
- package/dist/chunk-KQ4NGOJI.js +14 -0
- package/dist/chunk-KQYQOXMS.js +342 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KUEWV2JH.js +593 -0
- package/dist/chunk-KXYDV3HP.js +1523 -0
- package/dist/chunk-KZ4JMGZ5.js +96 -0
- package/dist/chunk-KZWGBUIO.js +2659 -0
- package/dist/chunk-L2CBG5IX.js +1892 -0
- package/dist/chunk-L2S235CU.js +314 -0
- package/dist/chunk-L32WD57F.js +98 -0
- package/dist/chunk-LDJCKAJP.js +123 -0
- package/dist/chunk-LDWNBEN7.js +541 -0
- package/dist/chunk-LJVH57G2.js +241 -0
- package/dist/chunk-LPQ7NZEN.js +209 -0
- package/dist/chunk-LRGH3CCV.js +148 -0
- package/dist/chunk-LULOGMMM.js +1133 -0
- package/dist/chunk-LWGKN2GY.js +8284 -0
- package/dist/chunk-LX4F24ZH.js +157 -0
- package/dist/chunk-M4BUEXXF.js +839 -0
- package/dist/chunk-M6UHOC4I.js +553 -0
- package/dist/chunk-MCBNWZNO.js +657 -0
- package/dist/chunk-MEMSY6VY.js +223 -0
- package/dist/chunk-MHLEZQQI.js +145 -0
- package/dist/chunk-MJ6WNSIX.js +192 -0
- package/dist/chunk-MKASIKWZ.js +442 -0
- package/dist/chunk-MNOVBYFU.js +60 -0
- package/dist/chunk-MO2BYAAF.js +2896 -0
- package/dist/chunk-MP6P7Y2A.js +180 -0
- package/dist/chunk-MQ4TFJAG.js +3365 -0
- package/dist/chunk-MT2WAIR3.js +131 -0
- package/dist/chunk-MTU7VTNN.js +1122 -0
- package/dist/chunk-MULMXX4V.js +150 -0
- package/dist/chunk-MW2DWWVA.js +147 -0
- package/dist/chunk-MY3ZPUN4.js +151 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N3Q2JNPV.js +249 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-NAINGKHF.js +35 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NHNPEZS4.js +1408 -0
- package/dist/chunk-NHTIXJCR.js +17 -0
- package/dist/chunk-NKLPMBNV.js +1154 -0
- package/dist/chunk-NMVZ5YES.js +199 -0
- package/dist/chunk-NRFRY3GE.js +256 -0
- package/dist/chunk-NTD6CSI6.js +1266 -0
- package/dist/chunk-NUO7B3SH.js +318 -0
- package/dist/chunk-NVOHIJKG.js +1319 -0
- package/dist/chunk-NXH3RX7O.js +523 -0
- package/dist/chunk-NXZ3DURF.js +257 -0
- package/dist/chunk-O26L6F7F.js +209 -0
- package/dist/chunk-O5RRIUEP.js +124 -0
- package/dist/chunk-O5X6BKAO.js +10 -0
- package/dist/chunk-O6ZQUVTR.js +31 -0
- package/dist/chunk-O7FE75QZ.js +306 -0
- package/dist/chunk-OC7Z6AJQ.js +81 -0
- package/dist/chunk-OH7IEZ63.js +307 -0
- package/dist/chunk-OI734GRS.js +115 -0
- package/dist/chunk-OJBH62DK.js +684 -0
- package/dist/chunk-OKJLVSTJ.js +247 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ONHAMBN6.js +12 -0
- package/dist/chunk-ONYPLIVE.js +753 -0
- package/dist/chunk-OSK7QWPD.js +637 -0
- package/dist/chunk-OTXLMJKP.js +438 -0
- package/dist/chunk-OV5PKQMA.js +213 -0
- package/dist/chunk-OXK5HA4B.js +31 -0
- package/dist/chunk-OYDWFQA4.js +272 -0
- package/dist/chunk-OZAUKWHB.js +22 -0
- package/dist/chunk-P5P2EKGH.js +94 -0
- package/dist/chunk-P6SSWAKY.js +29 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-P7RVCHQV.js +197 -0
- package/dist/chunk-PEBWDYIW.js +726 -0
- package/dist/chunk-PFCXFO7A.js +9193 -0
- package/dist/chunk-PFLDWMX6.js +168 -0
- package/dist/chunk-PHZGLQSK.js +15883 -0
- package/dist/chunk-PNFTPNSR.js +20 -0
- package/dist/chunk-PPHXX4W7.js +175 -0
- package/dist/chunk-PPZESPJT.js +262 -0
- package/dist/chunk-PRDHI2L5.js +296 -0
- package/dist/chunk-PSTKWHHN.js +32 -0
- package/dist/chunk-PV53ODOT.js +555 -0
- package/dist/chunk-PVJBTI7P.js +24 -0
- package/dist/chunk-PXKERBKX.js +100 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3DHEZA5.js +85 -0
- package/dist/chunk-Q5RZDVQK.js +204 -0
- package/dist/chunk-Q64IR2Z2.js +4933 -0
- package/dist/chunk-Q6Z5UGPL.js +398 -0
- package/dist/chunk-QAMAV4DT.js +627 -0
- package/dist/chunk-QBWF4444.js +112 -0
- package/dist/chunk-QDKYT4CI.js +22805 -0
- package/dist/chunk-QFLBRWCJ.js +126 -0
- package/dist/chunk-QILAQACE.js +107 -0
- package/dist/chunk-QJ6OEH4K.js +3683 -0
- package/dist/chunk-QK3NLYTV.js +320 -0
- package/dist/chunk-QOFLUS24.js +535 -0
- package/dist/chunk-QOIASNEJ.js +98 -0
- package/dist/chunk-QP4UTSOV.js +3587 -0
- package/dist/chunk-QP5ST7RI.js +904 -0
- package/dist/chunk-QS5AGRZN.js +167 -0
- package/dist/chunk-QTAOU53N.js +107 -0
- package/dist/chunk-QTCLJHLZ.js +351 -0
- package/dist/chunk-QTOLTG4L.js +119 -0
- package/dist/chunk-QWDV4OSL.js +142 -0
- package/dist/chunk-QWMQ74KD.js +508 -0
- package/dist/chunk-QX6LOMMH.js +158 -0
- package/dist/chunk-QXBKIHY7.js +238 -0
- package/dist/chunk-QXBKTJT6.js +270 -0
- package/dist/chunk-QYQOVOLR.js +196 -0
- package/dist/chunk-R2CKS6WT.js +73 -0
- package/dist/chunk-R2TIYG7U.js +68 -0
- package/dist/chunk-R2W3LESH.js +465 -0
- package/dist/chunk-R5EXHMUN.js +100 -0
- package/dist/chunk-RBBTBCHX.js +289 -0
- package/dist/chunk-RBKLOUXA.js +121 -0
- package/dist/chunk-RCP2Y6DQ.js +86 -0
- package/dist/chunk-RCYEJSD5.js +260 -0
- package/dist/chunk-RFDVSI4N.js +134 -0
- package/dist/chunk-RGJU63FV.js +1717 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RIMARPCF.js +152 -0
- package/dist/chunk-RJ6RSNT6.js +42 -0
- package/dist/chunk-RNI2GUIF.js +131 -0
- package/dist/chunk-RPHUWJ3K.js +264 -0
- package/dist/chunk-RPIMAMV7.js +361 -0
- package/dist/chunk-RQFX6YRN.js +49 -0
- package/dist/chunk-RRC5BL7B.js +285 -0
- package/dist/chunk-RSYTSBOO.js +79 -0
- package/dist/chunk-RWQQIKIC.js +35 -0
- package/dist/chunk-RZUBOZH2.js +373 -0
- package/dist/chunk-S2E632FQ.js +380 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S2XIMQJQ.js +681 -0
- package/dist/chunk-S47ISY27.js +183 -0
- package/dist/chunk-S6I4KISU.js +142 -0
- package/dist/chunk-S6TAFQI5.js +2403 -0
- package/dist/chunk-SBBNT5ZJ.js +27 -0
- package/dist/chunk-SDJV2DTQ.js +491 -0
- package/dist/chunk-SDPIFNLD.js +33 -0
- package/dist/chunk-SF6YK3JI.js +835 -0
- package/dist/chunk-SK3ZFONO.js +976 -0
- package/dist/chunk-SKTFRFQ7.js +1775 -0
- package/dist/chunk-SNGM452K.js +2002 -0
- package/dist/chunk-STW7OFOA.js +581 -0
- package/dist/chunk-SU6UFUQY.js +1351 -0
- package/dist/chunk-SVJM3LJA.js +27 -0
- package/dist/chunk-SVUNZOPG.js +129 -0
- package/dist/chunk-T5XLUYV3.js +34 -0
- package/dist/chunk-T7E5JS6N.js +57 -0
- package/dist/chunk-TA3N5MMO.js +450 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TDEMDRN2.js +1639 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TGCVL74Y.js +28 -0
- package/dist/chunk-TGXXYDL4.js +1012 -0
- package/dist/chunk-TIKTYUJO.js +296 -0
- package/dist/chunk-TIZAJD4M.js +6029 -0
- package/dist/chunk-TLSEZZCG.js +5549 -0
- package/dist/chunk-TMBDRHAX.js +19 -0
- package/dist/chunk-TRURQUBP.js +1217 -0
- package/dist/chunk-TSQ256N5.js +152 -0
- package/dist/chunk-TUQMMFEQ.js +2244 -0
- package/dist/chunk-U424MJLC.js +81 -0
- package/dist/chunk-U4DRS6ZW.js +11 -0
- package/dist/chunk-U5KS22IN.js +204 -0
- package/dist/chunk-UAPHFJPN.js +673 -0
- package/dist/chunk-UB7LQITD.js +1040 -0
- package/dist/chunk-UBVRHGAZ.js +237 -0
- package/dist/chunk-UDNNAQN5.js +98 -0
- package/dist/chunk-UJP3XM54.js +342 -0
- package/dist/chunk-UJYDH2MW.js +305 -0
- package/dist/chunk-UKMPASIC.js +399 -0
- package/dist/chunk-UKUL3EHK.js +763 -0
- package/dist/chunk-UPHU64NB.js +1327 -0
- package/dist/chunk-URMSZXAD.js +43 -0
- package/dist/chunk-US3RT6CY.js +525 -0
- package/dist/chunk-UTDEOPKV.js +30 -0
- package/dist/chunk-UUUDKV7N.js +13 -0
- package/dist/chunk-UVKWU3SM.js +39 -0
- package/dist/chunk-V2TVPO3X.js +521 -0
- package/dist/chunk-V3KLVK7B.js +398 -0
- package/dist/chunk-V3YFFHDR.js +17 -0
- package/dist/chunk-VAG6GUBR.js +1162 -0
- package/dist/chunk-VBENAHDA.js +1814 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VCGSWN5M.js +1300 -0
- package/dist/chunk-VE2O2I7M.js +115 -0
- package/dist/chunk-VIFHYTI5.js +31 -0
- package/dist/chunk-VJER3FUS.js +5080 -0
- package/dist/chunk-VLMDM56Q.js +268 -0
- package/dist/chunk-VMFZEVVX.js +99 -0
- package/dist/chunk-VNDPQVUZ.js +1853 -0
- package/dist/chunk-VSPJRLGA.js +4783 -0
- package/dist/chunk-VSWC5ZXI.js +103 -0
- package/dist/chunk-VYI4DSZP.js +2017 -0
- package/dist/chunk-W4BAIQ6L.js +1212 -0
- package/dist/chunk-W7VQAL2K.js +2203 -0
- package/dist/chunk-WEI3ZDZH.js +33 -0
- package/dist/chunk-WEIUS5NT.js +26 -0
- package/dist/chunk-WEW7UFWE.js +546 -0
- package/dist/chunk-WGQAXLNA.js +384 -0
- package/dist/chunk-WJAL4JL3.js +206 -0
- package/dist/chunk-WKFOJ5ZG.js +141 -0
- package/dist/chunk-WMWQEFLN.js +2177 -0
- package/dist/chunk-WNECKKO2.js +154 -0
- package/dist/chunk-WPKLYOAM.js +195 -0
- package/dist/chunk-WSH37YQN.js +243 -0
- package/dist/chunk-WV3KQDPE.js +84 -0
- package/dist/chunk-WZFC4V4U.js +389 -0
- package/dist/chunk-XCOIBNET.js +13 -0
- package/dist/chunk-XDM65LBX.js +53 -0
- package/dist/chunk-XGU6PBO5.js +645 -0
- package/dist/chunk-XHOI4V62.js +43 -0
- package/dist/chunk-XIBVFMLQ.js +6858 -0
- package/dist/chunk-XJ2E7YEJ.js +117 -0
- package/dist/chunk-XON4ZCYX.js +69 -0
- package/dist/chunk-XQPEWJPI.js +127 -0
- package/dist/chunk-XSJTIFUY.js +31 -0
- package/dist/chunk-XT7OVWM3.js +64 -0
- package/dist/chunk-XTXYIISX.js +1104 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XWIZ6B5S.js +109 -0
- package/dist/chunk-XWLIKOBX.js +240 -0
- package/dist/chunk-XZOXEIN4.js +156 -0
- package/dist/chunk-XZRS7I7W.js +3673 -0
- package/dist/chunk-Y4ZDDHB6.js +44 -0
- package/dist/chunk-Y733RB7T.js +194 -0
- package/dist/chunk-YBHUB4XR.js +14931 -0
- package/dist/chunk-YKC75XK4.js +147 -0
- package/dist/chunk-YKMXFVK7.js +27 -0
- package/dist/chunk-YO5BAJ55.js +225 -0
- package/dist/chunk-YPDCLXNZ.js +568 -0
- package/dist/chunk-YSQPGVFK.js +116 -0
- package/dist/chunk-YTVVTIWJ.js +716 -0
- package/dist/chunk-YVCMDVBB.js +698 -0
- package/dist/chunk-YYTOA6DP.js +114 -0
- package/dist/chunk-Z5WGNULV.js +88 -0
- package/dist/chunk-Z6NGVVXG.js +66 -0
- package/dist/chunk-Z7CDQNH3.js +20 -0
- package/dist/chunk-ZH4G3FU5.js +187 -0
- package/dist/chunk-ZJB6SQPA.js +257 -0
- package/dist/chunk-ZL6E4HMY.js +42 -0
- package/dist/chunk-ZMLOC3DW.js +62 -0
- package/dist/chunk-ZORW64QH.js +101 -0
- package/dist/chunk-ZVVD2XGV.js +1369 -0
- package/dist/cli-utils-ER33ODC5.js +31 -0
- package/dist/cli-validation-6CHPTANV.js +146 -0
- package/dist/commit-utils-3STAY7C7.js +49 -0
- package/dist/compatibility-notice-IHNXR2JR.js +21 -0
- package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
- package/dist/compete-results-XUM3PJHS.js +318 -0
- package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
- package/dist/config-4HHE4OIK.js +238 -0
- package/dist/config-D6YAZASH.js +10 -0
- package/dist/config-MIPRDYYV.js +46 -0
- package/dist/config-audit-GA5DCUAI.js +371 -0
- package/dist/config-explain-4LMO556H.js +71 -0
- package/dist/config-loader-RGP634PC.js +51 -0
- package/dist/config-manager-J2UTO635.js +114 -0
- package/dist/config-merger-TVUJBZTS.js +14 -0
- package/dist/config-validator-H4ZVIE56.js +122 -0
- package/dist/cost-HSIV5U2Y.js +85 -0
- package/dist/data-migration-state-OAUL2WPQ.js +35 -0
- package/dist/db-migrator-NUOW5NKR.js +34 -0
- package/dist/default-paths-UILB62BN.js +49 -0
- package/dist/discovery-V6GYUJUC.js +53 -0
- package/dist/doctor-BBSMCDWE.js +271 -0
- package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
- package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
- package/dist/doctor-purpose-6YJOXPBE.js +30 -0
- package/dist/domain-analyzer-S35ABUNA.js +48 -0
- package/dist/ensure-initialized-Q6PEU55O.js +78 -0
- package/dist/entry.js +922 -0
- package/dist/error-parser-core-MZFMFANN.js +70 -0
- package/dist/evaluation-context-GQBYDAEV.js +63 -0
- package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
- package/dist/execution-data-writer-YCG5UH37.js +72 -0
- package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
- package/dist/execution-preflight-DASJWWQF.js +223 -0
- package/dist/execution-roles-KNSNNB2D.js +112 -0
- package/dist/exit-code-error-QIY5DOKF.js +18 -0
- package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
- package/dist/factory-YCYCB7AE.js +209 -0
- package/dist/file-utils-ZQYNBQBW.js +45 -0
- package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
- package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
- package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
- package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
- package/dist/flywheel-executor-JWU43KAY.js +437 -0
- package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
- package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
- package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
- package/dist/flywheel-preflight-III2C7TH.js +163 -0
- package/dist/flywheel-resume-2O45MS7Y.js +31 -0
- package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
- package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
- package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
- package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
- package/dist/git-message-style-KCXGMTDT.js +104 -0
- package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
- package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
- package/dist/identity-XLQJ4YJF.js +25 -0
- package/dist/index-query-6NUY4FKX.js +37 -0
- package/dist/index.js +85588 -0
- package/dist/indexer-DL54CM3K.js +44 -0
- package/dist/init-OUZ72344.js +245 -0
- package/dist/instructions-import-offer-NSYXSITC.js +363 -0
- package/dist/instructions-run-NBII7W36.js +114 -0
- package/dist/interrupted-work-E4TLKJLE.js +43 -0
- package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
- package/dist/judge-scoring.json +64 -0
- package/dist/lab-mode-YXQ2Y3NG.js +44 -0
- package/dist/lab-utils-5QAMDJR5.js +29 -0
- package/dist/linux-host-class-YDWQX65O.js +31 -0
- package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
- package/dist/local-ci-config-VZ4WWDT5.js +48 -0
- package/dist/local-executor-DCMDDRX2.js +280 -0
- package/dist/logger-5KNKS3O4.js +154 -0
- package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
- package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
- package/dist/merge-batch-RTEEMVDB.js +313 -0
- package/dist/merge-batch-members-VIWGVRHP.js +24 -0
- package/dist/merge-queue-TIIUSOY6.js +126 -0
- package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
- package/dist/metadata-UKSMWM6J.js +29 -0
- package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
- package/dist/model-invocations-db-XTUPPQ34.js +70 -0
- package/dist/model-resolver-JFZ2MHOJ.js +39 -0
- package/dist/model-resolver-SCQ57KAQ.js +217 -0
- package/dist/model-roles-D3OR4NAH.js +110 -0
- package/dist/monitor-F4SIU5G2.js +96 -0
- package/dist/nested-worktrees-2WUWQ75N.js +23 -0
- package/dist/next-S3IRLVER.js +96 -0
- package/dist/next-UKPFRXOO.js +148 -0
- package/dist/opportunistic-TL4S26VX.js +86 -0
- package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
- package/dist/package-HJR4QJT4.js +10 -0
- package/dist/parse-git-status-7PIYFPS2.js +21 -0
- package/dist/path-security-5WJSY56G.js +41 -0
- package/dist/paths-JUI7C4KU.js +45 -0
- package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
- package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
- package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
- package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
- package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
- package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
- package/dist/preflight-PJHGTJNZ.js +12 -0
- package/dist/process-registry-B4ZUCN7T.js +28 -0
- package/dist/process-scanner-TU3QHWVB.js +23 -0
- package/dist/processes-6FHVGT72.js +28 -0
- package/dist/processes-json-fast-YWTMUWXB.js +72 -0
- package/dist/profile-HWTC56SG.js +209 -0
- package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
- package/dist/protected-branches-N36LDBFY.js +71 -0
- package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
- package/dist/provider-matrix-2KO2V5OM.js +214 -0
- package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
- package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
- package/dist/provider-registry-WCZ65QHC.js +136 -0
- package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
- package/dist/prune-engine-EOQVWEP6.js +448 -0
- package/dist/publication-turn-S42RZTQF.js +44 -0
- package/dist/quality-gate-JQMVCKOO.js +72 -0
- package/dist/quickstart-3O6FRCFB.js +105 -0
- package/dist/readiness-GW6ABRHB.js +139 -0
- package/dist/readiness-KFZCXDTT.js +215 -0
- package/dist/registry-7XG376SY.js +217 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-YG6MQZDW.js +28 -0
- package/dist/resolution-engine-FFEK7HQ2.js +187 -0
- package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
- package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
- package/dist/routing-events-AW6SRZ6G.js +13 -0
- package/dist/routing-history-store-FMDDQL6W.js +39 -0
- package/dist/routing-planner-C6OHO3EY.js +221 -0
- package/dist/runner-KHDNQ5KU.js +1194 -0
- package/dist/runner-PIS2BW7Y.js +1801 -0
- package/dist/safety-E3JJVSFL.js +67 -0
- package/dist/safety-stanza-HE6GXP4G.js +49 -0
- package/dist/sandbox-7LBZC5PE.js +72 -0
- package/dist/sandbox-ASWCDYZL.js +80 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-JI22TBEB.js +17 -0
- package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
- package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
- package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
- package/dist/shared-utils-34HJZ6JX.js +40 -0
- package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
- package/dist/sprint-N7JFTEGL.js +403 -0
- package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
- package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
- package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
- package/dist/sprint-manager-MU2ZUWN2.js +140 -0
- package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
- package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
- package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
- package/dist/storage-access-I3V5LER7.js +31 -0
- package/dist/store-XVG64ZR2.js +25 -0
- package/dist/task-classifier-MN2DYE5R.js +558 -0
- package/dist/task-list-fast-7AT6LTTA.js +112 -0
- package/dist/task-manager-LHPBBRRE.js +136 -0
- package/dist/task-report-OLPAWBTK.js +64 -0
- package/dist/task-type-detector-6RG3YNZF.js +27 -0
- package/dist/task-type-resolver-EQHOUIHT.js +64 -0
- package/dist/telemetry-5VYSK772.js +212 -0
- package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
- package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
- package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
- package/dist/transports-NREZQTJK.js +106 -0
- package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
- package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
- package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
- package/dist/user-agents-QLIGPLP2.js +30 -0
- package/dist/variant-execution-I4ZZUAWY.js +319 -0
- package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
- package/dist/variant-request-input-PL37N3AN.js +11 -0
- package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
- package/dist/variant-worker-COB5YTJX.js +4517 -0
- package/dist/variant-worker.js +26 -0
- package/dist/variants-XGDSXYG3.js +267 -0
- package/dist/verification-command-YSPU4VBH.js +66 -0
- package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
- package/dist/verify-commands-LOHMOFKC.js +65 -0
- package/dist/warning-JSKSFKBT.js +13 -0
- package/dist/work-edge-K65NHVWY.js +50 -0
- package/dist/work-item-writer-KY5ZWZWW.js +30 -0
- package/dist/worker-JNXXEIT6.js +1504 -0
- package/dist/worker.js +21 -0
- package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
- package/dist/worktree-environment-WTVABAQW.js +64 -0
- package/dist/worktree-metrics-VZRQI26T.js +66 -0
- package/dist/writeback-TQFQBJNU.js +50 -0
- package/docs/CLI_REFERENCE.md +8287 -0
- package/docs/PROVIDER_SETUP.md +1737 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2209 -0
- package/docs/getting-started/QUICK_START.md +260 -0
- package/docs/getting-started/README.md +198 -0
- package/docs/getting-started/SETUP.md +25 -0
- package/docs/reference/AGENT_CONTEXT.md +84 -0
- package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/SECURITY.md +291 -0
- package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
- package/integrations/bb-plugin-build/README.md +391 -0
- package/integrations/bb-plugin-build/app.css +431 -0
- package/integrations/bb-plugin-build/app.tsx +6210 -0
- package/integrations/bb-plugin-build/package.json +45 -0
- package/integrations/bb-plugin-build/server.ts +488 -0
- package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
- package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
- package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-build/src/contract.ts +718 -0
- package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
- package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
- package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
- package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-build/src/model.ts +1318 -0
- package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
- package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
- package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
- package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
- package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
- package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
- package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-build/src/project-target.ts +45 -0
- package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
- package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
- package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
- package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
- package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
- package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
- package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
- package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
- package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
- package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
- package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
- package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
- package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
- package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
- package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
- package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
- package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
- package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
- package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
- package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
- package/package.json +306 -4
- package/schemas/README.md +80 -0
- package/schemas/build.config.schema.json +2110 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +257 -0
- package/schemas/provider-config.schema.json +179 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +146 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/setup-git-hooks.mjs +39 -0
- package/scripts/sync.sh +2871 -0
- package/scripts/write-node-launcher.sh +138 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.build-project/README.md +41 -0
- package/templates/.build-project/agents/README.md +50 -0
- package/templates/.build-project/agents/architect.md +10 -0
- package/templates/.build-project/agents/code-reviewer.md +10 -0
- package/templates/.build-project/agents/debugger.md +10 -0
- package/templates/.build-project/agents/documenter.md +10 -0
- package/templates/.build-project/agents/test-automator.md +10 -0
- package/templates/.build-project/config/build.config.jsonc +64 -0
- package/templates/.build-project/config/project-context.template.json +6 -0
- package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.build-project/config/task-type-aliases.yaml +29 -0
- package/templates/.build-project/gitignore.template +70 -0
- package/templates/.build-project/task-types/analysis.yaml +38 -0
- package/templates/.build-project/task-types/architecture.yaml +35 -0
- package/templates/.build-project/task-types/doc.yaml +34 -0
- package/templates/.build-project/task-types/feature.yaml +22 -0
- package/templates/.build-project/task-types/general.yaml +4 -0
- package/templates/.build-project/task-types/security.yaml +38 -0
- package/templates/AGENTS.template.md +85 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
- package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
- package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
- package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
- package/templates/ci/incident-intake.mjs +787 -0
- package/templates/ci/incidents.yml.template +103 -0
- package/templates/ci/node.yml.template +17 -0
- package/templates/ci/python.yml.template +17 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
- package/templates/guard-kit/build-guard.yml.template +189 -0
- package/templates/guard-kit/check.mjs.template +932 -0
- package/templates/guard-kit/config.json.template +7 -0
- package/templates/guard-kit/policy.json.template +1 -0
- package/templates/profiles/README.md +57 -0
- package/templates/profiles/frontier-claude.json +23 -0
- package/templates/profiles/frontier-codex.json +23 -0
|
@@ -0,0 +1,3365 @@
|
|
|
1
|
+
import {
|
|
2
|
+
recordReadinessDiagnostic
|
|
3
|
+
} from "./chunk-2OUAEH67.js";
|
|
4
|
+
import {
|
|
5
|
+
GUARD_KIT_VERSION,
|
|
6
|
+
classifyGuardState,
|
|
7
|
+
knowledgeDoctorChecks
|
|
8
|
+
} from "./chunk-DNIOIISW.js";
|
|
9
|
+
import {
|
|
10
|
+
inspectCiIntegration,
|
|
11
|
+
renderCiIntegration
|
|
12
|
+
} from "./chunk-S2XIMQJQ.js";
|
|
13
|
+
import {
|
|
14
|
+
formatPreflightDetails,
|
|
15
|
+
runExecutionRolesPreflight
|
|
16
|
+
} from "./chunk-DOA5G7IA.js";
|
|
17
|
+
import {
|
|
18
|
+
getAvailableMemoryGB
|
|
19
|
+
} from "./chunk-WPKLYOAM.js";
|
|
20
|
+
import {
|
|
21
|
+
detectHostConcurrencyBudget
|
|
22
|
+
} from "./chunk-BZRLSZNV.js";
|
|
23
|
+
import {
|
|
24
|
+
formatDurationMs
|
|
25
|
+
} from "./chunk-2L36U25O.js";
|
|
26
|
+
import {
|
|
27
|
+
listPendingNudges,
|
|
28
|
+
readPendingOfferNotice,
|
|
29
|
+
renderPendingNudges
|
|
30
|
+
} from "./chunk-KZWGBUIO.js";
|
|
31
|
+
import {
|
|
32
|
+
DECLARE_STACK_COMMAND,
|
|
33
|
+
describeDimension,
|
|
34
|
+
detectStack,
|
|
35
|
+
isStackDriftKind,
|
|
36
|
+
isStackUndetectedAndUndeclared,
|
|
37
|
+
readIntentContract,
|
|
38
|
+
resolveIntent
|
|
39
|
+
} from "./chunk-VYI4DSZP.js";
|
|
40
|
+
import {
|
|
41
|
+
readProviderDryRunFreshness
|
|
42
|
+
} from "./chunk-GT6HFKJB.js";
|
|
43
|
+
import {
|
|
44
|
+
RuntimeRequirements,
|
|
45
|
+
ensureBuildInitialized,
|
|
46
|
+
resolveRepoRootForBuildDir
|
|
47
|
+
} from "./chunk-UB7LQITD.js";
|
|
48
|
+
import {
|
|
49
|
+
hasStrictMcpConfigBeenProbed,
|
|
50
|
+
isStrictMcpConfigSupported,
|
|
51
|
+
probeStrictMcpConfig
|
|
52
|
+
} from "./chunk-7HYBSRPZ.js";
|
|
53
|
+
import {
|
|
54
|
+
planningStorageUnavailableReason
|
|
55
|
+
} from "./chunk-T5XLUYV3.js";
|
|
56
|
+
import {
|
|
57
|
+
listYamlFiles
|
|
58
|
+
} from "./chunk-6T6NHXK4.js";
|
|
59
|
+
import {
|
|
60
|
+
loadAllItems
|
|
61
|
+
} from "./chunk-NXH3RX7O.js";
|
|
62
|
+
import {
|
|
63
|
+
getPlanningRoot
|
|
64
|
+
} from "./chunk-CSXLLEGB.js";
|
|
65
|
+
import {
|
|
66
|
+
getBucketArchivePathByKind,
|
|
67
|
+
getBucketPathByKind
|
|
68
|
+
} from "./chunk-V3KLVK7B.js";
|
|
69
|
+
import {
|
|
70
|
+
ConfigManager
|
|
71
|
+
} from "./chunk-W4BAIQ6L.js";
|
|
72
|
+
import {
|
|
73
|
+
discoverOpencode
|
|
74
|
+
} from "./chunk-KQYQOXMS.js";
|
|
75
|
+
import {
|
|
76
|
+
probeProviderCliVersion
|
|
77
|
+
} from "./chunk-S47ISY27.js";
|
|
78
|
+
import {
|
|
79
|
+
SCHEMA_VERSION,
|
|
80
|
+
checkSchemaVersion
|
|
81
|
+
} from "./chunk-XIBVFMLQ.js";
|
|
82
|
+
import {
|
|
83
|
+
SETUP_CHECK_READINESS_OPTIONS,
|
|
84
|
+
getProviderReadiness,
|
|
85
|
+
isGateReadyProvider,
|
|
86
|
+
probeProviderCli,
|
|
87
|
+
readinessProbeContextForOptions,
|
|
88
|
+
resolveNamedReadiness,
|
|
89
|
+
resolveProviderParityLabel,
|
|
90
|
+
resolveReadinessScope
|
|
91
|
+
} from "./chunk-XTXYIISX.js";
|
|
92
|
+
import {
|
|
93
|
+
resolvePlanningContextForRoot
|
|
94
|
+
} from "./chunk-BBPIILO2.js";
|
|
95
|
+
import {
|
|
96
|
+
BooleanFlagSchema,
|
|
97
|
+
validateCommandOptions
|
|
98
|
+
} from "./chunk-I7SCBHJN.js";
|
|
99
|
+
import {
|
|
100
|
+
formatSandboxDegradedSummary
|
|
101
|
+
} from "./chunk-3H4MWYIC.js";
|
|
102
|
+
import {
|
|
103
|
+
detectUnbornBranch,
|
|
104
|
+
parseCurrentBranchRef
|
|
105
|
+
} from "./chunk-XGU6PBO5.js";
|
|
106
|
+
import {
|
|
107
|
+
invalidateAuthOutcome,
|
|
108
|
+
lookupAuthOutcome,
|
|
109
|
+
recordAuthOutcome
|
|
110
|
+
} from "./chunk-PPZESPJT.js";
|
|
111
|
+
import {
|
|
112
|
+
TelemetryPathResolver
|
|
113
|
+
} from "./chunk-C3KCMAJP.js";
|
|
114
|
+
import {
|
|
115
|
+
execGit
|
|
116
|
+
} from "./chunk-MHLEZQQI.js";
|
|
117
|
+
import {
|
|
118
|
+
normalizeEngineId,
|
|
119
|
+
resolveProviderCliCommand
|
|
120
|
+
} from "./chunk-KKQKWAMF.js";
|
|
121
|
+
import {
|
|
122
|
+
ProviderRegistry,
|
|
123
|
+
getProviderConfig,
|
|
124
|
+
isConfigValidityError
|
|
125
|
+
} from "./chunk-QJ6OEH4K.js";
|
|
126
|
+
import {
|
|
127
|
+
resolveProvidersConfigPath
|
|
128
|
+
} from "./chunk-35J4NE6S.js";
|
|
129
|
+
import {
|
|
130
|
+
formatSupportedNodeMajors,
|
|
131
|
+
isSupportedNodeMajor,
|
|
132
|
+
parseNodeMajor
|
|
133
|
+
} from "./chunk-ZORW64QH.js";
|
|
134
|
+
import {
|
|
135
|
+
getDataDir,
|
|
136
|
+
getUserConfigFile
|
|
137
|
+
} from "./chunk-56WPYDJH.js";
|
|
138
|
+
import {
|
|
139
|
+
getBuiltInTaskTypesDir,
|
|
140
|
+
getRejectedPathEnvWarnings,
|
|
141
|
+
getRepoTaskTypesDir
|
|
142
|
+
} from "./chunk-STW7OFOA.js";
|
|
143
|
+
import {
|
|
144
|
+
BUILD_CODEX_BACKEND,
|
|
145
|
+
getRuntimeEnv,
|
|
146
|
+
readEnv
|
|
147
|
+
} from "./chunk-7VRJHTGZ.js";
|
|
148
|
+
|
|
149
|
+
// src/cli/commands/doctor-ci.ts
|
|
150
|
+
async function checkCiIntegrationHealth(root) {
|
|
151
|
+
try {
|
|
152
|
+
const snapshot = await inspectCiIntegration(root);
|
|
153
|
+
return {
|
|
154
|
+
name: "CI integration",
|
|
155
|
+
category: "git",
|
|
156
|
+
status: "info",
|
|
157
|
+
fixable: false,
|
|
158
|
+
message: snapshot.intentError ?? (snapshot.primary === "local" ? "Local CI is configured; host enforcement is not checked by doctor." : snapshot.workflows.length ? "Workflow definitions found; hosted activity and enforcement are not checked by doctor." : "No CI workflow or Local CI hook detected."),
|
|
159
|
+
details: `${renderCiIntegration(snapshot).join("\n")}
|
|
160
|
+
build ci status can inspect GitHub; build init can review setup.`
|
|
161
|
+
};
|
|
162
|
+
} catch {
|
|
163
|
+
return {
|
|
164
|
+
name: "CI integration",
|
|
165
|
+
category: "git",
|
|
166
|
+
status: "info",
|
|
167
|
+
fixable: false,
|
|
168
|
+
message: "CI inspection is unavailable; no configuration was changed."
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/cli/commands/doctor-init-notices.ts
|
|
174
|
+
function checkPendingInitNotices(cwd) {
|
|
175
|
+
let count = 0;
|
|
176
|
+
try {
|
|
177
|
+
count = listPendingNudges(cwd).filter((record) => isStackDriftKind(record.kind)).length;
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
if (count === 0) {
|
|
181
|
+
return {
|
|
182
|
+
name: "Pending Init Notices",
|
|
183
|
+
category: "knowledge",
|
|
184
|
+
status: "pass",
|
|
185
|
+
message: "No pending init notices",
|
|
186
|
+
notices_pending: 0
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
name: "Pending Init Notices",
|
|
191
|
+
category: "knowledge",
|
|
192
|
+
status: "info",
|
|
193
|
+
message: `${count} init notice(s) pending review`,
|
|
194
|
+
details: "Notices are shown below (human output) or after the summary.",
|
|
195
|
+
notices_pending: count
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function checkPendingOfferNotice(cwd) {
|
|
199
|
+
let notice;
|
|
200
|
+
try {
|
|
201
|
+
notice = readPendingOfferNotice(cwd);
|
|
202
|
+
} catch {
|
|
203
|
+
notice = void 0;
|
|
204
|
+
}
|
|
205
|
+
if (!notice) {
|
|
206
|
+
return {
|
|
207
|
+
name: "Knowledge Offer",
|
|
208
|
+
category: "knowledge",
|
|
209
|
+
status: "pass",
|
|
210
|
+
message: "No pending knowledge-kit offer"
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
name: "Knowledge Offer",
|
|
215
|
+
category: "knowledge",
|
|
216
|
+
// Both skip causes (non-interactive, suppressed) are normal headless states.
|
|
217
|
+
status: "info",
|
|
218
|
+
message: notice.message,
|
|
219
|
+
details: `Offer skipped ${notice.skippedAt.slice(0, 10)} (${notice.cause}); it clears when an offer runs.`
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function renderDoctorPendingNudges(cwd, options, renderOptions = {}) {
|
|
223
|
+
const policy = {
|
|
224
|
+
json: options.json === true,
|
|
225
|
+
quiet: false,
|
|
226
|
+
verbose: options.verbose === true,
|
|
227
|
+
debug: false,
|
|
228
|
+
noColor: false,
|
|
229
|
+
level: "normal"
|
|
230
|
+
};
|
|
231
|
+
try {
|
|
232
|
+
return renderPendingNudges(cwd, policy, {
|
|
233
|
+
...renderOptions,
|
|
234
|
+
includeAge: true,
|
|
235
|
+
readOnly: true,
|
|
236
|
+
includeRecord: (record) => isStackDriftKind(record.kind)
|
|
237
|
+
});
|
|
238
|
+
} catch {
|
|
239
|
+
return { rendered: false, deliveredKeys: [], pendingCount: 0 };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/cli/commands/doctor.ts
|
|
244
|
+
import * as fs3 from "fs/promises";
|
|
245
|
+
import * as os from "os";
|
|
246
|
+
import * as path3 from "path";
|
|
247
|
+
import { fileURLToPath } from "url";
|
|
248
|
+
import chalk2 from "chalk";
|
|
249
|
+
import { Command as Command2 } from "commander";
|
|
250
|
+
import { execa } from "execa";
|
|
251
|
+
import { z as z2 } from "zod";
|
|
252
|
+
|
|
253
|
+
// src/planning/archive-reconciliation.ts
|
|
254
|
+
function hasArchivedMarker(item) {
|
|
255
|
+
const fm = item.frontMatter;
|
|
256
|
+
return typeof fm.archivedAt === "string" && fm.archivedAt.length > 0;
|
|
257
|
+
}
|
|
258
|
+
function indexById(items) {
|
|
259
|
+
const map = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const item of items) {
|
|
261
|
+
if (!map.has(item.id)) {
|
|
262
|
+
map.set(item.id, item);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return map;
|
|
266
|
+
}
|
|
267
|
+
function detectDuplicates(kind, active, archived) {
|
|
268
|
+
const archivedById = indexById(archived);
|
|
269
|
+
const issues = [];
|
|
270
|
+
for (const activeItem of active) {
|
|
271
|
+
const archivedMatch = archivedById.get(activeItem.id);
|
|
272
|
+
if (!archivedMatch) continue;
|
|
273
|
+
const preferred = hasArchivedMarker(archivedMatch) ? "archive" : hasArchivedMarker(activeItem) ? "active" : "archive";
|
|
274
|
+
issues.push({
|
|
275
|
+
kind,
|
|
276
|
+
id: activeItem.id,
|
|
277
|
+
activePath: activeItem.filePath,
|
|
278
|
+
archivePath: archivedMatch.filePath,
|
|
279
|
+
preferred
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return issues;
|
|
283
|
+
}
|
|
284
|
+
function detectDanglingSprintMembership(sprints, activeTaskIds, archivedTaskIds) {
|
|
285
|
+
const issues = [];
|
|
286
|
+
for (const sprint of sprints) {
|
|
287
|
+
const taskIdsRaw = sprint.frontMatter.taskIds;
|
|
288
|
+
if (!Array.isArray(taskIdsRaw)) continue;
|
|
289
|
+
for (const taskId of taskIdsRaw) {
|
|
290
|
+
if (typeof taskId !== "string") continue;
|
|
291
|
+
if (activeTaskIds.has(taskId)) continue;
|
|
292
|
+
if (archivedTaskIds.has(taskId)) {
|
|
293
|
+
issues.push({ sprintId: sprint.id, taskId, reason: "archived-but-listed" });
|
|
294
|
+
} else {
|
|
295
|
+
issues.push({ sprintId: sprint.id, taskId, reason: "missing" });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return issues;
|
|
300
|
+
}
|
|
301
|
+
function detectOrphanedFollowups(backlogActive, backlogArchived) {
|
|
302
|
+
const knownBacklogIds = /* @__PURE__ */ new Set();
|
|
303
|
+
for (const item of [...backlogActive, ...backlogArchived]) {
|
|
304
|
+
knownBacklogIds.add(item.id);
|
|
305
|
+
}
|
|
306
|
+
const issues = [];
|
|
307
|
+
for (const item of backlogActive) {
|
|
308
|
+
const spawnedFrom = item.frontMatter.spawnedFrom;
|
|
309
|
+
if (typeof spawnedFrom !== "string" || spawnedFrom.length === 0) continue;
|
|
310
|
+
if (!knownBacklogIds.has(spawnedFrom)) {
|
|
311
|
+
issues.push({ followupId: item.id, parentId: spawnedFrom, followupPath: item.filePath });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return issues;
|
|
315
|
+
}
|
|
316
|
+
async function detectArchiveReconciliationIssues(planningRoot = getPlanningRoot()) {
|
|
317
|
+
const taskActiveDir = getBucketPathByKind("task", planningRoot);
|
|
318
|
+
const taskArchiveDir = getBucketArchivePathByKind("task", planningRoot);
|
|
319
|
+
const backlogActiveDir = getBucketPathByKind("backlog", planningRoot);
|
|
320
|
+
const backlogArchiveDir = getBucketArchivePathByKind("backlog", planningRoot);
|
|
321
|
+
const sprintActiveDir = getBucketPathByKind("sprint", planningRoot);
|
|
322
|
+
const [activeTasks, archivedTasks, activeBacklog, archivedBacklog, sprints] = await Promise.all([
|
|
323
|
+
loadAllItems(planningRoot, [taskActiveDir]),
|
|
324
|
+
loadAllItems(planningRoot, [taskArchiveDir]),
|
|
325
|
+
loadAllItems(planningRoot, [backlogActiveDir]),
|
|
326
|
+
loadAllItems(planningRoot, [backlogArchiveDir]),
|
|
327
|
+
loadAllItems(planningRoot, [sprintActiveDir])
|
|
328
|
+
]);
|
|
329
|
+
const duplicateRecords = [
|
|
330
|
+
...detectDuplicates("task", activeTasks, archivedTasks),
|
|
331
|
+
...detectDuplicates("backlog", activeBacklog, archivedBacklog)
|
|
332
|
+
];
|
|
333
|
+
const activeTaskIds = new Set(activeTasks.map((t) => t.id));
|
|
334
|
+
const archivedTaskIds = new Set(archivedTasks.map((t) => t.id));
|
|
335
|
+
const danglingSprintMembership = detectDanglingSprintMembership(
|
|
336
|
+
sprints,
|
|
337
|
+
activeTaskIds,
|
|
338
|
+
archivedTaskIds
|
|
339
|
+
);
|
|
340
|
+
const orphanedFollowups = detectOrphanedFollowups(activeBacklog, archivedBacklog);
|
|
341
|
+
return {
|
|
342
|
+
duplicateRecords,
|
|
343
|
+
danglingSprintMembership,
|
|
344
|
+
orphanedFollowups,
|
|
345
|
+
totalIssues: duplicateRecords.length + danglingSprintMembership.length + orphanedFollowups.length
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function formatArchiveReconciliationReport(report) {
|
|
349
|
+
if (report.totalIssues === 0) return "";
|
|
350
|
+
const lines = [];
|
|
351
|
+
if (report.duplicateRecords.length > 0) {
|
|
352
|
+
lines.push(`Duplicate active+archive records: ${report.duplicateRecords.length}`);
|
|
353
|
+
for (const dup of report.duplicateRecords) {
|
|
354
|
+
lines.push(` ${dup.kind} ${dup.id} (keep: ${dup.preferred})`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (report.danglingSprintMembership.length > 0) {
|
|
358
|
+
lines.push(`Dangling sprint membership: ${report.danglingSprintMembership.length}`);
|
|
359
|
+
for (const ref of report.danglingSprintMembership) {
|
|
360
|
+
lines.push(` sprint ${ref.sprintId} -> ${ref.taskId} (${ref.reason})`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (report.orphanedFollowups.length > 0) {
|
|
364
|
+
lines.push(`Orphaned followups: ${report.orphanedFollowups.length}`);
|
|
365
|
+
for (const orphan of report.orphanedFollowups) {
|
|
366
|
+
lines.push(` ${orphan.followupId} -> missing parent ${orphan.parentId}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return lines.join("\n");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/cli/commands/doctor-claude-alias-advisory.ts
|
|
373
|
+
var CLAUDE_ALIAS_FLAGSHIP_MIN_VERSION = "2.1.219";
|
|
374
|
+
var CLAUDE_BEDROCK_REGION_PREFIX_MIN_VERSION = "2.1.224";
|
|
375
|
+
var CHECK_NAME = "Claude family aliases";
|
|
376
|
+
function parseClaudeCliRelease(version) {
|
|
377
|
+
const match = /(\d+(?:\.\d+)*)/.exec(version?.trim() ?? "");
|
|
378
|
+
return match?.[1] ? match[1].split(".").map(Number) : void 0;
|
|
379
|
+
}
|
|
380
|
+
function isOlderThan(version, minimum) {
|
|
381
|
+
const observed = parseClaudeCliRelease(version);
|
|
382
|
+
if (!observed) return false;
|
|
383
|
+
const required = parseClaudeCliRelease(minimum) ?? [];
|
|
384
|
+
for (let index = 0; index < Math.max(observed.length, required.length); index++) {
|
|
385
|
+
const left = observed[index] ?? 0;
|
|
386
|
+
const right = required[index] ?? 0;
|
|
387
|
+
if (left !== right) return left < right;
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
function claudeAliasAdvisories(input) {
|
|
392
|
+
const version = input.version?.trim();
|
|
393
|
+
const advisories = [];
|
|
394
|
+
if (isOlderThan(version, CLAUDE_ALIAS_FLAGSHIP_MIN_VERSION)) {
|
|
395
|
+
advisories.push(
|
|
396
|
+
`Claude CLI ${version} predates ${CLAUDE_ALIAS_FLAGSHIP_MIN_VERSION}: a family alias resolves to an older flagship on this CLI, so registry roles that use one run that older model.`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (input.backend === "bedrock" && isOlderThan(version, CLAUDE_BEDROCK_REGION_PREFIX_MIN_VERSION)) {
|
|
400
|
+
advisories.push(
|
|
401
|
+
`Claude CLI ${version} predates ${CLAUDE_BEDROCK_REGION_PREFIX_MIN_VERSION}: ANTHROPIC_BEDROCK_REGION_PREFIX is unavailable, so a family alias on Bedrock uses the CLI's own default region prefix.`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return advisories;
|
|
405
|
+
}
|
|
406
|
+
async function checkClaudeAliasAdvisory() {
|
|
407
|
+
let command;
|
|
408
|
+
let prefixArgs;
|
|
409
|
+
try {
|
|
410
|
+
const resolved = resolveProviderCliCommand("claude");
|
|
411
|
+
command = resolved.command;
|
|
412
|
+
prefixArgs = resolved.prefixArgs ?? [];
|
|
413
|
+
} catch {
|
|
414
|
+
return {
|
|
415
|
+
name: CHECK_NAME,
|
|
416
|
+
category: "providers",
|
|
417
|
+
status: "info",
|
|
418
|
+
message: "Claude CLI not configured; skipping family-alias version advisory"
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const version = await probeProviderCliVersion({ command, prefixArgs });
|
|
422
|
+
if (!version) {
|
|
423
|
+
return {
|
|
424
|
+
name: CHECK_NAME,
|
|
425
|
+
category: "providers",
|
|
426
|
+
status: "info",
|
|
427
|
+
message: "Claude CLI version not reported; family-alias resolution not assessed"
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (!parseClaudeCliRelease(version)) {
|
|
431
|
+
return {
|
|
432
|
+
name: CHECK_NAME,
|
|
433
|
+
category: "providers",
|
|
434
|
+
status: "info",
|
|
435
|
+
message: `Claude CLI version "${version}" carries no release number; family-alias resolution not assessed`
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
const { resolveClaudeBackend } = await import("./model-resolver-JFZ2MHOJ.js");
|
|
439
|
+
const backend = resolveClaudeBackend(process.env, process.cwd());
|
|
440
|
+
const advisories = claudeAliasAdvisories({ version, backend });
|
|
441
|
+
if (advisories.length === 0) {
|
|
442
|
+
return {
|
|
443
|
+
name: CHECK_NAME,
|
|
444
|
+
category: "providers",
|
|
445
|
+
status: "pass",
|
|
446
|
+
message: `Claude CLI ${version} resolves family aliases to the current flagship`
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
name: CHECK_NAME,
|
|
451
|
+
category: "providers",
|
|
452
|
+
status: "info",
|
|
453
|
+
message: advisories.join(" "),
|
|
454
|
+
details: `Upgrade the Claude CLI to ${CLAUDE_BEDROCK_REGION_PREFIX_MIN_VERSION} or newer, or redirect the alias with ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL. Build passes the alias through by design; the CLI owns what the family means. See docs/PROVIDER_SETUP.md.`
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/cli/commands/doctor-configured-providers.ts
|
|
459
|
+
function explicitDefaultsProviderIds(sources) {
|
|
460
|
+
const ids = [];
|
|
461
|
+
for (const source of sources) {
|
|
462
|
+
if (source.type !== "project" && source.type !== "user" || !source.exists) continue;
|
|
463
|
+
const providers = source.data?.defaults?.providers;
|
|
464
|
+
if (providers && typeof providers === "object") ids.push(...Object.keys(providers));
|
|
465
|
+
}
|
|
466
|
+
return ids;
|
|
467
|
+
}
|
|
468
|
+
function configuredProviderIds(config, sources) {
|
|
469
|
+
const enabled = Object.entries(config?.providers ?? {}).filter(([, providerConfig]) => providerConfig?.enabled).map(([providerId]) => providerId);
|
|
470
|
+
return [...enabled, ...explicitDefaultsProviderIds(sources)];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/cli/commands/doctor-fix-summary.ts
|
|
474
|
+
function describeUnrepairedChecks(checks) {
|
|
475
|
+
const manual = checks.filter((c) => !c.fixable && (c.status === "warn" || c.status === "fail"));
|
|
476
|
+
if (manual.length === 0) return void 0;
|
|
477
|
+
return `--fix does not repair ${manual.length} remaining issue(s): ${manual.map((c) => c.name).join(", ")}. Follow the instructions in their messages above.`;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/cli/commands/doctor-guard.ts
|
|
481
|
+
import { promises as fs } from "fs";
|
|
482
|
+
import * as path from "path";
|
|
483
|
+
var GUARD_DIR_REL = path.join(".build-project", "guard");
|
|
484
|
+
var GUARD_LOCK_REL = path.join(GUARD_DIR_REL, "checks.lock");
|
|
485
|
+
var INIT_INSTALL_CMD = "build init --guardrails";
|
|
486
|
+
var INIT_REINSTALL_CMD = "build init --guardrails --reinstall-guard";
|
|
487
|
+
async function readGuardLock(repoRoot) {
|
|
488
|
+
try {
|
|
489
|
+
const raw = await fs.readFile(path.join(repoRoot, GUARD_LOCK_REL), "utf8");
|
|
490
|
+
const parsed = JSON.parse(raw);
|
|
491
|
+
const version = typeof parsed.version === "string" ? parsed.version : typeof parsed.kitVersion === "string" ? parsed.kitVersion : typeof parsed.kit_version === "string" ? parsed.kit_version : void 0;
|
|
492
|
+
const container = parsed.files ?? parsed.hashes;
|
|
493
|
+
let files = [];
|
|
494
|
+
if (Array.isArray(container)) {
|
|
495
|
+
files = container.filter((f) => typeof f === "string");
|
|
496
|
+
} else if (container && typeof container === "object") {
|
|
497
|
+
files = Object.keys(container);
|
|
498
|
+
}
|
|
499
|
+
return { version, files };
|
|
500
|
+
} catch {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
var defaultGuardInspector = async (repoRoot) => {
|
|
505
|
+
const state = classifyGuardState(repoRoot).state;
|
|
506
|
+
const lock = await readGuardLock(repoRoot);
|
|
507
|
+
return {
|
|
508
|
+
state,
|
|
509
|
+
bundledVersion: GUARD_KIT_VERSION,
|
|
510
|
+
installedVersion: lock?.version,
|
|
511
|
+
generatedFiles: lock?.files
|
|
512
|
+
};
|
|
513
|
+
};
|
|
514
|
+
async function checkGuardrails(repoRoot, options = {}) {
|
|
515
|
+
const inspect = options.inspect ?? defaultGuardInspector;
|
|
516
|
+
let inspection;
|
|
517
|
+
try {
|
|
518
|
+
inspection = await inspect(repoRoot);
|
|
519
|
+
} catch (error) {
|
|
520
|
+
return [
|
|
521
|
+
{
|
|
522
|
+
name: "Guardrails",
|
|
523
|
+
category: "guardrails",
|
|
524
|
+
status: "info",
|
|
525
|
+
message: "guardrail kit status unavailable",
|
|
526
|
+
details: error instanceof Error ? error.message : String(error)
|
|
527
|
+
}
|
|
528
|
+
];
|
|
529
|
+
}
|
|
530
|
+
if (!inspection) {
|
|
531
|
+
return [
|
|
532
|
+
{
|
|
533
|
+
name: "Guardrails",
|
|
534
|
+
category: "guardrails",
|
|
535
|
+
status: "info",
|
|
536
|
+
message: "guardrail kit module unavailable",
|
|
537
|
+
details: "Guard kit could not be loaded in this install; guardrails status is unknown."
|
|
538
|
+
}
|
|
539
|
+
];
|
|
540
|
+
}
|
|
541
|
+
return [checkForState(inspection)];
|
|
542
|
+
}
|
|
543
|
+
function checkForState(inspection) {
|
|
544
|
+
const { state, bundledVersion, installedVersion, generatedFiles } = inspection;
|
|
545
|
+
switch (state) {
|
|
546
|
+
case "absent":
|
|
547
|
+
return {
|
|
548
|
+
name: "Guardrails",
|
|
549
|
+
category: "guardrails",
|
|
550
|
+
status: "info",
|
|
551
|
+
message: "guardrail kit not installed",
|
|
552
|
+
details: `Optional: install the read-only guardrail kit with "${INIT_INSTALL_CMD}".`
|
|
553
|
+
};
|
|
554
|
+
case "current":
|
|
555
|
+
return {
|
|
556
|
+
name: "Guardrails",
|
|
557
|
+
category: "guardrails",
|
|
558
|
+
status: "pass",
|
|
559
|
+
message: `guardrail kit current (version ${installedVersion ?? bundledVersion}); run build guard verify`
|
|
560
|
+
};
|
|
561
|
+
case "upgradable":
|
|
562
|
+
return {
|
|
563
|
+
name: "Guardrails",
|
|
564
|
+
category: "guardrails",
|
|
565
|
+
status: "info",
|
|
566
|
+
message: `guardrail kit upgradable (installed ${installedVersion ?? "unknown"}, bundled ${bundledVersion}); run build guard verify after upgrade`,
|
|
567
|
+
details: `Run "${INIT_INSTALL_CMD}" to upgrade, then "build guard verify" (config.json is preserved).`
|
|
568
|
+
};
|
|
569
|
+
case "tampered":
|
|
570
|
+
return {
|
|
571
|
+
name: "Guardrails",
|
|
572
|
+
category: "guardrails",
|
|
573
|
+
status: "warn",
|
|
574
|
+
message: "guardrail kit modified (hash mismatch)",
|
|
575
|
+
details: formatMismatchDetails(generatedFiles)
|
|
576
|
+
};
|
|
577
|
+
case "partial":
|
|
578
|
+
return {
|
|
579
|
+
name: "Guardrails",
|
|
580
|
+
category: "guardrails",
|
|
581
|
+
status: "warn",
|
|
582
|
+
message: "guardrail kit incomplete (missing lock or generated files)",
|
|
583
|
+
details: formatMismatchDetails(generatedFiles)
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function formatMismatchDetails(generatedFiles) {
|
|
588
|
+
const suffix = `Run "${INIT_REINSTALL_CMD}" to restore the generated guard files.`;
|
|
589
|
+
if (generatedFiles && generatedFiles.length > 0) {
|
|
590
|
+
return `Generated guard files: ${generatedFiles.join(", ")}. ${suffix}`;
|
|
591
|
+
}
|
|
592
|
+
return suffix;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/cli/commands/doctor-metrics.ts
|
|
596
|
+
import chalk from "chalk";
|
|
597
|
+
import { Command } from "commander";
|
|
598
|
+
import { z } from "zod";
|
|
599
|
+
async function loadTelemetryHealthTracker() {
|
|
600
|
+
const mod = await import("./telemetry-health-tracker-4GFW2EMP.js");
|
|
601
|
+
return mod.TelemetryHealthTracker;
|
|
602
|
+
}
|
|
603
|
+
var DoctorMetricsOptionsSchema = z.object({
|
|
604
|
+
json: BooleanFlagSchema.optional(),
|
|
605
|
+
prometheus: BooleanFlagSchema.optional(),
|
|
606
|
+
verbose: BooleanFlagSchema.optional()
|
|
607
|
+
}).strict();
|
|
608
|
+
var DoctorMetricsMonitorOptionsSchema = z.object({
|
|
609
|
+
interval: z.number().optional(),
|
|
610
|
+
json: BooleanFlagSchema.optional(),
|
|
611
|
+
prometheus: BooleanFlagSchema.optional()
|
|
612
|
+
}).strict();
|
|
613
|
+
function buildUnavailableHealthMetrics() {
|
|
614
|
+
return {
|
|
615
|
+
queueDepth: 0,
|
|
616
|
+
fallbackMode: false,
|
|
617
|
+
errorRate: 0,
|
|
618
|
+
databaseHealthy: false,
|
|
619
|
+
lastErrorTimestamp: null,
|
|
620
|
+
errorsLastHour: 0,
|
|
621
|
+
unresolvedErrors: 0
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function isHealthSchemaPresent(db) {
|
|
625
|
+
const row = db.prepare(
|
|
626
|
+
"SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'telemetry_errors' LIMIT 1"
|
|
627
|
+
).get();
|
|
628
|
+
return row !== void 0;
|
|
629
|
+
}
|
|
630
|
+
async function openTelemetryDb() {
|
|
631
|
+
const { openConfiguredTelemetryDb } = await import("./storage-access-I3V5LER7.js");
|
|
632
|
+
const TelemetryHealthTracker = await loadTelemetryHealthTracker();
|
|
633
|
+
const readonlyResult = openConfiguredTelemetryDb();
|
|
634
|
+
if (!readonlyResult.db) {
|
|
635
|
+
return readonlyResult;
|
|
636
|
+
}
|
|
637
|
+
const { dbPath } = readonlyResult;
|
|
638
|
+
if (isHealthSchemaPresent(readonlyResult.db)) {
|
|
639
|
+
return readonlyResult;
|
|
640
|
+
}
|
|
641
|
+
readonlyResult.db.close();
|
|
642
|
+
const writableResult = openConfiguredTelemetryDb({ writable: true });
|
|
643
|
+
if (!writableResult.db) {
|
|
644
|
+
return writableResult;
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
new TelemetryHealthTracker(writableResult.db);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
writableResult.db.close();
|
|
650
|
+
return {
|
|
651
|
+
db: null,
|
|
652
|
+
dbPath,
|
|
653
|
+
error: error instanceof Error ? error.message : String(error)
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
writableResult.db.close();
|
|
657
|
+
return openConfiguredTelemetryDb();
|
|
658
|
+
}
|
|
659
|
+
function readDoctorMetricsOptions(command) {
|
|
660
|
+
const globals = command.optsWithGlobals();
|
|
661
|
+
return validateCommandOptions(DoctorMetricsOptionsSchema, {
|
|
662
|
+
json: globals.json,
|
|
663
|
+
prometheus: globals.prometheus,
|
|
664
|
+
verbose: globals.verbose
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
async function collectHealthMetrics(db) {
|
|
668
|
+
const TelemetryHealthTracker = await loadTelemetryHealthTracker();
|
|
669
|
+
const tracker = new TelemetryHealthTracker(db);
|
|
670
|
+
const queueDepth = 0;
|
|
671
|
+
const fallbackMode = false;
|
|
672
|
+
const health = tracker.getHealthMetrics(queueDepth, fallbackMode);
|
|
673
|
+
const metrics = {
|
|
674
|
+
queueDepth: health.queue_depth,
|
|
675
|
+
fallbackMode: health.fallback_mode_active,
|
|
676
|
+
errorRate: health.error_rate,
|
|
677
|
+
databaseHealthy: health.database_healthy,
|
|
678
|
+
lastErrorTimestamp: health.last_error_timestamp || null,
|
|
679
|
+
errorsLastHour: health.errors_last_hour,
|
|
680
|
+
unresolvedErrors: health.unresolved_errors
|
|
681
|
+
};
|
|
682
|
+
try {
|
|
683
|
+
const mttr = tracker.calculateMTTR(24);
|
|
684
|
+
metrics.mttr = {
|
|
685
|
+
meanTimeToResolution: Math.round(mttr.mean_time_to_resolution),
|
|
686
|
+
p50: Math.round(mttr.p50_resolution_time),
|
|
687
|
+
p95: Math.round(mttr.p95_resolution_time),
|
|
688
|
+
p99: Math.round(mttr.p99_resolution_time),
|
|
689
|
+
resolutionRate: mttr.resolution_rate,
|
|
690
|
+
errorCount24h: mttr.error_count_last_24h
|
|
691
|
+
};
|
|
692
|
+
} catch (_error) {
|
|
693
|
+
}
|
|
694
|
+
return metrics;
|
|
695
|
+
}
|
|
696
|
+
function formatMetricsJson(metrics, extras = {}) {
|
|
697
|
+
try {
|
|
698
|
+
getDataDir();
|
|
699
|
+
} catch {
|
|
700
|
+
}
|
|
701
|
+
const pathEnvWarnings = getRejectedPathEnvWarnings();
|
|
702
|
+
return JSON.stringify(
|
|
703
|
+
{
|
|
704
|
+
status: getHealthStatus(metrics),
|
|
705
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
706
|
+
uptime: Math.floor(process.uptime()),
|
|
707
|
+
metrics,
|
|
708
|
+
...extras.telemetryDb ? { telemetryDb: extras.telemetryDb } : {},
|
|
709
|
+
...extras.databaseError ? { databaseError: extras.databaseError } : {},
|
|
710
|
+
...pathEnvWarnings.length > 0 ? { pathEnvWarnings } : {}
|
|
711
|
+
},
|
|
712
|
+
null,
|
|
713
|
+
2
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
function formatMetricsPrometheus(metrics) {
|
|
717
|
+
const lines = [];
|
|
718
|
+
lines.push("# HELP build_error_rate Error rate as decimal (0.0-1.0)");
|
|
719
|
+
lines.push("# TYPE build_error_rate gauge");
|
|
720
|
+
lines.push(`build_error_rate ${metrics.errorRate.toFixed(4)}`);
|
|
721
|
+
lines.push("# HELP build_database_healthy Database health status (1=healthy, 0=unhealthy)");
|
|
722
|
+
lines.push("# TYPE build_database_healthy gauge");
|
|
723
|
+
lines.push(`build_database_healthy ${metrics.databaseHealthy ? 1 : 0}`);
|
|
724
|
+
lines.push("# HELP build_errors_last_hour Number of errors in last hour");
|
|
725
|
+
lines.push("# TYPE build_errors_last_hour gauge");
|
|
726
|
+
lines.push(`build_errors_last_hour ${metrics.errorsLastHour}`);
|
|
727
|
+
lines.push("# HELP build_unresolved_errors Number of unresolved errors");
|
|
728
|
+
lines.push("# TYPE build_unresolved_errors gauge");
|
|
729
|
+
lines.push(`build_unresolved_errors ${metrics.unresolvedErrors}`);
|
|
730
|
+
if (metrics.mttr) {
|
|
731
|
+
lines.push("# HELP build_mttr_ms Mean time to resolution in milliseconds");
|
|
732
|
+
lines.push("# TYPE build_mttr_ms gauge");
|
|
733
|
+
lines.push(`build_mttr_ms ${metrics.mttr.meanTimeToResolution}`);
|
|
734
|
+
lines.push("# HELP build_mttr_p50_ms 50th percentile time to resolution (ms)");
|
|
735
|
+
lines.push("# TYPE build_mttr_p50_ms gauge");
|
|
736
|
+
lines.push(`build_mttr_p50_ms ${metrics.mttr.p50}`);
|
|
737
|
+
lines.push("# HELP build_mttr_p95_ms 95th percentile time to resolution (ms)");
|
|
738
|
+
lines.push("# TYPE build_mttr_p95_ms gauge");
|
|
739
|
+
lines.push(`build_mttr_p95_ms ${metrics.mttr.p95}`);
|
|
740
|
+
lines.push("# HELP build_mttr_p99_ms 99th percentile time to resolution (ms)");
|
|
741
|
+
lines.push("# TYPE build_mttr_p99_ms gauge");
|
|
742
|
+
lines.push(`build_mttr_p99_ms ${metrics.mttr.p99}`);
|
|
743
|
+
lines.push("# HELP build_resolution_rate Error resolution rate (0.0-1.0)");
|
|
744
|
+
lines.push("# TYPE build_resolution_rate gauge");
|
|
745
|
+
lines.push(`build_resolution_rate ${metrics.mttr.resolutionRate.toFixed(4)}`);
|
|
746
|
+
lines.push("# HELP build_error_count_24h Error count in last 24 hours");
|
|
747
|
+
lines.push("# TYPE build_error_count_24h gauge");
|
|
748
|
+
lines.push(`build_error_count_24h ${metrics.mttr.errorCount24h}`);
|
|
749
|
+
}
|
|
750
|
+
lines.push(`# Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
751
|
+
return `${lines.join("\n")}
|
|
752
|
+
`;
|
|
753
|
+
}
|
|
754
|
+
function formatMetricsTable(metrics, verbose = false) {
|
|
755
|
+
const lines = [];
|
|
756
|
+
lines.push(chalk.bold.cyan("\nBuild Telemetry Metrics\n"));
|
|
757
|
+
const status = getHealthStatus(metrics);
|
|
758
|
+
const statusColor = status === "healthy" ? chalk.green : status === "degraded" ? chalk.yellow : chalk.red;
|
|
759
|
+
const statusIcon = status === "healthy" ? "OK" : status === "degraded" ? "Warning: " : "Error";
|
|
760
|
+
lines.push(
|
|
761
|
+
`${statusColor(statusIcon)} Status: ${statusColor(status.toUpperCase())}`
|
|
762
|
+
);
|
|
763
|
+
lines.push(`Error Rate: ${(metrics.errorRate * 100).toFixed(2)}%`);
|
|
764
|
+
lines.push(
|
|
765
|
+
`${metrics.databaseHealthy ? "OK" : "Error"} Database: ${metrics.databaseHealthy ? chalk.green("Healthy") : chalk.red("Unhealthy")}`
|
|
766
|
+
);
|
|
767
|
+
if (metrics.errorsLastHour > 0) {
|
|
768
|
+
lines.push(chalk.yellow(`Warning: Errors (last hour): ${metrics.errorsLastHour}`));
|
|
769
|
+
} else {
|
|
770
|
+
lines.push("OK Errors (last hour): 0");
|
|
771
|
+
}
|
|
772
|
+
if (metrics.unresolvedErrors > 0) {
|
|
773
|
+
lines.push(chalk.yellow(`Warning: Unresolved: ${metrics.unresolvedErrors}`));
|
|
774
|
+
} else {
|
|
775
|
+
lines.push("OK Unresolved: 0");
|
|
776
|
+
}
|
|
777
|
+
if (verbose && metrics.lastErrorTimestamp) {
|
|
778
|
+
const lastErrorTime = new Date(metrics.lastErrorTimestamp);
|
|
779
|
+
const minutesAgo = Math.floor((Date.now() - lastErrorTime.getTime()) / 6e4);
|
|
780
|
+
lines.push(`Last Error: ${minutesAgo} minutes ago`);
|
|
781
|
+
}
|
|
782
|
+
if (verbose && metrics.mttr) {
|
|
783
|
+
lines.push(chalk.bold.cyan("\nResolution Metrics (24h)\n"));
|
|
784
|
+
lines.push(
|
|
785
|
+
`Time: MTTR (mean): ${formatDurationMs(metrics.mttr.meanTimeToResolution)}`
|
|
786
|
+
);
|
|
787
|
+
lines.push(`MTTR (P50): ${formatDurationMs(metrics.mttr.p50)}`);
|
|
788
|
+
lines.push(`MTTR (P95): ${formatDurationMs(metrics.mttr.p95)}`);
|
|
789
|
+
lines.push(`MTTR (P99): ${formatDurationMs(metrics.mttr.p99)}`);
|
|
790
|
+
lines.push(`OK Resolution Rate: ${(metrics.mttr.resolutionRate * 100).toFixed(1)}%`);
|
|
791
|
+
lines.push(`Error Count (24h): ${metrics.mttr.errorCount24h}`);
|
|
792
|
+
}
|
|
793
|
+
if (verbose && metrics.lastErrorTimestamp) {
|
|
794
|
+
lines.push(chalk.bold.cyan("\nDetails\n"));
|
|
795
|
+
lines.push(`Last Error: ${metrics.lastErrorTimestamp}`);
|
|
796
|
+
}
|
|
797
|
+
lines.push("");
|
|
798
|
+
return lines.join("\n");
|
|
799
|
+
}
|
|
800
|
+
function getHealthStatus(metrics) {
|
|
801
|
+
if (!metrics.databaseHealthy) {
|
|
802
|
+
return "unhealthy";
|
|
803
|
+
}
|
|
804
|
+
if (metrics.errorRate > 0.1 || metrics.unresolvedErrors > 0) {
|
|
805
|
+
return "degraded";
|
|
806
|
+
}
|
|
807
|
+
return "healthy";
|
|
808
|
+
}
|
|
809
|
+
async function displayDatabaseStatus(options = {}) {
|
|
810
|
+
const { db, dbPath, error } = await openTelemetryDb();
|
|
811
|
+
if (!db) {
|
|
812
|
+
const metrics = buildUnavailableHealthMetrics();
|
|
813
|
+
if (options.json) {
|
|
814
|
+
console.log(
|
|
815
|
+
formatMetricsJson(metrics, {
|
|
816
|
+
telemetryDb: dbPath,
|
|
817
|
+
databaseError: error ?? "Telemetry database unavailable"
|
|
818
|
+
})
|
|
819
|
+
);
|
|
820
|
+
process.exit(1);
|
|
821
|
+
}
|
|
822
|
+
console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
|
|
823
|
+
console.log(` Path: ${dbPath}`);
|
|
824
|
+
console.log(" Run: build init");
|
|
825
|
+
process.exit(1);
|
|
826
|
+
}
|
|
827
|
+
try {
|
|
828
|
+
const metrics = await collectHealthMetrics(db);
|
|
829
|
+
if (options.json) {
|
|
830
|
+
console.log(formatMetricsJson(metrics, { telemetryDb: dbPath }));
|
|
831
|
+
} else if (options.prometheus) {
|
|
832
|
+
console.log(formatMetricsPrometheus(metrics));
|
|
833
|
+
} else {
|
|
834
|
+
console.log(formatMetricsTable(metrics, options.verbose ?? false));
|
|
835
|
+
}
|
|
836
|
+
} catch (error2) {
|
|
837
|
+
if (options.json) {
|
|
838
|
+
const metrics = buildUnavailableHealthMetrics();
|
|
839
|
+
console.log(
|
|
840
|
+
formatMetricsJson(metrics, {
|
|
841
|
+
telemetryDb: dbPath,
|
|
842
|
+
databaseError: error2 instanceof Error ? error2.message : String(error2)
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
process.exit(1);
|
|
846
|
+
}
|
|
847
|
+
console.error(
|
|
848
|
+
chalk.red("Failed to collect health metrics:"),
|
|
849
|
+
error2 instanceof Error ? error2.message : String(error2)
|
|
850
|
+
);
|
|
851
|
+
process.exit(1);
|
|
852
|
+
} finally {
|
|
853
|
+
db.close();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async function monitorHealth(options) {
|
|
857
|
+
const { db, dbPath, error } = await openTelemetryDb();
|
|
858
|
+
if (!db) {
|
|
859
|
+
if (options.json) {
|
|
860
|
+
const metrics = buildUnavailableHealthMetrics();
|
|
861
|
+
console.log(
|
|
862
|
+
formatMetricsJson(metrics, {
|
|
863
|
+
telemetryDb: dbPath,
|
|
864
|
+
databaseError: error ?? "Telemetry database unavailable"
|
|
865
|
+
})
|
|
866
|
+
);
|
|
867
|
+
process.exit(1);
|
|
868
|
+
}
|
|
869
|
+
console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
|
|
870
|
+
console.log(` Path: ${dbPath}`);
|
|
871
|
+
console.log(" Run: build init");
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const interval = (options.interval || 2) * 1e3;
|
|
875
|
+
const isJson = options.json || false;
|
|
876
|
+
const isPrometheus = options.prometheus || false;
|
|
877
|
+
try {
|
|
878
|
+
while (true) {
|
|
879
|
+
const metrics = await collectHealthMetrics(db);
|
|
880
|
+
if (isJson) {
|
|
881
|
+
console.log(formatMetricsJson(metrics));
|
|
882
|
+
} else if (isPrometheus) {
|
|
883
|
+
console.log(formatMetricsPrometheus(metrics));
|
|
884
|
+
} else {
|
|
885
|
+
console.clear?.();
|
|
886
|
+
console.log(formatMetricsTable(metrics, false));
|
|
887
|
+
}
|
|
888
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
889
|
+
}
|
|
890
|
+
} catch (error2) {
|
|
891
|
+
console.error(
|
|
892
|
+
chalk.red("Error Monitoring error:"),
|
|
893
|
+
error2 instanceof Error ? error2.message : String(error2)
|
|
894
|
+
);
|
|
895
|
+
process.exit(1);
|
|
896
|
+
} finally {
|
|
897
|
+
db.close();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function createDoctorMetricsCommand() {
|
|
901
|
+
const metricsCmd = new Command("metrics");
|
|
902
|
+
metricsCmd.description(
|
|
903
|
+
"Monitor telemetry-derived operational metrics (not the install doctor)\n\nNote: For install / provider auth diagnostics use `build doctor`.\n`build doctor metrics` reports runtime telemetry from the local SQLite store.\n\nExamples:\n build doctor metrics # Show metrics status\n build doctor metrics --json # Output as JSON\n build doctor metrics --prometheus # Prometheus format\n build doctor metrics monitor # Real-time monitoring\n build doctor metrics monitor --interval 5 # Update every 5 seconds"
|
|
904
|
+
);
|
|
905
|
+
const checkCmd = new Command("check");
|
|
906
|
+
checkCmd.description("Run telemetry metrics check and display results");
|
|
907
|
+
checkCmd.option("--json", "Output as JSON");
|
|
908
|
+
checkCmd.option("--prometheus", "Output in Prometheus text format");
|
|
909
|
+
checkCmd.option("-v, --verbose", "Show detailed information");
|
|
910
|
+
checkCmd.action(async function() {
|
|
911
|
+
const validatedOptions = readDoctorMetricsOptions(this);
|
|
912
|
+
const { db, dbPath, error } = await openTelemetryDb();
|
|
913
|
+
if (!db) {
|
|
914
|
+
const metrics = buildUnavailableHealthMetrics();
|
|
915
|
+
if (validatedOptions.json) {
|
|
916
|
+
console.log(
|
|
917
|
+
formatMetricsJson(metrics, {
|
|
918
|
+
telemetryDb: dbPath,
|
|
919
|
+
databaseError: error ?? "Telemetry database unavailable"
|
|
920
|
+
})
|
|
921
|
+
);
|
|
922
|
+
process.exit(1);
|
|
923
|
+
}
|
|
924
|
+
console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
|
|
925
|
+
console.log(` Path: ${dbPath}`);
|
|
926
|
+
console.log(" Run: build init");
|
|
927
|
+
process.exit(1);
|
|
928
|
+
}
|
|
929
|
+
let degraded = false;
|
|
930
|
+
try {
|
|
931
|
+
const metrics = await collectHealthMetrics(db);
|
|
932
|
+
const status = getHealthStatus(metrics);
|
|
933
|
+
degraded = status !== "healthy";
|
|
934
|
+
if (validatedOptions.json) {
|
|
935
|
+
console.log(formatMetricsJson(metrics, { telemetryDb: dbPath }));
|
|
936
|
+
} else if (validatedOptions.prometheus) {
|
|
937
|
+
console.log(formatMetricsPrometheus(metrics));
|
|
938
|
+
} else {
|
|
939
|
+
console.log(formatMetricsTable(metrics, validatedOptions.verbose));
|
|
940
|
+
}
|
|
941
|
+
} finally {
|
|
942
|
+
db.close();
|
|
943
|
+
}
|
|
944
|
+
if (degraded) process.exit(1);
|
|
945
|
+
});
|
|
946
|
+
const monitorCmd = new Command("monitor");
|
|
947
|
+
monitorCmd.description("Watch telemetry metrics in real-time");
|
|
948
|
+
monitorCmd.option("--interval <seconds>", "Update interval in seconds", "2");
|
|
949
|
+
monitorCmd.option("--json", "Output as JSON");
|
|
950
|
+
monitorCmd.option("--prometheus", "Output in Prometheus text format");
|
|
951
|
+
monitorCmd.action(async (options) => {
|
|
952
|
+
const monitorOptions = {
|
|
953
|
+
interval: options.interval ? Number.parseInt(String(options.interval), 10) : 2,
|
|
954
|
+
json: Boolean(options.json),
|
|
955
|
+
prometheus: Boolean(options.prometheus)
|
|
956
|
+
};
|
|
957
|
+
const validatedOptions = validateCommandOptions(
|
|
958
|
+
DoctorMetricsMonitorOptionsSchema,
|
|
959
|
+
monitorOptions
|
|
960
|
+
);
|
|
961
|
+
await monitorHealth(validatedOptions);
|
|
962
|
+
});
|
|
963
|
+
metricsCmd.addCommand(checkCmd);
|
|
964
|
+
metricsCmd.addCommand(monitorCmd);
|
|
965
|
+
metricsCmd.option("--json", "Output as JSON");
|
|
966
|
+
metricsCmd.option("--prometheus", "Output in Prometheus text format");
|
|
967
|
+
metricsCmd.option("-v, --verbose", "Show detailed information");
|
|
968
|
+
metricsCmd.action(async function() {
|
|
969
|
+
const validatedOptions = readDoctorMetricsOptions(this);
|
|
970
|
+
await displayDatabaseStatus(validatedOptions);
|
|
971
|
+
});
|
|
972
|
+
return metricsCmd;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/cli/commands/doctor-native-sqlite.ts
|
|
976
|
+
var loadBetterSqlite3 = async () => (await import("better-sqlite3")).default;
|
|
977
|
+
async function checkNativeSqlite(load2 = loadBetterSqlite3) {
|
|
978
|
+
try {
|
|
979
|
+
const Database = await load2();
|
|
980
|
+
const db = new Database(":memory:");
|
|
981
|
+
try {
|
|
982
|
+
const row = db.prepare("SELECT 1 AS ok").get();
|
|
983
|
+
if (row?.ok !== 1) throw new Error("in-memory query returned an unexpected result");
|
|
984
|
+
} finally {
|
|
985
|
+
db.close();
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
name: "Native SQLite",
|
|
989
|
+
category: "build",
|
|
990
|
+
status: "pass",
|
|
991
|
+
message: "better-sqlite3 loads"
|
|
992
|
+
};
|
|
993
|
+
} catch (error) {
|
|
994
|
+
return {
|
|
995
|
+
name: "Native SQLite",
|
|
996
|
+
category: "build",
|
|
997
|
+
status: "fail",
|
|
998
|
+
message: "better-sqlite3 cannot load; telemetry and planning databases will not work",
|
|
999
|
+
details: `${error instanceof Error ? error.message : String(error)}. Reinstall Build under a supported Node version. Linux glibc prebuilds require glibc >=2.34 and libstdc++ with GLIBCXX_3.4.29; older glibc systems have no source-build fallback.`
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/cli/commands/doctor-storage.ts
|
|
1005
|
+
function checkPlanningStorageBackend(repoRoot) {
|
|
1006
|
+
try {
|
|
1007
|
+
if (resolvePlanningContextForRoot(repoRoot).backend !== "checkout") return void 0;
|
|
1008
|
+
} catch {
|
|
1009
|
+
return void 0;
|
|
1010
|
+
}
|
|
1011
|
+
return {
|
|
1012
|
+
name: "Planning Storage",
|
|
1013
|
+
category: "build",
|
|
1014
|
+
status: "info",
|
|
1015
|
+
message: "Planning buckets are tracked in the checkout; `build plan storage enable` moves them to the build/work branch"
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
async function checkBuildBranchNameConflict(repoRoot) {
|
|
1019
|
+
let refs;
|
|
1020
|
+
try {
|
|
1021
|
+
const result = await execGit(
|
|
1022
|
+
["for-each-ref", "--format=%(refname)", "refs/heads/", "refs/remotes/"],
|
|
1023
|
+
{ cwd: repoRoot }
|
|
1024
|
+
);
|
|
1025
|
+
if (result.exitCode !== 0) return void 0;
|
|
1026
|
+
refs = result.stdout;
|
|
1027
|
+
} catch {
|
|
1028
|
+
return void 0;
|
|
1029
|
+
}
|
|
1030
|
+
const conflicts = refs.split("\n").map((ref) => ref.trim()).filter((ref) => {
|
|
1031
|
+
const parts = ref.split("/");
|
|
1032
|
+
const name = parts[1] === "heads" ? parts.slice(2) : parts[1] === "remotes" ? parts.slice(3) : [];
|
|
1033
|
+
return name.length === 1 && name[0]?.toLowerCase() === "build";
|
|
1034
|
+
}).map((ref) => ref.replace(/^refs\/(heads|remotes)\//, ""));
|
|
1035
|
+
if (conflicts.length === 0) return void 0;
|
|
1036
|
+
return {
|
|
1037
|
+
name: "Build Branch Names",
|
|
1038
|
+
category: "git",
|
|
1039
|
+
status: "warn",
|
|
1040
|
+
message: `A branch named ${conflicts.join(", ")} blocks Build from creating its build/... branches`,
|
|
1041
|
+
details: 'Git cannot hold a branch named "build" alongside branches under "build/" (on macOS the names clash in any letter case). Rename that branch, locally with `git branch -m <old> <new>` and on the remote by pushing the new name and deleting the old one.'
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// src/core/task-types/packaged-copies.ts
|
|
1046
|
+
import { createHash } from "crypto";
|
|
1047
|
+
import * as fs2 from "fs/promises";
|
|
1048
|
+
import * as path2 from "path";
|
|
1049
|
+
import * as yaml from "js-yaml";
|
|
1050
|
+
|
|
1051
|
+
// src/core/task-types/shipped-task-type-fingerprints.json
|
|
1052
|
+
var shipped_task_type_fingerprints_default = {
|
|
1053
|
+
about: "sha256 of every templates/.build-project/task-types version that build init could copy into a repository before it stopped copying task types. Generated by scripts/generate-task-type-fingerprints.mjs; no maintenance needed.",
|
|
1054
|
+
fingerprints: {
|
|
1055
|
+
"4c9a35f0939a138008cb46679cc07889f45fc3592c1c284f8c6cae34b338f750": {
|
|
1056
|
+
id: "analysis",
|
|
1057
|
+
shipped: "2026-08-17"
|
|
1058
|
+
},
|
|
1059
|
+
"64b66ece5f41b0144fc494bd0a7ed5cadf30bbff20f54e69850904f37aaf0127": {
|
|
1060
|
+
id: "analysis",
|
|
1061
|
+
shipped: "2026-09-15"
|
|
1062
|
+
},
|
|
1063
|
+
a562c77b1744f6f2d3178bcc0bd9e171c6d1c18a7e7393375511830c210f0008: {
|
|
1064
|
+
id: "analysis",
|
|
1065
|
+
shipped: "2026-09-26"
|
|
1066
|
+
},
|
|
1067
|
+
"280a7a8f9c46de1e4c42f01efbbcef378ec728ea9e36b67a055101dd7e195075": {
|
|
1068
|
+
id: "architecture",
|
|
1069
|
+
shipped: "2026-08-17"
|
|
1070
|
+
},
|
|
1071
|
+
f76e6ef5160b568ad7d54eb8a51bfa2b12bf910bde4f9b110216104bc0b2c796: {
|
|
1072
|
+
id: "architecture",
|
|
1073
|
+
shipped: "2026-09-15"
|
|
1074
|
+
},
|
|
1075
|
+
"7ac93c055b45d7f3c3ab794e35d6f9b9768e5edd46f9fd6dfde3a5e6057ef77d": {
|
|
1076
|
+
id: "architecture",
|
|
1077
|
+
shipped: "2026-09-26"
|
|
1078
|
+
},
|
|
1079
|
+
"8c5c152f7fe44436b5edf63bd8cdab9a395cdb579dd1b111cabb86788dacb43d": {
|
|
1080
|
+
id: "doc",
|
|
1081
|
+
shipped: "2026-08-17"
|
|
1082
|
+
},
|
|
1083
|
+
bc81f4c08c4b410ec89c41828c653da20071788beee29643721a1887575226fc: {
|
|
1084
|
+
id: "doc",
|
|
1085
|
+
shipped: "2026-09-26"
|
|
1086
|
+
},
|
|
1087
|
+
"6f3e5218c85e2ca1b47b880c14101b3bbae63613bd715ccb596a3f686f3eeb6e": {
|
|
1088
|
+
id: "feature",
|
|
1089
|
+
shipped: "2026-08-17"
|
|
1090
|
+
},
|
|
1091
|
+
"01fe455c0db9e813d6c8280654b5df1407c26c72e7e2bf0aa3c1796329b67337": {
|
|
1092
|
+
id: "feature",
|
|
1093
|
+
shipped: "2026-09-26"
|
|
1094
|
+
},
|
|
1095
|
+
f0e1e4f76ef434529fa8c52a6a23f28c4262b9b2bfb93e4ad1b372488f608d22: {
|
|
1096
|
+
id: "general",
|
|
1097
|
+
shipped: "2026-08-17"
|
|
1098
|
+
},
|
|
1099
|
+
"169a3316cc15d9cfebf8af76f96a914ea68bd73452d94c7da44515ff6b60c9f8": {
|
|
1100
|
+
id: "general",
|
|
1101
|
+
shipped: "2026-09-26"
|
|
1102
|
+
},
|
|
1103
|
+
e813bb3fd1587a9e1d9a82095b43680d19c5393ba5e10536d232fa8f8fca2cb2: {
|
|
1104
|
+
id: "security",
|
|
1105
|
+
shipped: "2026-08-17"
|
|
1106
|
+
},
|
|
1107
|
+
"70e6d7e168fc96b7b5dfde0603f2025ff53af48ed717f361a0623d5bcd443365": {
|
|
1108
|
+
id: "security",
|
|
1109
|
+
shipped: "2026-09-26"
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
// src/core/task-types/packaged-copies.ts
|
|
1115
|
+
var SHIPPED_TASK_TYPE_VERSIONS = shipped_task_type_fingerprints_default.fingerprints;
|
|
1116
|
+
function sha256(bytes) {
|
|
1117
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
1118
|
+
}
|
|
1119
|
+
function taskTypeId(bytes, filePath) {
|
|
1120
|
+
try {
|
|
1121
|
+
const parsed = yaml.load(bytes.toString("utf8"), { schema: yaml.JSON_SCHEMA });
|
|
1122
|
+
const id = parsed?.id;
|
|
1123
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
1124
|
+
} catch {
|
|
1125
|
+
}
|
|
1126
|
+
return path2.basename(filePath).replace(/\.ya?ml$/, "");
|
|
1127
|
+
}
|
|
1128
|
+
async function findPackagedTaskTypeCopies(options) {
|
|
1129
|
+
const repoFiles = await listYamlFiles(options.repoDir);
|
|
1130
|
+
if (repoFiles.length === 0) return { fileCount: 0, copies: [] };
|
|
1131
|
+
const shipped = options.shipped ?? SHIPPED_TASK_TYPE_VERSIONS;
|
|
1132
|
+
const currentIdByHash = /* @__PURE__ */ new Map();
|
|
1133
|
+
for (const filePath of await listYamlFiles(options.builtInDir ?? getBuiltInTaskTypesDir())) {
|
|
1134
|
+
const bytes = await fs2.readFile(filePath);
|
|
1135
|
+
currentIdByHash.set(sha256(bytes), taskTypeId(bytes, filePath));
|
|
1136
|
+
}
|
|
1137
|
+
const currentIds = new Set(currentIdByHash.values());
|
|
1138
|
+
const repoEntries = [];
|
|
1139
|
+
const repoIdCounts = /* @__PURE__ */ new Map();
|
|
1140
|
+
for (const filePath of repoFiles) {
|
|
1141
|
+
const bytes = await fs2.readFile(filePath);
|
|
1142
|
+
const id = taskTypeId(bytes, filePath);
|
|
1143
|
+
repoEntries.push({ file: path2.basename(filePath), hash: sha256(bytes), id });
|
|
1144
|
+
repoIdCounts.set(id, (repoIdCounts.get(id) ?? 0) + 1);
|
|
1145
|
+
}
|
|
1146
|
+
const copies = [];
|
|
1147
|
+
for (const { file, hash, id } of repoEntries) {
|
|
1148
|
+
const version = shipped[hash];
|
|
1149
|
+
const currentId = currentIdByHash.get(hash);
|
|
1150
|
+
const sharedId = (repoIdCounts.get(id) ?? 0) > 1;
|
|
1151
|
+
if (currentId !== void 0) {
|
|
1152
|
+
copies.push({ file, id: currentId, kind: "current", shipped: version?.shipped, sharedId });
|
|
1153
|
+
} else if (version) {
|
|
1154
|
+
const kind = currentIds.has(version.id) ? "older" : "retired";
|
|
1155
|
+
copies.push({ file, id: version.id, kind, shipped: version.shipped, sharedId });
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return { fileCount: repoFiles.length, copies };
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// src/cli/commands/doctor-task-types.ts
|
|
1162
|
+
var CHECK_NAME2 = "Repo Task Types";
|
|
1163
|
+
function describeCopy(copy) {
|
|
1164
|
+
const file = `.build-project/task-types/${copy.file}`;
|
|
1165
|
+
const shipped = copy.shipped ? ` shipped ${copy.shipped}` : "";
|
|
1166
|
+
const shared = `Another repo file also defines ${copy.id}, so packaged updates still won't apply after deleting this one.`;
|
|
1167
|
+
switch (copy.kind) {
|
|
1168
|
+
case "older":
|
|
1169
|
+
return copy.sharedId ? `${file} matches the packaged ${copy.id} task type${shipped}. ${shared}` : `${file} matches the packaged ${copy.id} task type${shipped}; it overrides newer packaged versions. Delete it to receive updates, or keep it to stay on that version.`;
|
|
1170
|
+
case "current":
|
|
1171
|
+
return copy.sharedId ? `${file} matches the current packaged ${copy.id} task type. ${shared}` : `${file} matches the current packaged ${copy.id} task type; it will override future packaged versions. Delete it to receive future updates, or keep it to stay on this version.`;
|
|
1172
|
+
case "retired":
|
|
1173
|
+
return `${file} matches the packaged ${copy.id} task type${shipped}, which this install no longer ships; ${copy.sharedId ? `another repo file also defines ${copy.id}.` : `it is now the only definition of ${copy.id}.`}`;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
async function checkRepoTaskTypes(repoRoot, options = {}) {
|
|
1177
|
+
let report;
|
|
1178
|
+
try {
|
|
1179
|
+
report = await findPackagedTaskTypeCopies({
|
|
1180
|
+
...options,
|
|
1181
|
+
repoDir: getRepoTaskTypesDir(repoRoot)
|
|
1182
|
+
});
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
return {
|
|
1185
|
+
name: CHECK_NAME2,
|
|
1186
|
+
category: "build",
|
|
1187
|
+
status: "info",
|
|
1188
|
+
message: "Could not compare repo task types with packaged versions",
|
|
1189
|
+
details: error instanceof Error ? error.message : String(error)
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
const { fileCount, copies } = report;
|
|
1193
|
+
if (copies.length === 0) {
|
|
1194
|
+
return {
|
|
1195
|
+
name: CHECK_NAME2,
|
|
1196
|
+
category: "build",
|
|
1197
|
+
status: "pass",
|
|
1198
|
+
message: fileCount === 0 ? "No repo task-type files; packaged task types apply" : `${fileCount} repo task-type override(s); none match a packaged version`
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
const lines = copies.map(describeCopy);
|
|
1202
|
+
const overrides = fileCount - copies.length;
|
|
1203
|
+
if (overrides > 0) {
|
|
1204
|
+
lines.push(`${overrides} other file(s) match no packaged version and are repo overrides.`);
|
|
1205
|
+
}
|
|
1206
|
+
return {
|
|
1207
|
+
name: CHECK_NAME2,
|
|
1208
|
+
category: "build",
|
|
1209
|
+
status: "info",
|
|
1210
|
+
message: `${copies.length} of ${fileCount} repo task-type file(s) match a packaged version; run build doctor --verbose for details`,
|
|
1211
|
+
details: lines.join("\n ")
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// src/cli/commands/doctor.ts
|
|
1216
|
+
var BetterSqlite3Ctor = null;
|
|
1217
|
+
async function getBetterSqlite3() {
|
|
1218
|
+
if (!BetterSqlite3Ctor) {
|
|
1219
|
+
const mod = await import("better-sqlite3");
|
|
1220
|
+
BetterSqlite3Ctor = mod.default;
|
|
1221
|
+
}
|
|
1222
|
+
return BetterSqlite3Ctor;
|
|
1223
|
+
}
|
|
1224
|
+
var HOMEBREW_NODE_24_PATH = "/opt/homebrew/opt/node@24/bin/node";
|
|
1225
|
+
async function preferredNode24Path() {
|
|
1226
|
+
try {
|
|
1227
|
+
await fs3.access(HOMEBREW_NODE_24_PATH);
|
|
1228
|
+
return HOMEBREW_NODE_24_PATH;
|
|
1229
|
+
} catch {
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
function formatNode24Remediation(preferredPath) {
|
|
1234
|
+
if (preferredPath) {
|
|
1235
|
+
return `Build requires Node.js ${formatSupportedNodeMajors()}; use ${preferredPath} or put /opt/homebrew/opt/node@24/bin first in PATH.`;
|
|
1236
|
+
}
|
|
1237
|
+
return `Build requires Node.js ${formatSupportedNodeMajors()} first on PATH. Run: nvm install 24; export PATH="$(dirname "$(nvm which 24)"):$PATH"; hash -r; command -v node; node --version; npm config get prefix.`;
|
|
1238
|
+
}
|
|
1239
|
+
function isLikelyBetterSqlite3NativeLoadMismatch(error) {
|
|
1240
|
+
const code = error?.code;
|
|
1241
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1242
|
+
return code === "ERR_DLOPEN_FAILED" || message.includes("NODE_MODULE_VERSION") || /compiled against a different Node\.js version/i.test(message) || /was compiled for a different Node\.js version/i.test(message) || /module did not self-register/i.test(message) || /invalid ELF header/i.test(message) || /incompatible architecture/i.test(message) || /better[-_]sqlite3/i.test(message) && /\.node\b/i.test(message);
|
|
1243
|
+
}
|
|
1244
|
+
function formatDatabaseNativeErrorDetails(error) {
|
|
1245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1246
|
+
if (!isLikelyBetterSqlite3NativeLoadMismatch(error)) {
|
|
1247
|
+
return message;
|
|
1248
|
+
}
|
|
1249
|
+
return `${message}
|
|
1250
|
+
Remediation: reinstall Build under the active Node (npm install -g @ebrindley/build; source checkout: rm -rf node_modules && npm ci), then rerun build doctor.`;
|
|
1251
|
+
}
|
|
1252
|
+
var HealthCommandOptionsSchema = z2.object({
|
|
1253
|
+
json: BooleanFlagSchema.optional(),
|
|
1254
|
+
verbose: BooleanFlagSchema.optional(),
|
|
1255
|
+
fix: BooleanFlagSchema.optional(),
|
|
1256
|
+
all: BooleanFlagSchema.optional(),
|
|
1257
|
+
provider: z2.string().optional(),
|
|
1258
|
+
providersOnly: BooleanFlagSchema.optional(),
|
|
1259
|
+
matrix: BooleanFlagSchema.optional(),
|
|
1260
|
+
strict: BooleanFlagSchema.optional(),
|
|
1261
|
+
requireReady: BooleanFlagSchema.optional(),
|
|
1262
|
+
probe: BooleanFlagSchema.optional(),
|
|
1263
|
+
refresh: BooleanFlagSchema.optional(),
|
|
1264
|
+
checkLinks: BooleanFlagSchema.optional()
|
|
1265
|
+
}).strict();
|
|
1266
|
+
function isStrictBlockingWarning(check) {
|
|
1267
|
+
if (check.name === "Linux write boundary" || check.name === "Agent Wrapper Imports") {
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
if (check.category === "providers" && check.name === "Provider CLIs") {
|
|
1271
|
+
return false;
|
|
1272
|
+
}
|
|
1273
|
+
if (check.category === "providers" && (check.name === "OpenCode discovery" || /auth not observed/i.test(check.message))) {
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
1276
|
+
return true;
|
|
1277
|
+
}
|
|
1278
|
+
function isRequireReadyFailure(check) {
|
|
1279
|
+
return check.category === "providers" && check.name === "Provider CLIs" && (check.readinessGateLabel === "zero-ready" || check.readinessReadyCount === 0);
|
|
1280
|
+
}
|
|
1281
|
+
function exitForHealthFailures(checks, options) {
|
|
1282
|
+
const failedChecks = checks.filter((c) => c.status === "fail");
|
|
1283
|
+
const warnChecks = checks.filter((c) => c.status === "warn" && isStrictBlockingWarning(c));
|
|
1284
|
+
if (failedChecks.length > 0) {
|
|
1285
|
+
process.exit(1);
|
|
1286
|
+
}
|
|
1287
|
+
if (options.requireReady && checks.some((check) => isRequireReadyFailure(check))) {
|
|
1288
|
+
process.exit(1);
|
|
1289
|
+
}
|
|
1290
|
+
if (options.strict && warnChecks.length > 0) {
|
|
1291
|
+
process.exit(1);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
async function checkGit() {
|
|
1295
|
+
try {
|
|
1296
|
+
const result = await execa("git", ["--version"]);
|
|
1297
|
+
const version = result.stdout.trim();
|
|
1298
|
+
let details = version;
|
|
1299
|
+
try {
|
|
1300
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1301
|
+
const whichResult = await execa(whichCmd, ["git"]);
|
|
1302
|
+
details = `${version} \u2014 ${whichResult.stdout.trim()}`;
|
|
1303
|
+
} catch (_whichError) {
|
|
1304
|
+
}
|
|
1305
|
+
return {
|
|
1306
|
+
name: "Git",
|
|
1307
|
+
category: "git",
|
|
1308
|
+
status: "pass",
|
|
1309
|
+
message: version,
|
|
1310
|
+
details
|
|
1311
|
+
};
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
return {
|
|
1314
|
+
name: "Git",
|
|
1315
|
+
category: "git",
|
|
1316
|
+
status: "fail",
|
|
1317
|
+
message: "Git not found or not accessible",
|
|
1318
|
+
details: error instanceof Error ? error.message : String(error)
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
var PROVIDER_CLI_SPECS = [
|
|
1323
|
+
{ id: "claude", versionArgs: ["--version"] },
|
|
1324
|
+
{ id: "codex", versionArgs: ["--version"] },
|
|
1325
|
+
{ id: "copilot", versionArgs: ["--version"] },
|
|
1326
|
+
{ id: "cursor", versionArgs: ["--version"] },
|
|
1327
|
+
{ id: "gemini", versionArgs: ["--version"] },
|
|
1328
|
+
{ id: "grok", versionArgs: ["--version"] },
|
|
1329
|
+
{ id: "kiro", versionArgs: ["--version"] },
|
|
1330
|
+
{ id: "pi", versionArgs: ["--version"] },
|
|
1331
|
+
{ id: "antigravity", versionArgs: ["--version"] },
|
|
1332
|
+
{ id: "opencode", versionArgs: ["--version"] }
|
|
1333
|
+
];
|
|
1334
|
+
var PROVIDER_CLI_SPEC_IDS = PROVIDER_CLI_SPECS.map((s) => s.id);
|
|
1335
|
+
var AUTH_PROBE_HINT_PROVIDERS = /* @__PURE__ */ new Set(["grok"]);
|
|
1336
|
+
var PROVIDER_EXTRA_HEALTH_CHECKS = [
|
|
1337
|
+
{ providerId: "opencode", runWithoutProviderFilter: false, check: checkOpencodeDiscovery },
|
|
1338
|
+
{ providerId: "claude", runWithoutProviderFilter: true, check: checkClaudeStrictMcpConfig },
|
|
1339
|
+
{ providerId: "claude", runWithoutProviderFilter: true, check: checkLegacyClaudePlugin },
|
|
1340
|
+
{ providerId: "claude", runWithoutProviderFilter: true, check: checkClaudeAliasAdvisory }
|
|
1341
|
+
];
|
|
1342
|
+
async function collectProviderExtraHealthChecks(options) {
|
|
1343
|
+
const checks = [];
|
|
1344
|
+
for (const entry of PROVIDER_EXTRA_HEALTH_CHECKS) {
|
|
1345
|
+
const requested = options.all === true || options.provider === entry.providerId || entry.runWithoutProviderFilter && !options.provider;
|
|
1346
|
+
if (requested) {
|
|
1347
|
+
checks.push(await entry.check());
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return checks;
|
|
1351
|
+
}
|
|
1352
|
+
function providerHealthReadinessOptions(providerId) {
|
|
1353
|
+
const activeAuthProviders = /* @__PURE__ */ new Set(["cursor", "grok"]);
|
|
1354
|
+
return {
|
|
1355
|
+
mode: "execute",
|
|
1356
|
+
authProbeMode: activeAuthProviders.has(providerId) ? "active" : "passive"
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
async function formatLastSeenAuth(providerId) {
|
|
1360
|
+
const lookup = await lookupAuthOutcome(providerId);
|
|
1361
|
+
if (!lookup || lookup.stale || !lookup.contextMatches) {
|
|
1362
|
+
return "none";
|
|
1363
|
+
}
|
|
1364
|
+
return `${lookup.entry.result}, ${lookup.entry.observedAt}`;
|
|
1365
|
+
}
|
|
1366
|
+
async function recordHealthAuthOutcome(providerId, result, message) {
|
|
1367
|
+
await recordAuthOutcome(providerId, result, {
|
|
1368
|
+
source: "health-probe",
|
|
1369
|
+
message
|
|
1370
|
+
}).catch(() => {
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function normalizeProviderId(value) {
|
|
1374
|
+
return normalizeEngineId(value.trim());
|
|
1375
|
+
}
|
|
1376
|
+
async function checkOpencodeDiscovery() {
|
|
1377
|
+
let command;
|
|
1378
|
+
let prefixArgs;
|
|
1379
|
+
try {
|
|
1380
|
+
({ command, prefixArgs } = resolveProviderCliCommand("opencode"));
|
|
1381
|
+
} catch {
|
|
1382
|
+
return {
|
|
1383
|
+
name: "OpenCode discovery",
|
|
1384
|
+
category: "providers",
|
|
1385
|
+
status: "fail",
|
|
1386
|
+
message: "OpenCode CLI not resolved",
|
|
1387
|
+
details: "Install OpenCode or set BUILD_OPENCODE_CLI_COMMAND. Run: build doctor --provider opencode"
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
const cliProbe = await probeProviderCli(command, ["--version"], prefixArgs, {
|
|
1391
|
+
providerId: "opencode"
|
|
1392
|
+
});
|
|
1393
|
+
const cliInstalled = cliProbe.present;
|
|
1394
|
+
const discovery = await discoverOpencode({
|
|
1395
|
+
cliCommand: command,
|
|
1396
|
+
prefixArgs,
|
|
1397
|
+
cliInstalled,
|
|
1398
|
+
refreshModels: false,
|
|
1399
|
+
includeModelsDev: true,
|
|
1400
|
+
modelsDevCacheDir: path3.join(
|
|
1401
|
+
getRuntimeEnv("BUILD_TELEMETRY_DIR") ?? path3.join(process.cwd(), ".build-project"),
|
|
1402
|
+
"cache",
|
|
1403
|
+
"opencode"
|
|
1404
|
+
)
|
|
1405
|
+
});
|
|
1406
|
+
const parts = [];
|
|
1407
|
+
parts.push(`CLI installed: ${cliInstalled ? "yes" : "no"}`);
|
|
1408
|
+
if (discovery.authList.rawOk) {
|
|
1409
|
+
const credentialNames = discovery.authList.credentialProviders ?? [];
|
|
1410
|
+
const environmentNames = discovery.authList.environmentProviders ?? [];
|
|
1411
|
+
parts.push(`Auth: ${credentialNames.length > 0 ? credentialNames.join(", ") : "not observed"}`);
|
|
1412
|
+
if (environmentNames.length > 0) {
|
|
1413
|
+
parts.push(`Capability (parent-env): ${environmentNames.join(", ")}`);
|
|
1414
|
+
}
|
|
1415
|
+
} else {
|
|
1416
|
+
parts.push(
|
|
1417
|
+
`Providers (auth list): unsupported or failed${discovery.authList.unsupportedMessage ? ` (${discovery.authList.unsupportedMessage.slice(0, 80)})` : ""}`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
if (discovery.models.rawOk) {
|
|
1421
|
+
parts.push(
|
|
1422
|
+
`Models: ${discovery.models.modelIds.length > 0 ? discovery.models.modelIds.slice(0, 15).join(", ") + (discovery.models.modelIds.length > 15 ? ` (+${discovery.models.modelIds.length - 15} more)` : "") : "none"}`
|
|
1423
|
+
);
|
|
1424
|
+
} else {
|
|
1425
|
+
parts.push(
|
|
1426
|
+
`Models: unsupported or failed${discovery.models.unsupportedMessage ? ` (${discovery.models.unsupportedMessage.slice(0, 80)})` : ""}`
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
if (discovery.modelsDev) {
|
|
1430
|
+
const source = discovery.modelsDev.source === "network" ? "fresh" : "cache";
|
|
1431
|
+
parts.push(
|
|
1432
|
+
`models.dev: ${source}${discovery.modelsDev.fetchedAt ? ` @ ${new Date(discovery.modelsDev.fetchedAt).toISOString()}` : ""}`
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
const credentialProviders = discovery.authList.credentialProviders ?? [];
|
|
1436
|
+
const environmentProviders = discovery.authList.environmentProviders ?? [];
|
|
1437
|
+
const hasCredentialAuth = credentialProviders.length > 0;
|
|
1438
|
+
const envOnlyCapability = cliInstalled && discovery.authList.rawOk && !hasCredentialAuth && (environmentProviders.length > 0 || discovery.models.modelIds.length > 0);
|
|
1439
|
+
const status = cliInstalled && discovery.authList.rawOk && discovery.models.rawOk && hasCredentialAuth ? "pass" : cliInstalled ? "warn" : "fail";
|
|
1440
|
+
const message = status === "pass" ? `CLI installed; ${credentialProviders.length} credential provider(s); ${discovery.models.modelIds.length} model(s)` : envOnlyCapability ? "CLI present; auth not observed (parent-env vendors are capability only)" : cliInstalled ? discovery.authList.rawOk && discovery.models.rawOk ? "CLI present; no OpenCode providers or models discovered" : "CLI present; auth list or models unavailable" : "OpenCode CLI not installed";
|
|
1441
|
+
return {
|
|
1442
|
+
name: "OpenCode discovery",
|
|
1443
|
+
category: "providers",
|
|
1444
|
+
status,
|
|
1445
|
+
message,
|
|
1446
|
+
details: parts.join(" | ")
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
async function getProvidersToCheck(options) {
|
|
1450
|
+
const validIds = PROVIDER_CLI_SPECS.map((s) => s.id);
|
|
1451
|
+
if (options.provider) {
|
|
1452
|
+
const requested = normalizeProviderId(options.provider);
|
|
1453
|
+
if (!validIds.includes(requested)) {
|
|
1454
|
+
throw new Error(
|
|
1455
|
+
`Unknown provider: ${options.provider}. Valid providers: ${validIds.join(", ")}`
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
return {
|
|
1459
|
+
providers: [requested],
|
|
1460
|
+
isDiscoveryMode: false,
|
|
1461
|
+
selectionScope: "provider"
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
if (options.all) {
|
|
1465
|
+
return {
|
|
1466
|
+
providers: PROVIDER_CLI_SPECS.map((s) => s.id),
|
|
1467
|
+
isDiscoveryMode: false,
|
|
1468
|
+
selectionScope: "all"
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
const configured = /* @__PURE__ */ new Set();
|
|
1472
|
+
const defaultProvider = process.env.BUILD_DEFAULT_PROVIDER;
|
|
1473
|
+
if (defaultProvider) {
|
|
1474
|
+
const normalized = normalizeProviderId(defaultProvider);
|
|
1475
|
+
if (validIds.includes(normalized)) {
|
|
1476
|
+
configured.add(normalized);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
try {
|
|
1480
|
+
const { ConfigManager: ConfigManager2 } = await import("./config-manager-J2UTO635.js");
|
|
1481
|
+
const manager = new ConfigManager2({
|
|
1482
|
+
enableCache: false,
|
|
1483
|
+
cwd: process.cwd()
|
|
1484
|
+
});
|
|
1485
|
+
const resolved = await manager.loadConfig({}, true);
|
|
1486
|
+
const config = resolved.config;
|
|
1487
|
+
const hasExplicitConfig = resolved.sources.some(
|
|
1488
|
+
(source) => (source.type === "project" || source.type === "user") && source.exists
|
|
1489
|
+
);
|
|
1490
|
+
if (hasExplicitConfig) {
|
|
1491
|
+
for (const providerId of configuredProviderIds(config, resolved.sources)) {
|
|
1492
|
+
configured.add(normalizeProviderId(providerId));
|
|
1493
|
+
}
|
|
1494
|
+
const configuredDefault = config?.defaults?.provider ?? config?.defaultProvider;
|
|
1495
|
+
if (configuredDefault) {
|
|
1496
|
+
configured.add(normalizeProviderId(String(configuredDefault)));
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
if (isConfigValidityError(error)) {
|
|
1501
|
+
throw error;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (configured.size > 0) {
|
|
1505
|
+
const validIds2 = new Set(PROVIDER_CLI_SPECS.map((s) => s.id));
|
|
1506
|
+
const filteredProviders = [...configured].filter((id) => validIds2.has(id));
|
|
1507
|
+
if (filteredProviders.length > 0) {
|
|
1508
|
+
return {
|
|
1509
|
+
providers: filteredProviders,
|
|
1510
|
+
isDiscoveryMode: false,
|
|
1511
|
+
selectionScope: "configured"
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return {
|
|
1516
|
+
providers: PROVIDER_CLI_SPECS.map((s) => s.id),
|
|
1517
|
+
isDiscoveryMode: true,
|
|
1518
|
+
selectionScope: "discovery"
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
async function checkProviderCLIs(optionsOrVerbose = false) {
|
|
1522
|
+
const options = typeof optionsOrVerbose === "boolean" ? { includeVerboseDetails: optionsOrVerbose } : optionsOrVerbose;
|
|
1523
|
+
const includeVerboseDetails = options.includeVerboseDetails ?? false;
|
|
1524
|
+
const {
|
|
1525
|
+
providers: providersToCheck,
|
|
1526
|
+
isDiscoveryMode,
|
|
1527
|
+
selectionScope
|
|
1528
|
+
} = await getProvidersToCheck(options);
|
|
1529
|
+
const specsToCheck = PROVIDER_CLI_SPECS.filter((spec) => providersToCheck.includes(spec.id));
|
|
1530
|
+
const uncheckedProviderIds = selectionScope === "configured" ? PROVIDER_CLI_SPECS.map((spec) => spec.id).filter((id) => !providersToCheck.includes(id)) : [];
|
|
1531
|
+
const available = [];
|
|
1532
|
+
const degraded = [];
|
|
1533
|
+
const authUnobserved = [];
|
|
1534
|
+
const sandboxBlocked = [];
|
|
1535
|
+
const missing = [];
|
|
1536
|
+
const providerBreakdown = [];
|
|
1537
|
+
const providerReadiness = [];
|
|
1538
|
+
const gateReadyProviders = /* @__PURE__ */ new Set();
|
|
1539
|
+
const authMissing = [];
|
|
1540
|
+
const authReady = [];
|
|
1541
|
+
const lastSeenAuthMap = /* @__PURE__ */ new Map();
|
|
1542
|
+
for (const spec of specsToCheck) {
|
|
1543
|
+
lastSeenAuthMap.set(spec.id, await formatLastSeenAuth(spec.id));
|
|
1544
|
+
}
|
|
1545
|
+
const repoRoot = resolveRepoRootForBuildDir(process.cwd());
|
|
1546
|
+
for (const spec of specsToCheck) {
|
|
1547
|
+
let config;
|
|
1548
|
+
try {
|
|
1549
|
+
config = getProviderConfig(spec.id);
|
|
1550
|
+
} catch {
|
|
1551
|
+
missing.push({ id: spec.id });
|
|
1552
|
+
if (includeVerboseDetails) {
|
|
1553
|
+
providerBreakdown.push({ id: spec.id, status: "missing" });
|
|
1554
|
+
}
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (!config?.cli) {
|
|
1558
|
+
missing.push({ id: spec.id });
|
|
1559
|
+
if (includeVerboseDetails) {
|
|
1560
|
+
providerBreakdown.push({ id: spec.id, status: "missing" });
|
|
1561
|
+
}
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
const readinessOptions = options.probe ? {
|
|
1565
|
+
...providerHealthReadinessOptions(spec.id)
|
|
1566
|
+
} : {
|
|
1567
|
+
...SETUP_CHECK_READINESS_OPTIONS,
|
|
1568
|
+
cliProbeMode: "execute",
|
|
1569
|
+
sandboxProbeMode: "execute",
|
|
1570
|
+
probeAuth: async () => "unknown"
|
|
1571
|
+
};
|
|
1572
|
+
const probeContext = readinessProbeContextForOptions(readinessOptions);
|
|
1573
|
+
const readiness = await getProviderReadiness(spec.id, readinessOptions);
|
|
1574
|
+
if (isGateReadyProvider(readiness)) gateReadyProviders.add(readiness.provider);
|
|
1575
|
+
if (options.probe) {
|
|
1576
|
+
void recordReadinessDiagnostic(readiness, {
|
|
1577
|
+
phase: "health",
|
|
1578
|
+
cliProbeMode: "execute",
|
|
1579
|
+
...readinessOptions.authProbeMode ? { authProbeMode: readinessOptions.authProbeMode } : {}
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
const dryRun = readProviderDryRunFreshness(spec.id, repoRoot);
|
|
1583
|
+
const resolved = resolveNamedReadiness(readiness, dryRun);
|
|
1584
|
+
const scope = resolveReadinessScope(readiness, resolved);
|
|
1585
|
+
const parityLabel = resolveProviderParityLabel(readiness, resolved);
|
|
1586
|
+
providerReadiness.push({
|
|
1587
|
+
provider: spec.id,
|
|
1588
|
+
known: readiness.known,
|
|
1589
|
+
configured: readiness.configured,
|
|
1590
|
+
installed: readiness.installed,
|
|
1591
|
+
cliHealthy: readiness.cliHealthy,
|
|
1592
|
+
authenticated: readiness.authenticated,
|
|
1593
|
+
ready: readiness.ready,
|
|
1594
|
+
scope: scope.label,
|
|
1595
|
+
...scope.detail ? { scopeDetail: scope.detail } : {},
|
|
1596
|
+
...readiness.readinessNote ? { readinessNote: readiness.readinessNote } : {},
|
|
1597
|
+
...readiness.reasonCode ? { reasonCode: readiness.reasonCode } : {},
|
|
1598
|
+
parityLabel,
|
|
1599
|
+
resolved,
|
|
1600
|
+
probe: probeContext
|
|
1601
|
+
});
|
|
1602
|
+
const lastSeenAuth = lastSeenAuthMap.get(spec.id) ?? "";
|
|
1603
|
+
if (!readiness.command && !readiness.installed) {
|
|
1604
|
+
missing.push({
|
|
1605
|
+
id: spec.id,
|
|
1606
|
+
installCommand: config.cli?.install_command
|
|
1607
|
+
});
|
|
1608
|
+
if (includeVerboseDetails) {
|
|
1609
|
+
providerBreakdown.push({
|
|
1610
|
+
id: spec.id,
|
|
1611
|
+
status: "missing",
|
|
1612
|
+
installCommand: config.cli?.install_command,
|
|
1613
|
+
lastSeenAuth
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
if (!readiness.installed) {
|
|
1619
|
+
missing.push({
|
|
1620
|
+
id: spec.id,
|
|
1621
|
+
installCommand: config.cli?.install_command
|
|
1622
|
+
});
|
|
1623
|
+
if (includeVerboseDetails) {
|
|
1624
|
+
providerBreakdown.push({
|
|
1625
|
+
id: spec.id,
|
|
1626
|
+
status: "missing",
|
|
1627
|
+
resolvedCommand: readiness.command,
|
|
1628
|
+
installCommand: config.cli?.install_command,
|
|
1629
|
+
lastSeenAuth
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
} else if (!readiness.cliHealthy) {
|
|
1633
|
+
degraded.push({
|
|
1634
|
+
id: spec.id,
|
|
1635
|
+
reason: readiness.reason ?? "Unknown issue"
|
|
1636
|
+
});
|
|
1637
|
+
if (includeVerboseDetails) {
|
|
1638
|
+
providerBreakdown.push({
|
|
1639
|
+
id: spec.id,
|
|
1640
|
+
status: "degraded",
|
|
1641
|
+
resolvedCommand: readiness.command,
|
|
1642
|
+
degradedReason: readiness.reason,
|
|
1643
|
+
lastSeenAuth
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
} else {
|
|
1647
|
+
if (readiness.sandboxCapable === false) {
|
|
1648
|
+
const deniedPaths = readiness.sandboxDeniedPaths?.join(", ") ?? "";
|
|
1649
|
+
const summary = deniedPaths ? `${spec.id}: sandbox blocked (${deniedPaths})` : formatSandboxDegradedSummary({
|
|
1650
|
+
providerId: spec.id,
|
|
1651
|
+
capable: false,
|
|
1652
|
+
probed: [],
|
|
1653
|
+
missing: []
|
|
1654
|
+
});
|
|
1655
|
+
const verboseReason = readiness.sandboxReason ?? "CLI present, sandbox capability denied";
|
|
1656
|
+
sandboxBlocked.push({ id: spec.id, paths: deniedPaths });
|
|
1657
|
+
degraded.push({ id: spec.id, reason: summary });
|
|
1658
|
+
if (includeVerboseDetails) {
|
|
1659
|
+
providerBreakdown.push({
|
|
1660
|
+
id: spec.id,
|
|
1661
|
+
status: "degraded",
|
|
1662
|
+
resolvedCommand: readiness.command,
|
|
1663
|
+
degradedReason: verboseReason,
|
|
1664
|
+
lastSeenAuth
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
continue;
|
|
1668
|
+
}
|
|
1669
|
+
const authState = readiness.authenticated;
|
|
1670
|
+
if (authState === "no") {
|
|
1671
|
+
if (options.probe) {
|
|
1672
|
+
await recordHealthAuthOutcome(
|
|
1673
|
+
spec.id,
|
|
1674
|
+
"failed",
|
|
1675
|
+
readiness.reason ?? "Health probe reported authentication missing"
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
authMissing.push(spec.id);
|
|
1679
|
+
const authMissingReason = spec.id === "codex" && readEnv(BUILD_CODEX_BACKEND).value?.trim().toLowerCase() === "bedrock" ? 'CLI present, auth missing; Bedrock runs need an AWS credential chain, not "codex login"' : "CLI present, auth missing";
|
|
1680
|
+
degraded.push({ id: spec.id, reason: authMissingReason });
|
|
1681
|
+
if (includeVerboseDetails) {
|
|
1682
|
+
providerBreakdown.push({
|
|
1683
|
+
id: spec.id,
|
|
1684
|
+
status: "degraded",
|
|
1685
|
+
resolvedCommand: readiness.command,
|
|
1686
|
+
degradedReason: authMissingReason,
|
|
1687
|
+
lastSeenAuth: lastSeenAuthMap.get(spec.id) ?? ""
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
if (authState === "unknown") {
|
|
1693
|
+
const activeProbeUnknown = options.probe === true && readiness.reason?.includes("active auth probe could not verify headless execution");
|
|
1694
|
+
const defaultProbeHintUnknown = AUTH_PROBE_HINT_PROVIDERS.has(spec.id) && options.provider === spec.id && options.probe !== true;
|
|
1695
|
+
if (activeProbeUnknown || defaultProbeHintUnknown) {
|
|
1696
|
+
const reason2 = defaultProbeHintUnknown ? `installed; auth checked on first run; run \`build doctor --probe --provider ${spec.id}\` to verify headless execution` : readiness.reason ?? "CLI present, active auth probe inconclusive";
|
|
1697
|
+
degraded.push({
|
|
1698
|
+
id: spec.id,
|
|
1699
|
+
reason: reason2
|
|
1700
|
+
});
|
|
1701
|
+
if (includeVerboseDetails) {
|
|
1702
|
+
providerBreakdown.push({
|
|
1703
|
+
id: spec.id,
|
|
1704
|
+
status: "degraded",
|
|
1705
|
+
resolvedCommand: readiness.command,
|
|
1706
|
+
degradedReason: reason2,
|
|
1707
|
+
lastSeenAuth
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
const cached = await lookupAuthOutcome(spec.id);
|
|
1713
|
+
const freshFailure = cached?.entry.result === "failed" && !cached.stale && cached.contextMatches;
|
|
1714
|
+
if (!freshFailure) {
|
|
1715
|
+
authUnobserved.push(spec.id);
|
|
1716
|
+
available.push(spec.id);
|
|
1717
|
+
if (includeVerboseDetails) {
|
|
1718
|
+
providerBreakdown.push({
|
|
1719
|
+
id: spec.id,
|
|
1720
|
+
status: "available",
|
|
1721
|
+
resolvedCommand: readiness.command,
|
|
1722
|
+
lastSeenAuth
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
const reason = "CLI present, cached auth failure";
|
|
1728
|
+
degraded.push({
|
|
1729
|
+
id: spec.id,
|
|
1730
|
+
reason
|
|
1731
|
+
});
|
|
1732
|
+
if (includeVerboseDetails) {
|
|
1733
|
+
providerBreakdown.push({
|
|
1734
|
+
id: spec.id,
|
|
1735
|
+
status: "degraded",
|
|
1736
|
+
resolvedCommand: readiness.command,
|
|
1737
|
+
degradedReason: reason,
|
|
1738
|
+
lastSeenAuth
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
if (authState === "yes") {
|
|
1744
|
+
if (readiness.authEvidence === "execution") {
|
|
1745
|
+
await recordHealthAuthOutcome(spec.id, "ok", "Health probe succeeded");
|
|
1746
|
+
}
|
|
1747
|
+
authReady.push(spec.id);
|
|
1748
|
+
}
|
|
1749
|
+
available.push(spec.id);
|
|
1750
|
+
if (includeVerboseDetails) {
|
|
1751
|
+
providerBreakdown.push({
|
|
1752
|
+
id: spec.id,
|
|
1753
|
+
status: "available",
|
|
1754
|
+
resolvedCommand: readiness.command,
|
|
1755
|
+
degradedReason: readiness.warning,
|
|
1756
|
+
lastSeenAuth: lastSeenAuthMap.get(spec.id) ?? ""
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
const missingDetails = missing.map(
|
|
1762
|
+
(entry) => entry.installCommand ? `${entry.id} (install: ${entry.installCommand})` : entry.id
|
|
1763
|
+
).join(", ");
|
|
1764
|
+
const degradedDetails = degraded.map((entry) => `${entry.id} (${entry.reason})`).join(", ");
|
|
1765
|
+
const lastSeenParts = specsToCheck.map(
|
|
1766
|
+
(spec) => `${spec.id}: ${lastSeenAuthMap.get(spec.id) ?? ""}`
|
|
1767
|
+
);
|
|
1768
|
+
const baseDetails = (() => {
|
|
1769
|
+
const parts = [];
|
|
1770
|
+
if (available.length > 0) {
|
|
1771
|
+
parts.push(`CLI present: ${available.join(", ")}`);
|
|
1772
|
+
}
|
|
1773
|
+
if (degraded.length > 0) {
|
|
1774
|
+
parts.push(`Degraded: ${degradedDetails}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (authUnobserved.length > 0) {
|
|
1777
|
+
parts.push(
|
|
1778
|
+
`No current auth observation: ${authUnobserved.join(", ")} (expected; provider execution is the probe)`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
if (authMissing.length > 0) {
|
|
1782
|
+
parts.push(`Auth missing: ${authMissing.join(", ")}`);
|
|
1783
|
+
}
|
|
1784
|
+
if (authReady.length > 0) {
|
|
1785
|
+
parts.push(`Auth ready: ${authReady.join(", ")}`);
|
|
1786
|
+
}
|
|
1787
|
+
const assumedReady = providerReadiness.filter((entry) => entry.readinessNote === "env-var-auth-assumed").map((entry) => entry.provider);
|
|
1788
|
+
if (assumedReady.length > 0) {
|
|
1789
|
+
parts.push(`Readiness assumed: ${assumedReady.join(", ")}`);
|
|
1790
|
+
}
|
|
1791
|
+
if (lastSeenParts.length > 0) {
|
|
1792
|
+
parts.push(`Last seen auth: ${lastSeenParts.join(", ")}`);
|
|
1793
|
+
}
|
|
1794
|
+
if (missing.length > 0) {
|
|
1795
|
+
parts.push(`Missing: ${missingDetails}`);
|
|
1796
|
+
}
|
|
1797
|
+
if (uncheckedProviderIds.length > 0) {
|
|
1798
|
+
parts.push(`Not checked: ${uncheckedProviderIds.join(", ")} (use --all to include)`);
|
|
1799
|
+
}
|
|
1800
|
+
if (parts.length === 0) {
|
|
1801
|
+
return "No provider CLIs checked";
|
|
1802
|
+
}
|
|
1803
|
+
return parts.join(" | ");
|
|
1804
|
+
})();
|
|
1805
|
+
const discoveryModeNote = isDiscoveryMode ? `
|
|
1806
|
+
|
|
1807
|
+
Info: Discovery mode: No configured providers found, checking all known providers.
|
|
1808
|
+
Tip: Set BUILD_DEFAULT_PROVIDER or configure providers in build.config.jsonc / ${getUserConfigFile()}` : "";
|
|
1809
|
+
const details = (() => {
|
|
1810
|
+
if (!includeVerboseDetails) return baseDetails + discoveryModeNote;
|
|
1811
|
+
if (providerBreakdown.length === 0) return baseDetails + discoveryModeNote;
|
|
1812
|
+
const breakdown = providerBreakdown.map((provider) => {
|
|
1813
|
+
let statusMark;
|
|
1814
|
+
switch (provider.status) {
|
|
1815
|
+
case "available":
|
|
1816
|
+
statusMark = "OK";
|
|
1817
|
+
break;
|
|
1818
|
+
case "degraded":
|
|
1819
|
+
statusMark = "Warning:";
|
|
1820
|
+
break;
|
|
1821
|
+
default:
|
|
1822
|
+
statusMark = "Error";
|
|
1823
|
+
}
|
|
1824
|
+
const commandPart = provider.resolvedCommand ? `: ${provider.resolvedCommand}` : ": missing";
|
|
1825
|
+
const statusSuffix = (() => {
|
|
1826
|
+
if (provider.status === "missing" && provider.installCommand) {
|
|
1827
|
+
return ` (install: ${provider.installCommand})`;
|
|
1828
|
+
}
|
|
1829
|
+
if (provider.status === "degraded" && provider.degradedReason) {
|
|
1830
|
+
const lastSeen = provider.lastSeenAuth ? `; lastSeenAuth: ${provider.lastSeenAuth}` : "";
|
|
1831
|
+
return ` (${provider.degradedReason}${lastSeen})`;
|
|
1832
|
+
}
|
|
1833
|
+
if (provider.lastSeenAuth) {
|
|
1834
|
+
return ` (lastSeenAuth: ${provider.lastSeenAuth})`;
|
|
1835
|
+
}
|
|
1836
|
+
return "";
|
|
1837
|
+
})();
|
|
1838
|
+
return `${statusMark} ${provider.id}${commandPart}${statusSuffix}`;
|
|
1839
|
+
}).join("\n ");
|
|
1840
|
+
return `${baseDetails}${discoveryModeNote}
|
|
1841
|
+
|
|
1842
|
+
${breakdown}`;
|
|
1843
|
+
})();
|
|
1844
|
+
const totalChecked = specsToCheck.length;
|
|
1845
|
+
const healthyCount = available.length;
|
|
1846
|
+
const degradedCount = degraded.length;
|
|
1847
|
+
const missingCount = missing.length;
|
|
1848
|
+
const gateReadyCount = gateReadyProviders.size;
|
|
1849
|
+
const readinessGateLabel = gateReadyCount === 0 ? "zero-ready" : gateReadyCount === 1 ? "one-ready" : "multi-ready";
|
|
1850
|
+
const gateMeta = {
|
|
1851
|
+
readinessGateLabel,
|
|
1852
|
+
readinessReadyCount: gateReadyCount
|
|
1853
|
+
};
|
|
1854
|
+
if (options.requireReady && gateReadyCount === 0) {
|
|
1855
|
+
return {
|
|
1856
|
+
name: "Provider CLIs",
|
|
1857
|
+
category: "providers",
|
|
1858
|
+
status: "fail",
|
|
1859
|
+
message: isDiscoveryMode ? "No ready providers (--require-ready, discovery mode)" : "No ready providers (--require-ready)",
|
|
1860
|
+
details,
|
|
1861
|
+
...providerReadiness.length > 0 ? { providerReadiness } : {},
|
|
1862
|
+
...gateMeta
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
if (healthyCount === 0 && degradedCount === 0) {
|
|
1866
|
+
return {
|
|
1867
|
+
name: "Provider CLIs",
|
|
1868
|
+
category: "providers",
|
|
1869
|
+
status: "fail",
|
|
1870
|
+
message: isDiscoveryMode ? "No provider CLIs present (discovery mode)" : "No provider CLIs present",
|
|
1871
|
+
details,
|
|
1872
|
+
...providerReadiness.length > 0 ? { providerReadiness } : {},
|
|
1873
|
+
...gateMeta
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
if (missingCount > 0 || degradedCount > 0) {
|
|
1877
|
+
const statusParts = [];
|
|
1878
|
+
if (healthyCount > 0) statusParts.push(`${healthyCount} healthy`);
|
|
1879
|
+
if (degradedCount > 0) statusParts.push(`${degradedCount} degraded`);
|
|
1880
|
+
if (missingCount > 0) statusParts.push(`${missingCount} missing`);
|
|
1881
|
+
if ((options.strict || options.requireReady) && gateReadyCount > 0) {
|
|
1882
|
+
statusParts.push(`${gateReadyCount} ready`);
|
|
1883
|
+
}
|
|
1884
|
+
return {
|
|
1885
|
+
name: "Provider CLIs",
|
|
1886
|
+
category: "providers",
|
|
1887
|
+
status: degradedCount > 0 || missingCount > 0 ? "warn" : "pass",
|
|
1888
|
+
message: (() => {
|
|
1889
|
+
if (options.provider) {
|
|
1890
|
+
const sandboxEntry = sandboxBlocked.find((entry) => entry.id === options.provider);
|
|
1891
|
+
if (sandboxEntry) {
|
|
1892
|
+
return sandboxEntry.paths ? `${options.provider} sandbox blocked (${sandboxEntry.paths})` : `${options.provider} sandbox/policy blocked`;
|
|
1893
|
+
}
|
|
1894
|
+
if (authMissing.includes(options.provider)) {
|
|
1895
|
+
return `${options.provider} CLI present, auth missing`;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
return `${statusParts.join(", ")} of ${totalChecked} ${selectionScope === "configured" ? "configured " : ""}checked${isDiscoveryMode ? " (discovery mode)" : ""}${uncheckedProviderIds.length > 0 ? `; ${uncheckedProviderIds.length} known provider(s) not checked` : ""}`;
|
|
1899
|
+
})(),
|
|
1900
|
+
details,
|
|
1901
|
+
...providerReadiness.length > 0 ? { providerReadiness } : {},
|
|
1902
|
+
...gateMeta
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
const scopedPassMessage = selectionScope === "configured" && uncheckedProviderIds.length > 0 ? `${healthyCount} of ${totalChecked} configured provider CLIs present; ${uncheckedProviderIds.length} known provider(s) not checked` : void 0;
|
|
1906
|
+
return {
|
|
1907
|
+
name: "Provider CLIs",
|
|
1908
|
+
category: "providers",
|
|
1909
|
+
status: "pass",
|
|
1910
|
+
message: options.provider && authReady.includes(options.provider) ? `${options.provider} CLI present and authenticated` : scopedPassMessage ?? (totalChecked === PROVIDER_CLI_SPECS.length ? `All ${healthyCount} provider CLIs present${isDiscoveryMode ? " (discovery mode)" : ""}` : `${healthyCount} of ${totalChecked} checked provider CLIs present${isDiscoveryMode ? " (discovery mode)" : ""}`),
|
|
1911
|
+
details,
|
|
1912
|
+
...providerReadiness.length > 0 ? { providerReadiness } : {},
|
|
1913
|
+
...gateMeta
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
async function checkBuildDirectory(cwd) {
|
|
1917
|
+
const buildDir = path3.join(cwd, ".build-project");
|
|
1918
|
+
try {
|
|
1919
|
+
await fs3.stat(buildDir);
|
|
1920
|
+
try {
|
|
1921
|
+
await fs3.access(buildDir, fs3.constants.W_OK);
|
|
1922
|
+
return {
|
|
1923
|
+
name: ".build-project Directory",
|
|
1924
|
+
category: "build",
|
|
1925
|
+
status: "pass",
|
|
1926
|
+
message: "Directory exists and is writable",
|
|
1927
|
+
details: buildDir
|
|
1928
|
+
};
|
|
1929
|
+
} catch (_error) {
|
|
1930
|
+
return {
|
|
1931
|
+
name: ".build-project Directory",
|
|
1932
|
+
category: "build",
|
|
1933
|
+
status: "fail",
|
|
1934
|
+
message: "Directory not writable",
|
|
1935
|
+
details: buildDir,
|
|
1936
|
+
fixable: false
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
} catch (_error) {
|
|
1940
|
+
return {
|
|
1941
|
+
name: ".build-project Directory",
|
|
1942
|
+
category: "build",
|
|
1943
|
+
status: "warn",
|
|
1944
|
+
message: "Directory not found",
|
|
1945
|
+
details: 'Run "build init" or "build doctor --fix" to initialize',
|
|
1946
|
+
fixable: true
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
async function checkNestedBuildDirectory(cwd) {
|
|
1951
|
+
const nestedDir = path3.join(cwd, ".build-project", ".build-project");
|
|
1952
|
+
try {
|
|
1953
|
+
const stats = await fs3.stat(nestedDir);
|
|
1954
|
+
if (stats.isDirectory()) {
|
|
1955
|
+
return {
|
|
1956
|
+
name: "Nested .build-project Directory",
|
|
1957
|
+
category: "build",
|
|
1958
|
+
status: "warn",
|
|
1959
|
+
message: "Found nested .build-project/.build-project/ directory (bug artifact)",
|
|
1960
|
+
details: `This directory was created by a bug and should be removed: ${nestedDir}`,
|
|
1961
|
+
fixable: true
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
} catch (_error) {
|
|
1965
|
+
}
|
|
1966
|
+
return null;
|
|
1967
|
+
}
|
|
1968
|
+
async function checkRequiredDirectories(cwd) {
|
|
1969
|
+
const missing = [];
|
|
1970
|
+
const present = [];
|
|
1971
|
+
for (const dir of RuntimeRequirements.directories) {
|
|
1972
|
+
const fullPath = path3.join(cwd, ".build-project", dir);
|
|
1973
|
+
try {
|
|
1974
|
+
await fs3.stat(fullPath);
|
|
1975
|
+
present.push(dir);
|
|
1976
|
+
} catch (_error) {
|
|
1977
|
+
missing.push(dir);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
if (missing.length === 0) {
|
|
1981
|
+
return {
|
|
1982
|
+
name: "Required Directories",
|
|
1983
|
+
category: "build",
|
|
1984
|
+
status: "pass",
|
|
1985
|
+
message: `All ${RuntimeRequirements.directories.length} directories present`,
|
|
1986
|
+
details: `All present: ${present.join(", ")}`
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
if (missing.length === RuntimeRequirements.directories.length) {
|
|
1990
|
+
return {
|
|
1991
|
+
name: "Required Directories",
|
|
1992
|
+
category: "build",
|
|
1993
|
+
status: "fail",
|
|
1994
|
+
message: "No directories found",
|
|
1995
|
+
details: `Present: ${present.join(", ")} | Missing: ${missing.join(", ")}`,
|
|
1996
|
+
fixable: true
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
return {
|
|
2000
|
+
name: "Required Directories",
|
|
2001
|
+
category: "build",
|
|
2002
|
+
status: "warn",
|
|
2003
|
+
message: `${missing.length} directories missing`,
|
|
2004
|
+
details: `Present: ${present.join(", ")} | Missing: ${missing.join(", ")}`,
|
|
2005
|
+
fixable: true
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
async function checkArchiveReconciliation() {
|
|
2009
|
+
let report;
|
|
2010
|
+
try {
|
|
2011
|
+
report = await detectArchiveReconciliationIssues();
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
const unavailable = planningStorageUnavailableReason(error);
|
|
2014
|
+
return {
|
|
2015
|
+
name: "Archive Reconciliation",
|
|
2016
|
+
category: "build",
|
|
2017
|
+
status: "warn",
|
|
2018
|
+
message: unavailable ? `Planning storage unavailable: ${unavailable}` : "Could not scan for archive reconciliation issues",
|
|
2019
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
if (report.totalIssues === 0) {
|
|
2023
|
+
return {
|
|
2024
|
+
name: "Archive Reconciliation",
|
|
2025
|
+
category: "build",
|
|
2026
|
+
status: "pass",
|
|
2027
|
+
message: "No archive/restore reconciliation issues"
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
return {
|
|
2031
|
+
name: "Archive Reconciliation",
|
|
2032
|
+
category: "build",
|
|
2033
|
+
status: "warn",
|
|
2034
|
+
message: `${report.totalIssues} archive reconciliation issue(s) detected`,
|
|
2035
|
+
details: formatArchiveReconciliationReport(report)
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
async function checkProviderConfig(cwd) {
|
|
2039
|
+
const providersConfigPath = resolveProvidersConfigPath({ cwd });
|
|
2040
|
+
try {
|
|
2041
|
+
ProviderRegistry.load(void 0, cwd);
|
|
2042
|
+
const ignoredUserModelEntries = ProviderRegistry.describeIgnoredUserModelEntries();
|
|
2043
|
+
if (ignoredUserModelEntries) {
|
|
2044
|
+
return {
|
|
2045
|
+
name: "Provider Config",
|
|
2046
|
+
category: "build",
|
|
2047
|
+
status: "warn",
|
|
2048
|
+
message: "Provider catalog loaded; invalid user model entries were ignored",
|
|
2049
|
+
details: ignoredUserModelEntries
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
const projectPolicyPresent = await fs3.stat(providersConfigPath).then(() => true).catch((error) => {
|
|
2053
|
+
if (error.code === "ENOENT") return false;
|
|
2054
|
+
throw error;
|
|
2055
|
+
});
|
|
2056
|
+
return {
|
|
2057
|
+
name: "Provider Config",
|
|
2058
|
+
category: "build",
|
|
2059
|
+
status: "pass",
|
|
2060
|
+
message: "Provider catalog loaded",
|
|
2061
|
+
details: projectPolicyPresent ? `Release catalog with project policy: ${providersConfigPath}` : `Release catalog: ${ProviderRegistry.getLoadedConfigPath()}`
|
|
2062
|
+
};
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
return {
|
|
2065
|
+
name: "Provider Config",
|
|
2066
|
+
category: "build",
|
|
2067
|
+
status: "warn",
|
|
2068
|
+
message: "Cannot load provider catalog",
|
|
2069
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
async function checkExecutionRoles() {
|
|
2074
|
+
try {
|
|
2075
|
+
const outcome = await runExecutionRolesPreflight(process.cwd());
|
|
2076
|
+
if (!outcome.result || !outcome.roles) {
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2079
|
+
const { errors, warnings } = outcome.result;
|
|
2080
|
+
const details = formatPreflightDetails(outcome.result);
|
|
2081
|
+
const reviewer = outcome.roles.reviewer;
|
|
2082
|
+
if (errors.length > 0) {
|
|
2083
|
+
return {
|
|
2084
|
+
name: "Execution Roles",
|
|
2085
|
+
category: "build",
|
|
2086
|
+
status: "fail",
|
|
2087
|
+
message: `Reviewer not ready for gate "${reviewer.gate}"`,
|
|
2088
|
+
details
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
if (warnings.length > 0) {
|
|
2092
|
+
const deferredAuthOnly = warnings.every(
|
|
2093
|
+
(warning) => warning.code === "reviewer_auth_unknown"
|
|
2094
|
+
);
|
|
2095
|
+
return {
|
|
2096
|
+
name: "Execution Roles",
|
|
2097
|
+
category: "build",
|
|
2098
|
+
status: deferredAuthOnly ? "info" : "warn",
|
|
2099
|
+
message: deferredAuthOnly ? `Reviewer installed; auth checked on first run (gate "${reviewer.gate}")` : `Reviewer degraded (gate "${reviewer.gate}")`,
|
|
2100
|
+
details
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
return {
|
|
2104
|
+
name: "Execution Roles",
|
|
2105
|
+
category: "build",
|
|
2106
|
+
status: "pass",
|
|
2107
|
+
message: `Reviewer ready (gate "${reviewer.gate}")`
|
|
2108
|
+
};
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
return {
|
|
2111
|
+
name: "Execution Roles",
|
|
2112
|
+
category: "build",
|
|
2113
|
+
status: "fail",
|
|
2114
|
+
message: "Invalid execution-role configuration",
|
|
2115
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
async function checkSteeringInstructions() {
|
|
2120
|
+
const { getShadowedLegacyInstructionsPath, getUserInstructionsPath, loadUserInstructionsFile } = await import("./user-agents-QLIGPLP2.js");
|
|
2121
|
+
const instructionsPath = getUserInstructionsPath();
|
|
2122
|
+
const loaded = loadUserInstructionsFile();
|
|
2123
|
+
const shadowedLegacy = getShadowedLegacyInstructionsPath();
|
|
2124
|
+
const shadowedNote = shadowedLegacy ? ` ${shadowedLegacy} is ignored because instructions.md exists beside it; merge and delete it.` : "";
|
|
2125
|
+
if (!loaded) {
|
|
2126
|
+
return {
|
|
2127
|
+
name: "Global Instructions",
|
|
2128
|
+
category: "build",
|
|
2129
|
+
status: "info",
|
|
2130
|
+
message: "No Build instructions file; using built-in steering defaults",
|
|
2131
|
+
details: `Optional: customize provider runs with "build config instructions edit" (${instructionsPath}). Steering-capable provider runs inherit it.`
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
if (!loaded.source.editedByUser) {
|
|
2135
|
+
return {
|
|
2136
|
+
name: "Global Instructions",
|
|
2137
|
+
category: "build",
|
|
2138
|
+
status: "info",
|
|
2139
|
+
message: "Build instructions file present (unedited default)",
|
|
2140
|
+
details: `Tip: tailor it with "build config instructions edit" (${instructionsPath}). Steering-capable provider runs inherit it.${shadowedNote}`
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
if (shadowedLegacy) {
|
|
2144
|
+
return {
|
|
2145
|
+
name: "Global Instructions",
|
|
2146
|
+
category: "build",
|
|
2147
|
+
status: "info",
|
|
2148
|
+
message: "Build instructions file present (customized); a legacy AGENTS.md is ignored",
|
|
2149
|
+
details: `${instructionsPath}.${shadowedNote}`
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
return {
|
|
2153
|
+
name: "Global Instructions",
|
|
2154
|
+
category: "build",
|
|
2155
|
+
status: "pass",
|
|
2156
|
+
message: "Build instructions file present (customized)",
|
|
2157
|
+
details: instructionsPath
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
async function checkLinuxWriteBoundary() {
|
|
2161
|
+
try {
|
|
2162
|
+
const { resolveLinuxHostClass, formatLinuxHostClassNote } = await import("./linux-host-class-YDWQX65O.js");
|
|
2163
|
+
const resolution = resolveLinuxHostClass();
|
|
2164
|
+
if (resolution.hostClass === "desktop-unacked") {
|
|
2165
|
+
return {
|
|
2166
|
+
name: "Linux write boundary",
|
|
2167
|
+
category: "system",
|
|
2168
|
+
status: "warn",
|
|
2169
|
+
message: "Linux desktop write consent is not recorded; on a VM, container or cloud host run `build sandbox trust linux --host isolated`, on a desktop `--ack-desktop`",
|
|
2170
|
+
details: "This is not a sandbox-blocked provider. Write-capable runs need `build sandbox trust linux --ack-desktop` or, for a VM/container/cloud instance, `build sandbox trust linux --host isolated`."
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
if (resolution.hostClass === "not-applicable") {
|
|
2174
|
+
return {
|
|
2175
|
+
name: "Linux write boundary",
|
|
2176
|
+
category: "system",
|
|
2177
|
+
status: "pass",
|
|
2178
|
+
message: "Host class not applicable on this platform",
|
|
2179
|
+
details: `source=${resolution.source}`
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
const note = resolution.note ?? formatLinuxHostClassNote(resolution);
|
|
2183
|
+
return {
|
|
2184
|
+
name: "Linux write boundary",
|
|
2185
|
+
category: "system",
|
|
2186
|
+
status: "pass",
|
|
2187
|
+
message: `Host class ${resolution.hostClass} (source=${resolution.source}); writes allowed`,
|
|
2188
|
+
details: note ?? "Provider CLIs can write anywhere $USER can. This is not Seatbelt-sandboxed."
|
|
2189
|
+
};
|
|
2190
|
+
} catch (error) {
|
|
2191
|
+
return {
|
|
2192
|
+
name: "Linux write boundary",
|
|
2193
|
+
category: "system",
|
|
2194
|
+
status: "info",
|
|
2195
|
+
message: "Linux host-class posture could not be resolved",
|
|
2196
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
async function checkVerificationPrivilegeBoundary() {
|
|
2201
|
+
let verificationLabel = "unavailable";
|
|
2202
|
+
try {
|
|
2203
|
+
const { PROJECT_VERIFICATION_SANDBOX_ID, resolveSandboxLabel } = await import("./safety-stanza-HE6GXP4G.js");
|
|
2204
|
+
verificationLabel = resolveSandboxLabel(
|
|
2205
|
+
PROJECT_VERIFICATION_SANDBOX_ID,
|
|
2206
|
+
PROJECT_VERIFICATION_SANDBOX_ID
|
|
2207
|
+
);
|
|
2208
|
+
} catch {
|
|
2209
|
+
verificationLabel = "unavailable";
|
|
2210
|
+
}
|
|
2211
|
+
return {
|
|
2212
|
+
name: "Verification Privileges",
|
|
2213
|
+
category: "system",
|
|
2214
|
+
status: "info",
|
|
2215
|
+
message: "Project verification wraps when a boundary is available; otherwise it is an ordinary child",
|
|
2216
|
+
details: `Test, build, lint, and typecheck commands from the target repository wrap when a generated macOS seatbelt or accepted container boundary is available. On unsupported platforms or when the sandbox is disabled, they run as ordinary child processes. Live verification posture: ${verificationLabel}. The command allowlist constrains the command string, not what the invoked script does. Treat running Build against an unfamiliar repository as running that repository's code. See docs/reference/SECURITY.md (Project Verification Executes Target-Repository Code).`
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
async function checkClaudeStrictMcpConfig() {
|
|
2220
|
+
let command;
|
|
2221
|
+
let prefixArgs;
|
|
2222
|
+
try {
|
|
2223
|
+
const resolved = resolveProviderCliCommand("claude");
|
|
2224
|
+
command = resolved.command;
|
|
2225
|
+
prefixArgs = resolved.prefixArgs ?? [];
|
|
2226
|
+
} catch {
|
|
2227
|
+
return {
|
|
2228
|
+
name: "Claude strict-mcp-config",
|
|
2229
|
+
category: "providers",
|
|
2230
|
+
status: "info",
|
|
2231
|
+
message: "Claude CLI not configured; skipping strict-mcp-config check"
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
const probe = await probeProviderCli(command, ["--version"], prefixArgs, {
|
|
2235
|
+
providerId: "claude"
|
|
2236
|
+
});
|
|
2237
|
+
if (!probe.present) {
|
|
2238
|
+
return {
|
|
2239
|
+
name: "Claude strict-mcp-config",
|
|
2240
|
+
category: "providers",
|
|
2241
|
+
status: "info",
|
|
2242
|
+
message: "Claude CLI not installed; skipping strict-mcp-config check"
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
if (!hasStrictMcpConfigBeenProbed()) {
|
|
2246
|
+
const { wrapWithSandbox } = await import("./sandbox-ASWCDYZL.js");
|
|
2247
|
+
const { metadata } = await import("./metadata-UKSMWM6J.js");
|
|
2248
|
+
const sandboxed = wrapWithSandbox({
|
|
2249
|
+
workingDir: process.cwd(),
|
|
2250
|
+
command,
|
|
2251
|
+
args: [...prefixArgs, "--help"],
|
|
2252
|
+
providerName: metadata.name,
|
|
2253
|
+
env: process.env
|
|
2254
|
+
});
|
|
2255
|
+
await probeStrictMcpConfig(
|
|
2256
|
+
sandboxed?.command ?? command,
|
|
2257
|
+
sandboxed?.args ?? [...prefixArgs, "--help"]
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2260
|
+
if (!hasStrictMcpConfigBeenProbed()) {
|
|
2261
|
+
return {
|
|
2262
|
+
name: "Claude strict-mcp-config",
|
|
2263
|
+
category: "providers",
|
|
2264
|
+
status: "info",
|
|
2265
|
+
message: "Claude CLI --strict-mcp-config support not determined; probe did not complete",
|
|
2266
|
+
details: "The claude --help probe timed out or failed to start; re-run doctor to retry."
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
const supported = isStrictMcpConfigSupported();
|
|
2270
|
+
if (supported) {
|
|
2271
|
+
return {
|
|
2272
|
+
name: "Claude strict-mcp-config",
|
|
2273
|
+
category: "providers",
|
|
2274
|
+
status: "pass",
|
|
2275
|
+
message: "Claude CLI supports --strict-mcp-config; judge MCP isolation active"
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
return {
|
|
2279
|
+
name: "Claude strict-mcp-config",
|
|
2280
|
+
category: "providers",
|
|
2281
|
+
status: "warn",
|
|
2282
|
+
message: "Claude CLI does not support --strict-mcp-config; judge MCP isolation unavailable",
|
|
2283
|
+
details: "Judge runs may inherit project MCP servers, reducing determinism. Upgrade the Claude CLI to enable strict MCP isolation."
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
async function checkLegacyClaudePlugin() {
|
|
2287
|
+
const { checkClaudeCode } = await import("./setup-claude-code-NF4ZSHVE.js");
|
|
2288
|
+
const { resolveBuildRoot } = await import("./build-root-S7DINODY.js");
|
|
2289
|
+
const result = await checkClaudeCode(resolveBuildRoot(), { scope: "user" });
|
|
2290
|
+
const cleanupCommand = "build provider disconnect claude";
|
|
2291
|
+
if (!result.hasLegacyResidue && result.pluginState.hasClaudeCli && !result.pluginState.listAvailable) {
|
|
2292
|
+
return {
|
|
2293
|
+
name: "Legacy Claude plugin",
|
|
2294
|
+
category: "providers",
|
|
2295
|
+
status: "info",
|
|
2296
|
+
message: "legacy plugin residue could not be checked",
|
|
2297
|
+
details: `Claude plugin enumeration was unavailable. Optional cleanup: ${cleanupCommand}. Does not affect native Claude CLI provider readiness.`
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
if (!result.hasLegacyResidue) {
|
|
2301
|
+
return {
|
|
2302
|
+
name: "Legacy Claude plugin",
|
|
2303
|
+
category: "providers",
|
|
2304
|
+
status: "pass",
|
|
2305
|
+
message: "no legacy Build Claude Code plugin residue"
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
return {
|
|
2309
|
+
name: "Legacy Claude plugin",
|
|
2310
|
+
category: "providers",
|
|
2311
|
+
status: "info",
|
|
2312
|
+
message: `legacy residue present (plugin ids: ${result.pluginState.installedIds.join(", ")})`,
|
|
2313
|
+
details: `Optional cleanup: ${cleanupCommand}. Does not affect native Claude CLI provider readiness.`
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
async function checkLegacyRepoLocalTelemetry(buildDir) {
|
|
2317
|
+
const { hasLegacyRepoLocalTelemetryPaths } = await import("./data-migration-state-OAUL2WPQ.js");
|
|
2318
|
+
const legacy = hasLegacyRepoLocalTelemetryPaths(buildDir);
|
|
2319
|
+
const present = [legacy.db, legacy.invocations, legacy.synthesis].filter(
|
|
2320
|
+
(entry) => entry !== null
|
|
2321
|
+
);
|
|
2322
|
+
if (present.length === 0) {
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
const parts = [];
|
|
2326
|
+
for (const legacyDb of [legacy.db, legacy.invocations]) {
|
|
2327
|
+
if (!legacyDb) continue;
|
|
2328
|
+
parts.push(
|
|
2329
|
+
`${legacyDb} is leftover checkout SQLite; the live databases are under the data root. Run "build data migrate execute" to merge those databases. Do not delete the checkout copy until migration has finished.`
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
if (legacy.synthesis) {
|
|
2333
|
+
parts.push(
|
|
2334
|
+
`${legacy.synthesis} is leftover checkout synthesis; live artifacts write under the resolved telemetry directory (printed by \`build doctor --verbose\`). \`build data migrate execute\` does not move synthesis records. Do not delete this path.`
|
|
2335
|
+
);
|
|
2336
|
+
}
|
|
2337
|
+
return {
|
|
2338
|
+
name: "Legacy repo-local telemetry",
|
|
2339
|
+
category: "build",
|
|
2340
|
+
status: "info",
|
|
2341
|
+
message: "Legacy repo-local telemetry present",
|
|
2342
|
+
details: parts.join(" ")
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
async function checkDatabases(cwd) {
|
|
2346
|
+
const buildDir = path3.join(cwd, ".build-project");
|
|
2347
|
+
const dbDir = TelemetryPathResolver.getInstance().resolveDbDir(buildDir);
|
|
2348
|
+
const variantsDbPath = path3.join(dbDir, "variants.db");
|
|
2349
|
+
const validationsDbPath = path3.join(dbDir, "validations.db");
|
|
2350
|
+
const checks = [];
|
|
2351
|
+
const legacyCheck = await checkLegacyRepoLocalTelemetry(buildDir);
|
|
2352
|
+
if (legacyCheck) {
|
|
2353
|
+
checks.push(legacyCheck);
|
|
2354
|
+
}
|
|
2355
|
+
let variantsDbExists = false;
|
|
2356
|
+
try {
|
|
2357
|
+
await fs3.stat(variantsDbPath);
|
|
2358
|
+
variantsDbExists = true;
|
|
2359
|
+
} catch {
|
|
2360
|
+
}
|
|
2361
|
+
if (!variantsDbExists) {
|
|
2362
|
+
checks.push({
|
|
2363
|
+
name: "variants.db",
|
|
2364
|
+
category: "build",
|
|
2365
|
+
status: "info",
|
|
2366
|
+
message: "Not initialized yet",
|
|
2367
|
+
details: 'Telemetry database is created automatically on first execution. Run "build init --db" to pre-create, or "build doctor --fix" to initialize now.',
|
|
2368
|
+
fixable: true
|
|
2369
|
+
});
|
|
2370
|
+
} else {
|
|
2371
|
+
try {
|
|
2372
|
+
const SqliteDB = await getBetterSqlite3();
|
|
2373
|
+
const db = new SqliteDB(variantsDbPath, { readonly: true });
|
|
2374
|
+
try {
|
|
2375
|
+
const result = db.prepare("SELECT 1 as test").get();
|
|
2376
|
+
if (result?.test !== 1) {
|
|
2377
|
+
checks.push({
|
|
2378
|
+
name: "variants.db",
|
|
2379
|
+
category: "build",
|
|
2380
|
+
status: "fail",
|
|
2381
|
+
message: "Database query failed",
|
|
2382
|
+
details: variantsDbPath,
|
|
2383
|
+
fixable: true
|
|
2384
|
+
});
|
|
2385
|
+
db.close();
|
|
2386
|
+
return checks;
|
|
2387
|
+
}
|
|
2388
|
+
const integrityResult = db.prepare("PRAGMA quick_check").get();
|
|
2389
|
+
if (integrityResult?.quick_check !== "ok") {
|
|
2390
|
+
checks.push({
|
|
2391
|
+
name: "variants.db",
|
|
2392
|
+
category: "build",
|
|
2393
|
+
status: "fail",
|
|
2394
|
+
message: "Database integrity check failed",
|
|
2395
|
+
details: `${variantsDbPath} - Run "build doctor --fix"; if corruption remains, follow the database quarantine procedure in docs/TROUBLESHOOTING.md`,
|
|
2396
|
+
fixable: true
|
|
2397
|
+
});
|
|
2398
|
+
db.close();
|
|
2399
|
+
return checks;
|
|
2400
|
+
}
|
|
2401
|
+
const schemaVersion = checkSchemaVersion(db);
|
|
2402
|
+
if (schemaVersion !== SCHEMA_VERSION) {
|
|
2403
|
+
checks.push({
|
|
2404
|
+
name: "variants.db",
|
|
2405
|
+
category: "build",
|
|
2406
|
+
status: "warn",
|
|
2407
|
+
message: `Schema version mismatch (expected ${SCHEMA_VERSION}, found ${schemaVersion})`,
|
|
2408
|
+
details: variantsDbPath,
|
|
2409
|
+
fixable: true
|
|
2410
|
+
});
|
|
2411
|
+
} else {
|
|
2412
|
+
checks.push({
|
|
2413
|
+
name: "variants.db",
|
|
2414
|
+
category: "build",
|
|
2415
|
+
status: "pass",
|
|
2416
|
+
message: `Healthy (schema v${schemaVersion})`,
|
|
2417
|
+
details: variantsDbPath
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
db.close();
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
db.close();
|
|
2423
|
+
checks.push({
|
|
2424
|
+
name: "variants.db",
|
|
2425
|
+
category: "build",
|
|
2426
|
+
status: "fail",
|
|
2427
|
+
message: "Database error",
|
|
2428
|
+
details: error instanceof Error ? error.message : String(error),
|
|
2429
|
+
fixable: true
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
} catch (error) {
|
|
2433
|
+
const likelyNativeMismatch = isLikelyBetterSqlite3NativeLoadMismatch(error);
|
|
2434
|
+
checks.push({
|
|
2435
|
+
name: "variants.db",
|
|
2436
|
+
category: "build",
|
|
2437
|
+
status: "fail",
|
|
2438
|
+
message: likelyNativeMismatch ? "better-sqlite3 native module load mismatch" : "Database could not be opened",
|
|
2439
|
+
details: formatDatabaseNativeErrorDetails(error),
|
|
2440
|
+
fixable: true
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
let validationsDbExists = false;
|
|
2445
|
+
try {
|
|
2446
|
+
await fs3.stat(validationsDbPath);
|
|
2447
|
+
validationsDbExists = true;
|
|
2448
|
+
} catch {
|
|
2449
|
+
}
|
|
2450
|
+
if (!validationsDbExists) {
|
|
2451
|
+
checks.push({
|
|
2452
|
+
name: "validations.db",
|
|
2453
|
+
category: "build",
|
|
2454
|
+
status: "info",
|
|
2455
|
+
message: "Not present (optional)"
|
|
2456
|
+
});
|
|
2457
|
+
} else {
|
|
2458
|
+
try {
|
|
2459
|
+
const SqliteDB = await getBetterSqlite3();
|
|
2460
|
+
const db = new SqliteDB(validationsDbPath, { readonly: true });
|
|
2461
|
+
try {
|
|
2462
|
+
const result = db.prepare("SELECT 1 as test").get();
|
|
2463
|
+
if (result?.test === 1) {
|
|
2464
|
+
checks.push({
|
|
2465
|
+
name: "validations.db",
|
|
2466
|
+
category: "build",
|
|
2467
|
+
status: "pass",
|
|
2468
|
+
message: "Accessible",
|
|
2469
|
+
details: validationsDbPath
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
db.close();
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
db.close();
|
|
2475
|
+
checks.push({
|
|
2476
|
+
name: "validations.db",
|
|
2477
|
+
category: "build",
|
|
2478
|
+
status: "warn",
|
|
2479
|
+
message: "Database error",
|
|
2480
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
const likelyNativeMismatch = isLikelyBetterSqlite3NativeLoadMismatch(error);
|
|
2485
|
+
checks.push({
|
|
2486
|
+
name: "validations.db",
|
|
2487
|
+
category: "build",
|
|
2488
|
+
status: "warn",
|
|
2489
|
+
message: likelyNativeMismatch ? "better-sqlite3 native module load mismatch" : "Database could not be opened",
|
|
2490
|
+
details: formatDatabaseNativeErrorDetails(error)
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return checks;
|
|
2495
|
+
}
|
|
2496
|
+
async function checkStateMarker(cwd) {
|
|
2497
|
+
const stateFile = path3.join(cwd, ".build-project", "state", "init.json");
|
|
2498
|
+
try {
|
|
2499
|
+
const content = await fs3.readFile(stateFile, "utf-8");
|
|
2500
|
+
const state = JSON.parse(content);
|
|
2501
|
+
const issues = [];
|
|
2502
|
+
if (state.schemaVersion !== SCHEMA_VERSION) {
|
|
2503
|
+
issues.push(`Schema v${state.schemaVersion} (expected v${SCHEMA_VERSION})`);
|
|
2504
|
+
}
|
|
2505
|
+
if (state.tier !== "runtime" && state.tier !== "full") {
|
|
2506
|
+
issues.push(`Invalid tier: ${state.tier}`);
|
|
2507
|
+
}
|
|
2508
|
+
if (issues.length > 0) {
|
|
2509
|
+
return {
|
|
2510
|
+
name: "State Marker",
|
|
2511
|
+
category: "build",
|
|
2512
|
+
status: "warn",
|
|
2513
|
+
message: "State file has issues",
|
|
2514
|
+
details: issues.join(", "),
|
|
2515
|
+
fixable: true
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2518
|
+
return {
|
|
2519
|
+
name: "State Marker",
|
|
2520
|
+
category: "build",
|
|
2521
|
+
status: "pass",
|
|
2522
|
+
message: `Initialized (v${state.version}, tier: ${state.tier})`,
|
|
2523
|
+
details: state.lastCheckedAt ? `Last checked: ${new Date(state.lastCheckedAt).toLocaleString()}` : "Last checked: unknown"
|
|
2524
|
+
};
|
|
2525
|
+
} catch (error) {
|
|
2526
|
+
if (error.code === "ENOENT") {
|
|
2527
|
+
return {
|
|
2528
|
+
name: "State Marker",
|
|
2529
|
+
category: "build",
|
|
2530
|
+
status: "warn",
|
|
2531
|
+
message: "State file not found",
|
|
2532
|
+
details: 'Run "build doctor --fix" to initialize',
|
|
2533
|
+
fixable: true
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
return {
|
|
2537
|
+
name: "State Marker",
|
|
2538
|
+
category: "build",
|
|
2539
|
+
status: "warn",
|
|
2540
|
+
message: "Cannot read state file",
|
|
2541
|
+
details: error instanceof Error ? error.message : String(error),
|
|
2542
|
+
fixable: true
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
async function checkTelemetryMode(repoRoot) {
|
|
2547
|
+
const buildDir = getRuntimeEnv("BUILD_TELEMETRY_DIR") ?? path3.join(repoRoot, ".build-project");
|
|
2548
|
+
const dbDir = TelemetryPathResolver.getInstance().resolveDbDir(buildDir);
|
|
2549
|
+
const variantsDbPath = path3.join(dbDir, "variants.db");
|
|
2550
|
+
try {
|
|
2551
|
+
await fs3.stat(variantsDbPath);
|
|
2552
|
+
const SqliteDB = await getBetterSqlite3();
|
|
2553
|
+
const db = new SqliteDB(variantsDbPath, { readonly: true });
|
|
2554
|
+
try {
|
|
2555
|
+
db.prepare("SELECT 1").get();
|
|
2556
|
+
db.close();
|
|
2557
|
+
return {
|
|
2558
|
+
name: "Telemetry Mode",
|
|
2559
|
+
category: "build",
|
|
2560
|
+
status: "pass",
|
|
2561
|
+
message: "Database mode (full features)",
|
|
2562
|
+
details: variantsDbPath
|
|
2563
|
+
};
|
|
2564
|
+
} catch (_error) {
|
|
2565
|
+
db.close();
|
|
2566
|
+
return {
|
|
2567
|
+
name: "Telemetry Mode",
|
|
2568
|
+
category: "build",
|
|
2569
|
+
status: "warn",
|
|
2570
|
+
message: "Degraded mode (JSONL-only)",
|
|
2571
|
+
details: variantsDbPath
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
} catch (_error) {
|
|
2575
|
+
return {
|
|
2576
|
+
name: "Telemetry Mode",
|
|
2577
|
+
category: "build",
|
|
2578
|
+
status: "warn",
|
|
2579
|
+
message: "Degraded mode (JSONL-only)",
|
|
2580
|
+
details: variantsDbPath
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
async function resolveBuildPackageRoot() {
|
|
2585
|
+
let current = path3.dirname(fileURLToPath(import.meta.url));
|
|
2586
|
+
while (true) {
|
|
2587
|
+
try {
|
|
2588
|
+
const packageJsonPath = path3.join(current, "package.json");
|
|
2589
|
+
const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf8"));
|
|
2590
|
+
if (packageJson.name === "@ebrindley/build") {
|
|
2591
|
+
return current;
|
|
2592
|
+
}
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
const parent = path3.dirname(current);
|
|
2596
|
+
if (parent === current) {
|
|
2597
|
+
return process.cwd();
|
|
2598
|
+
}
|
|
2599
|
+
current = parent;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
async function checkDistArtifacts() {
|
|
2603
|
+
try {
|
|
2604
|
+
const { checkBuildFreshness } = await import("./flywheel-preflight-III2C7TH.js");
|
|
2605
|
+
const result = await checkBuildFreshness(await resolveBuildPackageRoot());
|
|
2606
|
+
if (result.passed) {
|
|
2607
|
+
return {
|
|
2608
|
+
name: "Build Artifacts",
|
|
2609
|
+
category: "build",
|
|
2610
|
+
status: "pass",
|
|
2611
|
+
message: result.metadata?.packageMode ? "packaged dist artifacts are present" : "dist/entry.js is present and fresh",
|
|
2612
|
+
details: result.warning
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
return {
|
|
2616
|
+
name: "Build Artifacts",
|
|
2617
|
+
category: "build",
|
|
2618
|
+
status: "warn",
|
|
2619
|
+
message: "Build artifacts need refresh",
|
|
2620
|
+
details: result.error ? `${result.error} This is expected after source changes in a development checkout. Most source tests do not require fresh dist output; run npm run build when testing the bundled build binary against the latest source.` : "Most source tests do not require fresh dist output; run npm run build when testing the bundled build binary against the latest source."
|
|
2621
|
+
};
|
|
2622
|
+
} catch (error) {
|
|
2623
|
+
return {
|
|
2624
|
+
name: "Build Artifacts",
|
|
2625
|
+
category: "build",
|
|
2626
|
+
status: "warn",
|
|
2627
|
+
message: "Could not verify dist artifacts",
|
|
2628
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
async function calculateDirectorySize(dirPath) {
|
|
2633
|
+
let totalSize = 0;
|
|
2634
|
+
const entries = await fs3.readdir(dirPath, { withFileTypes: true });
|
|
2635
|
+
for (const entry of entries) {
|
|
2636
|
+
const entryPath = path3.join(dirPath, entry.name);
|
|
2637
|
+
if (entry.isDirectory()) {
|
|
2638
|
+
totalSize += await calculateDirectorySize(entryPath);
|
|
2639
|
+
} else if (entry.isFile()) {
|
|
2640
|
+
const stat2 = await fs3.stat(entryPath);
|
|
2641
|
+
totalSize += stat2.size;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return totalSize;
|
|
2645
|
+
}
|
|
2646
|
+
var FOREIGN_COORDINATION_LOCK_REMEDIATION = "Local process liveness cannot be verified for foreign locks. Verify the owning machine is not running Build against this repo, then delete the lock file:";
|
|
2647
|
+
var UNREADABLE_COORDINATION_LOCK_REMEDIATION = "Build cannot safely verify unreadable coordination locks. Verify no Build process owns the lock, then delete the lock file:";
|
|
2648
|
+
function formatCoordinationLockAge(ageMs) {
|
|
2649
|
+
const ageSec = Math.round(ageMs / 1e3);
|
|
2650
|
+
if (ageSec < 60) {
|
|
2651
|
+
return `${ageSec}s`;
|
|
2652
|
+
}
|
|
2653
|
+
const ageMin = Math.round(ageSec / 60);
|
|
2654
|
+
if (ageMin < 60) {
|
|
2655
|
+
return `${ageMin}m`;
|
|
2656
|
+
}
|
|
2657
|
+
const ageHours = Math.round(ageMin / 60);
|
|
2658
|
+
return `${ageHours}h`;
|
|
2659
|
+
}
|
|
2660
|
+
function formatCoordinationLockLine(diagnostic) {
|
|
2661
|
+
const machinePart = diagnostic.machineIdShort ?? "unknown";
|
|
2662
|
+
const pidPart = diagnostic.pid ?? "n/a";
|
|
2663
|
+
const agePart = formatCoordinationLockAge(diagnostic.ageMs);
|
|
2664
|
+
return `${diagnostic.name}: ${diagnostic.classification} (pid=${pidPart}, machine=${machinePart}, age=${agePart})`;
|
|
2665
|
+
}
|
|
2666
|
+
function isForeignStaleCoordinationLock(diagnostic, staleMs) {
|
|
2667
|
+
if (diagnostic.classification !== "foreign-machine") {
|
|
2668
|
+
return false;
|
|
2669
|
+
}
|
|
2670
|
+
return diagnostic.ageMs >= staleMs;
|
|
2671
|
+
}
|
|
2672
|
+
async function checkCoordinationLocks() {
|
|
2673
|
+
const { DEFAULT_STALE_LOCK_MS, diagnoseCoordinationLocks } = await import("./backend-YUTFDZXZ.js");
|
|
2674
|
+
const cwd = process.cwd();
|
|
2675
|
+
const locksDir = path3.join(cwd, ".build-project", "planning", "coordination", "locks");
|
|
2676
|
+
const diagnostics = diagnoseCoordinationLocks(locksDir);
|
|
2677
|
+
if (diagnostics.length === 0) {
|
|
2678
|
+
return null;
|
|
2679
|
+
}
|
|
2680
|
+
const foreignStaleLocks = diagnostics.filter(
|
|
2681
|
+
(diagnostic) => isForeignStaleCoordinationLock(diagnostic, DEFAULT_STALE_LOCK_MS)
|
|
2682
|
+
);
|
|
2683
|
+
const unreadableLocks = diagnostics.filter((d) => d.classification === "unreadable");
|
|
2684
|
+
const liveCount = diagnostics.filter((d) => d.classification === "live").length;
|
|
2685
|
+
const staleSameMachineCount = diagnostics.filter(
|
|
2686
|
+
(d) => d.classification === "stale-same-machine"
|
|
2687
|
+
).length;
|
|
2688
|
+
const foreignCount = diagnostics.filter((d) => d.classification === "foreign-machine").length;
|
|
2689
|
+
const detailLines = diagnostics.map(formatCoordinationLockLine);
|
|
2690
|
+
const remediationLines = foreignStaleLocks.map(
|
|
2691
|
+
(diagnostic) => `${FOREIGN_COORDINATION_LOCK_REMEDIATION} ${diagnostic.lockPath}`
|
|
2692
|
+
);
|
|
2693
|
+
remediationLines.push(
|
|
2694
|
+
...unreadableLocks.map(
|
|
2695
|
+
(diagnostic) => `${UNREADABLE_COORDINATION_LOCK_REMEDIATION} ${diagnostic.lockPath}`
|
|
2696
|
+
)
|
|
2697
|
+
);
|
|
2698
|
+
const manualRemediationCount = foreignStaleLocks.length + unreadableLocks.length;
|
|
2699
|
+
let status = "pass";
|
|
2700
|
+
if (foreignStaleLocks.length > 0 || unreadableLocks.length > 0) {
|
|
2701
|
+
status = "warn";
|
|
2702
|
+
} else if (staleSameMachineCount > 0) {
|
|
2703
|
+
status = "info";
|
|
2704
|
+
}
|
|
2705
|
+
const messageParts = [`${diagnostics.length} coordination lock(s)`];
|
|
2706
|
+
if (liveCount > 0) {
|
|
2707
|
+
messageParts.push(`${liveCount} live`);
|
|
2708
|
+
}
|
|
2709
|
+
if (staleSameMachineCount > 0) {
|
|
2710
|
+
messageParts.push(`${staleSameMachineCount} stale-same-machine`);
|
|
2711
|
+
}
|
|
2712
|
+
if (foreignCount > 0) {
|
|
2713
|
+
messageParts.push(`${foreignCount} foreign-machine`);
|
|
2714
|
+
}
|
|
2715
|
+
if (unreadableLocks.length > 0) {
|
|
2716
|
+
messageParts.push(`${unreadableLocks.length} unreadable`);
|
|
2717
|
+
}
|
|
2718
|
+
if (manualRemediationCount > 0) {
|
|
2719
|
+
messageParts.push(`${manualRemediationCount} require manual remediation`);
|
|
2720
|
+
}
|
|
2721
|
+
const detailsParts = [...detailLines];
|
|
2722
|
+
if (remediationLines.length > 0) {
|
|
2723
|
+
detailsParts.push("", ...remediationLines);
|
|
2724
|
+
}
|
|
2725
|
+
return {
|
|
2726
|
+
name: "Coordination Locks",
|
|
2727
|
+
category: "build",
|
|
2728
|
+
status,
|
|
2729
|
+
message: messageParts.join("; "),
|
|
2730
|
+
details: detailsParts.join("\n ")
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
async function checkDiskSpace(repoRoot) {
|
|
2734
|
+
const buildDir = getRuntimeEnv("BUILD_TELEMETRY_DIR") ?? path3.join(repoRoot, ".build-project");
|
|
2735
|
+
const telemetryDir = TelemetryPathResolver.getInstance().resolveTelemetryDir(buildDir);
|
|
2736
|
+
try {
|
|
2737
|
+
try {
|
|
2738
|
+
await fs3.stat(telemetryDir);
|
|
2739
|
+
} catch (_error) {
|
|
2740
|
+
return {
|
|
2741
|
+
name: "Telemetry Storage",
|
|
2742
|
+
category: "resources",
|
|
2743
|
+
status: "info",
|
|
2744
|
+
message: "Telemetry directory not found",
|
|
2745
|
+
details: 'Run "build init" to initialize'
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
const totalSize = await calculateDirectorySize(telemetryDir);
|
|
2749
|
+
const sizeMB = totalSize / (1024 * 1024);
|
|
2750
|
+
const sizeGB = sizeMB / 1024;
|
|
2751
|
+
if (sizeGB > 2) {
|
|
2752
|
+
return {
|
|
2753
|
+
name: "Telemetry Storage",
|
|
2754
|
+
category: "resources",
|
|
2755
|
+
status: "fail",
|
|
2756
|
+
message: `Telemetry directory very large: ${sizeGB.toFixed(2)} GB`,
|
|
2757
|
+
details: 'Consider running "build cleanup" to remove old data'
|
|
2758
|
+
};
|
|
2759
|
+
}
|
|
2760
|
+
if (sizeMB > 500) {
|
|
2761
|
+
return {
|
|
2762
|
+
name: "Telemetry Storage",
|
|
2763
|
+
category: "resources",
|
|
2764
|
+
status: "warn",
|
|
2765
|
+
message: `Telemetry directory: ${sizeMB.toFixed(1)} MB`,
|
|
2766
|
+
details: "Consider periodic cleanup"
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
return {
|
|
2770
|
+
name: "Telemetry Storage",
|
|
2771
|
+
category: "resources",
|
|
2772
|
+
status: "pass",
|
|
2773
|
+
message: `Telemetry directory: ${sizeMB.toFixed(1)} MB`,
|
|
2774
|
+
details: telemetryDir
|
|
2775
|
+
};
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
return {
|
|
2778
|
+
name: "Telemetry Storage",
|
|
2779
|
+
category: "resources",
|
|
2780
|
+
status: "fail",
|
|
2781
|
+
message: "Cannot measure telemetry directory size",
|
|
2782
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
async function checkMemory() {
|
|
2787
|
+
try {
|
|
2788
|
+
const totalGB = os.totalmem() / (1024 * 1024 * 1024);
|
|
2789
|
+
const freeGB = getAvailableMemoryGB();
|
|
2790
|
+
const usedPercent = Math.max(0, (totalGB - freeGB) / totalGB * 100);
|
|
2791
|
+
if (freeGB < 0.5) {
|
|
2792
|
+
return {
|
|
2793
|
+
name: "Memory",
|
|
2794
|
+
category: "resources",
|
|
2795
|
+
status: "fail",
|
|
2796
|
+
message: `Critical: only ${freeGB.toFixed(2)} GB available`,
|
|
2797
|
+
details: `${usedPercent.toFixed(1)}% used of ${totalGB.toFixed(1)} GB total`
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
if (freeGB < 1) {
|
|
2801
|
+
return {
|
|
2802
|
+
name: "Memory",
|
|
2803
|
+
category: "resources",
|
|
2804
|
+
status: "warn",
|
|
2805
|
+
message: `Low memory: ${freeGB.toFixed(2)} GB available`,
|
|
2806
|
+
details: `${usedPercent.toFixed(1)}% used of ${totalGB.toFixed(1)} GB total`
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
return {
|
|
2810
|
+
name: "Memory",
|
|
2811
|
+
category: "resources",
|
|
2812
|
+
status: "pass",
|
|
2813
|
+
message: `${freeGB.toFixed(2)} GB available of ${totalGB.toFixed(1)} GB`,
|
|
2814
|
+
details: `${usedPercent.toFixed(1)}% used`
|
|
2815
|
+
};
|
|
2816
|
+
} catch (error) {
|
|
2817
|
+
return {
|
|
2818
|
+
name: "Memory",
|
|
2819
|
+
category: "resources",
|
|
2820
|
+
status: "fail",
|
|
2821
|
+
message: "Cannot check system memory",
|
|
2822
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
async function checkNodeVersion() {
|
|
2827
|
+
const currentVersion = process.version;
|
|
2828
|
+
const preferredPath = await preferredNode24Path();
|
|
2829
|
+
if (!isSupportedNodeMajor(parseNodeMajor(currentVersion))) {
|
|
2830
|
+
return {
|
|
2831
|
+
name: "Node.js",
|
|
2832
|
+
category: "system",
|
|
2833
|
+
status: "fail",
|
|
2834
|
+
message: `Node.js ${currentVersion} is not supported`,
|
|
2835
|
+
details: formatNode24Remediation(preferredPath)
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
const homebrewDetail = preferredPath ? ` | Homebrew Node 24: ${preferredPath}` : "";
|
|
2839
|
+
return {
|
|
2840
|
+
name: "Node.js",
|
|
2841
|
+
category: "system",
|
|
2842
|
+
status: "pass",
|
|
2843
|
+
message: `Node.js ${currentVersion}`,
|
|
2844
|
+
details: `${process.platform}/${process.arch} \u2014 ${process.execPath}${homebrewDetail}`
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
async function checkGitRepository() {
|
|
2848
|
+
try {
|
|
2849
|
+
const result = await execa("git", ["rev-parse", "--is-inside-work-tree"]);
|
|
2850
|
+
if (result.stdout.trim() === "true") {
|
|
2851
|
+
let branchName = "unknown";
|
|
2852
|
+
let unbornBranch;
|
|
2853
|
+
const branch = await execa("git", ["rev-parse", "--symbolic-full-name", "HEAD"], {
|
|
2854
|
+
reject: false
|
|
2855
|
+
});
|
|
2856
|
+
const parsedBranch = branch.exitCode === 0 ? parseCurrentBranchRef(branch.stdout) : "HEAD";
|
|
2857
|
+
if (parsedBranch !== "HEAD") {
|
|
2858
|
+
branchName = parsedBranch;
|
|
2859
|
+
} else {
|
|
2860
|
+
unbornBranch = await detectUnbornBranch(process.cwd());
|
|
2861
|
+
if (unbornBranch) {
|
|
2862
|
+
branchName = unbornBranch;
|
|
2863
|
+
} else {
|
|
2864
|
+
const symbolicBranch = await execa("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
2865
|
+
reject: false
|
|
2866
|
+
});
|
|
2867
|
+
if (symbolicBranch.exitCode === 0 && symbolicBranch.stdout.trim()) {
|
|
2868
|
+
branchName = symbolicBranch.stdout.trim();
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
const unbornNote = unbornBranch ? "; repository has no commits yet" : "";
|
|
2873
|
+
let details;
|
|
2874
|
+
try {
|
|
2875
|
+
const remoteResult = await execa("git", ["remote", "get-url", "origin"], { reject: false });
|
|
2876
|
+
const remoteUrl = remoteResult.stdout.trim();
|
|
2877
|
+
details = remoteUrl ? `branch: ${branchName}, remote: ${remoteUrl}` : `branch: ${branchName}, no remote`;
|
|
2878
|
+
} catch (_remoteError) {
|
|
2879
|
+
details = `branch: ${branchName}, no remote`;
|
|
2880
|
+
}
|
|
2881
|
+
try {
|
|
2882
|
+
const missing = [];
|
|
2883
|
+
for (const key of ["user.name", "user.email"]) {
|
|
2884
|
+
const identity = await execGit(["config", "--get", key], { cwd: process.cwd() });
|
|
2885
|
+
if (identity.exitCode !== 0 || !identity.stdout.trim()) missing.push(key);
|
|
2886
|
+
}
|
|
2887
|
+
if (missing.length > 0) {
|
|
2888
|
+
return {
|
|
2889
|
+
name: "Git Repository",
|
|
2890
|
+
category: "git",
|
|
2891
|
+
status: "warn",
|
|
2892
|
+
message: `Inside git repository (branch: ${branchName})${unbornNote}; commit identity is incomplete`,
|
|
2893
|
+
details: `${details}. Set ${missing.map((key) => `git config ${key} "${key === "user.name" ? "Your Name" : "you@example.com"}"`).join(" and ")} (or use --global for your usual identity).`
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
} catch {
|
|
2897
|
+
return {
|
|
2898
|
+
name: "Git Repository",
|
|
2899
|
+
category: "git",
|
|
2900
|
+
status: "warn",
|
|
2901
|
+
message: `Inside git repository (branch: ${branchName})${unbornNote}; commit identity could not be checked`,
|
|
2902
|
+
details
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
if (unbornBranch) {
|
|
2906
|
+
return {
|
|
2907
|
+
name: "Git Repository",
|
|
2908
|
+
category: "git",
|
|
2909
|
+
status: "warn",
|
|
2910
|
+
message: `Inside git repository (branch: ${branchName})${unbornNote}`,
|
|
2911
|
+
details: `${details}. HEAD does not resolve to a commit yet; the first commit will create ${branchName}.`
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
return {
|
|
2915
|
+
name: "Git Repository",
|
|
2916
|
+
category: "git",
|
|
2917
|
+
status: "pass",
|
|
2918
|
+
message: `Inside git repository (branch: ${branchName})`,
|
|
2919
|
+
details
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
return {
|
|
2923
|
+
name: "Git Repository",
|
|
2924
|
+
category: "git",
|
|
2925
|
+
status: "warn",
|
|
2926
|
+
message: "Not inside a git repository",
|
|
2927
|
+
details: "Some features may not work correctly"
|
|
2928
|
+
};
|
|
2929
|
+
} catch (_error) {
|
|
2930
|
+
return {
|
|
2931
|
+
name: "Git Repository",
|
|
2932
|
+
category: "git",
|
|
2933
|
+
status: "warn",
|
|
2934
|
+
message: "Not inside a git repository",
|
|
2935
|
+
details: "Some features may not work correctly"
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
var DOCTOR_LINK_CHECK_TIMEOUT_MS = 5e3;
|
|
2940
|
+
async function checkKnowledgeKit(cwd, options = {}) {
|
|
2941
|
+
const checkLinks = options.checkLinks === true;
|
|
2942
|
+
try {
|
|
2943
|
+
const result = await knowledgeDoctorChecks(cwd, {
|
|
2944
|
+
checkLinks,
|
|
2945
|
+
linkTimeoutMs: DOCTOR_LINK_CHECK_TIMEOUT_MS,
|
|
2946
|
+
...options.probeLink ? { probeLink: options.probeLink } : {}
|
|
2947
|
+
});
|
|
2948
|
+
if (!result.manifestPresent) {
|
|
2949
|
+
const intent = readIntentContract(cwd);
|
|
2950
|
+
if (intent.status === "ok" && intent.contract.status !== "retired") {
|
|
2951
|
+
const resolved = resolveIntent(detectStack(cwd), intent.contract).contract;
|
|
2952
|
+
const lines = resolved.dimensions.map((d) => describeDimension(d));
|
|
2953
|
+
const hasConflict = resolved.status === "conflict";
|
|
2954
|
+
return {
|
|
2955
|
+
name: "Knowledge Kit",
|
|
2956
|
+
category: "knowledge",
|
|
2957
|
+
status: "warn",
|
|
2958
|
+
message: hasConflict ? `No kit accepted; stack intent "${intent.contract.spec}" has unresolved conflicts \u2014 run build init knowledge offer (headless) or build init in a terminal to receive the kit` : `No knowledge kit accepted yet (stack intent "${intent.contract.spec}" pending) \u2014 run build init knowledge offer (headless) or build init in a terminal to receive the kit`,
|
|
2959
|
+
details: `Declared ${intent.contract.declaredAt.slice(0, 10)}${intent.contract.rationale ? ` \u2014 ${intent.contract.rationale}` : ""}
|
|
2960
|
+
${lines.join("\n ")}
|
|
2961
|
+
Run 'build init knowledge offer' (headless) or 'build init' in a terminal to review recommendations${hasConflict ? " and resolve the conflict" : ""}.`
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
return {
|
|
2965
|
+
name: "Knowledge Kit",
|
|
2966
|
+
category: "knowledge",
|
|
2967
|
+
status: "info",
|
|
2968
|
+
message: isStackUndetectedAndUndeclared(cwd) ? `No knowledge kit accepted yet; no stack detected. If the intended stack is decided, declare it: ${DECLARE_STACK_COMMAND}` : "No knowledge kit accepted yet",
|
|
2969
|
+
details: "Run 'build init' in an interactive session to review stack recommendations."
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
if (result.findings.length === 0) {
|
|
2973
|
+
return {
|
|
2974
|
+
name: "Knowledge Kit",
|
|
2975
|
+
category: "knowledge",
|
|
2976
|
+
status: "pass",
|
|
2977
|
+
message: checkLinks ? "Knowledge manifest present; no issues found (links checked)" : "Knowledge manifest present; no issues found"
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
const severityToStatus = (severity) => severity === "error" ? "fail" : severity === "warn" ? "warn" : "info";
|
|
2981
|
+
const worst = result.findings.some((f) => f.severity === "error") ? "fail" : result.findings.some((f) => f.severity === "warn") ? "warn" : "info";
|
|
2982
|
+
const details = result.findings.map((finding) => `[${severityToStatus(finding.severity)}] ${finding.message}`).join("\n ");
|
|
2983
|
+
return {
|
|
2984
|
+
name: "Knowledge Kit",
|
|
2985
|
+
category: "knowledge",
|
|
2986
|
+
status: worst,
|
|
2987
|
+
message: `${result.findings.length} knowledge finding(s)`,
|
|
2988
|
+
details
|
|
2989
|
+
};
|
|
2990
|
+
} catch (error) {
|
|
2991
|
+
return {
|
|
2992
|
+
name: "Knowledge Kit",
|
|
2993
|
+
category: "knowledge",
|
|
2994
|
+
status: "warn",
|
|
2995
|
+
message: "Could not run knowledge checks",
|
|
2996
|
+
details: error instanceof Error ? error.message : String(error)
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
async function runAllHealthChecks(options) {
|
|
3001
|
+
const allChecks = [];
|
|
3002
|
+
if (options.providersOnly) {
|
|
3003
|
+
const includeVerboseProviderDetails2 = Boolean(options.verbose) && !options.json;
|
|
3004
|
+
allChecks.push(
|
|
3005
|
+
await checkProviderCLIs({
|
|
3006
|
+
includeVerboseDetails: includeVerboseProviderDetails2,
|
|
3007
|
+
all: options.all,
|
|
3008
|
+
provider: options.provider,
|
|
3009
|
+
probe: options.probe,
|
|
3010
|
+
strict: options.strict,
|
|
3011
|
+
requireReady: options.requireReady
|
|
3012
|
+
})
|
|
3013
|
+
);
|
|
3014
|
+
allChecks.push(...await collectProviderExtraHealthChecks(options));
|
|
3015
|
+
return allChecks;
|
|
3016
|
+
}
|
|
3017
|
+
allChecks.push(await checkNodeVersion());
|
|
3018
|
+
allChecks.push(await checkMemory());
|
|
3019
|
+
allChecks.push(await checkGit());
|
|
3020
|
+
allChecks.push(await checkGitRepository());
|
|
3021
|
+
const includeVerboseProviderDetails = Boolean(options.verbose) && !options.json;
|
|
3022
|
+
allChecks.push(
|
|
3023
|
+
await checkProviderCLIs({
|
|
3024
|
+
includeVerboseDetails: includeVerboseProviderDetails,
|
|
3025
|
+
all: options.all,
|
|
3026
|
+
provider: options.provider,
|
|
3027
|
+
probe: options.probe,
|
|
3028
|
+
strict: options.strict,
|
|
3029
|
+
requireReady: options.requireReady
|
|
3030
|
+
})
|
|
3031
|
+
);
|
|
3032
|
+
allChecks.push(...await collectProviderExtraHealthChecks(options));
|
|
3033
|
+
allChecks.push(await checkVerificationPrivilegeBoundary());
|
|
3034
|
+
allChecks.push(await checkLinuxWriteBoundary());
|
|
3035
|
+
const repoRoot = resolveRepoRootForBuildDir(process.cwd());
|
|
3036
|
+
allChecks.push(await checkBuildDirectory(repoRoot));
|
|
3037
|
+
const nestedCheck = await checkNestedBuildDirectory(repoRoot);
|
|
3038
|
+
if (nestedCheck) allChecks.push(nestedCheck);
|
|
3039
|
+
allChecks.push(await checkRequiredDirectories(repoRoot));
|
|
3040
|
+
allChecks.push(await checkRepoTaskTypes(repoRoot));
|
|
3041
|
+
allChecks.push(await checkArchiveReconciliation());
|
|
3042
|
+
allChecks.push(await checkProviderConfig(repoRoot));
|
|
3043
|
+
const executionRolesCheck = await checkExecutionRoles();
|
|
3044
|
+
if (executionRolesCheck) allChecks.push(executionRolesCheck);
|
|
3045
|
+
allChecks.push(await checkSteeringInstructions());
|
|
3046
|
+
allChecks.push((await import("./doctor-instructions-import-JXHDDOE5.js")).checkInstructionsImport());
|
|
3047
|
+
allChecks.push(await checkNativeSqlite());
|
|
3048
|
+
allChecks.push(...await checkDatabases(repoRoot));
|
|
3049
|
+
allChecks.push(await checkStateMarker(repoRoot));
|
|
3050
|
+
allChecks.push(await checkTelemetryMode(repoRoot));
|
|
3051
|
+
allChecks.push(await checkDistArtifacts());
|
|
3052
|
+
const coordinationLockCheck = await checkCoordinationLocks();
|
|
3053
|
+
if (coordinationLockCheck) allChecks.push(coordinationLockCheck);
|
|
3054
|
+
const knowledgeCwd = repoRoot;
|
|
3055
|
+
allChecks.push(await checkKnowledgeKit(knowledgeCwd, { checkLinks: options.checkLinks }));
|
|
3056
|
+
allChecks.push(checkPendingInitNotices(knowledgeCwd));
|
|
3057
|
+
allChecks.push(checkPendingOfferNotice(knowledgeCwd));
|
|
3058
|
+
const purposeCheck = (await import("./doctor-purpose-6YJOXPBE.js")).checkPurposeSuggestion(knowledgeCwd);
|
|
3059
|
+
if (purposeCheck) allChecks.push(purposeCheck);
|
|
3060
|
+
allChecks.push(...(await import("./doctor-context-blocks-LPY75VZA.js")).checkContextBlocks(knowledgeCwd));
|
|
3061
|
+
allChecks.push(await checkCiIntegrationHealth(knowledgeCwd));
|
|
3062
|
+
allChecks.push(...await checkGuardrails(knowledgeCwd));
|
|
3063
|
+
const storageCheck = checkPlanningStorageBackend(repoRoot);
|
|
3064
|
+
if (storageCheck) allChecks.push(storageCheck);
|
|
3065
|
+
const branchNameCheck = await checkBuildBranchNameConflict(repoRoot);
|
|
3066
|
+
if (branchNameCheck) allChecks.push(branchNameCheck);
|
|
3067
|
+
allChecks.push(await checkDiskSpace(repoRoot));
|
|
3068
|
+
allChecks.push(await checkConcurrencyBudget());
|
|
3069
|
+
return allChecks;
|
|
3070
|
+
}
|
|
3071
|
+
async function checkConcurrencyBudget() {
|
|
3072
|
+
try {
|
|
3073
|
+
const budget = detectHostConcurrencyBudget();
|
|
3074
|
+
const basis = `${budget.totalMemoryGB.toFixed(0)} GB RAM, ${budget.performanceCores} performance cores`;
|
|
3075
|
+
const recommendation = `up to ${budget.legSlots} concurrent legs, ${budget.validationSlots} concurrent validations (${basis})`;
|
|
3076
|
+
let effectiveValue;
|
|
3077
|
+
try {
|
|
3078
|
+
const merged = await new ConfigManager().loadConfig();
|
|
3079
|
+
const asValidConcurrency = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
3080
|
+
effectiveValue = asValidConcurrency(merged.config.variants?.maxConcurrent) ?? asValidConcurrency(merged.config.worktree?.maxConcurrent);
|
|
3081
|
+
} catch {
|
|
3082
|
+
}
|
|
3083
|
+
if (effectiveValue !== void 0 && effectiveValue > budget.legSlots) {
|
|
3084
|
+
return {
|
|
3085
|
+
name: "Concurrency budget",
|
|
3086
|
+
category: "resources",
|
|
3087
|
+
status: "warn",
|
|
3088
|
+
message: `Effective max concurrent (${effectiveValue}) exceeds this host's recommendation`,
|
|
3089
|
+
details: `This host recommends ${recommendation}. The configured value still runs as asked; validation pacing covers only repair-loop gates and baseline capture, and memory pressure shedding runs only in health-assisted mode and the flywheel.`
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
return {
|
|
3093
|
+
name: "Concurrency budget",
|
|
3094
|
+
category: "resources",
|
|
3095
|
+
status: "info",
|
|
3096
|
+
message: `Recommended: ${recommendation}`,
|
|
3097
|
+
details: `Effective max concurrent (config or default): ${effectiveValue ?? 4}. Recommendations are advisory; Build runs what you ask for.`
|
|
3098
|
+
};
|
|
3099
|
+
} catch (error) {
|
|
3100
|
+
return {
|
|
3101
|
+
name: "Concurrency budget",
|
|
3102
|
+
category: "resources",
|
|
3103
|
+
status: "info",
|
|
3104
|
+
message: "Could not compute a host concurrency recommendation",
|
|
3105
|
+
details: error instanceof Error ? error.message : String(error)
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
function displayHealthResults(checks, options) {
|
|
3110
|
+
if (options.json) {
|
|
3111
|
+
console.log(JSON.stringify(checks, null, 2));
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
console.log(chalk2.bold.cyan("\nBuild Health Check\n"));
|
|
3115
|
+
const categories = {
|
|
3116
|
+
system: [],
|
|
3117
|
+
git: [],
|
|
3118
|
+
providers: [],
|
|
3119
|
+
build: [],
|
|
3120
|
+
knowledge: [],
|
|
3121
|
+
guardrails: [],
|
|
3122
|
+
resources: []
|
|
3123
|
+
};
|
|
3124
|
+
for (const check of checks) {
|
|
3125
|
+
categories[check.category]?.push(check);
|
|
3126
|
+
}
|
|
3127
|
+
const categoryNames = {
|
|
3128
|
+
system: "System Environment",
|
|
3129
|
+
git: "Git Configuration",
|
|
3130
|
+
providers: "AI Providers",
|
|
3131
|
+
build: "Build Installation",
|
|
3132
|
+
knowledge: "Knowledge & Init Drift",
|
|
3133
|
+
guardrails: "Guardrails",
|
|
3134
|
+
resources: "System Resources"
|
|
3135
|
+
};
|
|
3136
|
+
for (const [category, categoryChecks] of Object.entries(categories)) {
|
|
3137
|
+
if (categoryChecks.length === 0) continue;
|
|
3138
|
+
console.log(chalk2.bold(categoryNames[category]));
|
|
3139
|
+
for (const check of categoryChecks) {
|
|
3140
|
+
let statusIcon = "";
|
|
3141
|
+
let statusColor;
|
|
3142
|
+
switch (check.status) {
|
|
3143
|
+
case "pass":
|
|
3144
|
+
statusIcon = "OK";
|
|
3145
|
+
statusColor = chalk2.green;
|
|
3146
|
+
break;
|
|
3147
|
+
case "warn":
|
|
3148
|
+
statusIcon = "Warning: ";
|
|
3149
|
+
statusColor = chalk2.yellow;
|
|
3150
|
+
break;
|
|
3151
|
+
case "fail":
|
|
3152
|
+
statusIcon = "Error";
|
|
3153
|
+
statusColor = chalk2.red;
|
|
3154
|
+
break;
|
|
3155
|
+
case "info":
|
|
3156
|
+
statusIcon = "Info:";
|
|
3157
|
+
statusColor = chalk2.blue;
|
|
3158
|
+
break;
|
|
3159
|
+
}
|
|
3160
|
+
const checkName = check.name.padEnd(25);
|
|
3161
|
+
console.log(` ${statusColor(statusIcon)} ${chalk2.bold(checkName)} ${check.message}`);
|
|
3162
|
+
if (options.verbose && check.details) {
|
|
3163
|
+
console.log(chalk2.gray(` ${check.details}`));
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
console.log();
|
|
3167
|
+
}
|
|
3168
|
+
const passCount = checks.filter((c) => c.status === "pass").length;
|
|
3169
|
+
const warnCount = checks.filter((c) => c.status === "warn").length;
|
|
3170
|
+
const failCount = checks.filter((c) => c.status === "fail").length;
|
|
3171
|
+
const infoCount = checks.filter((c) => c.status === "info").length;
|
|
3172
|
+
const fixableCount = checks.filter((c) => c.fixable && c.status !== "pass").length;
|
|
3173
|
+
console.log(chalk2.bold.cyan("Summary\n"));
|
|
3174
|
+
console.log(` ${chalk2.green("Passed")}: ${passCount}`);
|
|
3175
|
+
if (warnCount > 0) console.log(` ${chalk2.yellow("Warnings")}: ${warnCount}`);
|
|
3176
|
+
if (failCount > 0) console.log(` ${chalk2.red("Failed")}: ${failCount}`);
|
|
3177
|
+
if (infoCount > 0) console.log(` ${chalk2.blue("Info")}: ${infoCount}`);
|
|
3178
|
+
console.log();
|
|
3179
|
+
if (failCount > 0) {
|
|
3180
|
+
console.log(chalk2.bold.red("Error: Checks Failed\n"));
|
|
3181
|
+
console.log(chalk2.gray(" Some checks failed. Review the issues above."));
|
|
3182
|
+
if (fixableCount > 0) {
|
|
3183
|
+
console.log(chalk2.gray(` ${fixableCount} issue(s) can be fixed automatically.`));
|
|
3184
|
+
console.log(chalk2.gray(" Run: build doctor --fix"));
|
|
3185
|
+
}
|
|
3186
|
+
} else if (warnCount > 0) {
|
|
3187
|
+
console.log(chalk2.bold.yellow("Warning: Warnings Detected\n"));
|
|
3188
|
+
console.log(chalk2.gray(" System is operational but some issues were found."));
|
|
3189
|
+
if (fixableCount > 0) {
|
|
3190
|
+
console.log(chalk2.gray(` ${fixableCount} issue(s) can be fixed automatically.`));
|
|
3191
|
+
console.log(chalk2.gray(" Run: build doctor --fix"));
|
|
3192
|
+
}
|
|
3193
|
+
} else {
|
|
3194
|
+
console.log(chalk2.bold.green("OK: All Systems Healthy\n"));
|
|
3195
|
+
console.log(chalk2.gray(" Build is ready to use!"));
|
|
3196
|
+
}
|
|
3197
|
+
const unrepaired = options.fix ? describeUnrepairedChecks(checks) : void 0;
|
|
3198
|
+
if (unrepaired) console.log(chalk2.gray(` ${unrepaired}`));
|
|
3199
|
+
if (!options.verbose && (warnCount > 0 || failCount > 0)) {
|
|
3200
|
+
console.log(chalk2.gray("\nTip: Run with --verbose flag for more details"));
|
|
3201
|
+
}
|
|
3202
|
+
console.log();
|
|
3203
|
+
}
|
|
3204
|
+
function createDoctorCommand() {
|
|
3205
|
+
const health = new Command2("doctor");
|
|
3206
|
+
health.description(
|
|
3207
|
+
"Check system health and diagnose issues\n\nExamples:\n build doctor # Check configured providers only\n build doctor --all # Check all known providers\n build doctor --provider claude # Check specific provider\n build doctor --providers-only --provider claude # Check provider CLI and cached auth evidence\n build doctor --probe --provider claude # Probe provider auth and update cached evidence\n build doctor --verbose # Show detailed information\n build doctor --fix # Automatically repair issues\n build doctor --json # Output as JSON\n build doctor --matrix # Show provider support matrix"
|
|
3208
|
+
);
|
|
3209
|
+
health.aliases(["d"]);
|
|
3210
|
+
health.addCommand(createDoctorMetricsCommand());
|
|
3211
|
+
health.option("--json", "(OPTIONAL) Output results as JSON");
|
|
3212
|
+
health.option("-v, --verbose", "(OPTIONAL) Show detailed information");
|
|
3213
|
+
health.option("--fix", "(OPTIONAL) Attempt to repair issues automatically");
|
|
3214
|
+
health.option("-a, --all", "(OPTIONAL) Check all known providers");
|
|
3215
|
+
health.option("-p, --provider <id>", "(OPTIONAL) Check specific provider only");
|
|
3216
|
+
health.option("--providers-only", "(OPTIONAL) Check provider CLI and cached auth evidence only");
|
|
3217
|
+
health.option("--matrix", "(OPTIONAL) Show provider support matrix with tier and health status");
|
|
3218
|
+
health.option(
|
|
3219
|
+
"--strict",
|
|
3220
|
+
"(OPTIONAL) Exit non-zero on identity failures (CLI missing, config invalid). Unobserved auth and zero ready providers do not fail"
|
|
3221
|
+
);
|
|
3222
|
+
health.option(
|
|
3223
|
+
"--require-ready",
|
|
3224
|
+
"(OPTIONAL) Exit non-zero when no checked provider can run (opt-in readiness gate)"
|
|
3225
|
+
);
|
|
3226
|
+
health.option("--probe", "(OPTIONAL) Run provider auth probes and update auth liveness cache");
|
|
3227
|
+
health.option("--refresh", "(OPTIONAL) Clear cached auth liveness before checking");
|
|
3228
|
+
health.option(
|
|
3229
|
+
"--check-links",
|
|
3230
|
+
"(OPTIONAL) Probe knowledge-kit source URLs for dead links (network; 5s/link; default off)"
|
|
3231
|
+
);
|
|
3232
|
+
health.action(async (options) => {
|
|
3233
|
+
const validatedOptions = validateCommandOptions(HealthCommandOptionsSchema, options);
|
|
3234
|
+
if (validatedOptions.refresh) {
|
|
3235
|
+
await invalidateAuthOutcome(validatedOptions.provider);
|
|
3236
|
+
}
|
|
3237
|
+
if (validatedOptions.matrix) {
|
|
3238
|
+
const { collectProviderMatrix } = await import("./provider-matrix-2KO2V5OM.js");
|
|
3239
|
+
const { renderProviderMatrix } = await import("./provider-matrix-renderer-MEC3PER7.js");
|
|
3240
|
+
const rows = await collectProviderMatrix({
|
|
3241
|
+
probeAuth: true,
|
|
3242
|
+
providerFilter: validatedOptions.provider
|
|
3243
|
+
});
|
|
3244
|
+
if (validatedOptions.json) {
|
|
3245
|
+
const checks2 = await runAllHealthChecks(validatedOptions);
|
|
3246
|
+
console.log(JSON.stringify({ checks: checks2, providerMatrix: rows }, null, 2));
|
|
3247
|
+
exitForHealthFailures(checks2, validatedOptions);
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
console.log(renderProviderMatrix(rows));
|
|
3251
|
+
const skipNormalChecks = !validatedOptions.all && !validatedOptions.providersOnly && !validatedOptions.fix;
|
|
3252
|
+
if (skipNormalChecks) {
|
|
3253
|
+
if (rows.some((row) => row.status !== "available")) {
|
|
3254
|
+
process.exit(1);
|
|
3255
|
+
}
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
if (validatedOptions.fix) {
|
|
3260
|
+
const repairOutput = validatedOptions.json ? console.error : console.log;
|
|
3261
|
+
repairOutput(chalk2.bold.cyan("\nRepairing Build Installation\n"));
|
|
3262
|
+
const cwd = resolveRepoRootForBuildDir(process.cwd());
|
|
3263
|
+
const nestedDir = path3.join(cwd, ".build-project", ".build-project");
|
|
3264
|
+
try {
|
|
3265
|
+
const stats = await fs3.stat(nestedDir);
|
|
3266
|
+
if (stats.isDirectory()) {
|
|
3267
|
+
repairOutput(
|
|
3268
|
+
chalk2.gray("Removing nested .build-project/.build-project/ directory (bug artifact)...")
|
|
3269
|
+
);
|
|
3270
|
+
await fs3.rm(nestedDir, { recursive: true, force: true });
|
|
3271
|
+
repairOutput(chalk2.green(" OK Removed nested directory"));
|
|
3272
|
+
}
|
|
3273
|
+
} catch (_error) {
|
|
3274
|
+
}
|
|
3275
|
+
try {
|
|
3276
|
+
const result = await ensureBuildInitialized({
|
|
3277
|
+
cwd,
|
|
3278
|
+
tier: "runtime",
|
|
3279
|
+
repair: true,
|
|
3280
|
+
verbose: validatedOptions.verbose && !validatedOptions.json,
|
|
3281
|
+
allowDegraded: true
|
|
3282
|
+
});
|
|
3283
|
+
if (result.status === "ok") {
|
|
3284
|
+
repairOutput(chalk2.green("OK: Repair completed successfully"));
|
|
3285
|
+
} else if (result.status === "degraded") {
|
|
3286
|
+
repairOutput(chalk2.yellow("Warning: Repair completed with warnings (degraded mode)"));
|
|
3287
|
+
} else {
|
|
3288
|
+
repairOutput(chalk2.red("Error: Repair failed"));
|
|
3289
|
+
}
|
|
3290
|
+
if (result.actions.created.length > 0) {
|
|
3291
|
+
repairOutput(chalk2.gray(`
|
|
3292
|
+
Created ${result.actions.created.length} item(s):`));
|
|
3293
|
+
for (const item of result.actions.created) {
|
|
3294
|
+
repairOutput(chalk2.gray(` - ${item}`));
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
if (result.actions.repaired.length > 0) {
|
|
3298
|
+
repairOutput(chalk2.gray(`
|
|
3299
|
+
Repaired ${result.actions.repaired.length} item(s):`));
|
|
3300
|
+
for (const item of result.actions.repaired) {
|
|
3301
|
+
repairOutput(chalk2.gray(` - ${item}`));
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
if (result.warnings.length > 0) {
|
|
3305
|
+
repairOutput(chalk2.yellow(`
|
|
3306
|
+
Warnings (${result.warnings.length}):`));
|
|
3307
|
+
for (const warning of result.warnings) {
|
|
3308
|
+
repairOutput(chalk2.yellow(` Warning: ${warning}`));
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
if (result.errors.length > 0) {
|
|
3312
|
+
repairOutput(chalk2.red(`
|
|
3313
|
+
Errors (${result.errors.length}):`));
|
|
3314
|
+
for (const error of result.errors) {
|
|
3315
|
+
repairOutput(chalk2.red(` Error ${error.message}`));
|
|
3316
|
+
if (error.path) {
|
|
3317
|
+
repairOutput(chalk2.gray(` Path: ${error.path}`));
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
if (result.nextSteps.length > 0) {
|
|
3322
|
+
repairOutput(chalk2.bold("\nNext Steps:"));
|
|
3323
|
+
for (const step of result.nextSteps) {
|
|
3324
|
+
repairOutput(chalk2.gray(` \u2022 ${step}`));
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
repairOutput(chalk2.gray(`
|
|
3328
|
+
Completed in ${result.initDurationMs}ms`));
|
|
3329
|
+
repairOutput(chalk2.gray(`Telemetry mode: ${result.telemetryMode}`));
|
|
3330
|
+
if (result.schemaVersion) {
|
|
3331
|
+
repairOutput(chalk2.gray(`Schema version: ${result.schemaVersion}`));
|
|
3332
|
+
}
|
|
3333
|
+
repairOutput();
|
|
3334
|
+
repairOutput(chalk2.bold.cyan("Re-running Health Checks\n"));
|
|
3335
|
+
} catch (error) {
|
|
3336
|
+
console.error(
|
|
3337
|
+
chalk2.red("Error: Repair failed:"),
|
|
3338
|
+
error instanceof Error ? error.message : String(error)
|
|
3339
|
+
);
|
|
3340
|
+
process.exit(1);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
const checks = await runAllHealthChecks(validatedOptions);
|
|
3344
|
+
displayHealthResults(checks, validatedOptions);
|
|
3345
|
+
renderDoctorPendingNudges(resolveRepoRootForBuildDir(process.cwd()), validatedOptions);
|
|
3346
|
+
exitForHealthFailures(checks, validatedOptions);
|
|
3347
|
+
});
|
|
3348
|
+
return health;
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
export {
|
|
3352
|
+
checkPendingInitNotices,
|
|
3353
|
+
checkPendingOfferNotice,
|
|
3354
|
+
renderDoctorPendingNudges,
|
|
3355
|
+
isStrictBlockingWarning,
|
|
3356
|
+
PROVIDER_CLI_SPEC_IDS,
|
|
3357
|
+
checkProviderCLIs,
|
|
3358
|
+
checkLinuxWriteBoundary,
|
|
3359
|
+
checkCoordinationLocks,
|
|
3360
|
+
checkGitRepository,
|
|
3361
|
+
checkKnowledgeKit,
|
|
3362
|
+
runAllHealthChecks,
|
|
3363
|
+
createDoctorCommand
|
|
3364
|
+
};
|
|
3365
|
+
//# sourceMappingURL=chunk-MQ4TFJAG.js.map
|