@poetic-ai/poetic 1.42.1-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (781) hide show
  1. package/.github/SECURITY.md +47 -0
  2. package/.nvmrc +1 -0
  3. package/.poetic/README.md +37 -0
  4. package/.poetic/providers/catalog.json +12051 -0
  5. package/.poetic/providers/pricing.json +1272 -0
  6. package/.poetic/providers/registry.json +3369 -0
  7. package/CHANGELOG.md +552 -0
  8. package/CODE_OF_CONDUCT.md +40 -0
  9. package/CONTRIBUTING.md +23 -0
  10. package/INSTALL.md +454 -0
  11. package/LICENSE +21 -0
  12. package/README.md +474 -0
  13. package/dist/BasicOptimizationCompetitionRunner-5H5TBYO4.js +153 -0
  14. package/dist/ExecutionTracker-UVONC4C6.js +16 -0
  15. package/dist/OptimizationConfig-7DFY2TST.js +17 -0
  16. package/dist/OptimizationEngine-MXTSOSST.js +1597 -0
  17. package/dist/PromptStore-U6ZD3ZMQ.js +18 -0
  18. package/dist/actions-M3AESHFF.js +322 -0
  19. package/dist/agent-ingest-HO77TH26.js +53 -0
  20. package/dist/aggregator-DNCBINFO.js +12 -0
  21. package/dist/ai-judge-LDWHURKK.js +156 -0
  22. package/dist/allowlist-grounding-RO6HKUFI.js +16 -0
  23. package/dist/allowlist-utils-23V7TJ3G.js +28 -0
  24. package/dist/anchored-turn-service-GFYU6GCQ.js +489 -0
  25. package/dist/api-transport-FS3CQRMN.js +1087 -0
  26. package/dist/apply-completion-mode-JBT2CTQO.js +89 -0
  27. package/dist/artifact-migrator-23L45ISD.js +267 -0
  28. package/dist/ask-UYVZY7JH.js +135 -0
  29. package/dist/auth-CSQP5RHJ.js +67 -0
  30. package/dist/auth-E6XUNZ5M.js +68 -0
  31. package/dist/auth-KQLJPZ2E.js +134 -0
  32. package/dist/auth-OC4E6633.js +71 -0
  33. package/dist/auth-R3NQ2ZF5.js +78 -0
  34. package/dist/auth-S4CTX4GQ.js +44 -0
  35. package/dist/auth-YLH3S7EY.js +69 -0
  36. package/dist/auth-liveness-TDDEZY3T.js +46 -0
  37. package/dist/auto-optimizer-HA3T23SF.js +80 -0
  38. package/dist/autoloop-cleanup-JOGKTLWA.js +109 -0
  39. package/dist/autoloop-evidence-gates-ACB5YW5G.js +36 -0
  40. package/dist/autoloop-gate-policy-XZA5EQFN.js +175 -0
  41. package/dist/autoloop-helpers-EFJLBQUP.js +22 -0
  42. package/dist/autoloop-ledger-W7QO62PX.js +96 -0
  43. package/dist/autoloop-ledger-subscriber-Z5RW5EIE.js +162 -0
  44. package/dist/autoloop-run-defaults-FERGRZGP.js +19 -0
  45. package/dist/autoloop-run-lock-CT2NWSV3.js +197 -0
  46. package/dist/autoloop-success-OPR74YEX.js +85 -0
  47. package/dist/autoloop-termination-summary-RN75F422.js +58 -0
  48. package/dist/autoloop-trajectory-K5I45UBR.js +282 -0
  49. package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
  50. package/dist/autonomous-loop-controller-4QJMOIIN.js +4120 -0
  51. package/dist/backend-P3DAYRP7.js +29 -0
  52. package/dist/background-executor-CBN552V4.js +257 -0
  53. package/dist/backlog-execution-intent-U4XYPTV6.js +15 -0
  54. package/dist/backup-active-store-5WXG6SXT.js +17 -0
  55. package/dist/backup-telemetry-db-XGLXAA3N.js +254 -0
  56. package/dist/basic-competition-master-WXZHUT42.js +344 -0
  57. package/dist/branch-archive-manager-RP6FP3ZC.js +254 -0
  58. package/dist/branch-cleanup-manager-RSHAI7YR.js +20 -0
  59. package/dist/build-gate-YFASRB5Z.js +71 -0
  60. package/dist/change-summary-WY7J5TOX.js +33 -0
  61. package/dist/chunk-25QL3M3L.js +899 -0
  62. package/dist/chunk-2ABC6SEC.js +2626 -0
  63. package/dist/chunk-2AICM4P3.js +1763 -0
  64. package/dist/chunk-2AQUWJPW.js +225 -0
  65. package/dist/chunk-2DY5KNVM.js +276 -0
  66. package/dist/chunk-2QJ3H3L7.js +42 -0
  67. package/dist/chunk-2UH6VQRZ.js +328 -0
  68. package/dist/chunk-2VSQMRCB.js +18 -0
  69. package/dist/chunk-2XQXYBM2.js +130 -0
  70. package/dist/chunk-2YFUIER7.js +426 -0
  71. package/dist/chunk-2YXZCZTX.js +1160 -0
  72. package/dist/chunk-32VLDFRT.js +797 -0
  73. package/dist/chunk-34CNP2HB.js +148 -0
  74. package/dist/chunk-35P3W3JX.js +456 -0
  75. package/dist/chunk-36PEBZAF.js +18 -0
  76. package/dist/chunk-3733QTI7.js +6818 -0
  77. package/dist/chunk-3A5PDH5L.js +12513 -0
  78. package/dist/chunk-3AOKYKA7.js +22 -0
  79. package/dist/chunk-3BCUNHKF.js +475 -0
  80. package/dist/chunk-3C26DPFK.js +566 -0
  81. package/dist/chunk-3FLXLLB7.js +26 -0
  82. package/dist/chunk-3FOZ2VHV.js +100 -0
  83. package/dist/chunk-3G6FZCRF.js +106 -0
  84. package/dist/chunk-3JWNRJSB.js +736 -0
  85. package/dist/chunk-3LUMR3D3.js +156 -0
  86. package/dist/chunk-3M57DADF.js +44 -0
  87. package/dist/chunk-3S62LLWJ.js +543 -0
  88. package/dist/chunk-3VABAKT2.js +1784 -0
  89. package/dist/chunk-44N5WZZH.js +45 -0
  90. package/dist/chunk-45FPSA3B.js +125 -0
  91. package/dist/chunk-4BIPZBT7.js +791 -0
  92. package/dist/chunk-4BJ3O4RQ.js +929 -0
  93. package/dist/chunk-4BTDUT2S.js +42 -0
  94. package/dist/chunk-4CSS7WVI.js +143 -0
  95. package/dist/chunk-4CUY7AFY.js +247 -0
  96. package/dist/chunk-4HN6DF7V.js +1027 -0
  97. package/dist/chunk-4HXKHDNH.js +1064 -0
  98. package/dist/chunk-4KKI477Q.js +44 -0
  99. package/dist/chunk-4NA7FSTV.js +595 -0
  100. package/dist/chunk-4NXPXE62.js +84 -0
  101. package/dist/chunk-4SALLK63.js +9831 -0
  102. package/dist/chunk-4SHKPCGK.js +110 -0
  103. package/dist/chunk-4VJITV4P.js +445 -0
  104. package/dist/chunk-4W2NRXQL.js +1205 -0
  105. package/dist/chunk-4XICK3HN.js +784 -0
  106. package/dist/chunk-55EJVV3C.js +385 -0
  107. package/dist/chunk-5DLKYTQX.js +4003 -0
  108. package/dist/chunk-5EERLVZB.js +139 -0
  109. package/dist/chunk-5HDP7XZG.js +91 -0
  110. package/dist/chunk-5LAMRH4X.js +969 -0
  111. package/dist/chunk-5REDMNLZ.js +310 -0
  112. package/dist/chunk-5UCJARII.js +1984 -0
  113. package/dist/chunk-5WCQRDRB.js +588 -0
  114. package/dist/chunk-5WRLK5KW.js +700 -0
  115. package/dist/chunk-66AGFTBQ.js +131 -0
  116. package/dist/chunk-66CIO2SV.js +48 -0
  117. package/dist/chunk-6BM72ZMG.js +31 -0
  118. package/dist/chunk-6BTFXYRO.js +276 -0
  119. package/dist/chunk-6EDAJH2I.js +68 -0
  120. package/dist/chunk-6EQKFDBH.js +457 -0
  121. package/dist/chunk-6KMJKHIQ.js +2193 -0
  122. package/dist/chunk-6MFRHTRI.js +115 -0
  123. package/dist/chunk-6QMUKUHH.js +632 -0
  124. package/dist/chunk-6TZJRKNW.js +321 -0
  125. package/dist/chunk-6V7HGQFZ.js +20 -0
  126. package/dist/chunk-6Y5TWI7H.js +146 -0
  127. package/dist/chunk-6Y5U7UFY.js +91 -0
  128. package/dist/chunk-6ZLAK3XG.js +14 -0
  129. package/dist/chunk-72XCRQED.js +34 -0
  130. package/dist/chunk-72YTZMAM.js +589 -0
  131. package/dist/chunk-73NCTWKL.js +188 -0
  132. package/dist/chunk-77I6G4CO.js +1 -0
  133. package/dist/chunk-7D7QT3BE.js +688 -0
  134. package/dist/chunk-7DNSNKJV.js +78 -0
  135. package/dist/chunk-7FMJVYBS.js +307 -0
  136. package/dist/chunk-7GDRQBQC.js +419 -0
  137. package/dist/chunk-7IL4A2PP.js +948 -0
  138. package/dist/chunk-7NPSXSHO.js +975 -0
  139. package/dist/chunk-7PS6INZP.js +302 -0
  140. package/dist/chunk-7QCZSLBH.js +1 -0
  141. package/dist/chunk-7R2WLDOS.js +62 -0
  142. package/dist/chunk-7XV3QXLT.js +878 -0
  143. package/dist/chunk-A2WKSVX7.js +43 -0
  144. package/dist/chunk-A3YGID55.js +133 -0
  145. package/dist/chunk-A5MRWHMI.js +1604 -0
  146. package/dist/chunk-A722DEVA.js +14 -0
  147. package/dist/chunk-AG2Z2SUZ.js +554 -0
  148. package/dist/chunk-AHWAZ2MU.js +137 -0
  149. package/dist/chunk-AIAQ3HS3.js +119 -0
  150. package/dist/chunk-ALAB3PBR.js +2206 -0
  151. package/dist/chunk-APPNGGN7.js +365 -0
  152. package/dist/chunk-APV6MK5E.js +137 -0
  153. package/dist/chunk-AQNPGRWS.js +395 -0
  154. package/dist/chunk-ASEP3M2W.js +968 -0
  155. package/dist/chunk-AT4TNPWW.js +2931 -0
  156. package/dist/chunk-ATVKCSGT.js +250 -0
  157. package/dist/chunk-AX6GWE7V.js +47 -0
  158. package/dist/chunk-AZQILSNQ.js +1376 -0
  159. package/dist/chunk-B76KHLX6.js +13 -0
  160. package/dist/chunk-BAV2HJXS.js +194 -0
  161. package/dist/chunk-BBHRN366.js +89 -0
  162. package/dist/chunk-BCZAENOH.js +448 -0
  163. package/dist/chunk-BIGSACCY.js +19527 -0
  164. package/dist/chunk-BPYMCIVD.js +9243 -0
  165. package/dist/chunk-BSE4R6XD.js +322 -0
  166. package/dist/chunk-BVPWRDCO.js +2321 -0
  167. package/dist/chunk-BXI4NXL3.js +507 -0
  168. package/dist/chunk-C3L6YQ7P.js +147 -0
  169. package/dist/chunk-C57KJOUQ.js +121 -0
  170. package/dist/chunk-CCV7BPSY.js +118 -0
  171. package/dist/chunk-CD7ISXA4.js +826 -0
  172. package/dist/chunk-CE5OZBYY.js +21 -0
  173. package/dist/chunk-CFBIG37O.js +154 -0
  174. package/dist/chunk-CHQV73H5.js +408 -0
  175. package/dist/chunk-CJC6GZ46.js +457 -0
  176. package/dist/chunk-CJHRFSNY.js +924 -0
  177. package/dist/chunk-CKP2Q3TP.js +1905 -0
  178. package/dist/chunk-CLKSETWE.js +10 -0
  179. package/dist/chunk-CQM3A35X.js +844 -0
  180. package/dist/chunk-CSLAS5GW.js +4007 -0
  181. package/dist/chunk-CTLUBNCW.js +622 -0
  182. package/dist/chunk-CVMVZWG4.js +3544 -0
  183. package/dist/chunk-CYB6QDCT.js +263 -0
  184. package/dist/chunk-D5EP5D2W.js +26 -0
  185. package/dist/chunk-DC4ZYMXJ.js +115 -0
  186. package/dist/chunk-DCAVAABI.js +348 -0
  187. package/dist/chunk-DCYF7EKG.js +26 -0
  188. package/dist/chunk-DF5SLDE4.js +966 -0
  189. package/dist/chunk-DN7UMLF7.js +5678 -0
  190. package/dist/chunk-DSFM56OA.js +1203 -0
  191. package/dist/chunk-DTY4APYV.js +218 -0
  192. package/dist/chunk-E3NTPHEO.js +194 -0
  193. package/dist/chunk-E74LUIID.js +302 -0
  194. package/dist/chunk-EJ42BNGY.js +2904 -0
  195. package/dist/chunk-EJ4ZAWLV.js +305 -0
  196. package/dist/chunk-EJGCZGUF.js +1483 -0
  197. package/dist/chunk-EK2U3YEG.js +486 -0
  198. package/dist/chunk-ELMFYNV7.js +109 -0
  199. package/dist/chunk-ENRGKNR5.js +25 -0
  200. package/dist/chunk-EQGIDAUF.js +62 -0
  201. package/dist/chunk-EX3OM35G.js +20414 -0
  202. package/dist/chunk-F5JO7HAS.js +94 -0
  203. package/dist/chunk-F7ER6ANO.js +174 -0
  204. package/dist/chunk-FATUVIIN.js +374 -0
  205. package/dist/chunk-FC23CTXV.js +322 -0
  206. package/dist/chunk-FCNAS3W2.js +1033 -0
  207. package/dist/chunk-FD4ERYEG.js +22 -0
  208. package/dist/chunk-FDVZLG6Z.js +3104 -0
  209. package/dist/chunk-FEPIFY7D.js +511 -0
  210. package/dist/chunk-FEQ4HXL7.js +412 -0
  211. package/dist/chunk-FFH5HHJU.js +5351 -0
  212. package/dist/chunk-FH7WOJAE.js +79 -0
  213. package/dist/chunk-FKAUPHMR.js +96 -0
  214. package/dist/chunk-FRG57FPM.js +240 -0
  215. package/dist/chunk-FUVP64ER.js +31 -0
  216. package/dist/chunk-G2PTLLEL.js +284 -0
  217. package/dist/chunk-G35UYQ6Z.js +69 -0
  218. package/dist/chunk-G36W2T2S.js +1143 -0
  219. package/dist/chunk-G4THLFV3.js +148 -0
  220. package/dist/chunk-GAFJEYLK.js +29 -0
  221. package/dist/chunk-GCCAPO3S.js +118 -0
  222. package/dist/chunk-GG6KJYW6.js +85 -0
  223. package/dist/chunk-GHHFDU2U.js +55 -0
  224. package/dist/chunk-GHTZSQB7.js +57 -0
  225. package/dist/chunk-GJTWJNLV.js +1374 -0
  226. package/dist/chunk-GKN3HMN4.js +2809 -0
  227. package/dist/chunk-GNH3QGTL.js +1342 -0
  228. package/dist/chunk-GOAQ2I7Y.js +7000 -0
  229. package/dist/chunk-GSGD5E4C.js +30 -0
  230. package/dist/chunk-GXWAJQOJ.js +5011 -0
  231. package/dist/chunk-H2AYQIHW.js +224 -0
  232. package/dist/chunk-H6G7QRMQ.js +35 -0
  233. package/dist/chunk-H7JAGHZT.js +59 -0
  234. package/dist/chunk-HAC6EHZZ.js +79 -0
  235. package/dist/chunk-HE2KH6EQ.js +161 -0
  236. package/dist/chunk-HIHENQDX.js +66 -0
  237. package/dist/chunk-HIO33P3G.js +19 -0
  238. package/dist/chunk-HK4AQZSR.js +438 -0
  239. package/dist/chunk-HMVQIPH3.js +547 -0
  240. package/dist/chunk-HOYBYAOA.js +257 -0
  241. package/dist/chunk-HQLGXODD.js +123 -0
  242. package/dist/chunk-HQXYYUNY.js +1231 -0
  243. package/dist/chunk-HY3ESWCA.js +708 -0
  244. package/dist/chunk-I46EG2XQ.js +65 -0
  245. package/dist/chunk-I4NPDSCB.js +279 -0
  246. package/dist/chunk-I5WRT7ZR.js +514 -0
  247. package/dist/chunk-I6BL3PDH.js +1024 -0
  248. package/dist/chunk-IATQ7NBI.js +679 -0
  249. package/dist/chunk-IC7I6YIJ.js +301 -0
  250. package/dist/chunk-IDCSOZVN.js +78 -0
  251. package/dist/chunk-IEXU4RR6.js +5742 -0
  252. package/dist/chunk-IFCBYECK.js +112 -0
  253. package/dist/chunk-IFZCPEY2.js +66 -0
  254. package/dist/chunk-IKFP2KUR.js +48 -0
  255. package/dist/chunk-IP7BYVUV.js +338 -0
  256. package/dist/chunk-IRJ2I6KH.js +166 -0
  257. package/dist/chunk-IUXNGKA6.js +152 -0
  258. package/dist/chunk-IZYIQQ7X.js +53 -0
  259. package/dist/chunk-IZZK3H6I.js +2175 -0
  260. package/dist/chunk-J3J4P7RW.js +353 -0
  261. package/dist/chunk-J62KPGII.js +464 -0
  262. package/dist/chunk-J6P3FWWW.js +130 -0
  263. package/dist/chunk-JD6SIZED.js +712 -0
  264. package/dist/chunk-JEV6UI7H.js +537 -0
  265. package/dist/chunk-JKBHJQMT.js +434 -0
  266. package/dist/chunk-JLS65NUQ.js +14 -0
  267. package/dist/chunk-JOSYEJKY.js +141 -0
  268. package/dist/chunk-JPKSXKNB.js +94 -0
  269. package/dist/chunk-JQD5VYKR.js +483 -0
  270. package/dist/chunk-JSOWEFEQ.js +351 -0
  271. package/dist/chunk-JSWMRQYG.js +28 -0
  272. package/dist/chunk-K4XK7SGB.js +6783 -0
  273. package/dist/chunk-K54Y7MLK.js +160 -0
  274. package/dist/chunk-KAT4RXUT.js +521 -0
  275. package/dist/chunk-KDMKKIRK.js +103 -0
  276. package/dist/chunk-KM3F7SYE.js +702 -0
  277. package/dist/chunk-KNDYMVWN.js +102 -0
  278. package/dist/chunk-KNT72VR6.js +132 -0
  279. package/dist/chunk-KQEBSK3T.js +274 -0
  280. package/dist/chunk-KT6VHSPF.js +84 -0
  281. package/dist/chunk-KTMW65UX.js +14 -0
  282. package/dist/chunk-L4427KOE.js +26 -0
  283. package/dist/chunk-L6OM4A22.js +189 -0
  284. package/dist/chunk-L7E6XCRA.js +1042 -0
  285. package/dist/chunk-L7JZLXQL.js +173 -0
  286. package/dist/chunk-LAMKBTXX.js +40 -0
  287. package/dist/chunk-LDCF2MKN.js +132 -0
  288. package/dist/chunk-LEIIWPWQ.js +78 -0
  289. package/dist/chunk-LFTQVYBB.js +31 -0
  290. package/dist/chunk-LGHII5ET.js +23 -0
  291. package/dist/chunk-LH34OGSB.js +265 -0
  292. package/dist/chunk-LO3X5NLS.js +326 -0
  293. package/dist/chunk-LPITVM7M.js +339 -0
  294. package/dist/chunk-LR5IJIGV.js +810 -0
  295. package/dist/chunk-LUVKFFWR.js +315 -0
  296. package/dist/chunk-LWCBNGH6.js +35 -0
  297. package/dist/chunk-LWTN3WRV.js +260 -0
  298. package/dist/chunk-LZBBXYWF.js +1004 -0
  299. package/dist/chunk-M34PS4NE.js +2213 -0
  300. package/dist/chunk-M3HQYKQX.js +308 -0
  301. package/dist/chunk-MCPQNJMK.js +482 -0
  302. package/dist/chunk-MF5HP6XV.js +221 -0
  303. package/dist/chunk-MFADRRTR.js +112 -0
  304. package/dist/chunk-MN6PL4AN.js +120 -0
  305. package/dist/chunk-MQ4WA34C.js +206 -0
  306. package/dist/chunk-MTVZITQ2.js +185 -0
  307. package/dist/chunk-MWHF5V7U.js +223 -0
  308. package/dist/chunk-MWHLBPNU.js +211 -0
  309. package/dist/chunk-MWK5UZQ3.js +1068 -0
  310. package/dist/chunk-MYLH2S4G.js +64 -0
  311. package/dist/chunk-MZ5WYKNA.js +28 -0
  312. package/dist/chunk-N5DX4JAK.js +12 -0
  313. package/dist/chunk-NEDCMN7E.js +415 -0
  314. package/dist/chunk-NEQUQHOX.js +223 -0
  315. package/dist/chunk-NGK6PEOG.js +25 -0
  316. package/dist/chunk-NM6PMYY4.js +768 -0
  317. package/dist/chunk-NOZGR2QO.js +2373 -0
  318. package/dist/chunk-NP2V3L7K.js +226 -0
  319. package/dist/chunk-NPZJMUZ4.js +194 -0
  320. package/dist/chunk-NRLY536M.js +260 -0
  321. package/dist/chunk-NS33V3IM.js +51 -0
  322. package/dist/chunk-NWOYOA6H.js +378 -0
  323. package/dist/chunk-NWRUSEXG.js +322 -0
  324. package/dist/chunk-O6V4PNLO.js +63 -0
  325. package/dist/chunk-OGMSYL6J.js +227 -0
  326. package/dist/chunk-OIS7J26S.js +319 -0
  327. package/dist/chunk-OKBTRSZI.js +205 -0
  328. package/dist/chunk-OMX5VL43.js +79 -0
  329. package/dist/chunk-ON3G73BU.js +264 -0
  330. package/dist/chunk-OPYFYTWQ.js +220 -0
  331. package/dist/chunk-OQ6K46CK.js +1734 -0
  332. package/dist/chunk-OSDYVKHW.js +2440 -0
  333. package/dist/chunk-OUM5S64R.js +3429 -0
  334. package/dist/chunk-OWB7JXUS.js +751 -0
  335. package/dist/chunk-OYZYO7TV.js +47 -0
  336. package/dist/chunk-P47VN5F4.js +282 -0
  337. package/dist/chunk-P6VQROCO.js +594 -0
  338. package/dist/chunk-P7CQGPLS.js +30 -0
  339. package/dist/chunk-PA6BCSOX.js +2872 -0
  340. package/dist/chunk-PGSPX4SU.js +15 -0
  341. package/dist/chunk-PIKLF7BM.js +432 -0
  342. package/dist/chunk-PJWJ3SAV.js +22 -0
  343. package/dist/chunk-PKIFMV72.js +100 -0
  344. package/dist/chunk-PTH5E5XO.js +195 -0
  345. package/dist/chunk-PYITM4N2.js +79 -0
  346. package/dist/chunk-PZ5AY32C.js +10 -0
  347. package/dist/chunk-Q3RO7N35.js +45 -0
  348. package/dist/chunk-Q3WIC6GQ.js +2670 -0
  349. package/dist/chunk-QGJDMEOV.js +34 -0
  350. package/dist/chunk-QJYIHVXT.js +100 -0
  351. package/dist/chunk-QNOB37UH.js +58 -0
  352. package/dist/chunk-QOCOVLUH.js +462 -0
  353. package/dist/chunk-QTCJ5CC5.js +172 -0
  354. package/dist/chunk-QU6JQJMH.js +3063 -0
  355. package/dist/chunk-QVFV5IP2.js +232 -0
  356. package/dist/chunk-QVZMFDYG.js +29 -0
  357. package/dist/chunk-R2ZYWNW3.js +1581 -0
  358. package/dist/chunk-R4UWBC35.js +200 -0
  359. package/dist/chunk-R6W23LKN.js +254 -0
  360. package/dist/chunk-RAK5HOIJ.js +459 -0
  361. package/dist/chunk-RDFRCT64.js +168 -0
  362. package/dist/chunk-RE5VZDFQ.js +58 -0
  363. package/dist/chunk-RGROYC2G.js +638 -0
  364. package/dist/chunk-RGWHUI5E.js +68 -0
  365. package/dist/chunk-RHVG6UNL.js +205 -0
  366. package/dist/chunk-RI4EX2QE.js +18 -0
  367. package/dist/chunk-RIKFAWZF.js +71 -0
  368. package/dist/chunk-RJAAKDRX.js +17 -0
  369. package/dist/chunk-RSXSD4MH.js +1016 -0
  370. package/dist/chunk-RW7RVSDV.js +79 -0
  371. package/dist/chunk-RWREC3KJ.js +517 -0
  372. package/dist/chunk-RZY7RKM5.js +500 -0
  373. package/dist/chunk-S2MNWFAG.js +52 -0
  374. package/dist/chunk-S2VQCZO4.js +22 -0
  375. package/dist/chunk-S54MKU6V.js +117 -0
  376. package/dist/chunk-S77XPALC.js +239 -0
  377. package/dist/chunk-SCW4ZF6R.js +150 -0
  378. package/dist/chunk-SKHGYQ7W.js +405 -0
  379. package/dist/chunk-SNLBO4BF.js +247 -0
  380. package/dist/chunk-SP3LFU6B.js +60 -0
  381. package/dist/chunk-SQZRPY23.js +5181 -0
  382. package/dist/chunk-SSB5UQHA.js +4128 -0
  383. package/dist/chunk-STJOHHSF.js +605 -0
  384. package/dist/chunk-STV6LYYE.js +45 -0
  385. package/dist/chunk-SUN4OFTW.js +740 -0
  386. package/dist/chunk-SURZ2WFE.js +326 -0
  387. package/dist/chunk-SUZ7UYZH.js +1972 -0
  388. package/dist/chunk-SZ7DL357.js +1047 -0
  389. package/dist/chunk-T2SZELJL.js +624 -0
  390. package/dist/chunk-T6VPI2XP.js +10338 -0
  391. package/dist/chunk-TAJPCXSB.js +13 -0
  392. package/dist/chunk-TAOOYK3P.js +358 -0
  393. package/dist/chunk-TDSEX5CF.js +24 -0
  394. package/dist/chunk-TFTGP7Z7.js +174 -0
  395. package/dist/chunk-TGON4N4O.js +197 -0
  396. package/dist/chunk-TIBZPAD2.js +9924 -0
  397. package/dist/chunk-TK644IOG.js +1813 -0
  398. package/dist/chunk-TRSVUPCX.js +25 -0
  399. package/dist/chunk-TSPQONRT.js +4533 -0
  400. package/dist/chunk-TSXGRLPR.js +107 -0
  401. package/dist/chunk-TWYLVAS2.js +152 -0
  402. package/dist/chunk-U2S3YEX3.js +224 -0
  403. package/dist/chunk-U32HWI76.js +437 -0
  404. package/dist/chunk-U45RTRGY.js +818 -0
  405. package/dist/chunk-U62VCQGR.js +49 -0
  406. package/dist/chunk-UDMS5AD3.js +307 -0
  407. package/dist/chunk-UGX7GI37.js +94 -0
  408. package/dist/chunk-UH3JJSHK.js +1 -0
  409. package/dist/chunk-UHRBTZYY.js +208 -0
  410. package/dist/chunk-UHT2KRG5.js +193 -0
  411. package/dist/chunk-UI2F6DJ5.js +118 -0
  412. package/dist/chunk-UKMAFG7A.js +189 -0
  413. package/dist/chunk-UUH2RVKS.js +585 -0
  414. package/dist/chunk-UVBBFDFQ.js +270 -0
  415. package/dist/chunk-UXZF3B7T.js +75 -0
  416. package/dist/chunk-V6YLL5WA.js +3746 -0
  417. package/dist/chunk-V72RMBM4.js +48 -0
  418. package/dist/chunk-VBNCKCCI.js +16 -0
  419. package/dist/chunk-VCAUV5XQ.js +3679 -0
  420. package/dist/chunk-VDSH7O4Y.js +152 -0
  421. package/dist/chunk-VNSYKC25.js +24 -0
  422. package/dist/chunk-VTLNJQ44.js +133 -0
  423. package/dist/chunk-VV6HOXRC.js +302 -0
  424. package/dist/chunk-VVGEPBPS.js +218 -0
  425. package/dist/chunk-VXF4Q3FW.js +20 -0
  426. package/dist/chunk-W3ZGW3C5.js +331 -0
  427. package/dist/chunk-W4OMESPG.js +271 -0
  428. package/dist/chunk-WDUORIHF.js +877 -0
  429. package/dist/chunk-WDVTY4U6.js +148 -0
  430. package/dist/chunk-WGXYDLMX.js +4386 -0
  431. package/dist/chunk-WMPU2UOV.js +82 -0
  432. package/dist/chunk-WOEVVPDP.js +1125 -0
  433. package/dist/chunk-WTLMSPBQ.js +256 -0
  434. package/dist/chunk-X43NMU4Z.js +1124 -0
  435. package/dist/chunk-X5NTI5U5.js +368 -0
  436. package/dist/chunk-X6UIEADS.js +949 -0
  437. package/dist/chunk-XAONGNST.js +57 -0
  438. package/dist/chunk-XB5YLCLB.js +13 -0
  439. package/dist/chunk-XGYF2QMZ.js +351 -0
  440. package/dist/chunk-XHEKQENB.js +255 -0
  441. package/dist/chunk-XPHKYHO4.js +878 -0
  442. package/dist/chunk-XQD2B6BB.js +507 -0
  443. package/dist/chunk-XT2ZQVTC.js +558 -0
  444. package/dist/chunk-XUSRFFA7.js +1859 -0
  445. package/dist/chunk-XUXVDPSZ.js +50 -0
  446. package/dist/chunk-XV7NZL4B.js +57 -0
  447. package/dist/chunk-XXCGJGSQ.js +121 -0
  448. package/dist/chunk-XYGDZCB5.js +659 -0
  449. package/dist/chunk-XZAMZUX5.js +992 -0
  450. package/dist/chunk-XZE4XARH.js +563 -0
  451. package/dist/chunk-Y34DUC4A.js +1356 -0
  452. package/dist/chunk-Y3J4JMTU.js +61 -0
  453. package/dist/chunk-Y4RC2YFX.js +1270 -0
  454. package/dist/chunk-Y55LZN5U.js +52 -0
  455. package/dist/chunk-Y5677NDO.js +183 -0
  456. package/dist/chunk-YESZJFK6.js +220 -0
  457. package/dist/chunk-YGXKOBQQ.js +292 -0
  458. package/dist/chunk-YMX76IOS.js +31 -0
  459. package/dist/chunk-YOTXESEH.js +257 -0
  460. package/dist/chunk-YQAAKTVH.js +712 -0
  461. package/dist/chunk-YVORHQ2S.js +579 -0
  462. package/dist/chunk-YWNAHW24.js +1552 -0
  463. package/dist/chunk-Z476RATD.js +1689 -0
  464. package/dist/chunk-ZDTGECTN.js +235 -0
  465. package/dist/chunk-ZDTMICNY.js +316 -0
  466. package/dist/chunk-ZIPWI2LY.js +151 -0
  467. package/dist/chunk-ZIVYXF7A.js +2344 -0
  468. package/dist/chunk-ZX65UI5W.js +78 -0
  469. package/dist/chunk-ZYG7GGKO.js +218 -0
  470. package/dist/chunk-ZZZUL4OF.js +38 -0
  471. package/dist/cli-utils-HUOTBRM3.js +40 -0
  472. package/dist/cli-validation-WLXWLUJL.js +111 -0
  473. package/dist/commit-utils-3QV2P6RA.js +202 -0
  474. package/dist/compete-config-resolver-SNH3TT2Q.js +76 -0
  475. package/dist/compete-request-XWNPOARL.js +364 -0
  476. package/dist/compete-results-PAP67LDR.js +208 -0
  477. package/dist/competition-outcome-tracker-GOZXNAQU.js +38 -0
  478. package/dist/config-HWHUNA72.js +166 -0
  479. package/dist/config-audit-EL3GHXS7.js +360 -0
  480. package/dist/config-explain-TNILDEVJ.js +12 -0
  481. package/dist/config-loader-VPFPFO4B.js +38 -0
  482. package/dist/config-manager-L3VRNTSG.js +67 -0
  483. package/dist/config-validator-SIIQEOFQ.js +72 -0
  484. package/dist/cost-7CX37DU3.js +53 -0
  485. package/dist/coverage-orchestrator-MNKHD6YC.js +772 -0
  486. package/dist/coverage-scanner-YVD62MLH.js +12 -0
  487. package/dist/createCompetitionRunner-LJG6ZQSO.js +46 -0
  488. package/dist/data-migration-state-VJK64DLT.js +31 -0
  489. package/dist/db-migrator-XSEIOGWY.js +34 -0
  490. package/dist/discovery-JGYGDD25.js +25 -0
  491. package/dist/doctor-CGBCD7IQ.js +186 -0
  492. package/dist/domain-analyzer-DL6BDR2N.js +29 -0
  493. package/dist/ensure-initialized-4BY73HLE.js +52 -0
  494. package/dist/entry.js +940 -0
  495. package/dist/environment-IGVH5X53.js +81 -0
  496. package/dist/error-parser-core-PPZ36A4S.js +50 -0
  497. package/dist/escalation-ladder-NWY5BJ57.js +130 -0
  498. package/dist/evaluation-context-KMWDSDYD.js +35 -0
  499. package/dist/evidence-plan-resolver-YIVH76PU.js +85 -0
  500. package/dist/execution-data-writer-OHAQ7G5H.js +53 -0
  501. package/dist/execution-policy-applier-ZHD3DCF3.js +31 -0
  502. package/dist/execution-preflight-Z4Y64V3I.js +209 -0
  503. package/dist/execution-roles-KBH2MC3M.js +66 -0
  504. package/dist/exit-code-error-Z4SW2DVK.js +14 -0
  505. package/dist/external-temp-cleanup-N2RZH4QP.js +311 -0
  506. package/dist/factory-IYBMCZG7.js +149 -0
  507. package/dist/file-utils-XIVR2ZMA.js +47 -0
  508. package/dist/flywheel-autoloop-executor-R3KD2G4D.js +249 -0
  509. package/dist/flywheel-competition-executor-XCFGVJSK.js +259 -0
  510. package/dist/flywheel-executor-LTSIZN2L.js +369 -0
  511. package/dist/flywheel-git-isolation-CZ5YVVZY.js +48 -0
  512. package/dist/flywheel-manifest-5UC4VBMA.js +108 -0
  513. package/dist/flywheel-model-defaults-7CVKP53V.js +27 -0
  514. package/dist/flywheel-preflight-6E33DJDK.js +104 -0
  515. package/dist/flywheel-resume-7HELH7QS.js +23 -0
  516. package/dist/flywheel-safety-7U7DPOAP.js +28 -0
  517. package/dist/flywheel-scope-decision-ZHKF6ZIN.js +11 -0
  518. package/dist/get-telemetry-logger-AZ46EPRP.js +73 -0
  519. package/dist/git-worktree-IY6V6DUO.js +18 -0
  520. package/dist/github-pr-manager-4XUO2YTM.js +211 -0
  521. package/dist/guidance-profile-config-E7XJL2VP.js +117 -0
  522. package/dist/guidance-profile-renderer-QK2YMAAP.js +21 -0
  523. package/dist/index-query-4JEUKA44.js +31 -0
  524. package/dist/index.js +108644 -0
  525. package/dist/instructions-CM6THDV7.js +64 -0
  526. package/dist/invoke-provider-auth-V5TC5UDS.js +223 -0
  527. package/dist/judge-scoring.json +80 -0
  528. package/dist/judge-test.js +555 -0
  529. package/dist/lab-mode-OTWVHN63.js +39 -0
  530. package/dist/lab-utils-VA6VXNOV.js +23 -0
  531. package/dist/linux-host-class-UY4KVLFD.js +29 -0
  532. package/dist/llm-judge-executor-34YPIRJ7.js +133 -0
  533. package/dist/local-executor-LJJSCWZX.js +223 -0
  534. package/dist/logger-6PRYM56M.js +72 -0
  535. package/dist/loop-spec-XTLQAVZ7.js +191 -0
  536. package/dist/manager-2HJG5CLT.js +25 -0
  537. package/dist/matrix-prompt-builder-LHDTJ4TY.js +497 -0
  538. package/dist/matrix-result-parser-7W35EUOV.js +15 -0
  539. package/dist/metadata-HQ43NDEK.js +22 -0
  540. package/dist/model-invocations-db-QBO4Y7WZ.js +38 -0
  541. package/dist/monitor-MCI3F4PX.js +72 -0
  542. package/dist/next-HQKDXIIR.js +90 -0
  543. package/dist/next-JC3V2UCS.js +69 -0
  544. package/dist/optimization-config-resolver-QPSFLCJU.js +75 -0
  545. package/dist/package-3YCWIQQ5.js +10 -0
  546. package/dist/parallel-orchestrator-NL7YJJU2.js +297 -0
  547. package/dist/parallel-worker.js +279 -0
  548. package/dist/parse-git-status-PIAF3OTP.js +10 -0
  549. package/dist/path-security-ETTH6TZL.js +39 -0
  550. package/dist/pattern-bank-UNJR5ADV.js +15 -0
  551. package/dist/plan-backlog-list-fast-DZK24LSZ.js +270 -0
  552. package/dist/plan-backlog-show-fast-63GBBH6Q.js +406 -0
  553. package/dist/plan-sprint-list-fast-TJ6ZUF5E.js +162 -0
  554. package/dist/plan-sprint-status-fast-YWE5X3NG.js +208 -0
  555. package/dist/plan-task-status-fast-K22BTTBW.js +349 -0
  556. package/dist/plan-to-flywheel-VFGCPINA.js +456 -0
  557. package/dist/planning-backlog-Y6TIJYWU.js +319 -0
  558. package/dist/poetic-root-V5DNXEAE.js +17 -0
  559. package/dist/preflight-OPANWMME.js +12 -0
  560. package/dist/process-registry-PDZJPTAS.js +27 -0
  561. package/dist/process-scanner-C22SYXYD.js +22 -0
  562. package/dist/processes-KMQUD7HY.js +29 -0
  563. package/dist/processes-json-fast-IBDXZ6WW.js +135 -0
  564. package/dist/profile-2XP3FWH2.js +149 -0
  565. package/dist/prompts-EPAAAMMF.js +168 -0
  566. package/dist/protected-branches-RNTEX7ET.js +57 -0
  567. package/dist/provider-aware-resource-manager-QBTZJMNP.js +18 -0
  568. package/dist/provider-matrix-KWTYA5QV.js +154 -0
  569. package/dist/provider-matrix-renderer-MZDWKKID.js +139 -0
  570. package/dist/provider-output-forwarding-DERID3TP.js +30 -0
  571. package/dist/provider-registry-J5NRBJKI.js +128 -0
  572. package/dist/provider-temp-cleanup-KYGLF6YU.js +14 -0
  573. package/dist/prune-engine-Q6E2DXBN.js +451 -0
  574. package/dist/quality-gate-33SHK43X.js +70 -0
  575. package/dist/quickstart-E32SODTA.js +62 -0
  576. package/dist/readiness-REWW7OSV.js +96 -0
  577. package/dist/registry-N6L36RE2.js +153 -0
  578. package/dist/repair-helpers-TTV5FEF4.js +117 -0
  579. package/dist/repo-root-2IGD4H7X.js +22 -0
  580. package/dist/resolution-engine-LXQYDSKS.js +127 -0
  581. package/dist/resolve-provider-cli-O2P6XZBZ.js +35 -0
  582. package/dist/restore-telemetry-db-BQL6DTQF.js +360 -0
  583. package/dist/result-streamer-WEHW476R.js +19 -0
  584. package/dist/routing-events-DHPUZDDF.js +27 -0
  585. package/dist/routing-history-store-UWCEXAP3.js +46 -0
  586. package/dist/routing-planner-VGQU35QM.js +160 -0
  587. package/dist/run-poetic-tui-ZZXFDOQ4.js +13517 -0
  588. package/dist/run-simulation.js +376 -0
  589. package/dist/runner-2YPKOJ6A.js +1092 -0
  590. package/dist/runner-HEJJ65AJ.js +1571 -0
  591. package/dist/safety-OCC4KJJR.js +64 -0
  592. package/dist/safety-stanza-BSVE6OJU.js +60 -0
  593. package/dist/sandbox-3AG2WJLM.js +94 -0
  594. package/dist/sandbox-5JOFBS75.js +81 -0
  595. package/dist/schema-extensions.sql +234 -0
  596. package/dist/security-2KI5XNDG.js +17 -0
  597. package/dist/setup-bb-plugin-QAQWZCV5.js +738 -0
  598. package/dist/setup-claude-code-P7BTTSPO.js +317 -0
  599. package/dist/setup-temp-cleanup-GASCWZ2I.js +20 -0
  600. package/dist/shared-utils-D6U6UREP.js +37 -0
  601. package/dist/simple-artifact-goal-OTXNGZY6.js +25 -0
  602. package/dist/sprint-QGXLN3SO.js +338 -0
  603. package/dist/sprint-bridge-FNKQCJGL.js +68 -0
  604. package/dist/sprint-execution-service-4ELKNJDS.js +311 -0
  605. package/dist/sprint-manager-7TNCXCIX.js +91 -0
  606. package/dist/sqlite-wrapper-UDWCTNLJ.js +17 -0
  607. package/dist/stale-cleanup-orchestrator-H5XKGZP2.js +62 -0
  608. package/dist/storage-access-F6ALDN4S.js +22 -0
  609. package/dist/synthesis-TGQUVAW3.js +185 -0
  610. package/dist/task-classifier-DZIRBSS2.js +494 -0
  611. package/dist/task-list-fast-PJ2EXVZE.js +147 -0
  612. package/dist/task-manager-5742GMJK.js +93 -0
  613. package/dist/task-splitter-6MBXSWF6.js +79 -0
  614. package/dist/task-type-detector-OLVOMXZO.js +21 -0
  615. package/dist/task-type-resolver-4HG6TXFV.js +42 -0
  616. package/dist/telemetry-JX7EMNOZ.js +131 -0
  617. package/dist/telemetry-health-tracker-DMSSGBZ2.js +11 -0
  618. package/dist/telemetry-path-resolver-7ICSG276.js +28 -0
  619. package/dist/telemetry-reliability-2MLJ4J6B.js +125 -0
  620. package/dist/token-estimator-PDU345YE.js +37 -0
  621. package/dist/transports-3UEO6C5Y.js +255 -0
  622. package/dist/unified-cost-tracker-F3W5NMPG.js +35 -0
  623. package/dist/unified-state-cleanup-LJPF2BOW.js +276 -0
  624. package/dist/universal-cost-calculator-BAJV35GA.js +42 -0
  625. package/dist/user-agents-GHUGCSKN.js +22 -0
  626. package/dist/variant-delivery-state-XCOYVCM4.js +33 -0
  627. package/dist/variant-state-cleanup-QIEKRULB.js +280 -0
  628. package/dist/variant-success-PJXWE3ZV.js +12 -0
  629. package/dist/variant-worker-HPTUA37Z.js +4196 -0
  630. package/dist/variant-worker.js +24 -0
  631. package/dist/variants-2HDYZOCG.js +233 -0
  632. package/dist/verification-toolchains-VVKT3GBF.js +28 -0
  633. package/dist/verify-commands-5F6CJV4B.js +37 -0
  634. package/dist/warning-EGQGUUIO.js +12 -0
  635. package/dist/work-edge-RWEDWJZF.js +49 -0
  636. package/dist/work-item-writer-WHAVZXAY.js +19 -0
  637. package/dist/worker-AGWHD4D2.js +1435 -0
  638. package/dist/worker.js +19 -0
  639. package/dist/worktree-metrics-3DFUYVYE.js +26 -0
  640. package/dist/writeback-VZKHYHSN.js +44 -0
  641. package/docs/CLI_REFERENCE.md +7040 -0
  642. package/docs/PROVIDER_SETUP.md +1570 -0
  643. package/docs/README.md +96 -0
  644. package/docs/TROUBLESHOOTING.md +2232 -0
  645. package/docs/getting-started/QUICK_START.md +294 -0
  646. package/docs/getting-started/README.md +188 -0
  647. package/docs/getting-started/SETUP.md +52 -0
  648. package/docs/reference/AGENT_CONTEXT.md +38 -0
  649. package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
  650. package/docs/reference/README.md +381 -0
  651. package/docs/reference/SECURITY.md +262 -0
  652. package/integrations/bb-plugin-poetic/README.md +362 -0
  653. package/integrations/bb-plugin-poetic/app.css +341 -0
  654. package/integrations/bb-plugin-poetic/app.tsx +5059 -0
  655. package/integrations/bb-plugin-poetic/package.json +45 -0
  656. package/integrations/bb-plugin-poetic/server.ts +444 -0
  657. package/integrations/bb-plugin-poetic/src/adapter.ts +3312 -0
  658. package/integrations/bb-plugin-poetic/src/backlog-authoring-model.ts +150 -0
  659. package/integrations/bb-plugin-poetic/src/backlog-authoring-schema.ts +355 -0
  660. package/integrations/bb-plugin-poetic/src/backlog-authoring-service.ts +419 -0
  661. package/integrations/bb-plugin-poetic/src/backlog-authoring-view.ts +1026 -0
  662. package/integrations/bb-plugin-poetic/src/competition-defaults-schema.ts +122 -0
  663. package/integrations/bb-plugin-poetic/src/competition-defaults-service.ts +105 -0
  664. package/integrations/bb-plugin-poetic/src/competition-defaults-view.ts +138 -0
  665. package/integrations/bb-plugin-poetic/src/contract.ts +615 -0
  666. package/integrations/bb-plugin-poetic/src/finalize-model.ts +205 -0
  667. package/integrations/bb-plugin-poetic/src/finalize-schema.ts +158 -0
  668. package/integrations/bb-plugin-poetic/src/finalize-service.ts +382 -0
  669. package/integrations/bb-plugin-poetic/src/finalize-view.ts +159 -0
  670. package/integrations/bb-plugin-poetic/src/judge-operations-readback.ts +303 -0
  671. package/integrations/bb-plugin-poetic/src/judge-operations-schema.ts +33 -0
  672. package/integrations/bb-plugin-poetic/src/judge-operations-view.ts +459 -0
  673. package/integrations/bb-plugin-poetic/src/model.ts +1302 -0
  674. package/integrations/bb-plugin-poetic/src/monitor-service.ts +233 -0
  675. package/integrations/bb-plugin-poetic/src/panel-read-ux.ts +97 -0
  676. package/integrations/bb-plugin-poetic/src/patch-preview-schema.ts +166 -0
  677. package/integrations/bb-plugin-poetic/src/patch-preview-service.ts +480 -0
  678. package/integrations/bb-plugin-poetic/src/patch-preview-view.ts +109 -0
  679. package/integrations/bb-plugin-poetic/src/planning-readback.ts +204 -0
  680. package/integrations/bb-plugin-poetic/src/planning-service.ts +594 -0
  681. package/integrations/bb-plugin-poetic/src/planning-workspace-schema.ts +95 -0
  682. package/integrations/bb-plugin-poetic/src/planning-workspace.ts +323 -0
  683. package/integrations/bb-plugin-poetic/src/poll-handoff.ts +136 -0
  684. package/integrations/bb-plugin-poetic/src/project-target-server.ts +21 -0
  685. package/integrations/bb-plugin-poetic/src/project-target.ts +45 -0
  686. package/integrations/bb-plugin-poetic/src/reference-index.ts +192 -0
  687. package/integrations/bb-plugin-poetic/src/repository-schema.ts +30 -0
  688. package/integrations/bb-plugin-poetic/src/result-explorer-view.ts +597 -0
  689. package/integrations/bb-plugin-poetic/src/result-readback-model.ts +163 -0
  690. package/integrations/bb-plugin-poetic/src/result-readback-schema.ts +319 -0
  691. package/integrations/bb-plugin-poetic/src/result-readback-service.ts +333 -0
  692. package/integrations/bb-plugin-poetic/src/run-page-view.ts +177 -0
  693. package/integrations/bb-plugin-poetic/src/setup-config-schema.ts +291 -0
  694. package/integrations/bb-plugin-poetic/src/setup-config-service.ts +590 -0
  695. package/integrations/bb-plugin-poetic/src/setup-config-view.ts +413 -0
  696. package/integrations/bb-plugin-poetic/src/sprint-close-model.ts +150 -0
  697. package/integrations/bb-plugin-poetic/src/sprint-close-schema.ts +136 -0
  698. package/integrations/bb-plugin-poetic/src/sprint-close-service.ts +341 -0
  699. package/integrations/bb-plugin-poetic/src/sprint-close-view.ts +116 -0
  700. package/integrations/bb-plugin-poetic/src/sprint-composition-model.ts +714 -0
  701. package/integrations/bb-plugin-poetic/src/sprint-composition-readback.ts +231 -0
  702. package/integrations/bb-plugin-poetic/src/sprint-composition-schema.ts +601 -0
  703. package/integrations/bb-plugin-poetic/src/sprint-composition-service.ts +775 -0
  704. package/integrations/bb-plugin-poetic/src/sprint-composition-view.ts +1221 -0
  705. package/integrations/bb-plugin-poetic/src/task-authoring-model.ts +567 -0
  706. package/integrations/bb-plugin-poetic/src/task-authoring-schema.ts +635 -0
  707. package/integrations/bb-plugin-poetic/src/task-authoring-service.ts +842 -0
  708. package/integrations/bb-plugin-poetic/src/task-authoring-view.ts +1227 -0
  709. package/integrations/bb-plugin-poetic/src/task-detail-readback.ts +56 -0
  710. package/integrations/bb-plugin-poetic/src/task-detail-schema.ts +144 -0
  711. package/integrations/bb-plugin-poetic/src/task-navigation.ts +446 -0
  712. package/integrations/bb-plugin-poetic/src/workbench-route.ts +74 -0
  713. package/integrations/bb-plugin-poetic/tests/host-contract.test.ts +773 -0
  714. package/integrations/bb-plugin-poetic/tsconfig.json +18 -0
  715. package/integrations/bb-plugin-poetic/types/PROVENANCE.json +52 -0
  716. package/integrations/bb-plugin-poetic/types/bb-plugin-sdk-app.d.ts +1444 -0
  717. package/integrations/bb-plugin-poetic/types/bb-plugin-sdk.d.ts +13030 -0
  718. package/integrations/bb-plugin-poetic/vitest.host.config.ts +71 -0
  719. package/package.json +331 -0
  720. package/schemas/README.md +80 -0
  721. package/schemas/config-v1.schema.json +136 -0
  722. package/schemas/execution-config.schema.json +47 -0
  723. package/schemas/judge-scoring.schema.json +370 -0
  724. package/schemas/poetic.config.schema.json +2214 -0
  725. package/schemas/provider-config.schema.json +203 -0
  726. package/schemas/telemetry-config.schema.json +79 -0
  727. package/schemas/user-preferences.schema.json +141 -0
  728. package/scripts/assert-node-runtime.mjs +140 -0
  729. package/scripts/preflight-native.mjs +49 -0
  730. package/scripts/preinstall-node-check.mjs +78 -0
  731. package/scripts/setup-git-hooks.mjs +24 -0
  732. package/scripts/sync.sh +2722 -0
  733. package/scripts/write-node-launcher.sh +108 -0
  734. package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
  735. package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
  736. package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
  737. package/templates/.poetic/README.md +37 -0
  738. package/templates/.poetic/agents/README.md +296 -0
  739. package/templates/.poetic/agents/api-documenter.md +147 -0
  740. package/templates/.poetic/agents/backend-architect.md +31 -0
  741. package/templates/.poetic/agents/code-reviewer.md +157 -0
  742. package/templates/.poetic/agents/data-scientist.md +179 -0
  743. package/templates/.poetic/agents/database-optimizer.md +145 -0
  744. package/templates/.poetic/agents/debugger.md +31 -0
  745. package/templates/.poetic/agents/deployment-engineer.md +164 -0
  746. package/templates/.poetic/agents/devops-troubleshooter.md +139 -0
  747. package/templates/.poetic/agents/frontend-developer.md +150 -0
  748. package/templates/.poetic/agents/javascript-pro.md +36 -0
  749. package/templates/.poetic/agents/performance-engineer.md +151 -0
  750. package/templates/.poetic/agents/python-pro.md +137 -0
  751. package/templates/.poetic/agents/test-automator.md +147 -0
  752. package/templates/.poetic/agents/typescript-pro.md +34 -0
  753. package/templates/.poetic/config/poetic.config.jsonc +69 -0
  754. package/templates/.poetic/config/project-context.template.json +6 -0
  755. package/templates/.poetic/config/task-type-aliases.presets/kanban.yaml +14 -0
  756. package/templates/.poetic/config/task-type-aliases.presets/scrum.yaml +17 -0
  757. package/templates/.poetic/config/task-type-aliases.presets/xp.yaml +12 -0
  758. package/templates/.poetic/config/task-type-aliases.yaml +28 -0
  759. package/templates/.poetic/gitignore.template +55 -0
  760. package/templates/.poetic/task-types/analysis.yaml +40 -0
  761. package/templates/.poetic/task-types/architecture.yaml +38 -0
  762. package/templates/.poetic/task-types/doc.yaml +35 -0
  763. package/templates/.poetic/task-types/feature.yaml +23 -0
  764. package/templates/.poetic/task-types/general.yaml +6 -0
  765. package/templates/.poetic/task-types/security.yaml +39 -0
  766. package/templates/AGENTS.template.md +99 -0
  767. package/templates/CLAUDE.template.md +1 -0
  768. package/templates/GEMINI.template.md +1 -0
  769. package/templates/builtin-workflows/code-review.yaml +73 -0
  770. package/templates/builtin-workflows/compete-streak.yaml +78 -0
  771. package/templates/builtin-workflows/hello-verify.yaml +10 -0
  772. package/templates/builtin-workflows/judge-regression.yaml +114 -0
  773. package/templates/builtin-workflows/skills/code-review/SKILL.md +60 -0
  774. package/templates/builtin-workflows/skills/hello-verify/SKILL.md +6 -0
  775. package/templates/guard-kit/GUARD_SETUP.md.template +255 -0
  776. package/templates/guard-kit/check.mjs.template +777 -0
  777. package/templates/guard-kit/config.json.template +6 -0
  778. package/templates/guard-kit/poetic-guard.yml.template +189 -0
  779. package/templates/profiles/README.md +56 -0
  780. package/templates/profiles/frontier-claude.json +27 -0
  781. package/templates/profiles/frontier-codex.json +26 -0
@@ -0,0 +1,4128 @@
1
+ import {
2
+ recordReadinessDiagnostic
3
+ } from "./chunk-IDCSOZVN.js";
4
+ import {
5
+ formatPreflightIssue,
6
+ runExecutionRolesPreflight
7
+ } from "./chunk-S77XPALC.js";
8
+ import {
9
+ detectHostConcurrencyBudget
10
+ } from "./chunk-PYITM4N2.js";
11
+ import {
12
+ RuntimeRequirements,
13
+ ensurePoeticInitialized,
14
+ resolveRepoRootForPoeticDir
15
+ } from "./chunk-7XV3QXLT.js";
16
+ import {
17
+ readProviderDryRunFreshness
18
+ } from "./chunk-IKFP2KUR.js";
19
+ import {
20
+ formatDurationMs
21
+ } from "./chunk-F5JO7HAS.js";
22
+ import {
23
+ KNOWLEDGE_BLOCK_BEGIN,
24
+ KNOWLEDGE_BLOCK_END,
25
+ describeDimension,
26
+ detectStack,
27
+ readIntentContract,
28
+ readKnowledgeManifest,
29
+ resolveIntent
30
+ } from "./chunk-TK644IOG.js";
31
+ import {
32
+ getAvailableMemoryGB
33
+ } from "./chunk-PTH5E5XO.js";
34
+ import {
35
+ ConfigManager
36
+ } from "./chunk-GJTWJNLV.js";
37
+ import {
38
+ loadAllItems
39
+ } from "./chunk-3BCUNHKF.js";
40
+ import {
41
+ getPlanningRoot
42
+ } from "./chunk-OPYFYTWQ.js";
43
+ import {
44
+ getBucketArchivePathByKind,
45
+ getBucketPathByKind
46
+ } from "./chunk-HOYBYAOA.js";
47
+ import {
48
+ discoverOpencode
49
+ } from "./chunk-IC7I6YIJ.js";
50
+ import {
51
+ isCiEnvironment
52
+ } from "./chunk-A722DEVA.js";
53
+ import {
54
+ hasStrictMcpConfigBeenProbed,
55
+ isStrictMcpConfigSupported,
56
+ probeStrictMcpConfig
57
+ } from "./chunk-TIBZPAD2.js";
58
+ import {
59
+ SETUP_CHECK_READINESS_OPTIONS,
60
+ getProviderReadiness,
61
+ isGateReadyProvider,
62
+ probeProviderCli,
63
+ readinessProbeContextForOptions,
64
+ resolveNamedReadiness,
65
+ resolveProviderParityLabel,
66
+ resolveReadinessScope
67
+ } from "./chunk-25QL3M3L.js";
68
+ import {
69
+ invalidateAuthOutcome,
70
+ lookupAuthOutcome,
71
+ recordAuthOutcome
72
+ } from "./chunk-R6W23LKN.js";
73
+ import {
74
+ normalizeEngineId,
75
+ resolveProviderCliCommand
76
+ } from "./chunk-SURZ2WFE.js";
77
+ import {
78
+ SCHEMA_VERSION,
79
+ checkSchemaVersion
80
+ } from "./chunk-3733QTI7.js";
81
+ import {
82
+ formatSandboxDegradedSummary
83
+ } from "./chunk-5WCQRDRB.js";
84
+ import {
85
+ atomicWriteJsonSync
86
+ } from "./chunk-A3YGID55.js";
87
+ import {
88
+ detectProjectCapabilities
89
+ } from "./chunk-GNH3QGTL.js";
90
+ import {
91
+ getRuntimeEnv
92
+ } from "./chunk-ENRGKNR5.js";
93
+ import {
94
+ BooleanFlagSchema,
95
+ validateCommandOptions
96
+ } from "./chunk-QOCOVLUH.js";
97
+ import {
98
+ ProviderRegistry,
99
+ getProviderConfig,
100
+ isConfigValidityError
101
+ } from "./chunk-AT4TNPWW.js";
102
+ import {
103
+ resolveProvidersConfigPath
104
+ } from "./chunk-L6OM4A22.js";
105
+ import {
106
+ getPackageRoot,
107
+ getRejectedPathEnvWarnings
108
+ } from "./chunk-XZE4XARH.js";
109
+ import {
110
+ POETIC_CODEX_BACKEND,
111
+ readEnv
112
+ } from "./chunk-5DLKYTQX.js";
113
+
114
+ // src/cli/commands/doctor.ts
115
+ import * as fs5 from "fs/promises";
116
+ import * as os from "os";
117
+ import * as path10 from "path";
118
+ import { fileURLToPath } from "url";
119
+ import chalk2 from "chalk";
120
+ import { Command as Command2 } from "commander";
121
+ import { execa } from "execa";
122
+ import { z as z3 } from "zod";
123
+
124
+ // src/core/init-drift/fingerprint.ts
125
+ import { createHash } from "crypto";
126
+ import { readdirSync, readFileSync, statSync } from "fs";
127
+ import path from "path";
128
+ var STACK_FINGERPRINT_VERSION = 1;
129
+ var STACK_FILE_NAMES = [
130
+ "package.json",
131
+ "package-lock.json",
132
+ "pnpm-lock.yaml",
133
+ "yarn.lock",
134
+ "bun.lock",
135
+ "bun.lockb",
136
+ "pyproject.toml",
137
+ "poetry.lock",
138
+ "uv.lock",
139
+ "Pipfile",
140
+ "Pipfile.lock",
141
+ "go.mod",
142
+ "go.sum",
143
+ "Cargo.toml",
144
+ "Cargo.lock",
145
+ "Package.swift",
146
+ "Package.resolved",
147
+ "Gemfile",
148
+ "Gemfile.lock",
149
+ "composer.json",
150
+ "composer.lock",
151
+ "Dockerfile"
152
+ ];
153
+ var REQUIREMENTS_PATTERN = /^requirements[\w.-]*\.(?:txt|in)$/i;
154
+ var FRAMEWORK_SIGNAL_KEYS = [
155
+ "tsconfig",
156
+ "gradle",
157
+ "pomXml",
158
+ "cmakeLists",
159
+ "mesonBuild",
160
+ "dotnetSolution",
161
+ "dotnetProject",
162
+ "pubspecYaml",
163
+ "dbtProject",
164
+ "terraform",
165
+ "kubernetesManifests"
166
+ ];
167
+ function hashFileContent(filePath) {
168
+ try {
169
+ const content = readFileSync(filePath);
170
+ return createHash("sha256").update(content).digest("hex");
171
+ } catch {
172
+ return void 0;
173
+ }
174
+ }
175
+ function listRootEntries(root) {
176
+ try {
177
+ return readdirSync(root, { withFileTypes: true });
178
+ } catch {
179
+ return [];
180
+ }
181
+ }
182
+ function isDirectoryEntry(root, entry) {
183
+ if (entry.isDirectory()) return true;
184
+ if (!entry.isSymbolicLink()) return false;
185
+ try {
186
+ return statSync(path.join(root, entry.name)).isDirectory();
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
191
+ function sortedRecord(record) {
192
+ const out = {};
193
+ for (const key of Object.keys(record).sort()) {
194
+ const value = record[key];
195
+ if (value !== void 0) out[key] = value;
196
+ }
197
+ return out;
198
+ }
199
+ function computeStackFingerprint(cwd) {
200
+ const root = path.resolve(cwd);
201
+ const manifests = {};
202
+ const fileHashes = {};
203
+ const recordFile = (relativeName) => {
204
+ const hash = hashFileContent(path.join(root, relativeName));
205
+ if (hash === void 0) return;
206
+ manifests[relativeName] = true;
207
+ fileHashes[relativeName] = hash;
208
+ };
209
+ for (const name of STACK_FILE_NAMES) {
210
+ recordFile(name);
211
+ }
212
+ const rootEntries = listRootEntries(root);
213
+ for (const entry of rootEntries) {
214
+ if (REQUIREMENTS_PATTERN.test(entry.name) && !manifests[entry.name]) {
215
+ recordFile(entry.name);
216
+ continue;
217
+ }
218
+ if (/\.xcodeproj$/i.test(entry.name) && isDirectoryEntry(root, entry)) {
219
+ manifests[entry.name] = true;
220
+ const pbxprojRelative = path.join(entry.name, "project.pbxproj");
221
+ const hash = hashFileContent(path.join(root, pbxprojRelative));
222
+ if (hash !== void 0) fileHashes[pbxprojRelative] = hash;
223
+ continue;
224
+ }
225
+ if (/\.xcworkspace$/i.test(entry.name) && isDirectoryEntry(root, entry)) {
226
+ manifests[entry.name] = true;
227
+ }
228
+ }
229
+ const capabilities = detectProjectCapabilities(root);
230
+ const languages = [...capabilities.languages].sort();
231
+ const capabilityManifests = capabilities.manifests;
232
+ const frameworks = FRAMEWORK_SIGNAL_KEYS.filter(
233
+ (key) => capabilityManifests[key] === true
234
+ ).sort();
235
+ return {
236
+ version: STACK_FINGERPRINT_VERSION,
237
+ languages,
238
+ frameworks,
239
+ manifests: sortedRecord(manifests),
240
+ fileHashes: sortedRecord(fileHashes)
241
+ };
242
+ }
243
+ function fingerprintDigest(fingerprint) {
244
+ return createHash("sha256").update(JSON.stringify(fingerprint)).digest("hex");
245
+ }
246
+
247
+ // src/core/init-drift/state.ts
248
+ import fs from "fs";
249
+ import path2 from "path";
250
+ var DEFAULT_NUDGE_CAP = 100;
251
+ var DELIVERED_NUDGE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
252
+ function resolveStateDir(cwd) {
253
+ return path2.join(path2.resolve(cwd), ".poetic", "state");
254
+ }
255
+ function resolveInitFingerprintPath(cwd) {
256
+ return path2.join(resolveStateDir(cwd), "init-fingerprint.json");
257
+ }
258
+ function resolveInitNudgesPath(cwd) {
259
+ return path2.join(resolveStateDir(cwd), "init-nudges.json");
260
+ }
261
+ function readJsonFile(filePath) {
262
+ try {
263
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
264
+ } catch {
265
+ return void 0;
266
+ }
267
+ }
268
+ function writeJsonFile(filePath, value) {
269
+ fs.mkdirSync(path2.dirname(filePath), { recursive: true });
270
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
271
+ `, "utf-8");
272
+ }
273
+ function isRecord(value) {
274
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
275
+ }
276
+ function readStoredFingerprint(cwd) {
277
+ const parsed = readJsonFile(resolveInitFingerprintPath(cwd));
278
+ if (!isRecord(parsed)) return void 0;
279
+ const fingerprint = parsed.fingerprint;
280
+ if (!isRecord(fingerprint)) return void 0;
281
+ if (!Array.isArray(fingerprint.languages) || !Array.isArray(fingerprint.frameworks) || !isRecord(fingerprint.manifests) || !isRecord(fingerprint.fileHashes)) {
282
+ return void 0;
283
+ }
284
+ const stored = fingerprint;
285
+ return {
286
+ fingerprint: stored,
287
+ digest: typeof parsed.digest === "string" ? parsed.digest : fingerprintDigest(stored),
288
+ recordedAtMs: typeof parsed.recordedAtMs === "number" ? parsed.recordedAtMs : 0
289
+ };
290
+ }
291
+ function writeStoredFingerprint(cwd, fingerprint, options = {}) {
292
+ const now = options.now ?? Date.now;
293
+ const file = {
294
+ version: 1,
295
+ digest: fingerprintDigest(fingerprint),
296
+ recordedAtMs: now(),
297
+ fingerprint
298
+ };
299
+ writeJsonFile(resolveInitFingerprintPath(cwd), file);
300
+ }
301
+ function isDriftRecord(value) {
302
+ if (!isRecord(value)) return false;
303
+ return typeof value.key === "string" && typeof value.kind === "string" && typeof value.subject === "string" && (value.change === "added" || value.change === "removed") && Array.isArray(value.evidence) && typeof value.detectedAtMs === "number";
304
+ }
305
+ function isDeliveryRecord(value) {
306
+ if (!isRecord(value)) return false;
307
+ return typeof value.key === "string" && typeof value.deliveredAtMs === "number" && typeof value.driftDetectedAtMs === "number";
308
+ }
309
+ function readNudgeState(cwd) {
310
+ const parsed = readJsonFile(resolveInitNudgesPath(cwd));
311
+ const state = { version: 1, drift: {}, deliveries: {} };
312
+ if (!isRecord(parsed)) return state;
313
+ if (isRecord(parsed.drift)) {
314
+ for (const [key, value] of Object.entries(parsed.drift)) {
315
+ if (isDriftRecord(value)) state.drift[key] = value;
316
+ }
317
+ }
318
+ if (isRecord(parsed.deliveries)) {
319
+ for (const [key, value] of Object.entries(parsed.deliveries)) {
320
+ if (isDeliveryRecord(value)) state.deliveries[key] = value;
321
+ }
322
+ }
323
+ return state;
324
+ }
325
+ function isDelivered(record, deliveries) {
326
+ const delivery = deliveries[record.key];
327
+ return delivery !== void 0 && delivery.driftDetectedAtMs === record.detectedAtMs;
328
+ }
329
+ function pruneNudgeState(state, nowMs, cap, ttlMs) {
330
+ const drift = {};
331
+ for (const [key, record] of Object.entries(state.drift)) {
332
+ if (isDelivered(record, state.deliveries)) {
333
+ const delivery = state.deliveries[key];
334
+ const age = nowMs - (delivery?.deliveredAtMs ?? nowMs);
335
+ if (age >= ttlMs) continue;
336
+ }
337
+ drift[key] = record;
338
+ }
339
+ const entries = Object.entries(drift);
340
+ if (entries.length > cap) {
341
+ const byOldestFirst = (a, b) => a[1].detectedAtMs - b[1].detectedAtMs;
342
+ const delivered = entries.filter(([, r]) => isDelivered(r, state.deliveries));
343
+ const undelivered = entries.filter(([, r]) => !isDelivered(r, state.deliveries));
344
+ delivered.sort(byOldestFirst);
345
+ undelivered.sort(byOldestFirst);
346
+ let excess = entries.length - cap;
347
+ for (const [key] of delivered) {
348
+ if (excess <= 0) break;
349
+ delete drift[key];
350
+ excess -= 1;
351
+ }
352
+ for (const [key] of undelivered) {
353
+ if (excess <= 0) break;
354
+ delete drift[key];
355
+ excess -= 1;
356
+ }
357
+ }
358
+ const deliveries = {};
359
+ for (const [key, delivery] of Object.entries(state.deliveries)) {
360
+ if (drift[key] !== void 0) deliveries[key] = delivery;
361
+ }
362
+ return { version: 1, drift, deliveries };
363
+ }
364
+ function writeNudgeState(cwd, state, options) {
365
+ const now = options.now ?? Date.now;
366
+ const cap = options.cap ?? DEFAULT_NUDGE_CAP;
367
+ const ttlMs = options.ttlMs ?? DELIVERED_NUDGE_TTL_MS;
368
+ writeJsonFile(resolveInitNudgesPath(cwd), pruneNudgeState(state, now(), cap, ttlMs));
369
+ }
370
+ function recordDriftRecords(cwd, records, options = {}) {
371
+ if (records.length === 0) return;
372
+ const state = readNudgeState(cwd);
373
+ for (const record of records) {
374
+ state.drift[record.key] = record;
375
+ }
376
+ writeNudgeState(cwd, state, options);
377
+ }
378
+ function listPendingDrift(cwd, options = {}) {
379
+ const now = options.now ?? Date.now;
380
+ const nowMs = now();
381
+ const state = readNudgeState(cwd);
382
+ return Object.values(state.drift).filter((record) => !isDelivered(record, state.deliveries)).sort((a, b) => a.detectedAtMs - b.detectedAtMs).map((record) => ({ ...record, ageMs: Math.max(0, nowMs - record.detectedAtMs) }));
383
+ }
384
+ function recordDelivery(cwd, keys, options = {}) {
385
+ if (keys.length === 0) return;
386
+ const now = options.now ?? Date.now;
387
+ const nowMs = now();
388
+ const state = readNudgeState(cwd);
389
+ let changed = false;
390
+ for (const key of keys) {
391
+ const record = state.drift[key];
392
+ if (record === void 0) continue;
393
+ state.deliveries[key] = {
394
+ key,
395
+ deliveredAtMs: nowMs,
396
+ driftDetectedAtMs: record.detectedAtMs
397
+ };
398
+ changed = true;
399
+ }
400
+ if (changed) writeNudgeState(cwd, state, options);
401
+ }
402
+
403
+ // src/core/init-drift/nudge.ts
404
+ function diffSets(before, after) {
405
+ const beforeSet = new Set(before);
406
+ const afterSet = new Set(after);
407
+ return {
408
+ added: after.filter((item) => !beforeSet.has(item)),
409
+ removed: before.filter((item) => !afterSet.has(item))
410
+ };
411
+ }
412
+ function newManifests(stored, current) {
413
+ return Object.keys(current.manifests).filter((name) => stored.manifests[name] !== true);
414
+ }
415
+ function detectDrift(stored, current) {
416
+ const detectedAtMs = 0;
417
+ const records = [];
418
+ const appearedManifests = newManifests(stored, current);
419
+ const languages = diffSets(stored.languages, current.languages);
420
+ for (const language of languages.added) {
421
+ records.push({
422
+ key: `language:${language}`,
423
+ kind: "language-added",
424
+ subject: language,
425
+ change: "added",
426
+ evidence: appearedManifests,
427
+ detectedAtMs
428
+ });
429
+ }
430
+ for (const language of languages.removed) {
431
+ records.push({
432
+ key: `language:${language}`,
433
+ kind: "language-removed",
434
+ subject: language,
435
+ change: "removed",
436
+ evidence: [],
437
+ detectedAtMs
438
+ });
439
+ }
440
+ const frameworks = diffSets(stored.frameworks, current.frameworks);
441
+ for (const framework of frameworks.added) {
442
+ records.push({
443
+ key: `framework:${framework}`,
444
+ kind: "framework-changed",
445
+ subject: framework,
446
+ change: "added",
447
+ evidence: appearedManifests,
448
+ detectedAtMs
449
+ });
450
+ }
451
+ for (const framework of frameworks.removed) {
452
+ records.push({
453
+ key: `framework:${framework}`,
454
+ kind: "framework-changed",
455
+ subject: framework,
456
+ change: "removed",
457
+ evidence: [],
458
+ detectedAtMs
459
+ });
460
+ }
461
+ const explained = new Set(records.flatMap((record) => record.evidence));
462
+ for (const manifest of appearedManifests) {
463
+ if (explained.has(manifest)) continue;
464
+ records.push({
465
+ key: `manifest:${manifest}`,
466
+ kind: "manifest-appeared",
467
+ subject: manifest,
468
+ change: "added",
469
+ evidence: [manifest],
470
+ detectedAtMs
471
+ });
472
+ }
473
+ return records;
474
+ }
475
+ function checkDrift(cwd, options = {}) {
476
+ const now = options.now ?? Date.now;
477
+ const current = computeStackFingerprint(cwd);
478
+ const currentDigest = fingerprintDigest(current);
479
+ const stored = readStoredFingerprint(cwd);
480
+ if (stored === void 0) {
481
+ writeStoredFingerprint(cwd, current, options);
482
+ return {
483
+ hadBaseline: false,
484
+ newDrift: [],
485
+ pending: listPendingDrift(cwd, options),
486
+ currentDigest
487
+ };
488
+ }
489
+ if (stored.digest === currentDigest) {
490
+ return {
491
+ hadBaseline: true,
492
+ newDrift: [],
493
+ pending: listPendingDrift(cwd, options),
494
+ currentDigest
495
+ };
496
+ }
497
+ const nowMs = now();
498
+ const newDrift = detectDrift(stored.fingerprint, current).map((record) => ({
499
+ ...record,
500
+ detectedAtMs: nowMs
501
+ }));
502
+ if (newDrift.length > 0) {
503
+ recordDriftRecords(cwd, newDrift, options);
504
+ }
505
+ writeStoredFingerprint(cwd, current, options);
506
+ return {
507
+ hadBaseline: true,
508
+ newDrift,
509
+ pending: listPendingDrift(cwd, options),
510
+ currentDigest
511
+ };
512
+ }
513
+ var LANGUAGE_LABELS = {
514
+ python: { label: "Python", artifacts: "recommend Python docs/resources for coding agents" },
515
+ go: { label: "Go", artifacts: "recommend Go docs/resources for coding agents" },
516
+ rust: { label: "Rust", artifacts: "recommend Rust docs/resources for coding agents" },
517
+ swift: { label: "Swift", artifacts: "recommend Swift/Apple docs/resources for coding agents" },
518
+ typescript: {
519
+ label: "TypeScript",
520
+ artifacts: "recommend TypeScript docs/resources for coding agents"
521
+ },
522
+ javascript: {
523
+ label: "JavaScript",
524
+ artifacts: "recommend JavaScript docs/resources for coding agents"
525
+ },
526
+ node: { label: "Node.js", artifacts: "recommend Node.js docs/resources for coding agents" },
527
+ java: { label: "Java", artifacts: "recommend Java docs/resources for coding agents" },
528
+ kotlin: { label: "Kotlin", artifacts: "recommend Kotlin docs/resources for coding agents" },
529
+ ruby: { label: "Ruby", artifacts: "recommend Ruby docs/resources for coding agents" },
530
+ php: { label: "PHP", artifacts: "recommend PHP docs/resources for coding agents" },
531
+ dart: { label: "Dart", artifacts: "recommend Dart/Flutter docs/resources for coding agents" },
532
+ dotnet: { label: ".NET", artifacts: "recommend .NET docs/resources for coding agents" },
533
+ c: { label: "C", artifacts: "recommend C docs/resources for coding agents" },
534
+ cpp: { label: "C++", artifacts: "recommend C++ docs/resources for coding agents" }
535
+ };
536
+ function languageLabel(subject) {
537
+ return LANGUAGE_LABELS[subject] ?? {
538
+ label: subject,
539
+ artifacts: `recommend ${subject} docs/resources for coding agents`
540
+ };
541
+ }
542
+ function evidenceSuffix(record) {
543
+ if (record.evidence.length === 0) return "";
544
+ return ` (${record.evidence.join(", ")} added)`;
545
+ }
546
+ function composeNudgeMessage(record) {
547
+ const previewPromise = "Run 'poetic init' when ready \u2014 it will show you exactly what it proposes before changing anything.";
548
+ switch (record.kind) {
549
+ case "language-added": {
550
+ const { label, artifacts } = languageLabel(record.subject);
551
+ return `Notice: New since last check: ${label}${evidenceSuffix(record)}. Poetic can update the agent instructions (AGENTS.md) and ${artifacts}. ` + previewPromise;
552
+ }
553
+ case "language-removed": {
554
+ const { label } = languageLabel(record.subject);
555
+ return `Notice: Changed since last check: ${label} no longer detected in this repo. Poetic can update the agent instructions (AGENTS.md) and retire stale ${label} guidance for coding agents. ` + previewPromise;
556
+ }
557
+ case "framework-changed": {
558
+ const direction = record.change === "added" ? "added" : "removed";
559
+ return `Notice: Changed since last check: ${record.subject} toolchain signal ${direction}${evidenceSuffix(record)}. Poetic can update the agent instructions (AGENTS.md) and adjust the recommended docs/resources for coding agents. ` + previewPromise;
560
+ }
561
+ case "manifest-appeared":
562
+ return `Notice: New since last check: ${record.subject} appeared. Poetic can update the agent instructions (AGENTS.md) and recommend docs/resources matching the new stack surface. ` + previewPromise;
563
+ }
564
+ }
565
+
566
+ // src/core/init-drift/render.ts
567
+ function shouldSuppressNudges(policy, options = {}) {
568
+ if (policy.json || policy.quiet) return true;
569
+ if (isCiEnvironment(options.env ?? process.env)) return true;
570
+ const stderrIsTty = options.stderrIsTty ?? Boolean(process.stderr.isTTY);
571
+ return !stderrIsTty;
572
+ }
573
+ function formatAge(ageMs) {
574
+ const days = Math.floor(ageMs / (24 * 60 * 60 * 1e3));
575
+ if (days >= 2) return `${days} days`;
576
+ if (days === 1) return "1 day";
577
+ const hours = Math.floor(ageMs / (60 * 60 * 1e3));
578
+ if (hours >= 1) return hours === 1 ? "1 hour" : `${hours} hours`;
579
+ return "less than an hour";
580
+ }
581
+ function defaultWriteLine(line) {
582
+ process.stderr.write(`${line}
583
+ `);
584
+ }
585
+ function renderPendingNudges(cwd, streamPolicy, options = {}) {
586
+ const pending = listPendingDrift(cwd, options);
587
+ if (shouldSuppressNudges(streamPolicy, options)) {
588
+ return { rendered: false, deliveredKeys: [], pendingCount: pending.length };
589
+ }
590
+ if (pending.length === 0) {
591
+ return { rendered: false, deliveredKeys: [], pendingCount: 0 };
592
+ }
593
+ const writeLine = options.writeLine ?? defaultWriteLine;
594
+ for (const record of pending) {
595
+ const message = composeNudgeMessage(record);
596
+ const suffix = options.includeAge === true ? ` (not yet reviewed, ${formatAge(record.ageMs)})` : "";
597
+ writeLine(`${message}${suffix}`);
598
+ }
599
+ const deliveredKeys = pending.map((record) => record.key);
600
+ recordDelivery(cwd, deliveredKeys, options);
601
+ return { rendered: true, deliveredKeys, pendingCount: 0 };
602
+ }
603
+ function pendingNoticesCount(cwd, options = {}) {
604
+ return listPendingDrift(cwd, options).length;
605
+ }
606
+
607
+ // src/core/knowledge/doctor-checks.ts
608
+ import { existsSync, readFileSync as readFileSync3 } from "fs";
609
+ import { isIP } from "net";
610
+ import path4 from "path";
611
+
612
+ // src/core/knowledge/wrapper-visibility.ts
613
+ import { lstatSync, readFileSync as readFileSync2 } from "fs";
614
+ import path3 from "path";
615
+ var WRAPPER_FILENAMES = ["CLAUDE.md", "GEMINI.md"];
616
+ var MAX_WRAPPER_BYTES = 16e3;
617
+ function isRegularFile(filePath) {
618
+ try {
619
+ return lstatSync(filePath).isFile();
620
+ } catch {
621
+ return false;
622
+ }
623
+ }
624
+ function readTextSafe(filePath, maxBytes = MAX_WRAPPER_BYTES) {
625
+ try {
626
+ return readFileSync2(filePath, "utf8").slice(0, maxBytes);
627
+ } catch {
628
+ return "";
629
+ }
630
+ }
631
+ var AGENTS_INCLUDE_PATTERN = /@\S*AGENTS\.md/;
632
+ function checkWrapperVisibility(root) {
633
+ const resolved = path3.resolve(root);
634
+ if (!isRegularFile(path3.join(resolved, "AGENTS.md"))) {
635
+ return { applicable: false, wrappers: [] };
636
+ }
637
+ const wrappers = WRAPPER_FILENAMES.map((filename) => {
638
+ const wrapperPath = path3.join(resolved, filename);
639
+ if (!isRegularFile(wrapperPath)) {
640
+ return { filename, status: "missing" };
641
+ }
642
+ const content = readTextSafe(wrapperPath);
643
+ return {
644
+ filename,
645
+ status: AGENTS_INCLUDE_PATTERN.test(content) ? "visible" : "exists-no-reference"
646
+ };
647
+ });
648
+ return { applicable: true, wrappers };
649
+ }
650
+
651
+ // src/core/knowledge/doctor-checks.ts
652
+ var DEFAULT_STALE_REF_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
653
+ var BLOCKED_LINK_HOSTNAMES = /* @__PURE__ */ new Set([
654
+ "localhost",
655
+ "0.0.0.0",
656
+ "::",
657
+ "::1",
658
+ "metadata",
659
+ "metadata.google.internal",
660
+ "metadata.goog",
661
+ "kubernetes.default",
662
+ "kubernetes.default.svc"
663
+ ]);
664
+ function isBlockedHostname(hostname) {
665
+ const host = hostname.toLowerCase().replace(/\.$/, "");
666
+ if (BLOCKED_LINK_HOSTNAMES.has(host)) return true;
667
+ if (host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal")) {
668
+ return true;
669
+ }
670
+ if (host.endsWith(".lan") || host.endsWith(".home") || host.endsWith(".corp")) {
671
+ return true;
672
+ }
673
+ return false;
674
+ }
675
+ function isBlockedIpv4(ip) {
676
+ const parts = ip.split(".").map((p) => Number(p));
677
+ if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {
678
+ return true;
679
+ }
680
+ const [a, b] = parts;
681
+ if (a === 0) return true;
682
+ if (a === 10) return true;
683
+ if (a === 127) return true;
684
+ if (a === 169 && b === 254) return true;
685
+ if (a === 172 && b >= 16 && b <= 31) return true;
686
+ if (a === 192 && b === 168) return true;
687
+ if (a === 100 && b >= 64 && b <= 127) return true;
688
+ return false;
689
+ }
690
+ function ipv4FromHextets(high, low) {
691
+ return [high >> 8, high & 255, low >> 8, low & 255].join(".");
692
+ }
693
+ function parseIpv4Hextets(ip) {
694
+ const parts = ip.split(".").map((p) => Number(p));
695
+ if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {
696
+ return void 0;
697
+ }
698
+ const [a, b, c, d] = parts;
699
+ return [a << 8 | b, c << 8 | d];
700
+ }
701
+ function parseIpv6Hextets(ip) {
702
+ if (!ip || ip.includes(":::")) return void 0;
703
+ const sides = ip.toLowerCase().split("::");
704
+ if (sides.length > 2) return void 0;
705
+ const parseSide = (side) => {
706
+ if (!side) return [];
707
+ const result = [];
708
+ for (const rawPart of side.split(":")) {
709
+ if (!rawPart) return void 0;
710
+ if (rawPart.includes(".")) {
711
+ const hextets = parseIpv4Hextets(rawPart);
712
+ if (!hextets) return void 0;
713
+ result.push(...hextets);
714
+ continue;
715
+ }
716
+ if (!/^[0-9a-f]{1,4}$/i.test(rawPart)) return void 0;
717
+ result.push(Number.parseInt(rawPart, 16));
718
+ }
719
+ return result;
720
+ };
721
+ const left = parseSide(sides[0] ?? "");
722
+ const right = parseSide(sides[1] ?? "");
723
+ if (!left || !right) return void 0;
724
+ if (sides.length === 1) {
725
+ return left.length === 8 ? left : void 0;
726
+ }
727
+ const zeroCount = 8 - left.length - right.length;
728
+ if (zeroCount < 1) return void 0;
729
+ return [...left, ...Array(zeroCount).fill(0), ...right];
730
+ }
731
+ function isBlockedIpv6(ip) {
732
+ const normalized = ip.toLowerCase();
733
+ if (normalized === "::" || normalized === "::1") return true;
734
+ const hextets = parseIpv6Hextets(normalized);
735
+ if (!hextets || hextets.length !== 8) return true;
736
+ const [first] = hextets;
737
+ if ((first & 65024) === 64512) return true;
738
+ if ((first & 65472) === 65152) return true;
739
+ const firstFiveZero = hextets.slice(0, 5).every((part) => part === 0);
740
+ const firstSixZero = firstFiveZero && hextets[5] === 0;
741
+ const embeddedIpv4 = ipv4FromHextets(hextets[6], hextets[7]);
742
+ if (firstFiveZero && hextets[5] === 65535 && isBlockedIpv4(embeddedIpv4)) {
743
+ return true;
744
+ }
745
+ if (firstSixZero && isBlockedIpv4(embeddedIpv4)) return true;
746
+ if (hextets[0] === 100 && hextets[1] === 65435 && hextets.slice(2, 6).every((p) => p === 0)) {
747
+ return isBlockedIpv4(embeddedIpv4);
748
+ }
749
+ return false;
750
+ }
751
+ function unsafeLinkProbeReason(urlString) {
752
+ let url;
753
+ try {
754
+ url = new URL(urlString);
755
+ } catch {
756
+ return "invalid URL";
757
+ }
758
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
759
+ return `disallowed scheme ${url.protocol}`;
760
+ }
761
+ if (url.username || url.password) {
762
+ return "URL credentials not allowed";
763
+ }
764
+ let hostname = url.hostname.toLowerCase();
765
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
766
+ hostname = hostname.slice(1, -1);
767
+ }
768
+ if (!hostname) return "missing hostname";
769
+ if (isBlockedHostname(hostname)) return `blocked hostname ${hostname}`;
770
+ const ipVersion = isIP(hostname);
771
+ if (ipVersion === 4 && isBlockedIpv4(hostname)) {
772
+ return `blocked address ${hostname}`;
773
+ }
774
+ if (ipVersion === 6 && isBlockedIpv6(hostname)) {
775
+ return `blocked address ${hostname}`;
776
+ }
777
+ return void 0;
778
+ }
779
+ var MAX_LINK_PROBE_REDIRECTS = 5;
780
+ var FOLLOWED_REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
781
+ function redirectLocation(response) {
782
+ if (!FOLLOWED_REDIRECT_STATUSES.has(response.status)) return void 0;
783
+ const location = response.headers.get("location");
784
+ return location !== null && location.length > 0 ? location : void 0;
785
+ }
786
+ async function fetchWithScreenedRedirects(method, url, timeoutMs) {
787
+ const signal = AbortSignal.timeout(timeoutMs);
788
+ let current = url;
789
+ for (let hop = 0; hop <= MAX_LINK_PROBE_REDIRECTS; hop += 1) {
790
+ if (unsafeLinkProbeReason(current) !== void 0) return void 0;
791
+ const response = await fetch(current, { method, redirect: "manual", signal });
792
+ const location = redirectLocation(response);
793
+ if (location === void 0) return response;
794
+ try {
795
+ current = new URL(location, current).toString();
796
+ } catch {
797
+ return void 0;
798
+ }
799
+ }
800
+ return void 0;
801
+ }
802
+ async function defaultProbeLink(url, timeoutMs) {
803
+ if (unsafeLinkProbeReason(url) !== void 0) return false;
804
+ try {
805
+ const response = await fetchWithScreenedRedirects("HEAD", url, timeoutMs);
806
+ if (response === void 0) return false;
807
+ if (response.status === 405 || response.status === 501) {
808
+ const getResponse = await fetchWithScreenedRedirects("GET", url, timeoutMs);
809
+ if (getResponse === void 0) return false;
810
+ return getResponse.status < 400;
811
+ }
812
+ return response.status < 400;
813
+ } catch {
814
+ return false;
815
+ }
816
+ }
817
+ function buildWrapperVisibilityFinding(root) {
818
+ const visibility = checkWrapperVisibility(root);
819
+ if (!visibility.applicable) return void 0;
820
+ const missing = visibility.wrappers.filter((w) => w.status === "missing");
821
+ const unreferenced = visibility.wrappers.filter(
822
+ (w) => w.status === "exists-no-reference"
823
+ );
824
+ if (missing.length === 0 && unreferenced.length === 0) return void 0;
825
+ const parts = [];
826
+ if (missing.length > 0) {
827
+ parts.push(
828
+ `${missing.map((w) => w.filename).join(" and ")} missing \u2014 agents will not load the kit. Create the wrapper(s) with: poetic generate-instructions --target claude --target gemini.`
829
+ );
830
+ }
831
+ if (unreferenced.length > 0) {
832
+ parts.push(
833
+ `${unreferenced.map((w) => w.filename).join(" and ")} exists but does not reference AGENTS.md \u2014 add \`@./AGENTS.md\` to the file manually (Poetic will not overwrite it).`
834
+ );
835
+ }
836
+ return {
837
+ id: "wrapper-not-visible",
838
+ severity: "info",
839
+ message: `AGENTS.md is present but not visible to all agents: ${parts.join(" ")}`
840
+ };
841
+ }
842
+ async function knowledgeDoctorChecks(cwd, options = {}) {
843
+ const root = path4.resolve(cwd);
844
+ const findings = [];
845
+ const manifestResult = readKnowledgeManifest(root);
846
+ if (manifestResult.status === "absent") {
847
+ return { manifestPresent: false, findings };
848
+ }
849
+ if (manifestResult.status === "invalid") {
850
+ findings.push({
851
+ id: "manifest-invalid",
852
+ severity: "error",
853
+ message: `.poetic/context/knowledge/manifest.json is invalid: ${manifestResult.error}`
854
+ });
855
+ return { manifestPresent: true, findings };
856
+ }
857
+ const manifest = manifestResult.manifest;
858
+ let detection = detectStack(root);
859
+ const intent = readIntentContract(root);
860
+ if (intent.status === "ok" && intent.contract.status !== "retired") {
861
+ const resolution = resolveIntent(detection, intent.contract);
862
+ detection = resolution.effective;
863
+ for (const conflict of resolution.conflicts) {
864
+ findings.push({
865
+ id: "stack-intent-conflict",
866
+ severity: "warn",
867
+ message: `Stack intent conflict: declared ${conflict.dimension.token} vs detected ${conflict.detectedToken}. Resolve at an interactive 'poetic init' (keep both / accept detected).`
868
+ });
869
+ }
870
+ }
871
+ if (detection.fingerprint !== manifest.stackFingerprint) {
872
+ findings.push({
873
+ id: "manifest-drift",
874
+ severity: "warn",
875
+ message: `The repo stack has changed since the knowledge kit was accepted (fingerprint ${manifest.stackFingerprint} -> ${detection.fingerprint}). Re-run 'poetic init' to review updated recommendations.`
876
+ });
877
+ }
878
+ const staleTtlMs = options.staleTtlMs ?? DEFAULT_STALE_REF_TTL_MS;
879
+ const nowMs = (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
880
+ for (const item of manifest.items) {
881
+ const itemDate = Date.parse(item.versionOrDate);
882
+ if (!Number.isFinite(itemDate)) continue;
883
+ if (nowMs - itemDate > staleTtlMs) {
884
+ const days = Math.floor((nowMs - itemDate) / (24 * 60 * 60 * 1e3));
885
+ findings.push({
886
+ id: "stale-ref",
887
+ severity: "info",
888
+ itemId: item.id,
889
+ message: `Knowledge item "${item.id}" was accepted ${days} days ago and may be stale.`
890
+ });
891
+ }
892
+ }
893
+ const agentsPath = path4.join(root, "AGENTS.md");
894
+ let agentsContent = "";
895
+ try {
896
+ agentsContent = existsSync(agentsPath) ? readFileSync3(agentsPath, "utf8") : "";
897
+ } catch {
898
+ agentsContent = "";
899
+ }
900
+ const hasBegin = agentsContent.includes(KNOWLEDGE_BLOCK_BEGIN);
901
+ const hasEnd = agentsContent.includes(KNOWLEDGE_BLOCK_END);
902
+ const blockValid = hasBegin && hasEnd && agentsContent.indexOf(KNOWLEDGE_BLOCK_END) >= agentsContent.indexOf(KNOWLEDGE_BLOCK_BEGIN);
903
+ if (!hasBegin && !hasEnd) {
904
+ findings.push({
905
+ id: "agents-block-missing",
906
+ severity: "warn",
907
+ message: "A knowledge manifest exists but AGENTS.md has no poetic-knowledge managed block. Re-run 'poetic init' to restore it."
908
+ });
909
+ } else if (hasBegin !== hasEnd || agentsContent.indexOf(KNOWLEDGE_BLOCK_END) < agentsContent.indexOf(KNOWLEDGE_BLOCK_BEGIN)) {
910
+ findings.push({
911
+ id: "agents-block-malformed",
912
+ severity: "error",
913
+ message: "AGENTS.md contains a malformed poetic-knowledge block (unpaired or out-of-order markers)."
914
+ });
915
+ }
916
+ if (blockValid) {
917
+ const wrapperFinding = buildWrapperVisibilityFinding(root);
918
+ if (wrapperFinding) findings.push(wrapperFinding);
919
+ }
920
+ if (options.checkLinks === true) {
921
+ const probe = options.probeLink ?? defaultProbeLink;
922
+ const timeoutMs = options.linkTimeoutMs ?? 1e4;
923
+ for (const item of manifest.items) {
924
+ const blocked = unsafeLinkProbeReason(item.sourceUrl);
925
+ if (blocked !== void 0) {
926
+ findings.push({
927
+ id: "dead-link",
928
+ severity: "warn",
929
+ itemId: item.id,
930
+ message: `Knowledge item "${item.id}" source URL was not probed (${blocked}): ${item.sourceUrl}`
931
+ });
932
+ continue;
933
+ }
934
+ const alive = await probe(item.sourceUrl, timeoutMs);
935
+ if (!alive) {
936
+ findings.push({
937
+ id: "dead-link",
938
+ severity: "warn",
939
+ itemId: item.id,
940
+ message: `Knowledge item "${item.id}" source URL appears unreachable: ${item.sourceUrl}`
941
+ });
942
+ }
943
+ }
944
+ }
945
+ return { manifestPresent: true, findings };
946
+ }
947
+
948
+ // src/core/knowledge/pending-notice.ts
949
+ import { existsSync as existsSync2, readFileSync as readFileSync4, unlinkSync } from "fs";
950
+ import path5 from "path";
951
+ import { z } from "zod";
952
+ var PENDING_OFFER_NOTICE_MESSAGE = "Knowledge kit: offer skipped (non-interactive session). Run poetic knowledge offer for a headless offer, or re-run poetic init in a terminal.";
953
+ var PendingOfferNoticeSchema = z.object({
954
+ schemaVersion: z.literal(1),
955
+ skippedAt: z.string().min(1),
956
+ cause: z.union([z.literal("non-interactive"), z.literal("suppressed")]),
957
+ message: z.string().min(1)
958
+ });
959
+ function pendingOfferNoticePath(cwd) {
960
+ return path5.join(cwd, ".poetic", "state", "knowledge", "pending-offer-notice.json");
961
+ }
962
+ function writePendingOfferNotice(cwd, cause) {
963
+ const notice = {
964
+ schemaVersion: 1,
965
+ skippedAt: (/* @__PURE__ */ new Date()).toISOString(),
966
+ cause,
967
+ message: PENDING_OFFER_NOTICE_MESSAGE
968
+ };
969
+ try {
970
+ atomicWriteJsonSync(pendingOfferNoticePath(cwd), notice);
971
+ } catch {
972
+ }
973
+ }
974
+ function readPendingOfferNotice(cwd) {
975
+ const filePath = pendingOfferNoticePath(cwd);
976
+ if (!existsSync2(filePath)) return void 0;
977
+ try {
978
+ const parsed = PendingOfferNoticeSchema.safeParse(JSON.parse(readFileSync4(filePath, "utf8")));
979
+ if (!parsed.success) return void 0;
980
+ return { ...parsed.data, message: PENDING_OFFER_NOTICE_MESSAGE };
981
+ } catch {
982
+ return void 0;
983
+ }
984
+ }
985
+ function clearPendingOfferNotice(cwd) {
986
+ try {
987
+ unlinkSync(pendingOfferNoticePath(cwd));
988
+ } catch {
989
+ }
990
+ }
991
+
992
+ // src/planning/archive-reconciliation.ts
993
+ function hasArchivedMarker(item) {
994
+ const fm = item.frontMatter;
995
+ return typeof fm.archivedAt === "string" && fm.archivedAt.length > 0;
996
+ }
997
+ function indexById(items) {
998
+ const map = /* @__PURE__ */ new Map();
999
+ for (const item of items) {
1000
+ if (!map.has(item.id)) {
1001
+ map.set(item.id, item);
1002
+ }
1003
+ }
1004
+ return map;
1005
+ }
1006
+ function detectDuplicates(kind, active, archived) {
1007
+ const archivedById = indexById(archived);
1008
+ const issues = [];
1009
+ for (const activeItem of active) {
1010
+ const archivedMatch = archivedById.get(activeItem.id);
1011
+ if (!archivedMatch) continue;
1012
+ const preferred = hasArchivedMarker(archivedMatch) ? "archive" : hasArchivedMarker(activeItem) ? "active" : "archive";
1013
+ issues.push({
1014
+ kind,
1015
+ id: activeItem.id,
1016
+ activePath: activeItem.filePath,
1017
+ archivePath: archivedMatch.filePath,
1018
+ preferred
1019
+ });
1020
+ }
1021
+ return issues;
1022
+ }
1023
+ function detectDanglingSprintMembership(sprints, activeTaskIds, archivedTaskIds) {
1024
+ const issues = [];
1025
+ for (const sprint of sprints) {
1026
+ const taskIdsRaw = sprint.frontMatter.taskIds;
1027
+ if (!Array.isArray(taskIdsRaw)) continue;
1028
+ for (const taskId of taskIdsRaw) {
1029
+ if (typeof taskId !== "string") continue;
1030
+ if (activeTaskIds.has(taskId)) continue;
1031
+ if (archivedTaskIds.has(taskId)) {
1032
+ issues.push({ sprintId: sprint.id, taskId, reason: "archived-but-listed" });
1033
+ } else {
1034
+ issues.push({ sprintId: sprint.id, taskId, reason: "missing" });
1035
+ }
1036
+ }
1037
+ }
1038
+ return issues;
1039
+ }
1040
+ function detectOrphanedFollowups(backlogActive, backlogArchived) {
1041
+ const knownBacklogIds = /* @__PURE__ */ new Set();
1042
+ for (const item of [...backlogActive, ...backlogArchived]) {
1043
+ knownBacklogIds.add(item.id);
1044
+ }
1045
+ const issues = [];
1046
+ for (const item of backlogActive) {
1047
+ const spawnedFrom = item.frontMatter.spawnedFrom;
1048
+ if (typeof spawnedFrom !== "string" || spawnedFrom.length === 0) continue;
1049
+ if (!knownBacklogIds.has(spawnedFrom)) {
1050
+ issues.push({ followupId: item.id, parentId: spawnedFrom, followupPath: item.filePath });
1051
+ }
1052
+ }
1053
+ return issues;
1054
+ }
1055
+ async function detectArchiveReconciliationIssues(planningRoot = getPlanningRoot()) {
1056
+ const taskActiveDir = getBucketPathByKind("task", planningRoot);
1057
+ const taskArchiveDir = getBucketArchivePathByKind("task", planningRoot);
1058
+ const backlogActiveDir = getBucketPathByKind("backlog", planningRoot);
1059
+ const backlogArchiveDir = getBucketArchivePathByKind("backlog", planningRoot);
1060
+ const sprintActiveDir = getBucketPathByKind("sprint", planningRoot);
1061
+ const [activeTasks, archivedTasks, activeBacklog, archivedBacklog, sprints] = await Promise.all([
1062
+ loadAllItems(planningRoot, [taskActiveDir]),
1063
+ loadAllItems(planningRoot, [taskArchiveDir]),
1064
+ loadAllItems(planningRoot, [backlogActiveDir]),
1065
+ loadAllItems(planningRoot, [backlogArchiveDir]),
1066
+ loadAllItems(planningRoot, [sprintActiveDir])
1067
+ ]);
1068
+ const duplicateRecords = [
1069
+ ...detectDuplicates("task", activeTasks, archivedTasks),
1070
+ ...detectDuplicates("backlog", activeBacklog, archivedBacklog)
1071
+ ];
1072
+ const activeTaskIds = new Set(activeTasks.map((t) => t.id));
1073
+ const archivedTaskIds = new Set(archivedTasks.map((t) => t.id));
1074
+ const danglingSprintMembership = detectDanglingSprintMembership(
1075
+ sprints,
1076
+ activeTaskIds,
1077
+ archivedTaskIds
1078
+ );
1079
+ const orphanedFollowups = detectOrphanedFollowups(activeBacklog, archivedBacklog);
1080
+ return {
1081
+ duplicateRecords,
1082
+ danglingSprintMembership,
1083
+ orphanedFollowups,
1084
+ totalIssues: duplicateRecords.length + danglingSprintMembership.length + orphanedFollowups.length
1085
+ };
1086
+ }
1087
+ function formatArchiveReconciliationReport(report) {
1088
+ if (report.totalIssues === 0) return "";
1089
+ const lines = [];
1090
+ if (report.duplicateRecords.length > 0) {
1091
+ lines.push(`Duplicate active+archive records: ${report.duplicateRecords.length}`);
1092
+ for (const dup of report.duplicateRecords) {
1093
+ lines.push(` ${dup.kind} ${dup.id} (keep: ${dup.preferred})`);
1094
+ }
1095
+ }
1096
+ if (report.danglingSprintMembership.length > 0) {
1097
+ lines.push(`Dangling sprint membership: ${report.danglingSprintMembership.length}`);
1098
+ for (const ref of report.danglingSprintMembership) {
1099
+ lines.push(` sprint ${ref.sprintId} -> ${ref.taskId} (${ref.reason})`);
1100
+ }
1101
+ }
1102
+ if (report.orphanedFollowups.length > 0) {
1103
+ lines.push(`Orphaned followups: ${report.orphanedFollowups.length}`);
1104
+ for (const orphan of report.orphanedFollowups) {
1105
+ lines.push(` ${orphan.followupId} -> missing parent ${orphan.parentId}`);
1106
+ }
1107
+ }
1108
+ return lines.join("\n");
1109
+ }
1110
+
1111
+ // src/cli/commands/doctor-guard.ts
1112
+ import { promises as fs4 } from "fs";
1113
+ import * as path9 from "path";
1114
+
1115
+ // src/core/guard-kit/kit-version.ts
1116
+ var GUARD_KIT_VERSION = "0.2.2";
1117
+
1118
+ // src/core/guard-kit/state.ts
1119
+ import { createHash as createHash2 } from "crypto";
1120
+ import * as fs3 from "fs";
1121
+ import * as path8 from "path";
1122
+
1123
+ // src/core/guard-kit/case-verify.ts
1124
+ import { execFileSync } from "child_process";
1125
+ import * as fs2 from "fs";
1126
+ import * as path7 from "path";
1127
+
1128
+ // src/core/guard-kit/paths.ts
1129
+ import * as path6 from "path";
1130
+ var GUARD_DIR_REL = path6.join(".poetic", "guard");
1131
+ var CHECK_SCRIPT_REL = "check.mjs";
1132
+ var CONFIG_FILE_REL = "config.json";
1133
+ var LOCK_FILE_REL = "checks.lock";
1134
+ var SETUP_PLAYBOOK_REL = "GUARD_SETUP.md";
1135
+ var CI_WORKFLOW_TEMPLATE_FILE = "poetic-guard.yml.template";
1136
+ var BASELINES_DIR_REL = "baselines";
1137
+ var GENERATED_HASHED_FILES = [CHECK_SCRIPT_REL];
1138
+ function getGuardKitTemplatesDir() {
1139
+ return path6.join(getPackageRoot(), "templates", "guard-kit");
1140
+ }
1141
+
1142
+ // src/core/guard-kit/case-verify.ts
1143
+ var CANONICAL_GUARD_FILES = [
1144
+ CHECK_SCRIPT_REL,
1145
+ LOCK_FILE_REL,
1146
+ CONFIG_FILE_REL,
1147
+ BASELINES_DIR_REL
1148
+ ];
1149
+ function toPosix(value) {
1150
+ return value.replace(/\\/g, "/");
1151
+ }
1152
+ function findCaseVariantEntry(dir, expected) {
1153
+ let entries;
1154
+ try {
1155
+ entries = fs2.readdirSync(dir);
1156
+ } catch {
1157
+ return null;
1158
+ }
1159
+ for (const entry of entries) {
1160
+ if (entry !== expected && entry.toLowerCase() === expected.toLowerCase()) {
1161
+ return entry;
1162
+ }
1163
+ }
1164
+ return null;
1165
+ }
1166
+ function detectFilesystemCaseIssues(repoRoot) {
1167
+ const issues = [];
1168
+ const guardSegments = GUARD_DIR_REL.split(path7.sep);
1169
+ let currentDir = repoRoot;
1170
+ let relPrefix = "";
1171
+ for (const segment of guardSegments) {
1172
+ const variant = findCaseVariantEntry(currentDir, segment);
1173
+ if (variant !== null) {
1174
+ issues.push({
1175
+ source: "filesystem",
1176
+ actual: toPosix(path7.join(relPrefix, variant)),
1177
+ expected: toPosix(path7.join(relPrefix, segment))
1178
+ });
1179
+ }
1180
+ relPrefix = path7.join(relPrefix, segment);
1181
+ currentDir = path7.join(currentDir, segment);
1182
+ }
1183
+ for (const name of CANONICAL_GUARD_FILES) {
1184
+ const variant = findCaseVariantEntry(currentDir, name);
1185
+ if (variant !== null) {
1186
+ issues.push({
1187
+ source: "filesystem",
1188
+ actual: toPosix(path7.join(relPrefix, variant)),
1189
+ expected: toPosix(path7.join(relPrefix, name))
1190
+ });
1191
+ }
1192
+ }
1193
+ return issues;
1194
+ }
1195
+ function detectGitCaseIssues(repoRoot) {
1196
+ let stdout;
1197
+ try {
1198
+ stdout = execFileSync("git", ["ls-files", "-z"], {
1199
+ cwd: repoRoot,
1200
+ encoding: "utf8",
1201
+ maxBuffer: 64 * 1024 * 1024
1202
+ });
1203
+ } catch {
1204
+ return [];
1205
+ }
1206
+ const guardPrefix = `${toPosix(GUARD_DIR_REL)}/`;
1207
+ const guardPrefixLower = guardPrefix.toLowerCase();
1208
+ const issues = [];
1209
+ for (const raw of stdout.split("\0")) {
1210
+ if (!raw) continue;
1211
+ const tracked = toPosix(raw);
1212
+ if (!tracked.toLowerCase().startsWith(guardPrefixLower)) continue;
1213
+ if (tracked.startsWith(guardPrefix)) {
1214
+ const rest = tracked.slice(guardPrefix.length);
1215
+ const firstSegment = rest.split("/")[0] ?? "";
1216
+ const canonical = CANONICAL_GUARD_FILES.find(
1217
+ (name) => name !== firstSegment && name.toLowerCase() === firstSegment.toLowerCase()
1218
+ );
1219
+ if (canonical !== void 0) {
1220
+ issues.push({
1221
+ source: "git",
1222
+ actual: tracked,
1223
+ expected: `${guardPrefix}${canonical}${rest.slice(firstSegment.length)}`
1224
+ });
1225
+ }
1226
+ continue;
1227
+ }
1228
+ issues.push({
1229
+ source: "git",
1230
+ actual: tracked,
1231
+ expected: `${guardPrefix}${tracked.slice(guardPrefix.length)}`
1232
+ });
1233
+ }
1234
+ return issues;
1235
+ }
1236
+ function detectGuardCaseIssues(repoRoot) {
1237
+ const combined = [...detectFilesystemCaseIssues(repoRoot), ...detectGitCaseIssues(repoRoot)];
1238
+ const seen = /* @__PURE__ */ new Set();
1239
+ const issues = [];
1240
+ for (const issue of combined) {
1241
+ if (seen.has(issue.actual)) continue;
1242
+ seen.add(issue.actual);
1243
+ issues.push(issue);
1244
+ }
1245
+ return issues;
1246
+ }
1247
+ function repairGuardCaseIssues(repoRoot, issues) {
1248
+ const repaired = [];
1249
+ let remaining = [];
1250
+ let pending = issues;
1251
+ for (let round = 0; round < 5; round++) {
1252
+ const result = repairPass(repoRoot, pending);
1253
+ repaired.push(...result.repaired);
1254
+ remaining = result.remaining;
1255
+ if (result.repaired.length === 0) break;
1256
+ const fresh = detectGuardCaseIssues(repoRoot);
1257
+ if (fresh.length === 0) break;
1258
+ const alreadyKnown = new Set(remaining.map((issue) => issue.actual));
1259
+ pending = fresh.filter((issue) => !alreadyKnown.has(issue.actual));
1260
+ if (pending.length === 0) break;
1261
+ }
1262
+ return { repaired, remaining };
1263
+ }
1264
+ function repairPass(repoRoot, issues) {
1265
+ const repaired = [];
1266
+ const remaining = [];
1267
+ const ordered = [...issues].sort(
1268
+ (a, b) => a.actual.split("/").length - b.actual.split("/").length
1269
+ );
1270
+ for (const issue of ordered) {
1271
+ if (issue.source !== "filesystem") {
1272
+ remaining.push(issue);
1273
+ continue;
1274
+ }
1275
+ const actualAbs = path7.join(repoRoot, ...issue.actual.split("/"));
1276
+ const expectedAbs = path7.join(repoRoot, ...issue.expected.split("/"));
1277
+ const parent = path7.dirname(actualAbs);
1278
+ const expectedName = path7.basename(expectedAbs);
1279
+ let entries;
1280
+ try {
1281
+ entries = fs2.readdirSync(parent);
1282
+ } catch {
1283
+ remaining.push(issue);
1284
+ continue;
1285
+ }
1286
+ if (entries.includes(expectedName)) {
1287
+ remaining.push(issue);
1288
+ continue;
1289
+ }
1290
+ try {
1291
+ let tmp = `${actualAbs}.case-repair-tmp`;
1292
+ let suffix = 0;
1293
+ while (fs2.existsSync(tmp)) {
1294
+ suffix += 1;
1295
+ if (suffix > 100) throw new Error("no free temp name for case repair");
1296
+ tmp = `${actualAbs}.case-repair-tmp${suffix}`;
1297
+ }
1298
+ fs2.renameSync(actualAbs, tmp);
1299
+ fs2.renameSync(tmp, expectedAbs);
1300
+ repaired.push(issue);
1301
+ } catch {
1302
+ remaining.push(issue);
1303
+ }
1304
+ }
1305
+ return { repaired, remaining };
1306
+ }
1307
+ function formatGuardCaseIssues(issues) {
1308
+ return issues.map(
1309
+ (issue) => `Warning: guard path case mismatch (${issue.source}): ${issue.actual} should be ${issue.expected}. Rename to the exact casing (tracked paths need: git mv <actual> <expected>); case-insensitive filesystems silently merge these paths while CI sees them as distinct.`
1310
+ );
1311
+ }
1312
+
1313
+ // src/core/guard-kit/state.ts
1314
+ function sha256Hex(content) {
1315
+ return createHash2("sha256").update(content).digest("hex");
1316
+ }
1317
+ function readLock(guardDir) {
1318
+ const lockPath = path8.join(guardDir, LOCK_FILE_REL);
1319
+ let raw;
1320
+ try {
1321
+ raw = fs3.readFileSync(lockPath, "utf8");
1322
+ } catch {
1323
+ return null;
1324
+ }
1325
+ try {
1326
+ const parsed = JSON.parse(raw);
1327
+ if (typeof parsed.kitVersion !== "string" || typeof parsed.generatedAt !== "string" || typeof parsed.files !== "object" || parsed.files === null) {
1328
+ return null;
1329
+ }
1330
+ const files = {};
1331
+ const seenFolded = /* @__PURE__ */ new Set();
1332
+ for (const [file, hash] of Object.entries(parsed.files)) {
1333
+ if (typeof hash !== "string") return null;
1334
+ const folded = file.toLowerCase();
1335
+ if (seenFolded.has(folded)) return null;
1336
+ seenFolded.add(folded);
1337
+ files[file] = hash;
1338
+ }
1339
+ if (!(CHECK_SCRIPT_REL in files)) return null;
1340
+ return { kitVersion: parsed.kitVersion, generatedAt: parsed.generatedAt, files };
1341
+ } catch {
1342
+ return null;
1343
+ }
1344
+ }
1345
+ function compareKitVersions(a, b) {
1346
+ const partsA = a.split(".").map((segment) => Number.parseInt(segment, 10) || 0);
1347
+ const partsB = b.split(".").map((segment) => Number.parseInt(segment, 10) || 0);
1348
+ const length = Math.max(partsA.length, partsB.length);
1349
+ for (let index = 0; index < length; index++) {
1350
+ const diff = (partsA[index] ?? 0) - (partsB[index] ?? 0);
1351
+ if (diff !== 0) return diff;
1352
+ }
1353
+ return 0;
1354
+ }
1355
+ function classifyGuardState(repoRoot) {
1356
+ const bundledVersion = GUARD_KIT_VERSION;
1357
+ const guardDir = path8.join(repoRoot, GUARD_DIR_REL);
1358
+ const caseIssues = detectGuardCaseIssues(repoRoot);
1359
+ const caseMessages = formatGuardCaseIssues(caseIssues);
1360
+ const base = (state, extra = {}) => {
1361
+ const report = {
1362
+ state,
1363
+ bundledVersion,
1364
+ mismatchedFiles: [],
1365
+ missingFiles: [],
1366
+ messages: [],
1367
+ caseIssues,
1368
+ ...extra
1369
+ };
1370
+ report.messages = [...report.messages, ...caseMessages];
1371
+ return report;
1372
+ };
1373
+ if (caseIssues.length > 0) {
1374
+ return base("partial", {
1375
+ messages: [
1376
+ "Warning: guard kit path casing diverges from the canonical .poetic/guard layout; the trusted-base CI extraction will fail."
1377
+ ]
1378
+ });
1379
+ }
1380
+ if (!fs3.existsSync(guardDir)) {
1381
+ return base("absent", {
1382
+ messages: ["Info: guard kit not installed (.poetic/guard/ absent)."]
1383
+ });
1384
+ }
1385
+ const lock = readLock(guardDir);
1386
+ if (!lock) {
1387
+ return base("partial", {
1388
+ messages: ["Warning: guard kit lock (.poetic/guard/checks.lock) is missing or invalid."]
1389
+ });
1390
+ }
1391
+ const mismatchedFiles = [];
1392
+ const missingFiles = [];
1393
+ const guardDirResolved = path8.resolve(guardDir);
1394
+ for (const [file, expected] of Object.entries(lock.files)) {
1395
+ const filePath = path8.resolve(guardDirResolved, file);
1396
+ if (filePath !== guardDirResolved && !filePath.startsWith(guardDirResolved + path8.sep)) {
1397
+ return base("partial", {
1398
+ installedVersion: lock.kitVersion,
1399
+ messages: [
1400
+ `Warning: guard kit lock names a path outside .poetic/guard (${file}); the lock is invalid.`
1401
+ ]
1402
+ });
1403
+ }
1404
+ let content;
1405
+ try {
1406
+ content = fs3.readFileSync(filePath);
1407
+ } catch {
1408
+ missingFiles.push(file);
1409
+ continue;
1410
+ }
1411
+ const actual = `sha256:${sha256Hex(content)}`;
1412
+ if (actual !== expected) mismatchedFiles.push(file);
1413
+ }
1414
+ if (missingFiles.length > 0) {
1415
+ return base("partial", {
1416
+ installedVersion: lock.kitVersion,
1417
+ missingFiles: missingFiles.sort(),
1418
+ mismatchedFiles: mismatchedFiles.sort(),
1419
+ messages: [
1420
+ `Warning: guard kit is missing generated files: ${missingFiles.sort().join(", ")}.`
1421
+ ]
1422
+ });
1423
+ }
1424
+ if (mismatchedFiles.length > 0) {
1425
+ return base("tampered", {
1426
+ installedVersion: lock.kitVersion,
1427
+ mismatchedFiles: mismatchedFiles.sort(),
1428
+ messages: [
1429
+ `Warning: guard kit generated files were hand-edited: ${mismatchedFiles.sort().join(", ")}.`
1430
+ ]
1431
+ });
1432
+ }
1433
+ const versionDelta = compareKitVersions(lock.kitVersion, bundledVersion);
1434
+ if (versionDelta < 0) {
1435
+ return base("upgradable", {
1436
+ installedVersion: lock.kitVersion,
1437
+ messages: [`Info: guard kit ${lock.kitVersion} installed; ${bundledVersion} available.`]
1438
+ });
1439
+ }
1440
+ if (versionDelta > 0) {
1441
+ return base("current", {
1442
+ installedVersion: lock.kitVersion,
1443
+ messages: [
1444
+ `Info: guard kit ${lock.kitVersion} is newer than this Poetic's bundled ${bundledVersion}; leaving it untouched.`
1445
+ ]
1446
+ });
1447
+ }
1448
+ return base("current", {
1449
+ installedVersion: lock.kitVersion,
1450
+ messages: [`OK: guard kit ${lock.kitVersion} is current.`]
1451
+ });
1452
+ }
1453
+
1454
+ // src/cli/commands/doctor-guard.ts
1455
+ var GUARD_DIR_REL2 = path9.join(".poetic", "guard");
1456
+ var GUARD_LOCK_REL = path9.join(GUARD_DIR_REL2, "checks.lock");
1457
+ var GUARD_BASELINES_REL = path9.join(GUARD_DIR_REL2, "baselines");
1458
+ var INIT_INSTALL_CMD = "poetic init --guardrails";
1459
+ var INIT_REINSTALL_CMD = "poetic init --guardrails --reinstall-guard";
1460
+ async function readGuardLock(repoRoot) {
1461
+ try {
1462
+ const raw = await fs4.readFile(path9.join(repoRoot, GUARD_LOCK_REL), "utf8");
1463
+ const parsed = JSON.parse(raw);
1464
+ const version = typeof parsed.version === "string" ? parsed.version : typeof parsed.kitVersion === "string" ? parsed.kitVersion : typeof parsed.kit_version === "string" ? parsed.kit_version : void 0;
1465
+ const container = parsed.files ?? parsed.hashes;
1466
+ let files = [];
1467
+ if (Array.isArray(container)) {
1468
+ files = container.filter((f) => typeof f === "string");
1469
+ } else if (container && typeof container === "object") {
1470
+ files = Object.keys(container);
1471
+ }
1472
+ return { version, files };
1473
+ } catch {
1474
+ return null;
1475
+ }
1476
+ }
1477
+ function collectZeroCountKeys(value, prefix, out) {
1478
+ if (typeof value === "number") {
1479
+ if (value === 0) out.push(prefix);
1480
+ return;
1481
+ }
1482
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1483
+ for (const [key, child] of Object.entries(value)) {
1484
+ const next = prefix ? `${prefix}.${key}` : key;
1485
+ collectZeroCountKeys(child, next, out);
1486
+ }
1487
+ }
1488
+ }
1489
+ async function scanPromotableBaselineKeys(repoRoot) {
1490
+ const baselinesDir = path9.join(repoRoot, GUARD_BASELINES_REL);
1491
+ let entries;
1492
+ try {
1493
+ entries = await fs4.readdir(baselinesDir);
1494
+ } catch {
1495
+ return [];
1496
+ }
1497
+ const promotable = [];
1498
+ for (const entry of entries) {
1499
+ if (!entry.endsWith(".json")) continue;
1500
+ try {
1501
+ const raw = await fs4.readFile(path9.join(baselinesDir, entry), "utf8");
1502
+ const parsed = JSON.parse(raw);
1503
+ const keys = [];
1504
+ collectZeroCountKeys(parsed, "", keys);
1505
+ const baselineName = entry.replace(/\.json$/, "");
1506
+ for (const key of keys) promotable.push(key ? `${baselineName}:${key}` : baselineName);
1507
+ } catch {
1508
+ }
1509
+ }
1510
+ return promotable;
1511
+ }
1512
+ var defaultGuardInspector = async (repoRoot) => {
1513
+ const state = classifyGuardState(repoRoot).state;
1514
+ const lock = await readGuardLock(repoRoot);
1515
+ const promotableBaselineKeys = await scanPromotableBaselineKeys(repoRoot);
1516
+ return {
1517
+ state,
1518
+ bundledVersion: GUARD_KIT_VERSION,
1519
+ installedVersion: lock?.version,
1520
+ generatedFiles: lock?.files,
1521
+ promotableBaselineKeys
1522
+ };
1523
+ };
1524
+ async function checkGuardrails(repoRoot, options = {}) {
1525
+ const inspect = options.inspect ?? defaultGuardInspector;
1526
+ let inspection;
1527
+ try {
1528
+ inspection = await inspect(repoRoot);
1529
+ } catch (error) {
1530
+ return [
1531
+ {
1532
+ name: "Guardrails",
1533
+ category: "guardrails",
1534
+ status: "info",
1535
+ message: "guardrail kit status unavailable",
1536
+ details: error instanceof Error ? error.message : String(error)
1537
+ }
1538
+ ];
1539
+ }
1540
+ if (!inspection) {
1541
+ return [
1542
+ {
1543
+ name: "Guardrails",
1544
+ category: "guardrails",
1545
+ status: "info",
1546
+ message: "guardrail kit module unavailable",
1547
+ details: "Guard kit could not be loaded in this install; guardrails status is unknown."
1548
+ }
1549
+ ];
1550
+ }
1551
+ const checks = [checkForState(inspection)];
1552
+ const promotable = inspection.promotableBaselineKeys ?? [];
1553
+ if (promotable.length > 0) {
1554
+ checks.push({
1555
+ name: "Guard Baselines",
1556
+ category: "guardrails",
1557
+ status: "info",
1558
+ message: `${promotable.length} baseline key(s) promotable`,
1559
+ details: `Zero-count keys ready to promote: ${promotable.join(", ")}`
1560
+ });
1561
+ }
1562
+ return checks;
1563
+ }
1564
+ function checkForState(inspection) {
1565
+ const { state, bundledVersion, installedVersion, generatedFiles } = inspection;
1566
+ switch (state) {
1567
+ case "absent":
1568
+ return {
1569
+ name: "Guardrails",
1570
+ category: "guardrails",
1571
+ status: "info",
1572
+ message: "guardrail kit not installed",
1573
+ details: `Optional: install the read-only guardrail kit with "${INIT_INSTALL_CMD}".`
1574
+ };
1575
+ case "current":
1576
+ return {
1577
+ name: "Guardrails",
1578
+ category: "guardrails",
1579
+ status: "pass",
1580
+ message: `guardrail kit current (version ${installedVersion ?? bundledVersion})`
1581
+ };
1582
+ case "upgradable":
1583
+ return {
1584
+ name: "Guardrails",
1585
+ category: "guardrails",
1586
+ status: "info",
1587
+ message: `guardrail kit upgradable (installed ${installedVersion ?? "unknown"}, bundled ${bundledVersion})`,
1588
+ details: `Run "${INIT_INSTALL_CMD}" to upgrade the generated guard files (config.json is preserved).`
1589
+ };
1590
+ case "tampered":
1591
+ return {
1592
+ name: "Guardrails",
1593
+ category: "guardrails",
1594
+ status: "warn",
1595
+ message: "guardrail kit modified (hash mismatch)",
1596
+ details: formatMismatchDetails(generatedFiles)
1597
+ };
1598
+ case "partial":
1599
+ return {
1600
+ name: "Guardrails",
1601
+ category: "guardrails",
1602
+ status: "warn",
1603
+ message: "guardrail kit incomplete (missing lock or generated files)",
1604
+ details: formatMismatchDetails(generatedFiles)
1605
+ };
1606
+ }
1607
+ }
1608
+ function formatMismatchDetails(generatedFiles) {
1609
+ const suffix = `Run "${INIT_REINSTALL_CMD}" to restore the generated guard files.`;
1610
+ if (generatedFiles && generatedFiles.length > 0) {
1611
+ return `Generated guard files: ${generatedFiles.join(", ")}. ${suffix}`;
1612
+ }
1613
+ return suffix;
1614
+ }
1615
+
1616
+ // src/cli/commands/doctor-metrics.ts
1617
+ import chalk from "chalk";
1618
+ import { Command } from "commander";
1619
+ import { z as z2 } from "zod";
1620
+ async function loadTelemetryHealthTracker() {
1621
+ const mod = await import("./telemetry-health-tracker-DMSSGBZ2.js");
1622
+ return mod.TelemetryHealthTracker;
1623
+ }
1624
+ var DoctorMetricsOptionsSchema = z2.object({
1625
+ json: BooleanFlagSchema.optional(),
1626
+ prometheus: BooleanFlagSchema.optional(),
1627
+ verbose: BooleanFlagSchema.optional()
1628
+ }).strict();
1629
+ var DoctorMetricsMonitorOptionsSchema = z2.object({
1630
+ interval: z2.number().optional(),
1631
+ json: BooleanFlagSchema.optional(),
1632
+ prometheus: BooleanFlagSchema.optional()
1633
+ }).strict();
1634
+ function buildUnavailableHealthMetrics() {
1635
+ return {
1636
+ queueDepth: 0,
1637
+ fallbackMode: false,
1638
+ errorRate: 0,
1639
+ databaseHealthy: false,
1640
+ lastErrorTimestamp: null,
1641
+ errorsLastHour: 0,
1642
+ unresolvedErrors: 0
1643
+ };
1644
+ }
1645
+ function isHealthSchemaPresent(db) {
1646
+ const row = db.prepare(
1647
+ "SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'telemetry_errors' LIMIT 1"
1648
+ ).get();
1649
+ return row !== void 0;
1650
+ }
1651
+ async function openTelemetryDb() {
1652
+ const { openConfiguredTelemetryDb } = await import("./storage-access-F6ALDN4S.js");
1653
+ const TelemetryHealthTracker = await loadTelemetryHealthTracker();
1654
+ const readonlyResult = openConfiguredTelemetryDb();
1655
+ if (!readonlyResult.db) {
1656
+ return readonlyResult;
1657
+ }
1658
+ const { dbPath } = readonlyResult;
1659
+ if (isHealthSchemaPresent(readonlyResult.db)) {
1660
+ return readonlyResult;
1661
+ }
1662
+ readonlyResult.db.close();
1663
+ const writableResult = openConfiguredTelemetryDb({ writable: true });
1664
+ if (!writableResult.db) {
1665
+ return writableResult;
1666
+ }
1667
+ try {
1668
+ new TelemetryHealthTracker(writableResult.db);
1669
+ } catch (error) {
1670
+ writableResult.db.close();
1671
+ return {
1672
+ db: null,
1673
+ dbPath,
1674
+ error: error instanceof Error ? error.message : String(error)
1675
+ };
1676
+ }
1677
+ writableResult.db.close();
1678
+ return openConfiguredTelemetryDb();
1679
+ }
1680
+ function readDoctorMetricsOptions(command) {
1681
+ const globals = command.optsWithGlobals();
1682
+ return validateCommandOptions(DoctorMetricsOptionsSchema, {
1683
+ json: globals.json,
1684
+ prometheus: globals.prometheus,
1685
+ verbose: globals.verbose
1686
+ });
1687
+ }
1688
+ async function collectHealthMetrics(db) {
1689
+ const TelemetryHealthTracker = await loadTelemetryHealthTracker();
1690
+ const tracker = new TelemetryHealthTracker(db);
1691
+ const queueDepth = 0;
1692
+ const fallbackMode = false;
1693
+ const health = tracker.getHealthMetrics(queueDepth, fallbackMode);
1694
+ const metrics = {
1695
+ queueDepth: health.queue_depth,
1696
+ fallbackMode: health.fallback_mode_active,
1697
+ errorRate: health.error_rate,
1698
+ databaseHealthy: health.database_healthy,
1699
+ lastErrorTimestamp: health.last_error_timestamp || null,
1700
+ errorsLastHour: health.errors_last_hour,
1701
+ unresolvedErrors: health.unresolved_errors
1702
+ };
1703
+ try {
1704
+ const mttr = tracker.calculateMTTR(24);
1705
+ metrics.mttr = {
1706
+ meanTimeToResolution: Math.round(mttr.mean_time_to_resolution),
1707
+ p50: Math.round(mttr.p50_resolution_time),
1708
+ p95: Math.round(mttr.p95_resolution_time),
1709
+ p99: Math.round(mttr.p99_resolution_time),
1710
+ resolutionRate: mttr.resolution_rate,
1711
+ errorCount24h: mttr.error_count_last_24h
1712
+ };
1713
+ } catch (_error) {
1714
+ }
1715
+ return metrics;
1716
+ }
1717
+ function formatMetricsJson(metrics, extras = {}) {
1718
+ const pathEnvWarnings = getRejectedPathEnvWarnings();
1719
+ return JSON.stringify(
1720
+ {
1721
+ status: getHealthStatus(metrics),
1722
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1723
+ uptime: Math.floor(process.uptime()),
1724
+ metrics,
1725
+ ...extras.telemetryDb ? { telemetryDb: extras.telemetryDb } : {},
1726
+ ...extras.databaseError ? { databaseError: extras.databaseError } : {},
1727
+ ...pathEnvWarnings.length > 0 ? { pathEnvWarnings } : {}
1728
+ },
1729
+ null,
1730
+ 2
1731
+ );
1732
+ }
1733
+ function formatMetricsPrometheus(metrics) {
1734
+ const lines = [];
1735
+ lines.push("# HELP poetic_error_rate Error rate as decimal (0.0-1.0)");
1736
+ lines.push("# TYPE poetic_error_rate gauge");
1737
+ lines.push(`poetic_error_rate ${metrics.errorRate.toFixed(4)}`);
1738
+ lines.push("# HELP poetic_database_healthy Database health status (1=healthy, 0=unhealthy)");
1739
+ lines.push("# TYPE poetic_database_healthy gauge");
1740
+ lines.push(`poetic_database_healthy ${metrics.databaseHealthy ? 1 : 0}`);
1741
+ lines.push("# HELP poetic_errors_last_hour Number of errors in last hour");
1742
+ lines.push("# TYPE poetic_errors_last_hour gauge");
1743
+ lines.push(`poetic_errors_last_hour ${metrics.errorsLastHour}`);
1744
+ lines.push("# HELP poetic_unresolved_errors Number of unresolved errors");
1745
+ lines.push("# TYPE poetic_unresolved_errors gauge");
1746
+ lines.push(`poetic_unresolved_errors ${metrics.unresolvedErrors}`);
1747
+ if (metrics.mttr) {
1748
+ lines.push("# HELP poetic_mttr_ms Mean time to resolution in milliseconds");
1749
+ lines.push("# TYPE poetic_mttr_ms gauge");
1750
+ lines.push(`poetic_mttr_ms ${metrics.mttr.meanTimeToResolution}`);
1751
+ lines.push("# HELP poetic_mttr_p50_ms 50th percentile time to resolution (ms)");
1752
+ lines.push("# TYPE poetic_mttr_p50_ms gauge");
1753
+ lines.push(`poetic_mttr_p50_ms ${metrics.mttr.p50}`);
1754
+ lines.push("# HELP poetic_mttr_p95_ms 95th percentile time to resolution (ms)");
1755
+ lines.push("# TYPE poetic_mttr_p95_ms gauge");
1756
+ lines.push(`poetic_mttr_p95_ms ${metrics.mttr.p95}`);
1757
+ lines.push("# HELP poetic_mttr_p99_ms 99th percentile time to resolution (ms)");
1758
+ lines.push("# TYPE poetic_mttr_p99_ms gauge");
1759
+ lines.push(`poetic_mttr_p99_ms ${metrics.mttr.p99}`);
1760
+ lines.push("# HELP poetic_resolution_rate Error resolution rate (0.0-1.0)");
1761
+ lines.push("# TYPE poetic_resolution_rate gauge");
1762
+ lines.push(`poetic_resolution_rate ${metrics.mttr.resolutionRate.toFixed(4)}`);
1763
+ lines.push("# HELP poetic_error_count_24h Error count in last 24 hours");
1764
+ lines.push("# TYPE poetic_error_count_24h gauge");
1765
+ lines.push(`poetic_error_count_24h ${metrics.mttr.errorCount24h}`);
1766
+ }
1767
+ lines.push(`# Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1768
+ return `${lines.join("\n")}
1769
+ `;
1770
+ }
1771
+ function formatMetricsTable(metrics, verbose = false) {
1772
+ const lines = [];
1773
+ lines.push(chalk.bold.cyan("\nPoetic Telemetry Metrics\n"));
1774
+ const status = getHealthStatus(metrics);
1775
+ const statusColor = status === "healthy" ? chalk.green : status === "degraded" ? chalk.yellow : chalk.red;
1776
+ const statusIcon = status === "healthy" ? "OK" : status === "degraded" ? "Warning: " : "Error";
1777
+ lines.push(
1778
+ `${statusColor(statusIcon)} Status: ${statusColor(status.toUpperCase())}`
1779
+ );
1780
+ lines.push(`Error Rate: ${(metrics.errorRate * 100).toFixed(2)}%`);
1781
+ lines.push(
1782
+ `${metrics.databaseHealthy ? "OK" : "Error"} Database: ${metrics.databaseHealthy ? chalk.green("Healthy") : chalk.red("Unhealthy")}`
1783
+ );
1784
+ if (metrics.errorsLastHour > 0) {
1785
+ lines.push(chalk.yellow(`Warning: Errors (last hour): ${metrics.errorsLastHour}`));
1786
+ } else {
1787
+ lines.push("OK Errors (last hour): 0");
1788
+ }
1789
+ if (metrics.unresolvedErrors > 0) {
1790
+ lines.push(chalk.yellow(`Warning: Unresolved: ${metrics.unresolvedErrors}`));
1791
+ } else {
1792
+ lines.push("OK Unresolved: 0");
1793
+ }
1794
+ if (verbose && metrics.lastErrorTimestamp) {
1795
+ const lastErrorTime = new Date(metrics.lastErrorTimestamp);
1796
+ const minutesAgo = Math.floor((Date.now() - lastErrorTime.getTime()) / 6e4);
1797
+ lines.push(`Last Error: ${minutesAgo} minutes ago`);
1798
+ }
1799
+ if (verbose && metrics.mttr) {
1800
+ lines.push(chalk.bold.cyan("\nResolution Metrics (24h)\n"));
1801
+ lines.push(
1802
+ `Time: MTTR (mean): ${formatDurationMs(metrics.mttr.meanTimeToResolution)}`
1803
+ );
1804
+ lines.push(`MTTR (P50): ${formatDurationMs(metrics.mttr.p50)}`);
1805
+ lines.push(`MTTR (P95): ${formatDurationMs(metrics.mttr.p95)}`);
1806
+ lines.push(`MTTR (P99): ${formatDurationMs(metrics.mttr.p99)}`);
1807
+ lines.push(`OK Resolution Rate: ${(metrics.mttr.resolutionRate * 100).toFixed(1)}%`);
1808
+ lines.push(`Error Count (24h): ${metrics.mttr.errorCount24h}`);
1809
+ }
1810
+ if (verbose && metrics.lastErrorTimestamp) {
1811
+ lines.push(chalk.bold.cyan("\nDetails\n"));
1812
+ lines.push(`Last Error: ${metrics.lastErrorTimestamp}`);
1813
+ }
1814
+ lines.push("");
1815
+ return lines.join("\n");
1816
+ }
1817
+ function getHealthStatus(metrics) {
1818
+ if (!metrics.databaseHealthy) {
1819
+ return "unhealthy";
1820
+ }
1821
+ if (metrics.errorRate > 0.1 || metrics.unresolvedErrors > 0) {
1822
+ return "degraded";
1823
+ }
1824
+ return "healthy";
1825
+ }
1826
+ async function displayDatabaseStatus(options = {}) {
1827
+ const { db, dbPath, error } = await openTelemetryDb();
1828
+ if (!db) {
1829
+ const metrics = buildUnavailableHealthMetrics();
1830
+ if (options.json) {
1831
+ console.log(
1832
+ formatMetricsJson(metrics, {
1833
+ telemetryDb: dbPath,
1834
+ databaseError: error ?? "Telemetry database unavailable"
1835
+ })
1836
+ );
1837
+ process.exit(1);
1838
+ }
1839
+ console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
1840
+ console.log(` Path: ${dbPath}`);
1841
+ console.log(" Run: poetic init");
1842
+ process.exit(1);
1843
+ }
1844
+ try {
1845
+ const metrics = await collectHealthMetrics(db);
1846
+ if (options.json) {
1847
+ console.log(formatMetricsJson(metrics, { telemetryDb: dbPath }));
1848
+ } else if (options.prometheus) {
1849
+ console.log(formatMetricsPrometheus(metrics));
1850
+ } else {
1851
+ console.log(formatMetricsTable(metrics, options.verbose ?? false));
1852
+ }
1853
+ } catch (error2) {
1854
+ if (options.json) {
1855
+ const metrics = buildUnavailableHealthMetrics();
1856
+ console.log(
1857
+ formatMetricsJson(metrics, {
1858
+ telemetryDb: dbPath,
1859
+ databaseError: error2 instanceof Error ? error2.message : String(error2)
1860
+ })
1861
+ );
1862
+ process.exit(1);
1863
+ }
1864
+ console.error(
1865
+ chalk.red("Failed to collect health metrics:"),
1866
+ error2 instanceof Error ? error2.message : String(error2)
1867
+ );
1868
+ process.exit(1);
1869
+ } finally {
1870
+ db.close();
1871
+ }
1872
+ }
1873
+ async function monitorHealth(options) {
1874
+ const { db, dbPath, error } = await openTelemetryDb();
1875
+ if (!db) {
1876
+ if (options.json) {
1877
+ const metrics = buildUnavailableHealthMetrics();
1878
+ console.log(
1879
+ formatMetricsJson(metrics, {
1880
+ telemetryDb: dbPath,
1881
+ databaseError: error ?? "Telemetry database unavailable"
1882
+ })
1883
+ );
1884
+ process.exit(1);
1885
+ }
1886
+ console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
1887
+ console.log(` Path: ${dbPath}`);
1888
+ console.log(" Run: poetic init");
1889
+ return;
1890
+ }
1891
+ const interval = (options.interval || 2) * 1e3;
1892
+ const isJson = options.json || false;
1893
+ const isPrometheus = options.prometheus || false;
1894
+ try {
1895
+ while (true) {
1896
+ const metrics = await collectHealthMetrics(db);
1897
+ if (isJson) {
1898
+ console.log(formatMetricsJson(metrics));
1899
+ } else if (isPrometheus) {
1900
+ console.log(formatMetricsPrometheus(metrics));
1901
+ } else {
1902
+ console.clear?.();
1903
+ console.log(formatMetricsTable(metrics, false));
1904
+ }
1905
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
1906
+ }
1907
+ } catch (error2) {
1908
+ console.error(
1909
+ chalk.red("Error Monitoring error:"),
1910
+ error2 instanceof Error ? error2.message : String(error2)
1911
+ );
1912
+ process.exit(1);
1913
+ } finally {
1914
+ db.close();
1915
+ }
1916
+ }
1917
+ function createDoctorMetricsCommand() {
1918
+ const metricsCmd = new Command("metrics");
1919
+ metricsCmd.description(
1920
+ "Monitor telemetry-derived operational metrics (not the install doctor)\n\nNote: For install / provider auth diagnostics use `poetic doctor`.\n`poetic doctor metrics` reports runtime telemetry from the local SQLite store.\n\nExamples:\n poetic doctor metrics # Show metrics status\n poetic doctor metrics --json # Output as JSON\n poetic doctor metrics --prometheus # Prometheus format\n poetic doctor metrics monitor # Real-time monitoring\n poetic doctor metrics monitor --interval 5 # Update every 5 seconds"
1921
+ );
1922
+ const checkCmd = new Command("check");
1923
+ checkCmd.description("Run telemetry metrics check and display results");
1924
+ checkCmd.option("--json", "Output as JSON");
1925
+ checkCmd.option("--prometheus", "Output in Prometheus text format");
1926
+ checkCmd.option("-v, --verbose", "Show detailed information");
1927
+ checkCmd.action(async function() {
1928
+ const validatedOptions = readDoctorMetricsOptions(this);
1929
+ const { db, dbPath, error } = await openTelemetryDb();
1930
+ if (!db) {
1931
+ const metrics = buildUnavailableHealthMetrics();
1932
+ if (validatedOptions.json) {
1933
+ console.log(
1934
+ formatMetricsJson(metrics, {
1935
+ telemetryDb: dbPath,
1936
+ databaseError: error ?? "Telemetry database unavailable"
1937
+ })
1938
+ );
1939
+ process.exit(1);
1940
+ }
1941
+ console.log(chalk.yellow(`Warning: ${error ?? "Telemetry database not found"}`));
1942
+ console.log(` Path: ${dbPath}`);
1943
+ console.log(" Run: poetic init");
1944
+ process.exit(1);
1945
+ }
1946
+ let degraded = false;
1947
+ try {
1948
+ const metrics = await collectHealthMetrics(db);
1949
+ const status = getHealthStatus(metrics);
1950
+ degraded = status !== "healthy";
1951
+ if (validatedOptions.json) {
1952
+ console.log(formatMetricsJson(metrics, { telemetryDb: dbPath }));
1953
+ } else if (validatedOptions.prometheus) {
1954
+ console.log(formatMetricsPrometheus(metrics));
1955
+ } else {
1956
+ console.log(formatMetricsTable(metrics, validatedOptions.verbose));
1957
+ }
1958
+ } finally {
1959
+ db.close();
1960
+ }
1961
+ if (degraded) process.exit(1);
1962
+ });
1963
+ const monitorCmd = new Command("monitor");
1964
+ monitorCmd.description("Watch telemetry metrics in real-time");
1965
+ monitorCmd.option("--interval <seconds>", "Update interval in seconds", "2");
1966
+ monitorCmd.option("--json", "Output as JSON");
1967
+ monitorCmd.option("--prometheus", "Output in Prometheus text format");
1968
+ monitorCmd.action(async (options) => {
1969
+ const monitorOptions = {
1970
+ interval: options.interval ? Number.parseInt(String(options.interval), 10) : 2,
1971
+ json: Boolean(options.json),
1972
+ prometheus: Boolean(options.prometheus)
1973
+ };
1974
+ const validatedOptions = validateCommandOptions(
1975
+ DoctorMetricsMonitorOptionsSchema,
1976
+ monitorOptions
1977
+ );
1978
+ await monitorHealth(validatedOptions);
1979
+ });
1980
+ metricsCmd.addCommand(checkCmd);
1981
+ metricsCmd.addCommand(monitorCmd);
1982
+ metricsCmd.option("--json", "Output as JSON");
1983
+ metricsCmd.option("--prometheus", "Output in Prometheus text format");
1984
+ metricsCmd.option("-v, --verbose", "Show detailed information");
1985
+ metricsCmd.action(async function() {
1986
+ const validatedOptions = readDoctorMetricsOptions(this);
1987
+ await displayDatabaseStatus(validatedOptions);
1988
+ });
1989
+ return metricsCmd;
1990
+ }
1991
+
1992
+ // src/cli/commands/doctor.ts
1993
+ var BetterSqlite3Ctor = null;
1994
+ async function getBetterSqlite3() {
1995
+ if (!BetterSqlite3Ctor) {
1996
+ const mod = await import("better-sqlite3");
1997
+ BetterSqlite3Ctor = mod.default;
1998
+ }
1999
+ return BetterSqlite3Ctor;
2000
+ }
2001
+ var REQUIRED_NODE_MAJOR = 24;
2002
+ var HOMEBREW_NODE_24_PATH = "/opt/homebrew/opt/node@24/bin/node";
2003
+ async function preferredNode24Path() {
2004
+ try {
2005
+ await fs5.access(HOMEBREW_NODE_24_PATH);
2006
+ return HOMEBREW_NODE_24_PATH;
2007
+ } catch {
2008
+ return null;
2009
+ }
2010
+ }
2011
+ function formatNode24Remediation(preferredPath) {
2012
+ if (preferredPath) {
2013
+ return `Poetic requires Node.js v${REQUIRED_NODE_MAJOR}.x; use ${preferredPath} or put /opt/homebrew/opt/node@24/bin first in PATH.`;
2014
+ }
2015
+ return `Poetic requires Node.js v${REQUIRED_NODE_MAJOR}.x; install/select Node ${REQUIRED_NODE_MAJOR}, for example: nvm install ${REQUIRED_NODE_MAJOR} && nvm use ${REQUIRED_NODE_MAJOR}.`;
2016
+ }
2017
+ function isLikelyBetterSqlite3NativeLoadMismatch(error) {
2018
+ const code = error?.code;
2019
+ const message = error instanceof Error ? error.message : String(error);
2020
+ 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);
2021
+ }
2022
+ function formatDatabaseNativeErrorDetails(error) {
2023
+ const message = error instanceof Error ? error.message : String(error);
2024
+ if (!isLikelyBetterSqlite3NativeLoadMismatch(error)) {
2025
+ return message;
2026
+ }
2027
+ return `${message}
2028
+ Remediation: npm rebuild better-sqlite3 (then rerun poetic doctor). If it still fails, reinstall dependencies under Node ${REQUIRED_NODE_MAJOR}: rm -rf node_modules && npm ci.`;
2029
+ }
2030
+ var HealthCommandOptionsSchema = z3.object({
2031
+ json: BooleanFlagSchema.optional(),
2032
+ verbose: BooleanFlagSchema.optional(),
2033
+ fix: BooleanFlagSchema.optional(),
2034
+ all: BooleanFlagSchema.optional(),
2035
+ provider: z3.string().optional(),
2036
+ providersOnly: BooleanFlagSchema.optional(),
2037
+ matrix: BooleanFlagSchema.optional(),
2038
+ strict: BooleanFlagSchema.optional(),
2039
+ requireReady: BooleanFlagSchema.optional(),
2040
+ probe: BooleanFlagSchema.optional(),
2041
+ refresh: BooleanFlagSchema.optional(),
2042
+ checkLinks: BooleanFlagSchema.optional()
2043
+ }).strict();
2044
+ function isStrictBlockingWarning(check) {
2045
+ if (check.name === "Linux write boundary") {
2046
+ return false;
2047
+ }
2048
+ if (check.category === "providers" && check.name === "Provider CLIs") {
2049
+ return false;
2050
+ }
2051
+ if (check.category === "providers" && (check.name === "OpenCode discovery" || /auth not observed/i.test(check.message))) {
2052
+ return false;
2053
+ }
2054
+ return true;
2055
+ }
2056
+ function isRequireReadyFailure(check) {
2057
+ return check.category === "providers" && check.name === "Provider CLIs" && (check.readinessGateLabel === "zero-ready" || check.readinessReadyCount === 0);
2058
+ }
2059
+ function exitForHealthFailures(checks, options) {
2060
+ const failedChecks = checks.filter((c) => c.status === "fail");
2061
+ const warnChecks = checks.filter((c) => c.status === "warn" && isStrictBlockingWarning(c));
2062
+ if (failedChecks.length > 0) {
2063
+ process.exit(1);
2064
+ }
2065
+ if (options.requireReady && checks.some((check) => isRequireReadyFailure(check))) {
2066
+ process.exit(1);
2067
+ }
2068
+ if (options.strict && warnChecks.length > 0) {
2069
+ process.exit(1);
2070
+ }
2071
+ }
2072
+ async function checkGit() {
2073
+ try {
2074
+ const result = await execa("git", ["--version"]);
2075
+ const version = result.stdout.trim();
2076
+ let details = version;
2077
+ try {
2078
+ const whichCmd = process.platform === "win32" ? "where" : "which";
2079
+ const whichResult = await execa(whichCmd, ["git"]);
2080
+ details = `${version} \u2014 ${whichResult.stdout.trim()}`;
2081
+ } catch (_whichError) {
2082
+ }
2083
+ return {
2084
+ name: "Git",
2085
+ category: "git",
2086
+ status: "pass",
2087
+ message: version,
2088
+ details
2089
+ };
2090
+ } catch (error) {
2091
+ return {
2092
+ name: "Git",
2093
+ category: "git",
2094
+ status: "fail",
2095
+ message: "Git not found or not accessible",
2096
+ details: error instanceof Error ? error.message : String(error)
2097
+ };
2098
+ }
2099
+ }
2100
+ var PROVIDER_CLI_SPECS = [
2101
+ { id: "claude", versionArgs: ["--version"] },
2102
+ { id: "codex", versionArgs: ["--version"] },
2103
+ { id: "copilot", versionArgs: ["--version"] },
2104
+ { id: "cursor", versionArgs: ["--version"] },
2105
+ { id: "gemini", versionArgs: ["--version"] },
2106
+ { id: "grok", versionArgs: ["--version"] },
2107
+ { id: "kiro", versionArgs: ["--version"] },
2108
+ { id: "pi", versionArgs: ["--version"] },
2109
+ { id: "antigravity", versionArgs: ["--version"] },
2110
+ { id: "opencode", versionArgs: ["--version"] }
2111
+ ];
2112
+ var PROVIDER_CLI_SPEC_IDS = PROVIDER_CLI_SPECS.map((s) => s.id);
2113
+ var AUTH_PROBE_HINT_PROVIDERS = /* @__PURE__ */ new Set(["grok"]);
2114
+ var PROVIDER_EXTRA_HEALTH_CHECKS = [
2115
+ { providerId: "opencode", runWithoutProviderFilter: false, check: checkOpencodeDiscovery },
2116
+ { providerId: "claude", runWithoutProviderFilter: true, check: checkClaudeStrictMcpConfig },
2117
+ { providerId: "claude", runWithoutProviderFilter: true, check: checkLegacyClaudePlugin }
2118
+ ];
2119
+ async function collectProviderExtraHealthChecks(options) {
2120
+ const checks = [];
2121
+ for (const entry of PROVIDER_EXTRA_HEALTH_CHECKS) {
2122
+ const requested = options.all === true || options.provider === entry.providerId || entry.runWithoutProviderFilter && !options.provider;
2123
+ if (requested) {
2124
+ checks.push(await entry.check());
2125
+ }
2126
+ }
2127
+ return checks;
2128
+ }
2129
+ function providerHealthReadinessOptions(providerId) {
2130
+ const activeAuthProviders = /* @__PURE__ */ new Set(["cursor", "grok"]);
2131
+ return {
2132
+ mode: "execute",
2133
+ authProbeMode: activeAuthProviders.has(providerId) ? "active" : "passive"
2134
+ };
2135
+ }
2136
+ async function formatLastSeenAuth(providerId) {
2137
+ const lookup = await lookupAuthOutcome(providerId);
2138
+ if (!lookup || lookup.stale || !lookup.contextMatches) {
2139
+ return "none";
2140
+ }
2141
+ return `${lookup.entry.result}, ${lookup.entry.observedAt}`;
2142
+ }
2143
+ async function recordHealthAuthOutcome(providerId, result, message) {
2144
+ await recordAuthOutcome(providerId, result, {
2145
+ source: "health-probe",
2146
+ message
2147
+ }).catch(() => {
2148
+ });
2149
+ }
2150
+ function normalizeProviderId(value) {
2151
+ return normalizeEngineId(value.trim());
2152
+ }
2153
+ async function checkOpencodeDiscovery() {
2154
+ let command;
2155
+ let prefixArgs;
2156
+ try {
2157
+ ({ command, prefixArgs } = resolveProviderCliCommand("opencode"));
2158
+ } catch {
2159
+ return {
2160
+ name: "OpenCode discovery",
2161
+ category: "providers",
2162
+ status: "fail",
2163
+ message: "OpenCode CLI not resolved",
2164
+ details: "Install OpenCode or set POETIC_OPENCODE_CLI_COMMAND. Run: poetic doctor --provider opencode"
2165
+ };
2166
+ }
2167
+ const cliProbe = await probeProviderCli(command, ["--version"], prefixArgs);
2168
+ const cliInstalled = cliProbe.present;
2169
+ const discovery = await discoverOpencode({
2170
+ cliCommand: command,
2171
+ prefixArgs,
2172
+ cliInstalled,
2173
+ refreshModels: false,
2174
+ includeModelsDev: true,
2175
+ modelsDevCacheDir: path10.join(
2176
+ getRuntimeEnv("POETIC_TELEMETRY_DIR") ?? path10.join(process.cwd(), ".poetic"),
2177
+ "cache",
2178
+ "opencode"
2179
+ )
2180
+ });
2181
+ const parts = [];
2182
+ parts.push(`CLI installed: ${cliInstalled ? "yes" : "no"}`);
2183
+ if (discovery.authList.rawOk) {
2184
+ const credentialNames = discovery.authList.credentialProviders ?? [];
2185
+ const environmentNames = discovery.authList.environmentProviders ?? [];
2186
+ parts.push(`Auth: ${credentialNames.length > 0 ? credentialNames.join(", ") : "not observed"}`);
2187
+ if (environmentNames.length > 0) {
2188
+ parts.push(`Capability (parent-env): ${environmentNames.join(", ")}`);
2189
+ }
2190
+ } else {
2191
+ parts.push(
2192
+ `Providers (auth list): unsupported or failed${discovery.authList.unsupportedMessage ? ` (${discovery.authList.unsupportedMessage.slice(0, 80)})` : ""}`
2193
+ );
2194
+ }
2195
+ if (discovery.models.rawOk) {
2196
+ parts.push(
2197
+ `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"}`
2198
+ );
2199
+ } else {
2200
+ parts.push(
2201
+ `Models: unsupported or failed${discovery.models.unsupportedMessage ? ` (${discovery.models.unsupportedMessage.slice(0, 80)})` : ""}`
2202
+ );
2203
+ }
2204
+ if (discovery.modelsDev) {
2205
+ const source = discovery.modelsDev.source === "network" ? "fresh" : "cache";
2206
+ parts.push(
2207
+ `models.dev: ${source}${discovery.modelsDev.fetchedAt ? ` @ ${new Date(discovery.modelsDev.fetchedAt).toISOString()}` : ""}`
2208
+ );
2209
+ }
2210
+ const credentialProviders = discovery.authList.credentialProviders ?? [];
2211
+ const environmentProviders = discovery.authList.environmentProviders ?? [];
2212
+ const hasCredentialAuth = credentialProviders.length > 0;
2213
+ const envOnlyCapability = cliInstalled && discovery.authList.rawOk && !hasCredentialAuth && (environmentProviders.length > 0 || discovery.models.modelIds.length > 0);
2214
+ const status = cliInstalled && discovery.authList.rawOk && discovery.models.rawOk && hasCredentialAuth ? "pass" : cliInstalled ? "warn" : "fail";
2215
+ 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";
2216
+ return {
2217
+ name: "OpenCode discovery",
2218
+ category: "providers",
2219
+ status,
2220
+ message,
2221
+ details: parts.join(" | ")
2222
+ };
2223
+ }
2224
+ async function getProvidersToCheck(options) {
2225
+ const validIds = PROVIDER_CLI_SPECS.map((s) => s.id);
2226
+ if (options.provider) {
2227
+ const requested = normalizeProviderId(options.provider);
2228
+ if (!validIds.includes(requested)) {
2229
+ throw new Error(
2230
+ `Unknown provider: ${options.provider}. Valid providers: ${validIds.join(", ")}`
2231
+ );
2232
+ }
2233
+ return {
2234
+ providers: [requested],
2235
+ isDiscoveryMode: false,
2236
+ selectionScope: "provider"
2237
+ };
2238
+ }
2239
+ if (options.all) {
2240
+ return {
2241
+ providers: PROVIDER_CLI_SPECS.map((s) => s.id),
2242
+ isDiscoveryMode: false,
2243
+ selectionScope: "all"
2244
+ };
2245
+ }
2246
+ const configured = /* @__PURE__ */ new Set();
2247
+ const defaultProvider = process.env.POETIC_DEFAULT_PROVIDER;
2248
+ if (defaultProvider) {
2249
+ const normalized = normalizeProviderId(defaultProvider);
2250
+ if (validIds.includes(normalized)) {
2251
+ configured.add(normalized);
2252
+ }
2253
+ }
2254
+ try {
2255
+ const { ConfigManager: ConfigManager2 } = await import("./config-manager-L3VRNTSG.js");
2256
+ const manager = new ConfigManager2({
2257
+ enableCache: false,
2258
+ cwd: process.cwd()
2259
+ });
2260
+ const resolved = await manager.loadConfig({}, true);
2261
+ const config = resolved.config;
2262
+ const hasExplicitConfig = resolved.sources.some(
2263
+ (source) => (source.type === "project" || source.type === "user") && source.exists
2264
+ );
2265
+ if (hasExplicitConfig) {
2266
+ if (config?.providers) {
2267
+ const providerEntries = Object.entries(config.providers);
2268
+ for (const [providerId, providerConfig] of providerEntries) {
2269
+ if (providerConfig?.enabled) {
2270
+ configured.add(normalizeProviderId(providerId));
2271
+ }
2272
+ }
2273
+ }
2274
+ const configuredDefault = config?.defaults?.provider ?? config?.defaultProvider;
2275
+ if (configuredDefault) {
2276
+ configured.add(normalizeProviderId(String(configuredDefault)));
2277
+ }
2278
+ }
2279
+ } catch (error) {
2280
+ if (isConfigValidityError(error)) {
2281
+ throw error;
2282
+ }
2283
+ }
2284
+ if (configured.size > 0) {
2285
+ const validIds2 = new Set(PROVIDER_CLI_SPECS.map((s) => s.id));
2286
+ const filteredProviders = [...configured].filter((id) => validIds2.has(id));
2287
+ if (filteredProviders.length > 0) {
2288
+ return {
2289
+ providers: filteredProviders,
2290
+ isDiscoveryMode: false,
2291
+ selectionScope: "configured"
2292
+ };
2293
+ }
2294
+ }
2295
+ return {
2296
+ providers: PROVIDER_CLI_SPECS.map((s) => s.id),
2297
+ isDiscoveryMode: true,
2298
+ selectionScope: "discovery"
2299
+ };
2300
+ }
2301
+ async function checkProviderCLIs(optionsOrVerbose = false) {
2302
+ const options = typeof optionsOrVerbose === "boolean" ? { includeVerboseDetails: optionsOrVerbose } : optionsOrVerbose;
2303
+ const includeVerboseDetails = options.includeVerboseDetails ?? false;
2304
+ const {
2305
+ providers: providersToCheck,
2306
+ isDiscoveryMode,
2307
+ selectionScope
2308
+ } = await getProvidersToCheck(options);
2309
+ const specsToCheck = PROVIDER_CLI_SPECS.filter((spec) => providersToCheck.includes(spec.id));
2310
+ const uncheckedProviderIds = selectionScope === "configured" ? PROVIDER_CLI_SPECS.map((spec) => spec.id).filter((id) => !providersToCheck.includes(id)) : [];
2311
+ const available = [];
2312
+ const degraded = [];
2313
+ const authUnobserved = [];
2314
+ const sandboxBlocked = [];
2315
+ const missing = [];
2316
+ const providerBreakdown = [];
2317
+ const providerReadiness = [];
2318
+ const gateReadyProviders = /* @__PURE__ */ new Set();
2319
+ const authMissing = [];
2320
+ const authReady = [];
2321
+ const lastSeenAuthMap = /* @__PURE__ */ new Map();
2322
+ for (const spec of specsToCheck) {
2323
+ lastSeenAuthMap.set(spec.id, await formatLastSeenAuth(spec.id));
2324
+ }
2325
+ const repoRoot = resolveRepoRootForPoeticDir(process.cwd());
2326
+ for (const spec of specsToCheck) {
2327
+ let config;
2328
+ try {
2329
+ config = getProviderConfig(spec.id);
2330
+ } catch {
2331
+ missing.push({ id: spec.id });
2332
+ if (includeVerboseDetails) {
2333
+ providerBreakdown.push({ id: spec.id, status: "missing" });
2334
+ }
2335
+ continue;
2336
+ }
2337
+ if (!config?.cli) {
2338
+ missing.push({ id: spec.id });
2339
+ if (includeVerboseDetails) {
2340
+ providerBreakdown.push({ id: spec.id, status: "missing" });
2341
+ }
2342
+ continue;
2343
+ }
2344
+ const readinessOptions = options.probe ? {
2345
+ ...providerHealthReadinessOptions(spec.id)
2346
+ } : {
2347
+ ...SETUP_CHECK_READINESS_OPTIONS,
2348
+ cliProbeMode: "execute",
2349
+ sandboxProbeMode: "execute",
2350
+ probeAuth: async () => "unknown"
2351
+ };
2352
+ const probeContext = readinessProbeContextForOptions(readinessOptions);
2353
+ const readiness = await getProviderReadiness(spec.id, readinessOptions);
2354
+ if (isGateReadyProvider(readiness)) gateReadyProviders.add(readiness.provider);
2355
+ if (options.probe) {
2356
+ void recordReadinessDiagnostic(readiness, {
2357
+ phase: "health",
2358
+ cliProbeMode: "execute",
2359
+ ...readinessOptions.authProbeMode ? { authProbeMode: readinessOptions.authProbeMode } : {}
2360
+ });
2361
+ }
2362
+ const dryRun = readProviderDryRunFreshness(spec.id, repoRoot);
2363
+ const resolved = resolveNamedReadiness(readiness, dryRun);
2364
+ const scope = resolveReadinessScope(readiness, resolved);
2365
+ const parityLabel = resolveProviderParityLabel(readiness, resolved);
2366
+ providerReadiness.push({
2367
+ provider: spec.id,
2368
+ known: readiness.known,
2369
+ configured: readiness.configured,
2370
+ installed: readiness.installed,
2371
+ cliHealthy: readiness.cliHealthy,
2372
+ authenticated: readiness.authenticated,
2373
+ ready: readiness.ready,
2374
+ scope: scope.label,
2375
+ ...scope.detail ? { scopeDetail: scope.detail } : {},
2376
+ ...readiness.readinessNote ? { readinessNote: readiness.readinessNote } : {},
2377
+ ...readiness.reasonCode ? { reasonCode: readiness.reasonCode } : {},
2378
+ parityLabel,
2379
+ resolved,
2380
+ probe: probeContext
2381
+ });
2382
+ const lastSeenAuth = lastSeenAuthMap.get(spec.id) ?? "";
2383
+ if (!readiness.command && !readiness.installed) {
2384
+ missing.push({
2385
+ id: spec.id,
2386
+ installCommand: config.cli?.install_command
2387
+ });
2388
+ if (includeVerboseDetails) {
2389
+ providerBreakdown.push({
2390
+ id: spec.id,
2391
+ status: "missing",
2392
+ installCommand: config.cli?.install_command,
2393
+ lastSeenAuth
2394
+ });
2395
+ }
2396
+ continue;
2397
+ }
2398
+ if (!readiness.installed) {
2399
+ missing.push({
2400
+ id: spec.id,
2401
+ installCommand: config.cli?.install_command
2402
+ });
2403
+ if (includeVerboseDetails) {
2404
+ providerBreakdown.push({
2405
+ id: spec.id,
2406
+ status: "missing",
2407
+ resolvedCommand: readiness.command,
2408
+ installCommand: config.cli?.install_command,
2409
+ lastSeenAuth
2410
+ });
2411
+ }
2412
+ } else if (!readiness.cliHealthy) {
2413
+ degraded.push({
2414
+ id: spec.id,
2415
+ reason: readiness.reason ?? "Unknown issue"
2416
+ });
2417
+ if (includeVerboseDetails) {
2418
+ providerBreakdown.push({
2419
+ id: spec.id,
2420
+ status: "degraded",
2421
+ resolvedCommand: readiness.command,
2422
+ degradedReason: readiness.reason,
2423
+ lastSeenAuth
2424
+ });
2425
+ }
2426
+ } else {
2427
+ if (readiness.sandboxCapable === false) {
2428
+ const deniedPaths = readiness.sandboxDeniedPaths?.join(", ") ?? "";
2429
+ const summary = deniedPaths ? `${spec.id}: sandbox blocked (${deniedPaths})` : formatSandboxDegradedSummary({
2430
+ providerId: spec.id,
2431
+ capable: false,
2432
+ probed: [],
2433
+ missing: []
2434
+ });
2435
+ const verboseReason = readiness.sandboxReason ?? "CLI present, sandbox capability denied";
2436
+ sandboxBlocked.push({ id: spec.id, paths: deniedPaths });
2437
+ degraded.push({ id: spec.id, reason: summary });
2438
+ if (includeVerboseDetails) {
2439
+ providerBreakdown.push({
2440
+ id: spec.id,
2441
+ status: "degraded",
2442
+ resolvedCommand: readiness.command,
2443
+ degradedReason: verboseReason,
2444
+ lastSeenAuth
2445
+ });
2446
+ }
2447
+ continue;
2448
+ }
2449
+ const authState = readiness.authenticated;
2450
+ if (authState === "no") {
2451
+ if (options.probe) {
2452
+ await recordHealthAuthOutcome(
2453
+ spec.id,
2454
+ "failed",
2455
+ readiness.reason ?? "Health probe reported authentication missing"
2456
+ );
2457
+ }
2458
+ authMissing.push(spec.id);
2459
+ const authMissingReason = spec.id === "codex" && readEnv(POETIC_CODEX_BACKEND).value?.trim().toLowerCase() === "bedrock" ? 'CLI present, auth missing; Bedrock runs need an AWS credential chain, not "codex login"' : spec.id === "codex" && Boolean(
2460
+ process.env.AWS_ACCESS_KEY_ID?.trim() && process.env.AWS_SECRET_ACCESS_KEY?.trim()
2461
+ ) ? "CLI present, auth missing; AWS keys are present but Codex is not attested for Bedrock. Set POETIC_CODEX_BACKEND=bedrock" : "CLI present, auth missing";
2462
+ degraded.push({ id: spec.id, reason: authMissingReason });
2463
+ if (includeVerboseDetails) {
2464
+ providerBreakdown.push({
2465
+ id: spec.id,
2466
+ status: "degraded",
2467
+ resolvedCommand: readiness.command,
2468
+ degradedReason: authMissingReason,
2469
+ lastSeenAuth: lastSeenAuthMap.get(spec.id) ?? ""
2470
+ });
2471
+ }
2472
+ continue;
2473
+ }
2474
+ if (authState === "unknown") {
2475
+ const activeProbeUnknown = options.probe === true && readiness.reason?.includes("active auth probe could not verify headless execution");
2476
+ const defaultProbeHintUnknown = AUTH_PROBE_HINT_PROVIDERS.has(spec.id) && options.provider === spec.id && options.probe !== true;
2477
+ if (activeProbeUnknown || defaultProbeHintUnknown) {
2478
+ const reason2 = defaultProbeHintUnknown ? `CLI present, auth unknown; run \`poetic doctor --probe --provider ${spec.id}\` to verify headless execution` : readiness.reason ?? "CLI present, active auth probe inconclusive";
2479
+ degraded.push({
2480
+ id: spec.id,
2481
+ reason: reason2
2482
+ });
2483
+ if (includeVerboseDetails) {
2484
+ providerBreakdown.push({
2485
+ id: spec.id,
2486
+ status: "degraded",
2487
+ resolvedCommand: readiness.command,
2488
+ degradedReason: reason2,
2489
+ lastSeenAuth
2490
+ });
2491
+ }
2492
+ continue;
2493
+ }
2494
+ const cached = await lookupAuthOutcome(spec.id);
2495
+ const freshFailure = cached?.entry.result === "failed" && !cached.stale && cached.contextMatches;
2496
+ if (!freshFailure) {
2497
+ authUnobserved.push(spec.id);
2498
+ available.push(spec.id);
2499
+ if (includeVerboseDetails) {
2500
+ providerBreakdown.push({
2501
+ id: spec.id,
2502
+ status: "available",
2503
+ resolvedCommand: readiness.command,
2504
+ lastSeenAuth
2505
+ });
2506
+ }
2507
+ continue;
2508
+ }
2509
+ const reason = "CLI present, cached auth failure";
2510
+ degraded.push({
2511
+ id: spec.id,
2512
+ reason
2513
+ });
2514
+ if (includeVerboseDetails) {
2515
+ providerBreakdown.push({
2516
+ id: spec.id,
2517
+ status: "degraded",
2518
+ resolvedCommand: readiness.command,
2519
+ degradedReason: reason,
2520
+ lastSeenAuth
2521
+ });
2522
+ }
2523
+ continue;
2524
+ }
2525
+ if (authState === "yes") {
2526
+ if (options.probe) {
2527
+ await recordHealthAuthOutcome(spec.id, "ok", "Health probe succeeded");
2528
+ }
2529
+ authReady.push(spec.id);
2530
+ }
2531
+ available.push(spec.id);
2532
+ if (includeVerboseDetails) {
2533
+ providerBreakdown.push({
2534
+ id: spec.id,
2535
+ status: "available",
2536
+ resolvedCommand: readiness.command,
2537
+ degradedReason: readiness.warning,
2538
+ lastSeenAuth: lastSeenAuthMap.get(spec.id) ?? ""
2539
+ });
2540
+ }
2541
+ }
2542
+ }
2543
+ const missingDetails = missing.map(
2544
+ (entry) => entry.installCommand ? `${entry.id} (install: ${entry.installCommand})` : entry.id
2545
+ ).join(", ");
2546
+ const degradedDetails = degraded.map((entry) => `${entry.id} (${entry.reason})`).join(", ");
2547
+ const lastSeenParts = specsToCheck.map(
2548
+ (spec) => `${spec.id}: ${lastSeenAuthMap.get(spec.id) ?? ""}`
2549
+ );
2550
+ const baseDetails = (() => {
2551
+ const parts = [];
2552
+ if (available.length > 0) {
2553
+ parts.push(`CLI present: ${available.join(", ")}`);
2554
+ }
2555
+ if (degraded.length > 0) {
2556
+ parts.push(`Degraded: ${degradedDetails}`);
2557
+ }
2558
+ if (authUnobserved.length > 0) {
2559
+ parts.push(
2560
+ `No current auth observation: ${authUnobserved.join(", ")} (expected; provider execution is the probe)`
2561
+ );
2562
+ }
2563
+ if (authMissing.length > 0) {
2564
+ parts.push(`Auth missing: ${authMissing.join(", ")}`);
2565
+ }
2566
+ if (authReady.length > 0) {
2567
+ parts.push(`Auth ready: ${authReady.join(", ")}`);
2568
+ }
2569
+ const assumedReady = providerReadiness.filter((entry) => entry.readinessNote === "env-var-auth-assumed").map((entry) => entry.provider);
2570
+ if (assumedReady.length > 0) {
2571
+ parts.push(`Readiness assumed: ${assumedReady.join(", ")}`);
2572
+ }
2573
+ if (lastSeenParts.length > 0) {
2574
+ parts.push(`Last seen auth: ${lastSeenParts.join(", ")}`);
2575
+ }
2576
+ if (missing.length > 0) {
2577
+ parts.push(`Missing: ${missingDetails}`);
2578
+ }
2579
+ if (uncheckedProviderIds.length > 0) {
2580
+ parts.push(`Not checked: ${uncheckedProviderIds.join(", ")} (use --all to include)`);
2581
+ }
2582
+ if (parts.length === 0) {
2583
+ return "No provider CLIs checked";
2584
+ }
2585
+ return parts.join(" | ");
2586
+ })();
2587
+ const discoveryModeNote = isDiscoveryMode ? "\n\n Info: Discovery mode: No configured providers found, checking all known providers.\n Tip: Set POETIC_DEFAULT_PROVIDER or configure providers in poetic.config.jsonc / ~/.config/poetic/poetic.config.jsonc" : "";
2588
+ const details = (() => {
2589
+ if (!includeVerboseDetails) return baseDetails + discoveryModeNote;
2590
+ if (providerBreakdown.length === 0) return baseDetails + discoveryModeNote;
2591
+ const breakdown = providerBreakdown.map((provider) => {
2592
+ let statusMark;
2593
+ switch (provider.status) {
2594
+ case "available":
2595
+ statusMark = "OK";
2596
+ break;
2597
+ case "degraded":
2598
+ statusMark = "Warning:";
2599
+ break;
2600
+ default:
2601
+ statusMark = "Error";
2602
+ }
2603
+ const commandPart = provider.resolvedCommand ? `: ${provider.resolvedCommand}` : ": missing";
2604
+ const statusSuffix = (() => {
2605
+ if (provider.status === "missing" && provider.installCommand) {
2606
+ return ` (install: ${provider.installCommand})`;
2607
+ }
2608
+ if (provider.status === "degraded" && provider.degradedReason) {
2609
+ const lastSeen = provider.lastSeenAuth ? `; lastSeenAuth: ${provider.lastSeenAuth}` : "";
2610
+ return ` (${provider.degradedReason}${lastSeen})`;
2611
+ }
2612
+ if (provider.lastSeenAuth) {
2613
+ return ` (lastSeenAuth: ${provider.lastSeenAuth})`;
2614
+ }
2615
+ return "";
2616
+ })();
2617
+ return `${statusMark} ${provider.id}${commandPart}${statusSuffix}`;
2618
+ }).join("\n ");
2619
+ return `${baseDetails}${discoveryModeNote}
2620
+
2621
+ ${breakdown}`;
2622
+ })();
2623
+ const totalChecked = specsToCheck.length;
2624
+ const healthyCount = available.length;
2625
+ const degradedCount = degraded.length;
2626
+ const missingCount = missing.length;
2627
+ const gateReadyCount = gateReadyProviders.size;
2628
+ const readinessGateLabel = gateReadyCount === 0 ? "zero-ready" : gateReadyCount === 1 ? "one-ready" : "multi-ready";
2629
+ const gateMeta = {
2630
+ readinessGateLabel,
2631
+ readinessReadyCount: gateReadyCount
2632
+ };
2633
+ if (options.requireReady && gateReadyCount === 0) {
2634
+ return {
2635
+ name: "Provider CLIs",
2636
+ category: "providers",
2637
+ status: "fail",
2638
+ message: isDiscoveryMode ? "No ready providers (--require-ready, discovery mode)" : "No ready providers (--require-ready)",
2639
+ details,
2640
+ ...providerReadiness.length > 0 ? { providerReadiness } : {},
2641
+ ...gateMeta
2642
+ };
2643
+ }
2644
+ if (healthyCount === 0 && degradedCount === 0) {
2645
+ return {
2646
+ name: "Provider CLIs",
2647
+ category: "providers",
2648
+ status: "fail",
2649
+ message: isDiscoveryMode ? "No provider CLIs present (discovery mode)" : "No provider CLIs present",
2650
+ details,
2651
+ ...providerReadiness.length > 0 ? { providerReadiness } : {},
2652
+ ...gateMeta
2653
+ };
2654
+ }
2655
+ if (missingCount > 0 || degradedCount > 0) {
2656
+ const statusParts = [];
2657
+ if (healthyCount > 0) statusParts.push(`${healthyCount} healthy`);
2658
+ if (degradedCount > 0) statusParts.push(`${degradedCount} degraded`);
2659
+ if (missingCount > 0) statusParts.push(`${missingCount} missing`);
2660
+ if ((options.strict || options.requireReady) && gateReadyCount > 0) {
2661
+ statusParts.push(`${gateReadyCount} ready`);
2662
+ }
2663
+ return {
2664
+ name: "Provider CLIs",
2665
+ category: "providers",
2666
+ status: degradedCount > 0 || missingCount > 0 ? "warn" : "pass",
2667
+ message: (() => {
2668
+ if (options.provider) {
2669
+ const sandboxEntry = sandboxBlocked.find((entry) => entry.id === options.provider);
2670
+ if (sandboxEntry) {
2671
+ return sandboxEntry.paths ? `${options.provider} sandbox blocked (${sandboxEntry.paths})` : `${options.provider} sandbox/policy blocked`;
2672
+ }
2673
+ if (authMissing.includes(options.provider)) {
2674
+ return `${options.provider} CLI present, auth missing`;
2675
+ }
2676
+ }
2677
+ return `${statusParts.join(", ")} of ${totalChecked} ${selectionScope === "configured" ? "configured " : ""}checked${isDiscoveryMode ? " (discovery mode)" : ""}${uncheckedProviderIds.length > 0 ? `; ${uncheckedProviderIds.length} known provider(s) not checked` : ""}`;
2678
+ })(),
2679
+ details,
2680
+ ...providerReadiness.length > 0 ? { providerReadiness } : {},
2681
+ ...gateMeta
2682
+ };
2683
+ }
2684
+ const scopedPassMessage = selectionScope === "configured" && uncheckedProviderIds.length > 0 ? `${healthyCount} of ${totalChecked} configured provider CLIs present; ${uncheckedProviderIds.length} known provider(s) not checked` : void 0;
2685
+ return {
2686
+ name: "Provider CLIs",
2687
+ category: "providers",
2688
+ status: "pass",
2689
+ 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)" : ""}`),
2690
+ details,
2691
+ ...providerReadiness.length > 0 ? { providerReadiness } : {},
2692
+ ...gateMeta
2693
+ };
2694
+ }
2695
+ async function checkPoeticDirectory() {
2696
+ const cwd = process.cwd();
2697
+ const poeticDir = path10.join(cwd, ".poetic");
2698
+ try {
2699
+ await fs5.stat(poeticDir);
2700
+ try {
2701
+ await fs5.access(poeticDir, fs5.constants.W_OK);
2702
+ return {
2703
+ name: ".poetic Directory",
2704
+ category: "poetic",
2705
+ status: "pass",
2706
+ message: "Directory exists and is writable",
2707
+ details: poeticDir
2708
+ };
2709
+ } catch (_error) {
2710
+ return {
2711
+ name: ".poetic Directory",
2712
+ category: "poetic",
2713
+ status: "fail",
2714
+ message: "Directory not writable",
2715
+ details: poeticDir,
2716
+ fixable: false
2717
+ };
2718
+ }
2719
+ } catch (_error) {
2720
+ return {
2721
+ name: ".poetic Directory",
2722
+ category: "poetic",
2723
+ status: "warn",
2724
+ message: "Directory not found",
2725
+ details: 'Run "poetic init" or "poetic doctor --fix" to initialize',
2726
+ fixable: true
2727
+ };
2728
+ }
2729
+ }
2730
+ async function checkNestedPoeticDirectory() {
2731
+ const cwd = process.cwd();
2732
+ const nestedDir = path10.join(cwd, ".poetic", ".poetic");
2733
+ try {
2734
+ const stats = await fs5.stat(nestedDir);
2735
+ if (stats.isDirectory()) {
2736
+ return {
2737
+ name: "Nested .poetic Directory",
2738
+ category: "poetic",
2739
+ status: "warn",
2740
+ message: "Found nested .poetic/.poetic/ directory (bug artifact)",
2741
+ details: `This directory was created by a bug and should be removed: ${nestedDir}`,
2742
+ fixable: true
2743
+ };
2744
+ }
2745
+ } catch (_error) {
2746
+ }
2747
+ return null;
2748
+ }
2749
+ async function checkRequiredDirectories() {
2750
+ const cwd = process.cwd();
2751
+ const missing = [];
2752
+ const present = [];
2753
+ for (const dir of RuntimeRequirements.directories) {
2754
+ const fullPath = path10.join(cwd, ".poetic", dir);
2755
+ try {
2756
+ await fs5.stat(fullPath);
2757
+ present.push(dir);
2758
+ } catch (_error) {
2759
+ missing.push(dir);
2760
+ }
2761
+ }
2762
+ if (missing.length === 0) {
2763
+ return {
2764
+ name: "Required Directories",
2765
+ category: "poetic",
2766
+ status: "pass",
2767
+ message: `All ${RuntimeRequirements.directories.length} directories present`,
2768
+ details: `All present: ${present.join(", ")}`
2769
+ };
2770
+ }
2771
+ if (missing.length === RuntimeRequirements.directories.length) {
2772
+ return {
2773
+ name: "Required Directories",
2774
+ category: "poetic",
2775
+ status: "fail",
2776
+ message: "No directories found",
2777
+ details: `Present: ${present.join(", ")} | Missing: ${missing.join(", ")}`,
2778
+ fixable: true
2779
+ };
2780
+ }
2781
+ return {
2782
+ name: "Required Directories",
2783
+ category: "poetic",
2784
+ status: "warn",
2785
+ message: `${missing.length} directories missing`,
2786
+ details: `Present: ${present.join(", ")} | Missing: ${missing.join(", ")}`,
2787
+ fixable: true
2788
+ };
2789
+ }
2790
+ async function checkArchiveReconciliation() {
2791
+ let report;
2792
+ try {
2793
+ report = await detectArchiveReconciliationIssues();
2794
+ } catch (error) {
2795
+ return {
2796
+ name: "Archive Reconciliation",
2797
+ category: "poetic",
2798
+ status: "warn",
2799
+ message: "Could not scan for archive reconciliation issues",
2800
+ details: error instanceof Error ? error.message : String(error)
2801
+ };
2802
+ }
2803
+ if (report.totalIssues === 0) {
2804
+ return {
2805
+ name: "Archive Reconciliation",
2806
+ category: "poetic",
2807
+ status: "pass",
2808
+ message: "No archive/restore reconciliation issues"
2809
+ };
2810
+ }
2811
+ return {
2812
+ name: "Archive Reconciliation",
2813
+ category: "poetic",
2814
+ status: "warn",
2815
+ message: `${report.totalIssues} archive reconciliation issue(s) detected`,
2816
+ details: formatArchiveReconciliationReport(report)
2817
+ };
2818
+ }
2819
+ async function checkProviderConfig() {
2820
+ const cwd = process.cwd();
2821
+ const providersConfigPath = resolveProvidersConfigPath({ cwd });
2822
+ try {
2823
+ ProviderRegistry.load(void 0, cwd);
2824
+ const projectPolicyPresent = await fs5.stat(providersConfigPath).then(() => true).catch((error) => {
2825
+ if (error.code === "ENOENT") return false;
2826
+ throw error;
2827
+ });
2828
+ return {
2829
+ name: "Provider Config",
2830
+ category: "poetic",
2831
+ status: "pass",
2832
+ message: "Provider catalog loaded",
2833
+ details: projectPolicyPresent ? `Release catalog with project policy: ${providersConfigPath}` : `Release catalog: ${ProviderRegistry.getLoadedConfigPath()}`
2834
+ };
2835
+ } catch (error) {
2836
+ return {
2837
+ name: "Provider Config",
2838
+ category: "poetic",
2839
+ status: "warn",
2840
+ message: "Cannot load provider catalog",
2841
+ details: error instanceof Error ? error.message : String(error)
2842
+ };
2843
+ }
2844
+ }
2845
+ async function checkExecutionRoles() {
2846
+ try {
2847
+ const outcome = await runExecutionRolesPreflight(process.cwd());
2848
+ if (!outcome.result || !outcome.roles) {
2849
+ return null;
2850
+ }
2851
+ const { errors, warnings } = outcome.result;
2852
+ const detailLines = [
2853
+ ...errors.map((issue) => `Error: ${formatPreflightIssue(issue)}`),
2854
+ ...warnings.map((issue) => `Warning: ${formatPreflightIssue(issue)}`)
2855
+ ];
2856
+ const details = detailLines.length > 0 ? detailLines.join("\n") : void 0;
2857
+ const reviewer = outcome.roles.reviewer;
2858
+ if (errors.length > 0) {
2859
+ return {
2860
+ name: "Execution Roles",
2861
+ category: "poetic",
2862
+ status: "fail",
2863
+ message: `Reviewer not ready for gate "${reviewer.gate}"`,
2864
+ details
2865
+ };
2866
+ }
2867
+ if (warnings.length > 0) {
2868
+ return {
2869
+ name: "Execution Roles",
2870
+ category: "poetic",
2871
+ status: "warn",
2872
+ message: `Reviewer degraded (gate "${reviewer.gate}")`,
2873
+ details
2874
+ };
2875
+ }
2876
+ return {
2877
+ name: "Execution Roles",
2878
+ category: "poetic",
2879
+ status: "pass",
2880
+ message: `Reviewer ready (gate "${reviewer.gate}")`
2881
+ };
2882
+ } catch (error) {
2883
+ return {
2884
+ name: "Execution Roles",
2885
+ category: "poetic",
2886
+ status: "fail",
2887
+ message: "Invalid execution-role configuration",
2888
+ details: error instanceof Error ? error.message : String(error)
2889
+ };
2890
+ }
2891
+ }
2892
+ async function checkSteeringInstructions() {
2893
+ const { loadUserGlobalAgentsFile, getUserAgentsPath } = await import("./user-agents-GHUGCSKN.js");
2894
+ const agentsPath = getUserAgentsPath();
2895
+ const loaded = loadUserGlobalAgentsFile();
2896
+ if (!loaded) {
2897
+ return {
2898
+ name: "Global Instructions",
2899
+ category: "poetic",
2900
+ status: "info",
2901
+ message: "No machine-wide AGENTS.md; using built-in steering defaults",
2902
+ details: `Optional: customize provider runs with "poetic config instructions edit" (${agentsPath}). It is injected into steering-capable provider runs.`
2903
+ };
2904
+ }
2905
+ if (!loaded.source.editedByUser) {
2906
+ return {
2907
+ name: "Global Instructions",
2908
+ category: "poetic",
2909
+ status: "info",
2910
+ message: "Machine-wide AGENTS.md present (unedited default)",
2911
+ details: `Tip: tailor it with "poetic config instructions edit" (${agentsPath}). It is injected into steering-capable provider runs.`
2912
+ };
2913
+ }
2914
+ return {
2915
+ name: "Global Instructions",
2916
+ category: "poetic",
2917
+ status: "pass",
2918
+ message: "Machine-wide AGENTS.md present (customized)",
2919
+ details: agentsPath
2920
+ };
2921
+ }
2922
+ async function checkLinuxWriteBoundary() {
2923
+ try {
2924
+ const { resolveLinuxHostClass, formatLinuxHostClassNote } = await import("./linux-host-class-UY4KVLFD.js");
2925
+ const resolution = resolveLinuxHostClass();
2926
+ if (resolution.hostClass === "desktop-unacked") {
2927
+ return {
2928
+ name: "Linux write boundary",
2929
+ category: "system",
2930
+ status: "warn",
2931
+ message: "Linux desktop write consent is not recorded",
2932
+ details: "This is not a sandbox-blocked provider. Write-capable runs need `poetic sandbox trust linux --ack-desktop` or, for a VM/container/cloud instance, `poetic sandbox trust linux --host isolated`."
2933
+ };
2934
+ }
2935
+ if (resolution.hostClass === "not-applicable") {
2936
+ return {
2937
+ name: "Linux write boundary",
2938
+ category: "system",
2939
+ status: "pass",
2940
+ message: "Host class not applicable on this platform",
2941
+ details: `source=${resolution.source}`
2942
+ };
2943
+ }
2944
+ const note = resolution.note ?? formatLinuxHostClassNote(resolution);
2945
+ return {
2946
+ name: "Linux write boundary",
2947
+ category: "system",
2948
+ status: "pass",
2949
+ message: `Host class ${resolution.hostClass} (source=${resolution.source}); writes allowed`,
2950
+ details: note ?? "Provider CLIs can write anywhere $USER can. This is not Seatbelt-sandboxed."
2951
+ };
2952
+ } catch (error) {
2953
+ return {
2954
+ name: "Linux write boundary",
2955
+ category: "system",
2956
+ status: "info",
2957
+ message: "Linux host-class posture could not be resolved",
2958
+ details: error instanceof Error ? error.message : String(error)
2959
+ };
2960
+ }
2961
+ }
2962
+ async function checkVerificationPrivilegeBoundary() {
2963
+ let verificationLabel = "unavailable";
2964
+ try {
2965
+ const { PROJECT_VERIFICATION_SANDBOX_ID, resolveSandboxLabel } = await import("./safety-stanza-BSVE6OJU.js");
2966
+ verificationLabel = resolveSandboxLabel(
2967
+ PROJECT_VERIFICATION_SANDBOX_ID,
2968
+ PROJECT_VERIFICATION_SANDBOX_ID
2969
+ );
2970
+ } catch {
2971
+ verificationLabel = "unavailable";
2972
+ }
2973
+ return {
2974
+ name: "Verification Privileges",
2975
+ category: "system",
2976
+ status: "info",
2977
+ message: "Project verification wraps when a boundary is available; otherwise it is an ordinary child",
2978
+ 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 Poetic against an unfamiliar repository as running that repository's code. See docs/reference/SECURITY.md (Project Verification Executes Target-Repository Code).`
2979
+ };
2980
+ }
2981
+ async function checkClaudeStrictMcpConfig() {
2982
+ let command;
2983
+ let prefixArgs;
2984
+ try {
2985
+ const resolved = resolveProviderCliCommand("claude");
2986
+ command = resolved.command;
2987
+ prefixArgs = resolved.prefixArgs ?? [];
2988
+ } catch {
2989
+ return {
2990
+ name: "Claude strict-mcp-config",
2991
+ category: "providers",
2992
+ status: "info",
2993
+ message: "Claude CLI not configured; skipping strict-mcp-config check"
2994
+ };
2995
+ }
2996
+ const probe = await probeProviderCli(command, ["--version"], prefixArgs);
2997
+ if (!probe.present) {
2998
+ return {
2999
+ name: "Claude strict-mcp-config",
3000
+ category: "providers",
3001
+ status: "info",
3002
+ message: "Claude CLI not installed; skipping strict-mcp-config check"
3003
+ };
3004
+ }
3005
+ if (!hasStrictMcpConfigBeenProbed()) {
3006
+ const { wrapWithSandbox } = await import("./sandbox-3AG2WJLM.js");
3007
+ const { metadata } = await import("./metadata-HQ43NDEK.js");
3008
+ const sandboxed = wrapWithSandbox({
3009
+ workingDir: process.cwd(),
3010
+ command,
3011
+ args: [...prefixArgs, "--help"],
3012
+ providerName: metadata.name,
3013
+ env: process.env
3014
+ });
3015
+ await probeStrictMcpConfig(
3016
+ sandboxed?.command ?? command,
3017
+ sandboxed?.args ?? [...prefixArgs, "--help"]
3018
+ );
3019
+ }
3020
+ const supported = isStrictMcpConfigSupported();
3021
+ if (supported) {
3022
+ return {
3023
+ name: "Claude strict-mcp-config",
3024
+ category: "providers",
3025
+ status: "pass",
3026
+ message: "Claude CLI supports --strict-mcp-config; judge MCP isolation active"
3027
+ };
3028
+ }
3029
+ return {
3030
+ name: "Claude strict-mcp-config",
3031
+ category: "providers",
3032
+ status: "warn",
3033
+ message: "Claude CLI does not support --strict-mcp-config; judge MCP isolation unavailable",
3034
+ details: "Judge runs may inherit project MCP servers, reducing determinism. Upgrade the Claude CLI to enable strict MCP isolation."
3035
+ };
3036
+ }
3037
+ async function checkLegacyClaudePlugin() {
3038
+ const { checkClaudeCode } = await import("./setup-claude-code-P7BTTSPO.js");
3039
+ const { resolvePoeticRoot } = await import("./poetic-root-V5DNXEAE.js");
3040
+ const result = await checkClaudeCode(resolvePoeticRoot(), { scope: "user" });
3041
+ const cleanupCommand = "poetic provider disconnect claude";
3042
+ if (result.settingsError) {
3043
+ return {
3044
+ name: "Legacy Claude plugin",
3045
+ category: "providers",
3046
+ status: "info",
3047
+ message: "Claude settings unreadable; legacy residue not checked",
3048
+ details: result.settingsError
3049
+ };
3050
+ }
3051
+ if (!result.hasLegacyResidue && result.pluginState.hasClaudeCli && !result.pluginState.listAvailable) {
3052
+ return {
3053
+ name: "Legacy Claude plugin",
3054
+ category: "providers",
3055
+ status: "info",
3056
+ message: "legacy plugin residue could not be checked",
3057
+ details: `Claude plugin enumeration was unavailable. Optional cleanup: ${cleanupCommand}. Does not affect native Claude CLI provider readiness.`
3058
+ };
3059
+ }
3060
+ if (!result.hasLegacyResidue) {
3061
+ return {
3062
+ name: "Legacy Claude plugin",
3063
+ category: "providers",
3064
+ status: "pass",
3065
+ message: "no legacy Poetic Claude Code plugin residue"
3066
+ };
3067
+ }
3068
+ const bits = [];
3069
+ if (result.pluginState.installedIds.length > 0) {
3070
+ bits.push(`plugin ids: ${result.pluginState.installedIds.join(", ")}`);
3071
+ }
3072
+ if (result.legacyState.hasLegacyMcpServer || result.legacyState.hasLegacyPermissions) {
3073
+ bits.push("legacy MCP settings");
3074
+ }
3075
+ return {
3076
+ name: "Legacy Claude plugin",
3077
+ category: "providers",
3078
+ status: "info",
3079
+ message: `legacy residue present (${bits.join("; ")})`,
3080
+ details: `Optional cleanup: ${cleanupCommand}. Does not affect native Claude CLI provider readiness.`
3081
+ };
3082
+ }
3083
+ async function checkDatabases() {
3084
+ const cwd = process.cwd();
3085
+ const poeticDir = path10.join(cwd, ".poetic");
3086
+ const dbDir = path10.join(poeticDir, "telemetry", "db");
3087
+ const variantsDbPath = path10.join(dbDir, "variants.db");
3088
+ const validationsDbPath = path10.join(dbDir, "validations.db");
3089
+ const checks = [];
3090
+ let variantsDbExists = false;
3091
+ try {
3092
+ await fs5.stat(variantsDbPath);
3093
+ variantsDbExists = true;
3094
+ } catch {
3095
+ }
3096
+ if (!variantsDbExists) {
3097
+ checks.push({
3098
+ name: "variants.db",
3099
+ category: "poetic",
3100
+ status: "info",
3101
+ message: "Not initialized yet",
3102
+ details: 'Telemetry database is created automatically on first execution. Run "poetic init --db" to pre-create, or "poetic doctor --fix" to initialize now.',
3103
+ fixable: true
3104
+ });
3105
+ } else {
3106
+ try {
3107
+ const SqliteDB = await getBetterSqlite3();
3108
+ const db = new SqliteDB(variantsDbPath, { readonly: true });
3109
+ try {
3110
+ const result = db.prepare("SELECT 1 as test").get();
3111
+ if (result?.test !== 1) {
3112
+ checks.push({
3113
+ name: "variants.db",
3114
+ category: "poetic",
3115
+ status: "fail",
3116
+ message: "Database query failed",
3117
+ details: variantsDbPath,
3118
+ fixable: true
3119
+ });
3120
+ db.close();
3121
+ return checks;
3122
+ }
3123
+ const integrityResult = db.prepare("PRAGMA quick_check").get();
3124
+ if (integrityResult?.quick_check !== "ok") {
3125
+ checks.push({
3126
+ name: "variants.db",
3127
+ category: "poetic",
3128
+ status: "fail",
3129
+ message: "Database integrity check failed",
3130
+ details: `${variantsDbPath} - Run "poetic doctor --fix"; if corruption remains, follow the database quarantine procedure in docs/TROUBLESHOOTING.md`,
3131
+ fixable: true
3132
+ });
3133
+ db.close();
3134
+ return checks;
3135
+ }
3136
+ const schemaVersion = checkSchemaVersion(db);
3137
+ if (schemaVersion !== SCHEMA_VERSION) {
3138
+ checks.push({
3139
+ name: "variants.db",
3140
+ category: "poetic",
3141
+ status: "warn",
3142
+ message: `Schema version mismatch (expected ${SCHEMA_VERSION}, found ${schemaVersion})`,
3143
+ details: variantsDbPath,
3144
+ fixable: true
3145
+ });
3146
+ } else {
3147
+ checks.push({
3148
+ name: "variants.db",
3149
+ category: "poetic",
3150
+ status: "pass",
3151
+ message: `Healthy (schema v${schemaVersion})`,
3152
+ details: variantsDbPath
3153
+ });
3154
+ }
3155
+ db.close();
3156
+ } catch (error) {
3157
+ db.close();
3158
+ checks.push({
3159
+ name: "variants.db",
3160
+ category: "poetic",
3161
+ status: "fail",
3162
+ message: "Database error",
3163
+ details: error instanceof Error ? error.message : String(error),
3164
+ fixable: true
3165
+ });
3166
+ }
3167
+ } catch (error) {
3168
+ const likelyNativeMismatch = isLikelyBetterSqlite3NativeLoadMismatch(error);
3169
+ checks.push({
3170
+ name: "variants.db",
3171
+ category: "poetic",
3172
+ status: "fail",
3173
+ message: likelyNativeMismatch ? "better-sqlite3 native module load mismatch" : "Database could not be opened",
3174
+ details: formatDatabaseNativeErrorDetails(error),
3175
+ fixable: true
3176
+ });
3177
+ }
3178
+ }
3179
+ let validationsDbExists = false;
3180
+ try {
3181
+ await fs5.stat(validationsDbPath);
3182
+ validationsDbExists = true;
3183
+ } catch {
3184
+ }
3185
+ if (!validationsDbExists) {
3186
+ checks.push({
3187
+ name: "validations.db",
3188
+ category: "poetic",
3189
+ status: "info",
3190
+ message: "Not present (optional)"
3191
+ });
3192
+ } else {
3193
+ try {
3194
+ const SqliteDB = await getBetterSqlite3();
3195
+ const db = new SqliteDB(validationsDbPath, { readonly: true });
3196
+ try {
3197
+ const result = db.prepare("SELECT 1 as test").get();
3198
+ if (result?.test === 1) {
3199
+ checks.push({
3200
+ name: "validations.db",
3201
+ category: "poetic",
3202
+ status: "pass",
3203
+ message: "Accessible",
3204
+ details: validationsDbPath
3205
+ });
3206
+ }
3207
+ db.close();
3208
+ } catch (error) {
3209
+ db.close();
3210
+ checks.push({
3211
+ name: "validations.db",
3212
+ category: "poetic",
3213
+ status: "warn",
3214
+ message: "Database error",
3215
+ details: error instanceof Error ? error.message : String(error)
3216
+ });
3217
+ }
3218
+ } catch (error) {
3219
+ const likelyNativeMismatch = isLikelyBetterSqlite3NativeLoadMismatch(error);
3220
+ checks.push({
3221
+ name: "validations.db",
3222
+ category: "poetic",
3223
+ status: "warn",
3224
+ message: likelyNativeMismatch ? "better-sqlite3 native module load mismatch" : "Database could not be opened",
3225
+ details: formatDatabaseNativeErrorDetails(error)
3226
+ });
3227
+ }
3228
+ }
3229
+ return checks;
3230
+ }
3231
+ async function checkStateMarker() {
3232
+ const cwd = process.cwd();
3233
+ const stateFile = path10.join(cwd, ".poetic", "state", "init.json");
3234
+ try {
3235
+ const content = await fs5.readFile(stateFile, "utf-8");
3236
+ const state = JSON.parse(content);
3237
+ const issues = [];
3238
+ if (state.schemaVersion !== SCHEMA_VERSION) {
3239
+ issues.push(`Schema v${state.schemaVersion} (expected v${SCHEMA_VERSION})`);
3240
+ }
3241
+ if (state.tier !== "runtime" && state.tier !== "full") {
3242
+ issues.push(`Invalid tier: ${state.tier}`);
3243
+ }
3244
+ if (issues.length > 0) {
3245
+ return {
3246
+ name: "State Marker",
3247
+ category: "poetic",
3248
+ status: "warn",
3249
+ message: "State file has issues",
3250
+ details: issues.join(", "),
3251
+ fixable: true
3252
+ };
3253
+ }
3254
+ return {
3255
+ name: "State Marker",
3256
+ category: "poetic",
3257
+ status: "pass",
3258
+ message: `Initialized (v${state.version}, tier: ${state.tier})`,
3259
+ details: state.lastCheckedAt ? `Last checked: ${new Date(state.lastCheckedAt).toLocaleString()}` : "Last checked: unknown"
3260
+ };
3261
+ } catch (error) {
3262
+ if (error.code === "ENOENT") {
3263
+ return {
3264
+ name: "State Marker",
3265
+ category: "poetic",
3266
+ status: "warn",
3267
+ message: "State file not found",
3268
+ details: 'Run "poetic doctor --fix" to initialize',
3269
+ fixable: true
3270
+ };
3271
+ }
3272
+ return {
3273
+ name: "State Marker",
3274
+ category: "poetic",
3275
+ status: "warn",
3276
+ message: "Cannot read state file",
3277
+ details: error instanceof Error ? error.message : String(error),
3278
+ fixable: true
3279
+ };
3280
+ }
3281
+ }
3282
+ async function checkTelemetryMode() {
3283
+ const poeticDir = getRuntimeEnv("POETIC_TELEMETRY_DIR") ?? path10.join(process.cwd(), ".poetic");
3284
+ const dbDir = path10.join(poeticDir, "telemetry", "db");
3285
+ const variantsDbPath = path10.join(dbDir, "variants.db");
3286
+ try {
3287
+ await fs5.stat(variantsDbPath);
3288
+ const SqliteDB = await getBetterSqlite3();
3289
+ const db = new SqliteDB(variantsDbPath, { readonly: true });
3290
+ try {
3291
+ db.prepare("SELECT 1").get();
3292
+ db.close();
3293
+ return {
3294
+ name: "Telemetry Mode",
3295
+ category: "poetic",
3296
+ status: "pass",
3297
+ message: "Database mode (full features)",
3298
+ details: variantsDbPath
3299
+ };
3300
+ } catch (_error) {
3301
+ db.close();
3302
+ return {
3303
+ name: "Telemetry Mode",
3304
+ category: "poetic",
3305
+ status: "warn",
3306
+ message: "Degraded mode (JSONL-only)",
3307
+ details: variantsDbPath
3308
+ };
3309
+ }
3310
+ } catch (_error) {
3311
+ return {
3312
+ name: "Telemetry Mode",
3313
+ category: "poetic",
3314
+ status: "warn",
3315
+ message: "Degraded mode (JSONL-only)",
3316
+ details: variantsDbPath
3317
+ };
3318
+ }
3319
+ }
3320
+ async function resolvePoeticPackageRoot() {
3321
+ let current = path10.dirname(fileURLToPath(import.meta.url));
3322
+ while (true) {
3323
+ try {
3324
+ const packageJsonPath = path10.join(current, "package.json");
3325
+ const packageJson = JSON.parse(await fs5.readFile(packageJsonPath, "utf8"));
3326
+ if (packageJson.name === "@poetic-ai/poetic") {
3327
+ return current;
3328
+ }
3329
+ } catch {
3330
+ }
3331
+ const parent = path10.dirname(current);
3332
+ if (parent === current) {
3333
+ return process.cwd();
3334
+ }
3335
+ current = parent;
3336
+ }
3337
+ }
3338
+ async function checkDistArtifacts() {
3339
+ try {
3340
+ const { checkBuildFreshness } = await import("./flywheel-preflight-6E33DJDK.js");
3341
+ const result = await checkBuildFreshness(await resolvePoeticPackageRoot());
3342
+ if (result.passed) {
3343
+ return {
3344
+ name: "Build Artifacts",
3345
+ category: "poetic",
3346
+ status: "pass",
3347
+ message: result.metadata?.packageMode ? "packaged dist artifacts are present" : "dist/entry.js is present and fresh",
3348
+ details: result.warning
3349
+ };
3350
+ }
3351
+ return {
3352
+ name: "Build Artifacts",
3353
+ category: "poetic",
3354
+ status: "warn",
3355
+ message: "Build artifacts need refresh",
3356
+ 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 poetic binary against the latest source.` : "Most source tests do not require fresh dist output; run npm run build when testing the bundled poetic binary against the latest source."
3357
+ };
3358
+ } catch (error) {
3359
+ return {
3360
+ name: "Build Artifacts",
3361
+ category: "poetic",
3362
+ status: "warn",
3363
+ message: "Could not verify dist artifacts",
3364
+ details: error instanceof Error ? error.message : String(error)
3365
+ };
3366
+ }
3367
+ }
3368
+ async function calculateDirectorySize(dirPath) {
3369
+ let totalSize = 0;
3370
+ const entries = await fs5.readdir(dirPath, { withFileTypes: true });
3371
+ for (const entry of entries) {
3372
+ const entryPath = path10.join(dirPath, entry.name);
3373
+ if (entry.isDirectory()) {
3374
+ totalSize += await calculateDirectorySize(entryPath);
3375
+ } else if (entry.isFile()) {
3376
+ const stat2 = await fs5.stat(entryPath);
3377
+ totalSize += stat2.size;
3378
+ }
3379
+ }
3380
+ return totalSize;
3381
+ }
3382
+ var FOREIGN_COORDINATION_LOCK_REMEDIATION = "Local process liveness cannot be verified for foreign locks. Verify the owning machine is not running Poetic against this repo, then delete the lock file:";
3383
+ var UNREADABLE_COORDINATION_LOCK_REMEDIATION = "Poetic cannot safely verify unreadable coordination locks. Verify no Poetic process owns the lock, then delete the lock file:";
3384
+ function formatCoordinationLockAge(ageMs) {
3385
+ const ageSec = Math.round(ageMs / 1e3);
3386
+ if (ageSec < 60) {
3387
+ return `${ageSec}s`;
3388
+ }
3389
+ const ageMin = Math.round(ageSec / 60);
3390
+ if (ageMin < 60) {
3391
+ return `${ageMin}m`;
3392
+ }
3393
+ const ageHours = Math.round(ageMin / 60);
3394
+ return `${ageHours}h`;
3395
+ }
3396
+ function formatCoordinationLockLine(diagnostic) {
3397
+ const machinePart = diagnostic.machineIdShort ?? "unknown";
3398
+ const pidPart = diagnostic.pid ?? "n/a";
3399
+ const agePart = formatCoordinationLockAge(diagnostic.ageMs);
3400
+ return `${diagnostic.name}: ${diagnostic.classification} (pid=${pidPart}, machine=${machinePart}, age=${agePart})`;
3401
+ }
3402
+ function isForeignStaleCoordinationLock(diagnostic, staleMs) {
3403
+ if (diagnostic.classification !== "foreign-machine") {
3404
+ return false;
3405
+ }
3406
+ return diagnostic.ageMs >= staleMs;
3407
+ }
3408
+ async function checkCoordinationLocks() {
3409
+ const { DEFAULT_STALE_LOCK_MS, diagnoseCoordinationLocks } = await import("./backend-P3DAYRP7.js");
3410
+ const cwd = process.cwd();
3411
+ const locksDir = path10.join(cwd, ".poetic", "planning", "coordination", "locks");
3412
+ const diagnostics = diagnoseCoordinationLocks(locksDir);
3413
+ if (diagnostics.length === 0) {
3414
+ return null;
3415
+ }
3416
+ const foreignStaleLocks = diagnostics.filter(
3417
+ (diagnostic) => isForeignStaleCoordinationLock(diagnostic, DEFAULT_STALE_LOCK_MS)
3418
+ );
3419
+ const unreadableLocks = diagnostics.filter((d) => d.classification === "unreadable");
3420
+ const liveCount = diagnostics.filter((d) => d.classification === "live").length;
3421
+ const staleSameMachineCount = diagnostics.filter(
3422
+ (d) => d.classification === "stale-same-machine"
3423
+ ).length;
3424
+ const foreignCount = diagnostics.filter((d) => d.classification === "foreign-machine").length;
3425
+ const detailLines = diagnostics.map(formatCoordinationLockLine);
3426
+ const remediationLines = foreignStaleLocks.map(
3427
+ (diagnostic) => `${FOREIGN_COORDINATION_LOCK_REMEDIATION} ${diagnostic.lockPath}`
3428
+ );
3429
+ remediationLines.push(
3430
+ ...unreadableLocks.map(
3431
+ (diagnostic) => `${UNREADABLE_COORDINATION_LOCK_REMEDIATION} ${diagnostic.lockPath}`
3432
+ )
3433
+ );
3434
+ const manualRemediationCount = foreignStaleLocks.length + unreadableLocks.length;
3435
+ let status = "pass";
3436
+ if (foreignStaleLocks.length > 0 || unreadableLocks.length > 0) {
3437
+ status = "warn";
3438
+ } else if (staleSameMachineCount > 0) {
3439
+ status = "info";
3440
+ }
3441
+ const messageParts = [`${diagnostics.length} coordination lock(s)`];
3442
+ if (liveCount > 0) {
3443
+ messageParts.push(`${liveCount} live`);
3444
+ }
3445
+ if (staleSameMachineCount > 0) {
3446
+ messageParts.push(`${staleSameMachineCount} stale-same-machine`);
3447
+ }
3448
+ if (foreignCount > 0) {
3449
+ messageParts.push(`${foreignCount} foreign-machine`);
3450
+ }
3451
+ if (unreadableLocks.length > 0) {
3452
+ messageParts.push(`${unreadableLocks.length} unreadable`);
3453
+ }
3454
+ if (manualRemediationCount > 0) {
3455
+ messageParts.push(`${manualRemediationCount} require manual remediation`);
3456
+ }
3457
+ const detailsParts = [...detailLines];
3458
+ if (remediationLines.length > 0) {
3459
+ detailsParts.push("", ...remediationLines);
3460
+ }
3461
+ return {
3462
+ name: "Coordination Locks",
3463
+ category: "poetic",
3464
+ status,
3465
+ message: messageParts.join("; "),
3466
+ details: detailsParts.join("\n ")
3467
+ };
3468
+ }
3469
+ async function checkDiskSpace() {
3470
+ const poeticDir = getRuntimeEnv("POETIC_TELEMETRY_DIR") ?? path10.join(process.cwd(), ".poetic");
3471
+ const telemetryDir = path10.join(poeticDir, "telemetry");
3472
+ try {
3473
+ try {
3474
+ await fs5.stat(telemetryDir);
3475
+ } catch (_error) {
3476
+ return {
3477
+ name: "Disk Space",
3478
+ category: "resources",
3479
+ status: "info",
3480
+ message: "Telemetry directory not found",
3481
+ details: 'Run "poetic init" to initialize'
3482
+ };
3483
+ }
3484
+ const totalSize = await calculateDirectorySize(telemetryDir);
3485
+ const sizeMB = totalSize / (1024 * 1024);
3486
+ const sizeGB = sizeMB / 1024;
3487
+ if (sizeGB > 2) {
3488
+ return {
3489
+ name: "Disk Space",
3490
+ category: "resources",
3491
+ status: "fail",
3492
+ message: `Telemetry directory very large: ${sizeGB.toFixed(2)} GB`,
3493
+ details: 'Consider running "poetic cleanup" to remove old data'
3494
+ };
3495
+ }
3496
+ if (sizeMB > 500) {
3497
+ return {
3498
+ name: "Disk Space",
3499
+ category: "resources",
3500
+ status: "warn",
3501
+ message: `Telemetry directory: ${sizeMB.toFixed(1)} MB`,
3502
+ details: "Consider periodic cleanup"
3503
+ };
3504
+ }
3505
+ return {
3506
+ name: "Disk Space",
3507
+ category: "resources",
3508
+ status: "pass",
3509
+ message: `Telemetry directory: ${sizeMB.toFixed(1)} MB`,
3510
+ details: telemetryDir
3511
+ };
3512
+ } catch (error) {
3513
+ return {
3514
+ name: "Disk Space",
3515
+ category: "resources",
3516
+ status: "fail",
3517
+ message: "Cannot check disk space",
3518
+ details: error instanceof Error ? error.message : String(error)
3519
+ };
3520
+ }
3521
+ }
3522
+ async function checkMemory() {
3523
+ try {
3524
+ const totalGB = os.totalmem() / (1024 * 1024 * 1024);
3525
+ const freeGB = getAvailableMemoryGB();
3526
+ const usedPercent = Math.max(0, (totalGB - freeGB) / totalGB * 100);
3527
+ if (freeGB < 0.5) {
3528
+ return {
3529
+ name: "Memory",
3530
+ category: "resources",
3531
+ status: "fail",
3532
+ message: `Critical: only ${freeGB.toFixed(2)} GB available`,
3533
+ details: `${usedPercent.toFixed(1)}% used of ${totalGB.toFixed(1)} GB total`
3534
+ };
3535
+ }
3536
+ if (freeGB < 1) {
3537
+ return {
3538
+ name: "Memory",
3539
+ category: "resources",
3540
+ status: "warn",
3541
+ message: `Low memory: ${freeGB.toFixed(2)} GB available`,
3542
+ details: `${usedPercent.toFixed(1)}% used of ${totalGB.toFixed(1)} GB total`
3543
+ };
3544
+ }
3545
+ return {
3546
+ name: "Memory",
3547
+ category: "resources",
3548
+ status: "pass",
3549
+ message: `${freeGB.toFixed(2)} GB available of ${totalGB.toFixed(1)} GB`,
3550
+ details: `${usedPercent.toFixed(1)}% used`
3551
+ };
3552
+ } catch (error) {
3553
+ return {
3554
+ name: "Memory",
3555
+ category: "resources",
3556
+ status: "fail",
3557
+ message: "Cannot check system memory",
3558
+ details: error instanceof Error ? error.message : String(error)
3559
+ };
3560
+ }
3561
+ }
3562
+ async function checkNodeVersion() {
3563
+ const currentVersion = process.version;
3564
+ const versionPart = currentVersion.slice(1).split(".")[0];
3565
+ const major = versionPart ? Number.parseInt(versionPart, 10) : 0;
3566
+ const preferredPath = await preferredNode24Path();
3567
+ if (major !== REQUIRED_NODE_MAJOR) {
3568
+ return {
3569
+ name: "Node.js",
3570
+ category: "system",
3571
+ status: "fail",
3572
+ message: `Node.js ${currentVersion} is not supported`,
3573
+ details: formatNode24Remediation(preferredPath)
3574
+ };
3575
+ }
3576
+ const homebrewDetail = preferredPath ? ` | Homebrew Node 24: ${preferredPath}` : "";
3577
+ return {
3578
+ name: "Node.js",
3579
+ category: "system",
3580
+ status: "pass",
3581
+ message: `Node.js ${currentVersion}`,
3582
+ details: `${process.platform}/${process.arch} \u2014 ${process.execPath}${homebrewDetail}`
3583
+ };
3584
+ }
3585
+ async function checkGitRepository() {
3586
+ try {
3587
+ const result = await execa("git", ["rev-parse", "--is-inside-work-tree"]);
3588
+ if (result.stdout.trim() === "true") {
3589
+ let branchName = "unknown";
3590
+ const branch = await execa("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
3591
+ reject: false
3592
+ });
3593
+ if (branch.exitCode === 0 && branch.stdout.trim() && branch.stdout.trim() !== "HEAD") {
3594
+ branchName = branch.stdout.trim();
3595
+ } else {
3596
+ const symbolicBranch = await execa("git", ["symbolic-ref", "--short", "HEAD"], {
3597
+ reject: false
3598
+ });
3599
+ if (symbolicBranch.exitCode === 0 && symbolicBranch.stdout.trim()) {
3600
+ branchName = symbolicBranch.stdout.trim();
3601
+ }
3602
+ }
3603
+ let details;
3604
+ try {
3605
+ const remoteResult = await execa("git", ["remote", "get-url", "origin"], { reject: false });
3606
+ const remoteUrl = remoteResult.stdout.trim();
3607
+ details = remoteUrl ? `branch: ${branchName}, remote: ${remoteUrl}` : `branch: ${branchName}, no remote`;
3608
+ } catch (_remoteError) {
3609
+ details = `branch: ${branchName}, no remote`;
3610
+ }
3611
+ return {
3612
+ name: "Git Repository",
3613
+ category: "git",
3614
+ status: "pass",
3615
+ message: `Inside git repository (branch: ${branchName})`,
3616
+ details
3617
+ };
3618
+ }
3619
+ return {
3620
+ name: "Git Repository",
3621
+ category: "git",
3622
+ status: "warn",
3623
+ message: "Not inside a git repository",
3624
+ details: "Some features may not work correctly"
3625
+ };
3626
+ } catch (_error) {
3627
+ return {
3628
+ name: "Git Repository",
3629
+ category: "git",
3630
+ status: "warn",
3631
+ message: "Not inside a git repository",
3632
+ details: "Some features may not work correctly"
3633
+ };
3634
+ }
3635
+ }
3636
+ var DOCTOR_LINK_CHECK_TIMEOUT_MS = 5e3;
3637
+ async function checkKnowledgeKit(cwd, options = {}) {
3638
+ const checkLinks = options.checkLinks === true;
3639
+ try {
3640
+ const result = await knowledgeDoctorChecks(cwd, {
3641
+ checkLinks,
3642
+ linkTimeoutMs: DOCTOR_LINK_CHECK_TIMEOUT_MS,
3643
+ ...options.probeLink ? { probeLink: options.probeLink } : {}
3644
+ });
3645
+ if (!result.manifestPresent) {
3646
+ const intent = readIntentContract(cwd);
3647
+ if (intent.status === "ok" && intent.contract.status !== "retired") {
3648
+ const resolved = resolveIntent(detectStack(cwd), intent.contract).contract;
3649
+ const lines = resolved.dimensions.map((d) => describeDimension(d));
3650
+ const hasConflict = resolved.status === "conflict";
3651
+ return {
3652
+ name: "Knowledge Kit",
3653
+ category: "knowledge",
3654
+ status: "warn",
3655
+ message: hasConflict ? `No kit accepted; stack intent "${intent.contract.spec}" has unresolved conflicts \u2014 run poetic knowledge offer (headless) or poetic init in a terminal to receive the kit` : `No knowledge kit accepted yet (stack intent "${intent.contract.spec}" pending) \u2014 run poetic knowledge offer (headless) or poetic init in a terminal to receive the kit`,
3656
+ details: `Declared ${intent.contract.declaredAt.slice(0, 10)}${intent.contract.rationale ? ` \u2014 ${intent.contract.rationale}` : ""}
3657
+ ${lines.join("\n ")}
3658
+ Run 'poetic knowledge offer' (headless) or 'poetic init' in a terminal to review recommendations${hasConflict ? " and resolve the conflict" : ""}.`
3659
+ };
3660
+ }
3661
+ return {
3662
+ name: "Knowledge Kit",
3663
+ category: "knowledge",
3664
+ status: "info",
3665
+ message: "No knowledge kit accepted yet",
3666
+ details: "Run 'poetic init' in an interactive session to review stack recommendations. Greenfield repo (no code yet)? Declare the intended stack: poetic init --stack <spec>."
3667
+ };
3668
+ }
3669
+ if (result.findings.length === 0) {
3670
+ return {
3671
+ name: "Knowledge Kit",
3672
+ category: "knowledge",
3673
+ status: "pass",
3674
+ message: checkLinks ? "Knowledge manifest present; no issues found (links checked)" : "Knowledge manifest present; no issues found"
3675
+ };
3676
+ }
3677
+ const severityToStatus = (severity) => severity === "error" ? "fail" : severity === "warn" ? "warn" : "info";
3678
+ const worst = result.findings.some((f) => f.severity === "error") ? "fail" : result.findings.some((f) => f.severity === "warn") ? "warn" : "info";
3679
+ const details = result.findings.map((finding) => `[${severityToStatus(finding.severity)}] ${finding.message}`).join("\n ");
3680
+ return {
3681
+ name: "Knowledge Kit",
3682
+ category: "knowledge",
3683
+ status: worst,
3684
+ message: `${result.findings.length} knowledge finding(s)`,
3685
+ details
3686
+ };
3687
+ } catch (error) {
3688
+ return {
3689
+ name: "Knowledge Kit",
3690
+ category: "knowledge",
3691
+ status: "warn",
3692
+ message: "Could not run knowledge checks",
3693
+ details: error instanceof Error ? error.message : String(error)
3694
+ };
3695
+ }
3696
+ }
3697
+ function checkPendingInitNotices(cwd) {
3698
+ let count = 0;
3699
+ try {
3700
+ count = pendingNoticesCount(cwd);
3701
+ } catch {
3702
+ }
3703
+ if (count === 0) {
3704
+ return {
3705
+ name: "Pending Init Notices",
3706
+ category: "knowledge",
3707
+ status: "pass",
3708
+ message: "No pending init notices",
3709
+ notices_pending: 0
3710
+ };
3711
+ }
3712
+ return {
3713
+ name: "Pending Init Notices",
3714
+ category: "knowledge",
3715
+ status: "info",
3716
+ message: `${count} init notice(s) pending review`,
3717
+ details: "Notices are shown below (human output) or after the summary.",
3718
+ notices_pending: count
3719
+ };
3720
+ }
3721
+ function checkPendingOfferNotice(cwd) {
3722
+ let notice;
3723
+ try {
3724
+ notice = readPendingOfferNotice(cwd);
3725
+ } catch {
3726
+ notice = void 0;
3727
+ }
3728
+ if (!notice) {
3729
+ return {
3730
+ name: "Knowledge Offer",
3731
+ category: "knowledge",
3732
+ status: "pass",
3733
+ message: "No pending knowledge-kit offer"
3734
+ };
3735
+ }
3736
+ return {
3737
+ name: "Knowledge Offer",
3738
+ category: "knowledge",
3739
+ status: "warn",
3740
+ message: notice.message,
3741
+ details: `Offer skipped ${notice.skippedAt.slice(0, 10)} (${notice.cause}); it clears when an offer runs.`
3742
+ };
3743
+ }
3744
+ function renderDoctorPendingNudges(cwd, options, renderOptions = {}) {
3745
+ const policy = {
3746
+ json: options.json === true,
3747
+ quiet: false,
3748
+ verbose: options.verbose === true,
3749
+ debug: false,
3750
+ noColor: false,
3751
+ level: "normal"
3752
+ };
3753
+ try {
3754
+ return renderPendingNudges(cwd, policy, { ...renderOptions, includeAge: true });
3755
+ } catch {
3756
+ return { rendered: false, deliveredKeys: [], pendingCount: 0 };
3757
+ }
3758
+ }
3759
+ async function runAllHealthChecks(options) {
3760
+ const allChecks = [];
3761
+ if (options.providersOnly) {
3762
+ const includeVerboseProviderDetails2 = Boolean(options.verbose) && !options.json;
3763
+ allChecks.push(
3764
+ await checkProviderCLIs({
3765
+ includeVerboseDetails: includeVerboseProviderDetails2,
3766
+ all: options.all,
3767
+ provider: options.provider,
3768
+ probe: options.probe,
3769
+ strict: options.strict,
3770
+ requireReady: options.requireReady
3771
+ })
3772
+ );
3773
+ allChecks.push(...await collectProviderExtraHealthChecks(options));
3774
+ return allChecks;
3775
+ }
3776
+ allChecks.push(await checkNodeVersion());
3777
+ allChecks.push(await checkMemory());
3778
+ allChecks.push(await checkGit());
3779
+ allChecks.push(await checkGitRepository());
3780
+ const includeVerboseProviderDetails = Boolean(options.verbose) && !options.json;
3781
+ allChecks.push(
3782
+ await checkProviderCLIs({
3783
+ includeVerboseDetails: includeVerboseProviderDetails,
3784
+ all: options.all,
3785
+ provider: options.provider,
3786
+ probe: options.probe,
3787
+ strict: options.strict,
3788
+ requireReady: options.requireReady
3789
+ })
3790
+ );
3791
+ allChecks.push(...await collectProviderExtraHealthChecks(options));
3792
+ allChecks.push(await checkVerificationPrivilegeBoundary());
3793
+ allChecks.push(await checkLinuxWriteBoundary());
3794
+ allChecks.push(await checkPoeticDirectory());
3795
+ const nestedCheck = await checkNestedPoeticDirectory();
3796
+ if (nestedCheck) allChecks.push(nestedCheck);
3797
+ allChecks.push(await checkRequiredDirectories());
3798
+ allChecks.push(await checkArchiveReconciliation());
3799
+ allChecks.push(await checkProviderConfig());
3800
+ const executionRolesCheck = await checkExecutionRoles();
3801
+ if (executionRolesCheck) allChecks.push(executionRolesCheck);
3802
+ allChecks.push(await checkSteeringInstructions());
3803
+ allChecks.push(...await checkDatabases());
3804
+ allChecks.push(await checkStateMarker());
3805
+ allChecks.push(await checkTelemetryMode());
3806
+ allChecks.push(await checkDistArtifacts());
3807
+ const coordinationLockCheck = await checkCoordinationLocks();
3808
+ if (coordinationLockCheck) allChecks.push(coordinationLockCheck);
3809
+ const knowledgeCwd = resolveRepoRootForPoeticDir(process.cwd());
3810
+ allChecks.push(await checkKnowledgeKit(knowledgeCwd, { checkLinks: options.checkLinks }));
3811
+ allChecks.push(checkPendingInitNotices(knowledgeCwd));
3812
+ allChecks.push(checkPendingOfferNotice(knowledgeCwd));
3813
+ allChecks.push(...await checkGuardrails(knowledgeCwd));
3814
+ allChecks.push(await checkDiskSpace());
3815
+ allChecks.push(await checkConcurrencyBudget());
3816
+ return allChecks;
3817
+ }
3818
+ async function checkConcurrencyBudget() {
3819
+ try {
3820
+ const budget = detectHostConcurrencyBudget();
3821
+ const basis = `${budget.totalMemoryGB.toFixed(0)} GB RAM, ${budget.performanceCores} performance cores`;
3822
+ const recommendation = `up to ${budget.legSlots} concurrent legs, ${budget.validationSlots} concurrent validations (${basis})`;
3823
+ let effectiveValue;
3824
+ try {
3825
+ const merged = await new ConfigManager().loadConfig();
3826
+ const asValidConcurrency = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
3827
+ effectiveValue = asValidConcurrency(merged.config.variants?.maxConcurrent) ?? asValidConcurrency(merged.config.worktree?.maxConcurrent);
3828
+ } catch {
3829
+ }
3830
+ if (effectiveValue !== void 0 && effectiveValue > budget.legSlots) {
3831
+ return {
3832
+ name: "Concurrency budget",
3833
+ category: "resources",
3834
+ status: "warn",
3835
+ message: `Effective max concurrent (${effectiveValue}) exceeds this host's recommendation`,
3836
+ details: `This host recommends ${recommendation}. The configured value still runs as asked; validation pacing and memory pressure shedding are the safety net.`
3837
+ };
3838
+ }
3839
+ return {
3840
+ name: "Concurrency budget",
3841
+ category: "resources",
3842
+ status: "info",
3843
+ message: `Recommended: ${recommendation}`,
3844
+ details: `Effective max concurrent (config or default): ${effectiveValue ?? 4}. Recommendations are advisory; Poetic runs what you ask for.`
3845
+ };
3846
+ } catch (error) {
3847
+ return {
3848
+ name: "Concurrency budget",
3849
+ category: "resources",
3850
+ status: "info",
3851
+ message: "Could not compute a host concurrency recommendation",
3852
+ details: error instanceof Error ? error.message : String(error)
3853
+ };
3854
+ }
3855
+ }
3856
+ function displayHealthResults(checks, options) {
3857
+ if (options.json) {
3858
+ console.log(JSON.stringify(checks, null, 2));
3859
+ return;
3860
+ }
3861
+ console.log(chalk2.bold.cyan("\nPoetic Health Check\n"));
3862
+ const categories = {
3863
+ system: [],
3864
+ git: [],
3865
+ providers: [],
3866
+ poetic: [],
3867
+ knowledge: [],
3868
+ guardrails: [],
3869
+ resources: []
3870
+ };
3871
+ for (const check of checks) {
3872
+ categories[check.category]?.push(check);
3873
+ }
3874
+ const categoryNames = {
3875
+ system: "System Environment",
3876
+ git: "Git Configuration",
3877
+ providers: "AI Providers",
3878
+ poetic: "Poetic Installation",
3879
+ knowledge: "Knowledge & Init Drift",
3880
+ guardrails: "Guardrails",
3881
+ resources: "System Resources"
3882
+ };
3883
+ for (const [category, categoryChecks] of Object.entries(categories)) {
3884
+ if (categoryChecks.length === 0) continue;
3885
+ console.log(chalk2.bold(categoryNames[category]));
3886
+ for (const check of categoryChecks) {
3887
+ let statusIcon = "";
3888
+ let statusColor;
3889
+ switch (check.status) {
3890
+ case "pass":
3891
+ statusIcon = "OK";
3892
+ statusColor = chalk2.green;
3893
+ break;
3894
+ case "warn":
3895
+ statusIcon = "Warning: ";
3896
+ statusColor = chalk2.yellow;
3897
+ break;
3898
+ case "fail":
3899
+ statusIcon = "Error";
3900
+ statusColor = chalk2.red;
3901
+ break;
3902
+ case "info":
3903
+ statusIcon = "Info:";
3904
+ statusColor = chalk2.blue;
3905
+ break;
3906
+ }
3907
+ const checkName = check.name.padEnd(25);
3908
+ console.log(` ${statusColor(statusIcon)} ${chalk2.bold(checkName)} ${check.message}`);
3909
+ if (options.verbose && check.details) {
3910
+ console.log(chalk2.gray(` ${check.details}`));
3911
+ }
3912
+ }
3913
+ console.log();
3914
+ }
3915
+ const passCount = checks.filter((c) => c.status === "pass").length;
3916
+ const warnCount = checks.filter((c) => c.status === "warn").length;
3917
+ const failCount = checks.filter((c) => c.status === "fail").length;
3918
+ const infoCount = checks.filter((c) => c.status === "info").length;
3919
+ const fixableCount = checks.filter((c) => c.fixable && c.status !== "pass").length;
3920
+ console.log(chalk2.bold.cyan("Summary\n"));
3921
+ console.log(` ${chalk2.green("Passed")}: ${passCount}`);
3922
+ if (warnCount > 0) console.log(` ${chalk2.yellow("Warnings")}: ${warnCount}`);
3923
+ if (failCount > 0) console.log(` ${chalk2.red("Failed")}: ${failCount}`);
3924
+ if (infoCount > 0) console.log(` ${chalk2.blue("Info")}: ${infoCount}`);
3925
+ console.log();
3926
+ if (failCount > 0) {
3927
+ console.log(chalk2.bold.red("Error: Checks Failed\n"));
3928
+ console.log(chalk2.gray(" Some checks failed. Review the issues above."));
3929
+ if (fixableCount > 0) {
3930
+ console.log(chalk2.gray(` ${fixableCount} issue(s) can be fixed automatically.`));
3931
+ console.log(chalk2.gray(" Run: poetic doctor --fix"));
3932
+ }
3933
+ } else if (warnCount > 0) {
3934
+ console.log(chalk2.bold.yellow("Warning: Warnings Detected\n"));
3935
+ console.log(chalk2.gray(" System is operational but some issues were found."));
3936
+ if (fixableCount > 0) {
3937
+ console.log(chalk2.gray(` ${fixableCount} issue(s) can be fixed automatically.`));
3938
+ console.log(chalk2.gray(" Run: poetic doctor --fix"));
3939
+ }
3940
+ } else {
3941
+ console.log(chalk2.bold.green("OK: All Systems Healthy\n"));
3942
+ console.log(chalk2.gray(" Poetic is ready to use!"));
3943
+ }
3944
+ if (!options.verbose && (warnCount > 0 || failCount > 0)) {
3945
+ console.log(chalk2.gray("\nTip: Run with --verbose flag for more details"));
3946
+ }
3947
+ console.log();
3948
+ }
3949
+ function createDoctorCommand() {
3950
+ const health = new Command2("doctor");
3951
+ health.description(
3952
+ "Check system health and diagnose issues\n\nExamples:\n poetic doctor # Check configured providers only\n poetic doctor --all # Check all known providers\n poetic doctor --provider claude # Check specific provider\n poetic doctor --providers-only --provider claude # Check provider CLI and cached auth evidence\n poetic doctor --probe --provider claude # Probe provider auth and update cached evidence\n poetic doctor --verbose # Show detailed information\n poetic doctor --fix # Automatically repair issues\n poetic doctor --json # Output as JSON\n poetic doctor --matrix # Show provider support matrix"
3953
+ );
3954
+ health.aliases(["d"]);
3955
+ health.addCommand(createDoctorMetricsCommand());
3956
+ health.option("--json", "(OPTIONAL) Output results as JSON");
3957
+ health.option("-v, --verbose", "(OPTIONAL) Show detailed information");
3958
+ health.option("--fix", "(OPTIONAL) Attempt to repair issues automatically");
3959
+ health.option("-a, --all", "(OPTIONAL) Check all known providers");
3960
+ health.option("-p, --provider <id>", "(OPTIONAL) Check specific provider only");
3961
+ health.option("--providers-only", "(OPTIONAL) Check provider CLI and cached auth evidence only");
3962
+ health.option("--matrix", "(OPTIONAL) Show provider support matrix with tier and health status");
3963
+ health.option(
3964
+ "--strict",
3965
+ "(OPTIONAL) Exit non-zero on identity failures (CLI missing, config invalid). Unobserved auth and zero ready providers do not fail"
3966
+ );
3967
+ health.option(
3968
+ "--require-ready",
3969
+ "(OPTIONAL) Exit non-zero when no checked provider can run (opt-in readiness gate)"
3970
+ );
3971
+ health.option("--probe", "(OPTIONAL) Run provider auth probes and update auth liveness cache");
3972
+ health.option("--refresh", "(OPTIONAL) Clear cached auth liveness before checking");
3973
+ health.option(
3974
+ "--check-links",
3975
+ "(OPTIONAL) Probe knowledge-kit source URLs for dead links (network; 5s/link; default off)"
3976
+ );
3977
+ health.action(async (options) => {
3978
+ const validatedOptions = validateCommandOptions(HealthCommandOptionsSchema, options);
3979
+ if (validatedOptions.refresh) {
3980
+ await invalidateAuthOutcome(validatedOptions.provider);
3981
+ }
3982
+ if (validatedOptions.matrix) {
3983
+ const { collectProviderMatrix } = await import("./provider-matrix-KWTYA5QV.js");
3984
+ const { renderProviderMatrix } = await import("./provider-matrix-renderer-MZDWKKID.js");
3985
+ const rows = await collectProviderMatrix({
3986
+ probeAuth: true,
3987
+ providerFilter: validatedOptions.provider
3988
+ });
3989
+ if (validatedOptions.json) {
3990
+ const checks2 = await runAllHealthChecks(validatedOptions);
3991
+ console.log(JSON.stringify({ checks: checks2, providerMatrix: rows }, null, 2));
3992
+ exitForHealthFailures(checks2, validatedOptions);
3993
+ return;
3994
+ }
3995
+ console.log(renderProviderMatrix(rows));
3996
+ const skipNormalChecks = !validatedOptions.all && !validatedOptions.providersOnly && !validatedOptions.fix;
3997
+ if (skipNormalChecks) {
3998
+ if (rows.some((row) => row.status !== "available")) {
3999
+ process.exit(1);
4000
+ }
4001
+ return;
4002
+ }
4003
+ }
4004
+ if (validatedOptions.fix) {
4005
+ console.log(chalk2.bold.cyan("\nRepairing Poetic Installation\n"));
4006
+ const cwd = resolveRepoRootForPoeticDir(process.cwd());
4007
+ const nestedDir = path10.join(cwd, ".poetic", ".poetic");
4008
+ try {
4009
+ const stats = await fs5.stat(nestedDir);
4010
+ if (stats.isDirectory()) {
4011
+ console.log(chalk2.gray("Removing nested .poetic/.poetic/ directory (bug artifact)..."));
4012
+ await fs5.rm(nestedDir, { recursive: true, force: true });
4013
+ console.log(chalk2.green(" OK Removed nested directory"));
4014
+ }
4015
+ } catch (_error) {
4016
+ }
4017
+ try {
4018
+ const result = await ensurePoeticInitialized({
4019
+ cwd,
4020
+ tier: "runtime",
4021
+ repair: true,
4022
+ verbose: validatedOptions.verbose,
4023
+ allowDegraded: true
4024
+ });
4025
+ if (result.status === "ok") {
4026
+ console.log(chalk2.green("OK: Repair completed successfully"));
4027
+ } else if (result.status === "degraded") {
4028
+ console.log(chalk2.yellow("Warning: Repair completed with warnings (degraded mode)"));
4029
+ } else {
4030
+ console.log(chalk2.red("Error: Repair failed"));
4031
+ }
4032
+ if (result.actions.created.length > 0) {
4033
+ console.log(chalk2.gray(`
4034
+ Created ${result.actions.created.length} item(s):`));
4035
+ for (const item of result.actions.created) {
4036
+ console.log(chalk2.gray(` - ${item}`));
4037
+ }
4038
+ }
4039
+ if (result.actions.repaired.length > 0) {
4040
+ console.log(chalk2.gray(`
4041
+ Repaired ${result.actions.repaired.length} item(s):`));
4042
+ for (const item of result.actions.repaired) {
4043
+ console.log(chalk2.gray(` - ${item}`));
4044
+ }
4045
+ }
4046
+ if (result.warnings.length > 0) {
4047
+ console.log(chalk2.yellow(`
4048
+ Warnings (${result.warnings.length}):`));
4049
+ for (const warning of result.warnings) {
4050
+ console.log(chalk2.yellow(` Warning: ${warning}`));
4051
+ }
4052
+ }
4053
+ if (result.errors.length > 0) {
4054
+ console.log(chalk2.red(`
4055
+ Errors (${result.errors.length}):`));
4056
+ for (const error of result.errors) {
4057
+ console.log(chalk2.red(` Error ${error.message}`));
4058
+ if (error.path) {
4059
+ console.log(chalk2.gray(` Path: ${error.path}`));
4060
+ }
4061
+ }
4062
+ }
4063
+ if (result.nextSteps.length > 0) {
4064
+ console.log(chalk2.bold("\nNext Steps:"));
4065
+ for (const step of result.nextSteps) {
4066
+ console.log(chalk2.gray(` \u2022 ${step}`));
4067
+ }
4068
+ }
4069
+ console.log(chalk2.gray(`
4070
+ Completed in ${result.initDurationMs}ms`));
4071
+ console.log(chalk2.gray(`Telemetry mode: ${result.telemetryMode}`));
4072
+ if (result.schemaVersion) {
4073
+ console.log(chalk2.gray(`Schema version: ${result.schemaVersion}`));
4074
+ }
4075
+ console.log();
4076
+ console.log(chalk2.bold.cyan("Re-running Health Checks\n"));
4077
+ } catch (error) {
4078
+ console.error(
4079
+ chalk2.red("Error: Repair failed:"),
4080
+ error instanceof Error ? error.message : String(error)
4081
+ );
4082
+ process.exit(1);
4083
+ }
4084
+ }
4085
+ const checks = await runAllHealthChecks(validatedOptions);
4086
+ displayHealthResults(checks, validatedOptions);
4087
+ renderDoctorPendingNudges(resolveRepoRootForPoeticDir(process.cwd()), validatedOptions);
4088
+ exitForHealthFailures(checks, validatedOptions);
4089
+ });
4090
+ return health;
4091
+ }
4092
+
4093
+ export {
4094
+ recordDelivery,
4095
+ checkDrift,
4096
+ shouldSuppressNudges,
4097
+ renderPendingNudges,
4098
+ checkWrapperVisibility,
4099
+ PENDING_OFFER_NOTICE_MESSAGE,
4100
+ writePendingOfferNotice,
4101
+ readPendingOfferNotice,
4102
+ clearPendingOfferNotice,
4103
+ GUARD_KIT_VERSION,
4104
+ GUARD_DIR_REL,
4105
+ CHECK_SCRIPT_REL,
4106
+ CONFIG_FILE_REL,
4107
+ LOCK_FILE_REL,
4108
+ SETUP_PLAYBOOK_REL,
4109
+ CI_WORKFLOW_TEMPLATE_FILE,
4110
+ BASELINES_DIR_REL,
4111
+ GENERATED_HASHED_FILES,
4112
+ getGuardKitTemplatesDir,
4113
+ repairGuardCaseIssues,
4114
+ formatGuardCaseIssues,
4115
+ compareKitVersions,
4116
+ classifyGuardState,
4117
+ PROVIDER_CLI_SPEC_IDS,
4118
+ checkProviderCLIs,
4119
+ checkLinuxWriteBoundary,
4120
+ checkCoordinationLocks,
4121
+ checkKnowledgeKit,
4122
+ checkPendingInitNotices,
4123
+ checkPendingOfferNotice,
4124
+ renderDoctorPendingNudges,
4125
+ runAllHealthChecks,
4126
+ createDoctorCommand
4127
+ };
4128
+ //# sourceMappingURL=chunk-SSB5UQHA.js.map