@unbrained/pm-cli 2026.6.19 → 2026.6.21

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 (545) hide show
  1. package/CHANGELOG.md +328 -258
  2. package/README.md +1 -1
  3. package/dist/cli/bootstrap-args.d.ts +12 -1
  4. package/dist/cli/bootstrap-args.js +20 -9
  5. package/dist/cli/bootstrap-args.js.map +1 -1
  6. package/dist/cli/commander-usage.js +31 -25
  7. package/dist/cli/commander-usage.js.map +1 -1
  8. package/dist/cli/commands/completion.js +3 -3
  9. package/dist/cli/commands/completion.js.map +1 -1
  10. package/dist/cli/commands/contracts.js +4 -3
  11. package/dist/cli/commands/contracts.js.map +1 -1
  12. package/dist/cli/commands/eval.d.ts +62 -0
  13. package/dist/cli/commands/eval.js +158 -0
  14. package/dist/cli/commands/eval.js.map +1 -0
  15. package/dist/cli/commands/extension/install-sources.d.ts +12 -0
  16. package/dist/cli/commands/extension/install-sources.js +77 -10
  17. package/dist/cli/commands/extension/install-sources.js.map +1 -1
  18. package/dist/cli/commands/extension/scaffold.js +49 -3
  19. package/dist/cli/commands/extension/scaffold.js.map +1 -1
  20. package/dist/cli/commands/extension.js +9 -2
  21. package/dist/cli/commands/extension.js.map +1 -1
  22. package/dist/cli/commands/gc.d.ts +12 -1
  23. package/dist/cli/commands/gc.js +53 -6
  24. package/dist/cli/commands/gc.js.map +1 -1
  25. package/dist/cli/commands/health.js +27 -9
  26. package/dist/cli/commands/health.js.map +1 -1
  27. package/dist/cli/commands/index.d.ts +1 -0
  28. package/dist/cli/commands/index.js +3 -2
  29. package/dist/cli/commands/index.js.map +1 -1
  30. package/dist/cli/commands/linked-artifacts.js +13 -3
  31. package/dist/cli/commands/linked-artifacts.js.map +1 -1
  32. package/dist/cli/commands/list.js +12 -9
  33. package/dist/cli/commands/list.js.map +1 -1
  34. package/dist/cli/commands/plan.js +2 -2
  35. package/dist/cli/commands/plan.js.map +1 -1
  36. package/dist/cli/commands/restore.js +5 -11
  37. package/dist/cli/commands/restore.js.map +1 -1
  38. package/dist/cli/commands/search.d.ts +39 -1
  39. package/dist/cli/commands/search.js +89 -12
  40. package/dist/cli/commands/search.js.map +1 -1
  41. package/dist/cli/commands/update.js +96 -58
  42. package/dist/cli/commands/update.js.map +1 -1
  43. package/dist/cli/commands/validate.d.ts +1 -1
  44. package/dist/cli/commands/validate.js +44 -2
  45. package/dist/cli/commands/validate.js.map +1 -1
  46. package/dist/cli/extension-command-help.d.ts +9 -0
  47. package/dist/cli/extension-command-help.js +13 -2
  48. package/dist/cli/extension-command-help.js.map +1 -1
  49. package/dist/cli/extension-command-options.d.ts +8 -0
  50. package/dist/cli/extension-command-options.js +27 -2
  51. package/dist/cli/extension-command-options.js.map +1 -1
  52. package/dist/cli/help-content.js +15 -2
  53. package/dist/cli/help-content.js.map +1 -1
  54. package/dist/cli/main.d.ts +12 -0
  55. package/dist/cli/main.js +49 -9
  56. package/dist/cli/main.js.map +1 -1
  57. package/dist/cli/register-list-query.js +34 -4
  58. package/dist/cli/register-list-query.js.map +1 -1
  59. package/dist/cli/register-operations.js +3 -3
  60. package/dist/cli/register-operations.js.map +1 -1
  61. package/dist/cli-bundle/chunks/activity-CIHV64UC.js +18 -0
  62. package/dist/cli-bundle/chunks/{activity-XJ7WRNWS.js → activity-KW3IRP44.js} +11 -11
  63. package/dist/cli-bundle/chunks/{aggregate-LBCWF6UJ.js → aggregate-3ZKQG5SW.js} +10 -10
  64. package/dist/cli-bundle/chunks/{aggregate-OFT4YVB6.js → aggregate-FIGQEIL3.js} +10 -10
  65. package/dist/cli-bundle/chunks/{append-BP2ARHOK.js → append-EWJYJ7CH.js} +8 -8
  66. package/dist/cli-bundle/chunks/{append-5U3XR5SD.js → append-QEOLX3XB.js} +8 -8
  67. package/dist/cli-bundle/chunks/{chunk-CSRFOIHG.js → chunk-2EIMPCE4.js} +3 -3
  68. package/dist/cli-bundle/chunks/{chunk-MAKOMXVM.js → chunk-2GCDRRIF.js} +106 -67
  69. package/dist/cli-bundle/chunks/chunk-2GCDRRIF.js.map +1 -0
  70. package/dist/cli-bundle/chunks/{search-AZR2JITW.js → chunk-2PKHRP6G.js} +190 -22
  71. package/dist/cli-bundle/chunks/chunk-2PKHRP6G.js.map +7 -0
  72. package/dist/cli-bundle/chunks/{chunk-YWAYOWDQ.js → chunk-32R4PIC6.js} +118 -15
  73. package/dist/cli-bundle/chunks/chunk-32R4PIC6.js.map +1 -0
  74. package/dist/cli-bundle/chunks/{chunk-II3WDRCM.js → chunk-37QXHBIB.js} +3 -3
  75. package/dist/cli-bundle/chunks/{chunk-UCNEPMPY.js → chunk-3EWSGA7N.js} +105 -66
  76. package/dist/cli-bundle/chunks/chunk-3EWSGA7N.js.map +7 -0
  77. package/dist/cli-bundle/chunks/{chunk-UMTPQMEG.js → chunk-3MDHREEV.js} +3 -3
  78. package/dist/cli-bundle/chunks/{chunk-I2VJM36R.js → chunk-434HM6Z5.js} +21 -21
  79. package/dist/cli-bundle/chunks/chunk-434HM6Z5.js.map +1 -0
  80. package/dist/cli-bundle/chunks/{chunk-Y4IBSEQ2.js → chunk-52OCIDPG.js} +20 -8
  81. package/dist/cli-bundle/chunks/chunk-52OCIDPG.js.map +1 -0
  82. package/dist/cli-bundle/chunks/{chunk-DJ3YYLHG.js → chunk-5FIEHKEX.js} +5 -5
  83. package/dist/cli-bundle/chunks/{chunk-OURX4WVG.js → chunk-5IDI42VV.js} +7 -7
  84. package/dist/cli-bundle/chunks/{chunk-UA4ANRJY.js → chunk-5PCI3UBT.js} +119 -16
  85. package/dist/cli-bundle/chunks/chunk-5PCI3UBT.js.map +7 -0
  86. package/dist/cli-bundle/chunks/{chunk-RVG3WLY3.js → chunk-5QICDWMV.js} +2 -2
  87. package/dist/cli-bundle/chunks/{chunk-LIVRA6VG.js → chunk-6CJWZAN7.js} +7 -7
  88. package/dist/cli-bundle/chunks/{chunk-AVMSP2HH.js → chunk-6H4LF7AO.js} +2 -2
  89. package/dist/cli-bundle/chunks/{chunk-B2NEFANY.js → chunk-6M4RJS4M.js} +3 -3
  90. package/dist/cli-bundle/chunks/{chunk-4BFNWAMQ.js → chunk-6ZXM2XNF.js} +29 -12
  91. package/dist/cli-bundle/chunks/chunk-6ZXM2XNF.js.map +7 -0
  92. package/dist/cli-bundle/chunks/{chunk-A5JPMD4Q.js → chunk-76Z3YSHV.js} +2 -2
  93. package/dist/cli-bundle/chunks/{chunk-5NS6TJ2F.js → chunk-7D3SAQXM.js} +2 -2
  94. package/dist/cli-bundle/chunks/{chunk-DVUU3AHP.js → chunk-A4Z7ZOL5.js} +17 -6
  95. package/dist/cli-bundle/chunks/chunk-A4Z7ZOL5.js.map +1 -0
  96. package/dist/cli-bundle/chunks/{chunk-2E3KSWFG.js → chunk-AIS5PGI3.js} +2 -2
  97. package/dist/cli-bundle/chunks/{chunk-47BYN6CA.js → chunk-AZMOLBN3.js} +7 -7
  98. package/dist/cli-bundle/chunks/{chunk-DYZKSIVX.js → chunk-B473GZWW.js} +3 -3
  99. package/dist/cli-bundle/chunks/{chunk-RNWE2HZR.js → chunk-C3AZ5ZML.js} +15 -15
  100. package/dist/cli-bundle/chunks/{chunk-S673PVHD.js → chunk-C4N5I7ZW.js} +3 -3
  101. package/dist/cli-bundle/chunks/{chunk-TYQBGXH2.js → chunk-CF55WVVS.js} +2 -2
  102. package/dist/cli-bundle/chunks/{chunk-4EOS35RL.js → chunk-CZPXY7UW.js} +3 -3
  103. package/dist/cli-bundle/chunks/{chunk-MPEWACTG.js → chunk-CZUB4XWO.js} +2 -2
  104. package/dist/cli-bundle/chunks/{chunk-22YA6GCA.js → chunk-D7FYS6PG.js} +2 -2
  105. package/dist/cli-bundle/chunks/{chunk-5SRID7DV.js → chunk-EBH56IXK.js} +29 -12
  106. package/dist/cli-bundle/chunks/chunk-EBH56IXK.js.map +1 -0
  107. package/dist/cli-bundle/chunks/{chunk-UIMLAFOE.js → chunk-F5NZWFSW.js} +7 -7
  108. package/dist/cli-bundle/chunks/{chunk-PSWSQHM7.js → chunk-F7K5GWP3.js} +15 -19
  109. package/dist/cli-bundle/chunks/chunk-F7K5GWP3.js.map +1 -0
  110. package/dist/cli-bundle/chunks/{chunk-H3DP6OUM.js → chunk-FKBDKV7Y.js} +3 -3
  111. package/dist/cli-bundle/chunks/{chunk-SO2IOH37.js → chunk-FRUFKWNK.js} +2 -2
  112. package/dist/cli-bundle/chunks/{chunk-7R5YKVKO.js → chunk-FWPDUYEY.js} +7 -7
  113. package/dist/cli-bundle/chunks/{search-2JX776KX.js → chunk-GMHPNNYG.js} +184 -23
  114. package/dist/cli-bundle/chunks/chunk-GMHPNNYG.js.map +1 -0
  115. package/dist/cli-bundle/chunks/{chunk-USVOBLGB.js → chunk-GOZUHWFD.js} +2 -2
  116. package/dist/cli-bundle/chunks/{chunk-ZBVYKWT2.js → chunk-H7NJZWG2.js} +15 -15
  117. package/dist/cli-bundle/chunks/{chunk-FPQA4ZMA.js → chunk-HC4AWIKT.js} +18 -7
  118. package/dist/cli-bundle/chunks/chunk-HC4AWIKT.js.map +7 -0
  119. package/dist/cli-bundle/chunks/{chunk-4L7Q4OVT.js → chunk-HCU6WUSY.js} +9 -9
  120. package/dist/cli-bundle/chunks/{chunk-2RI3BDFK.js → chunk-HIP7WVTL.js} +10 -9
  121. package/dist/cli-bundle/chunks/chunk-HIP7WVTL.js.map +7 -0
  122. package/dist/cli-bundle/chunks/{chunk-5QLHQK7V.js → chunk-HMOADQVT.js} +26 -26
  123. package/dist/cli-bundle/chunks/chunk-HMOADQVT.js.map +7 -0
  124. package/dist/cli-bundle/chunks/{chunk-K5TD74VU.js → chunk-J4MYYSOL.js} +19 -7
  125. package/dist/cli-bundle/chunks/chunk-J4MYYSOL.js.map +7 -0
  126. package/dist/cli-bundle/chunks/{chunk-GDOTTY2S.js → chunk-JINYH2DB.js} +40 -2
  127. package/dist/cli-bundle/chunks/chunk-JINYH2DB.js.map +7 -0
  128. package/dist/cli-bundle/chunks/{chunk-EMFDZ7N3.js → chunk-LEU5GHRC.js} +55 -5
  129. package/dist/cli-bundle/chunks/chunk-LEU5GHRC.js.map +1 -0
  130. package/dist/cli-bundle/chunks/{chunk-PRUB7RI4.js → chunk-LUWW7L4B.js} +3 -3
  131. package/dist/cli-bundle/chunks/{chunk-N7LDJRB4.js → chunk-M5XALUSL.js} +2 -2
  132. package/dist/cli-bundle/chunks/{chunk-NPNNW5TU.js → chunk-N5ZJACJ6.js} +8 -8
  133. package/dist/cli-bundle/chunks/{chunk-NNMBNN6Q.js → chunk-NB3PYLQ6.js} +7 -5
  134. package/dist/cli-bundle/chunks/{chunk-USKTNI7P.js → chunk-NIGXMNNI.js} +2 -2
  135. package/dist/cli-bundle/chunks/{chunk-GL3QKIET.js → chunk-NIUEXCFS.js} +6 -6
  136. package/dist/cli-bundle/chunks/{chunk-OUG4CL4H.js → chunk-NJHMAJ6N.js} +8 -8
  137. package/dist/cli-bundle/chunks/{chunk-2K56AWDP.js → chunk-NJN7LL7U.js} +5 -5
  138. package/dist/cli-bundle/chunks/{chunk-5P2LQV2F.js → chunk-NSBZ2CKF.js} +2 -2
  139. package/dist/cli-bundle/chunks/{chunk-ZZEM2PH7.js → chunk-NXS7ZKAK.js} +19 -14
  140. package/dist/cli-bundle/chunks/chunk-NXS7ZKAK.js.map +1 -0
  141. package/dist/cli-bundle/chunks/{chunk-XV6N3QND.js → chunk-O47BPOPC.js} +2 -2
  142. package/dist/cli-bundle/chunks/{chunk-X26ODPU4.js → chunk-OFYMIDF2.js} +14 -18
  143. package/dist/cli-bundle/chunks/chunk-OFYMIDF2.js.map +7 -0
  144. package/dist/cli-bundle/chunks/{chunk-QCHNPN24.js → chunk-OTHIWLMB.js} +7 -5
  145. package/dist/cli-bundle/chunks/{chunk-YVYJQR6S.js → chunk-PFSRUHDH.js} +9 -9
  146. package/dist/cli-bundle/chunks/{chunk-M35R7LFX.js → chunk-PP2U4NMT.js} +5 -5
  147. package/dist/cli-bundle/chunks/{chunk-ZGEWEO7B.js → chunk-PUH4D67U.js} +2 -2
  148. package/dist/cli-bundle/chunks/{chunk-LEC57E7K.js → chunk-Q2J2HJUN.js} +2 -2
  149. package/dist/cli-bundle/chunks/{chunk-QSH34DSQ.js → chunk-QGG32QHW.js} +61 -4
  150. package/dist/cli-bundle/chunks/chunk-QGG32QHW.js.map +7 -0
  151. package/dist/cli-bundle/chunks/{chunk-QG6M6XQE.js → chunk-QQHISQBJ.js} +3 -3
  152. package/dist/cli-bundle/chunks/{chunk-BQVESTOG.js → chunk-QZTWBFXJ.js} +2 -2
  153. package/dist/cli-bundle/chunks/{chunk-COHNQLB2.js → chunk-R2EMWY7Q.js} +41 -3
  154. package/dist/cli-bundle/chunks/chunk-R2EMWY7Q.js.map +1 -0
  155. package/dist/cli-bundle/chunks/{chunk-5LZPDRFW.js → chunk-RLK7P77C.js} +5 -5
  156. package/dist/cli-bundle/chunks/{chunk-QTWWHFI3.js → chunk-RVHYI7SL.js} +8 -8
  157. package/dist/cli-bundle/chunks/{chunk-FVZSUIN5.js → chunk-SBGXDO7A.js} +3 -3
  158. package/dist/cli-bundle/chunks/{chunk-VP4Z36GO.js → chunk-SRYYRVPB.js} +2 -2
  159. package/dist/cli-bundle/chunks/{chunk-X7RKOJXL.js → chunk-SX75ADBK.js} +6 -6
  160. package/dist/cli-bundle/chunks/{chunk-MTDEEUGV.js → chunk-TKBHZPKD.js} +3 -3
  161. package/dist/cli-bundle/chunks/{chunk-HMQMRHQJ.js → chunk-UEMNL33O.js} +8 -8
  162. package/dist/cli-bundle/chunks/{chunk-SDUZTZ55.js → chunk-UPE3CDD7.js} +7 -7
  163. package/dist/cli-bundle/chunks/{chunk-VG3B2CYD.js → chunk-UV6IYE5U.js} +2 -2
  164. package/dist/cli-bundle/chunks/{chunk-YOGXK3ON.js → chunk-WQ7NIVDK.js} +18 -13
  165. package/dist/cli-bundle/chunks/chunk-WQ7NIVDK.js.map +7 -0
  166. package/dist/cli-bundle/chunks/{chunk-MTGMNUMJ.js → chunk-YCJX76PW.js} +2 -2
  167. package/dist/cli-bundle/chunks/{chunk-FP7YLQ52.js → chunk-Z6T4PQI2.js} +11 -10
  168. package/dist/cli-bundle/chunks/chunk-Z6T4PQI2.js.map +1 -0
  169. package/dist/cli-bundle/chunks/{claim-2BSYPJTJ.js → claim-RC56YUWG.js} +10 -10
  170. package/dist/cli-bundle/chunks/{claim-EZ6JKY5X.js → claim-THOJ6CMV.js} +10 -10
  171. package/dist/cli-bundle/chunks/close-DNMHGOCX.js +15 -0
  172. package/dist/cli-bundle/chunks/{close-3TSX6BYF.js → close-SG5MNCVQ.js} +8 -8
  173. package/dist/cli-bundle/chunks/{close-many-HB4UFCS3.js → close-many-3L3AQZBU.js} +15 -15
  174. package/dist/cli-bundle/chunks/{close-many-B7DBVA46.js → close-many-DKQLBGLV.js} +15 -15
  175. package/dist/cli-bundle/chunks/{comments-M7PAINZT.js → comments-M7ZOZNBX.js} +10 -10
  176. package/dist/cli-bundle/chunks/{comments-X7M7XM6I.js → comments-SKWMDWCW.js} +10 -10
  177. package/dist/cli-bundle/chunks/{config-YICACPHA.js → config-7CK62S7T.js} +30 -10
  178. package/dist/cli-bundle/chunks/config-7CK62S7T.js.map +1 -0
  179. package/dist/cli-bundle/chunks/{config-UWF3KRP5.js → config-OZCUABPB.js} +29 -9
  180. package/dist/cli-bundle/chunks/config-OZCUABPB.js.map +7 -0
  181. package/dist/cli-bundle/chunks/{context-EGUGU4K7.js → context-OEC366TO.js} +13 -13
  182. package/dist/cli-bundle/chunks/{context-V32ZAPL6.js → context-PUG2A7RC.js} +13 -13
  183. package/dist/cli-bundle/chunks/{contracts-B2YWYUV5.js → contracts-3THOLA7L.js} +10 -8
  184. package/dist/cli-bundle/chunks/contracts-3THOLA7L.js.map +7 -0
  185. package/dist/cli-bundle/chunks/{contracts-7G553BHP.js → contracts-N65FXEOL.js} +10 -8
  186. package/dist/cli-bundle/chunks/contracts-N65FXEOL.js.map +1 -0
  187. package/dist/cli-bundle/chunks/{copy-NWFFRYWM.js → copy-6QLHILAC.js} +7 -7
  188. package/dist/cli-bundle/chunks/{copy-RMLXLZOL.js → copy-JPX7VZCH.js} +7 -7
  189. package/dist/cli-bundle/chunks/create-NNP6TSFO.js +30 -0
  190. package/dist/cli-bundle/chunks/{create-GK4MX2LJ.js → create-Y54ZVVZH.js} +14 -14
  191. package/dist/cli-bundle/chunks/{delete-RMGNIL2S.js → delete-63PCMQAJ.js} +7 -7
  192. package/dist/cli-bundle/chunks/{delete-EFQLVEAS.js → delete-GDSMGAGJ.js} +7 -7
  193. package/dist/cli-bundle/chunks/{deps-WKHUPVD3.js → deps-T4ZF5G54.js} +7 -7
  194. package/dist/cli-bundle/chunks/{deps-K2I7QV4Y.js → deps-XYQ73XRH.js} +7 -7
  195. package/dist/cli-bundle/chunks/{docs-3FXRDYA7.js → docs-C75YIXHC.js} +10 -9
  196. package/dist/cli-bundle/chunks/{docs-3FXRDYA7.js.map → docs-C75YIXHC.js.map} +1 -1
  197. package/dist/cli-bundle/chunks/{docs-2H2UGIT6.js → docs-EHNPDWUL.js} +12 -11
  198. package/dist/cli-bundle/chunks/{docs-2H2UGIT6.js.map → docs-EHNPDWUL.js.map} +1 -1
  199. package/dist/cli-bundle/chunks/eval-3WKDTRMH.js +276 -0
  200. package/dist/cli-bundle/chunks/eval-3WKDTRMH.js.map +1 -0
  201. package/dist/cli-bundle/chunks/eval-GTENKE24.js +286 -0
  202. package/dist/cli-bundle/chunks/eval-GTENKE24.js.map +7 -0
  203. package/dist/cli-bundle/chunks/{extension-4LPTFTV4.js → extension-46NWACO3.js} +5 -5
  204. package/dist/cli-bundle/chunks/{extension-ASUPTDCC.js → extension-D5FDZ2CO.js} +5 -5
  205. package/dist/cli-bundle/chunks/{files-TXEQOA7C.js → files-DSZBOSSN.js} +12 -11
  206. package/dist/cli-bundle/chunks/{files-TXEQOA7C.js.map → files-DSZBOSSN.js.map} +1 -1
  207. package/dist/cli-bundle/chunks/{files-UAZFB3EA.js → files-X3H7CCBQ.js} +10 -9
  208. package/dist/cli-bundle/chunks/{files-UAZFB3EA.js.map → files-X3H7CCBQ.js.map} +1 -1
  209. package/dist/cli-bundle/chunks/{focus-UGROAMAX.js → focus-5STQXDPU.js} +8 -8
  210. package/dist/cli-bundle/chunks/{focus-ELOPN2SR.js → focus-MDVPOWB5.js} +8 -8
  211. package/dist/cli-bundle/chunks/gc-ZC7JHBG7.js +395 -0
  212. package/dist/cli-bundle/chunks/gc-ZC7JHBG7.js.map +7 -0
  213. package/dist/cli-bundle/chunks/gc-ZMST3WQP.js +385 -0
  214. package/dist/cli-bundle/chunks/gc-ZMST3WQP.js.map +1 -0
  215. package/dist/cli-bundle/chunks/{get-OTXPLWYW.js → get-J2IPJRZW.js} +11 -11
  216. package/dist/cli-bundle/chunks/{get-IWZ7IMPS.js → get-NT7L2HUZ.js} +11 -11
  217. package/dist/cli-bundle/chunks/{health-PPTJ7XKW.js → health-IVMA3XQC.js} +38 -26
  218. package/dist/cli-bundle/chunks/health-IVMA3XQC.js.map +1 -0
  219. package/dist/cli-bundle/chunks/{health-JEBESKID.js → health-WOYBR3KU.js} +37 -25
  220. package/dist/cli-bundle/chunks/health-WOYBR3KU.js.map +7 -0
  221. package/dist/cli-bundle/chunks/{history-HNWBUDMI.js → history-RLH2QCRO.js} +10 -10
  222. package/dist/cli-bundle/chunks/history-WX3MGFS2.js +20 -0
  223. package/dist/cli-bundle/chunks/{history-compact-MMQC7MCW.js → history-compact-UXDIFC33.js} +12 -12
  224. package/dist/cli-bundle/chunks/{history-compact-EQXVPK3V.js → history-compact-ZBEBGJM7.js} +12 -12
  225. package/dist/cli-bundle/chunks/{history-redact-UBDHBEJE.js → history-redact-P7I7AWMB.js} +11 -11
  226. package/dist/cli-bundle/chunks/history-redact-YQGU3XRA.js +21 -0
  227. package/dist/cli-bundle/chunks/{history-repair-GMMHFAGH.js → history-repair-52KINVIK.js} +12 -12
  228. package/dist/cli-bundle/chunks/{history-repair-7DSDRPMX.js → history-repair-RM5ITLU5.js} +12 -12
  229. package/dist/cli-bundle/chunks/{init-QGK4JFF6.js → init-2BNZOO6F.js} +10 -10
  230. package/dist/cli-bundle/chunks/init-SC2P6CHP.js +20 -0
  231. package/dist/cli-bundle/chunks/{learnings-DR53TWRA.js → learnings-W6F3FIPL.js} +10 -10
  232. package/dist/cli-bundle/chunks/{learnings-A6DGBMG3.js → learnings-YGYVHWJK.js} +10 -10
  233. package/dist/cli-bundle/chunks/{list-LJ5DBXC3.js → list-K6FPV4J4.js} +10 -10
  234. package/dist/cli-bundle/chunks/{list-KOJN23UL.js → list-KPAYU5KZ.js} +10 -10
  235. package/dist/cli-bundle/chunks/{notes-7VLTHKPW.js → notes-PU74EPKV.js} +10 -10
  236. package/dist/cli-bundle/chunks/{notes-K5I3TAYR.js → notes-YTD6ZILW.js} +10 -10
  237. package/dist/cli-bundle/chunks/{plan-X3STKLED.js → plan-FNXHXRCI.js} +15 -15
  238. package/dist/cli-bundle/chunks/plan-FNXHXRCI.js.map +7 -0
  239. package/dist/cli-bundle/chunks/{plan-BK3OMLGP.js → plan-GEQYUCBW.js} +16 -16
  240. package/dist/cli-bundle/chunks/plan-GEQYUCBW.js.map +1 -0
  241. package/dist/cli-bundle/chunks/{register-list-query-XDLFESU7.js → register-list-query-PHWYHENI.js} +43 -22
  242. package/dist/cli-bundle/chunks/register-list-query-PHWYHENI.js.map +1 -0
  243. package/dist/cli-bundle/chunks/{register-list-query-FUMXPWZ4.js → register-list-query-QI45RMKN.js} +42 -21
  244. package/dist/cli-bundle/chunks/register-list-query-QI45RMKN.js.map +7 -0
  245. package/dist/cli-bundle/chunks/{register-mutation-RXRX4P7D.js → register-mutation-X3BUOQEL.js} +33 -33
  246. package/dist/cli-bundle/chunks/{register-mutation-ZAGVKMBQ.js → register-mutation-YINRTS7L.js} +33 -33
  247. package/dist/cli-bundle/chunks/{register-operations-DGSHCS75.js → register-operations-E5JEASRK.js} +35 -35
  248. package/dist/cli-bundle/chunks/register-operations-E5JEASRK.js.map +1 -0
  249. package/dist/cli-bundle/chunks/{register-operations-6UURPZN5.js → register-operations-MIDTRW7S.js} +34 -34
  250. package/dist/cli-bundle/chunks/{register-operations-6UURPZN5.js.map → register-operations-MIDTRW7S.js.map} +2 -2
  251. package/dist/cli-bundle/chunks/{register-setup-LTJG52WP.js → register-setup-DYBFLEZX.js} +15 -15
  252. package/dist/cli-bundle/chunks/{register-setup-2FEGSFLM.js → register-setup-EG2U6CEU.js} +15 -15
  253. package/dist/cli-bundle/chunks/restore-5KHR2YYQ.js +19 -0
  254. package/dist/cli-bundle/chunks/{restore-SJUWYOCK.js → restore-XQYSWBV7.js} +11 -11
  255. package/dist/cli-bundle/chunks/{scheduling-shortcuts-VWAMP2S2.js → scheduling-shortcuts-HJMTEXKE.js} +14 -14
  256. package/dist/cli-bundle/chunks/{scheduling-shortcuts-W4F2LKHW.js → scheduling-shortcuts-NNT5ASZR.js} +14 -14
  257. package/dist/cli-bundle/chunks/{schema-N5ETYD4P.js → schema-263RI4WW.js} +9 -9
  258. package/dist/cli-bundle/chunks/{schema-DXQRIEFM.js → schema-TZXW6XWD.js} +9 -9
  259. package/dist/cli-bundle/chunks/search-LECJYSXE.js +33 -0
  260. package/dist/cli-bundle/chunks/search-SFRJOVDG.js +37 -0
  261. package/dist/cli-bundle/chunks/{stats-ZFESFVQC.js → stats-N7ER2PNK.js} +8 -8
  262. package/dist/cli-bundle/chunks/{stats-J563NJBY.js → stats-VPBGSRQH.js} +8 -8
  263. package/dist/cli-bundle/chunks/{telemetry-BNGLIFEM.js → telemetry-L6K3BUIQ.js} +4 -4
  264. package/dist/cli-bundle/chunks/{telemetry-SC7HPRXX.js → telemetry-PP3FUNBU.js} +4 -4
  265. package/dist/cli-bundle/chunks/{test-47UQGEXE.js → test-3EGNS3T5.js} +15 -15
  266. package/dist/cli-bundle/chunks/{test-55RJCGRU.js → test-RQCTSWUK.js} +15 -15
  267. package/dist/cli-bundle/chunks/{test-all-DN6KG4FX.js → test-all-NJ5V2QFS.js} +15 -15
  268. package/dist/cli-bundle/chunks/{test-all-5SFTTRNL.js → test-all-ZHFAWNC3.js} +15 -15
  269. package/dist/cli-bundle/chunks/{test-runs-NHRBT7SN.js → test-runs-AJHR6IQF.js} +5 -5
  270. package/dist/cli-bundle/chunks/{test-runs-DZEDJ4GH.js → test-runs-GN3C6GOI.js} +5 -5
  271. package/dist/cli-bundle/chunks/update-6YXOGHRT.js +24 -0
  272. package/dist/cli-bundle/chunks/update-6YXOGHRT.js.map +7 -0
  273. package/dist/cli-bundle/chunks/update-V33YUBRP.js +28 -0
  274. package/dist/cli-bundle/chunks/update-V33YUBRP.js.map +1 -0
  275. package/dist/cli-bundle/chunks/{update-many-IRNV3OTG.js → update-many-N2AN6ZDH.js} +23 -23
  276. package/dist/cli-bundle/chunks/{update-many-OKYWFCBO.js → update-many-RYQ64CFW.js} +23 -23
  277. package/dist/cli-bundle/chunks/{upgrade-KP4I45V5.js → upgrade-TJXFLZIX.js} +5 -5
  278. package/dist/cli-bundle/chunks/{upgrade-3RIAFQIK.js → upgrade-VPCIYAVW.js} +5 -5
  279. package/dist/cli-bundle/chunks/{validate-GVY4VE43.js → validate-JMNWEUYF.js} +62 -24
  280. package/dist/cli-bundle/chunks/validate-JMNWEUYF.js.map +1 -0
  281. package/dist/cli-bundle/chunks/{validate-ADKGN37M.js → validate-PHKIYGSY.js} +61 -23
  282. package/dist/cli-bundle/chunks/validate-PHKIYGSY.js.map +7 -0
  283. package/dist/cli-bundle/main.js +133 -55
  284. package/dist/cli-bundle/main.js.map +2 -2
  285. package/dist/core/checkpoint/checkpoint-gc.d.ts +69 -0
  286. package/dist/core/checkpoint/checkpoint-gc.js +175 -0
  287. package/dist/core/checkpoint/checkpoint-gc.js.map +1 -0
  288. package/dist/core/config/nested-settings.js +22 -2
  289. package/dist/core/config/nested-settings.js.map +1 -1
  290. package/dist/core/diagnostics/remediation.js +40 -2
  291. package/dist/core/diagnostics/remediation.js.map +1 -1
  292. package/dist/core/extensions/extension-types.d.ts +0 -9
  293. package/dist/core/extensions/extension-types.js +2 -27
  294. package/dist/core/extensions/extension-types.js.map +1 -1
  295. package/dist/core/fs/fs-utils.d.ts +17 -1
  296. package/dist/core/fs/fs-utils.js +19 -3
  297. package/dist/core/fs/fs-utils.js.map +1 -1
  298. package/dist/core/history/drift-scan.d.ts +17 -5
  299. package/dist/core/history/drift-scan.js +22 -14
  300. package/dist/core/history/drift-scan.js.map +1 -1
  301. package/dist/core/history/replay.d.ts +7 -0
  302. package/dist/core/history/replay.js +11 -6
  303. package/dist/core/history/replay.js.map +1 -1
  304. package/dist/core/item/item-format-version.d.ts +87 -0
  305. package/dist/core/item/item-format-version.js +85 -0
  306. package/dist/core/item/item-format-version.js.map +1 -0
  307. package/dist/core/item/item-format.js +8 -2
  308. package/dist/core/item/item-format.js.map +1 -1
  309. package/dist/core/item/status-filter.d.ts +7 -3
  310. package/dist/core/item/status-filter.js +26 -5
  311. package/dist/core/item/status-filter.js.map +1 -1
  312. package/dist/core/search/bm25.d.ts +113 -0
  313. package/dist/core/search/bm25.js +178 -0
  314. package/dist/core/search/bm25.js.map +1 -0
  315. package/dist/core/search/eval.d.ts +114 -0
  316. package/dist/core/search/eval.js +217 -0
  317. package/dist/core/search/eval.js.map +1 -0
  318. package/dist/core/sentry/instrument.d.ts +14 -0
  319. package/dist/core/sentry/instrument.js +43 -3
  320. package/dist/core/sentry/instrument.js.map +1 -1
  321. package/dist/core/shared/constants.js +6 -2
  322. package/dist/core/shared/constants.js.map +1 -1
  323. package/dist/core/store/paths.js +4 -3
  324. package/dist/core/store/paths.js.map +1 -1
  325. package/dist/core/store/settings-validator.d.ts +7 -0
  326. package/dist/core/store/settings-validator.js +10 -2
  327. package/dist/core/store/settings-validator.js.map +1 -1
  328. package/dist/core/store/settings.js +6 -2
  329. package/dist/core/store/settings.js.map +1 -1
  330. package/dist/mcp/tool-definitions.js +4 -3
  331. package/dist/mcp/tool-definitions.js.map +1 -1
  332. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  333. package/dist/sdk/cli-contracts/enum-contracts.js +3 -2
  334. package/dist/sdk/cli-contracts/enum-contracts.js.map +1 -1
  335. package/dist/sdk/cli-contracts/tool-parameter-tables.js +4 -4
  336. package/dist/sdk/cli-contracts/tool-parameter-tables.js.map +1 -1
  337. package/dist/sdk/cli-contracts.d.ts +1 -0
  338. package/dist/sdk/cli-contracts.js +11 -2
  339. package/dist/sdk/cli-contracts.js.map +1 -1
  340. package/dist/sdk/index.d.ts +2 -2
  341. package/dist/sdk/index.js +3 -3
  342. package/dist/sdk/index.js.map +1 -1
  343. package/dist/sdk/runtime.d.ts +1 -0
  344. package/dist/sdk/runtime.js +3 -2
  345. package/dist/sdk/runtime.js.map +1 -1
  346. package/dist/sdk/testing.d.ts +48 -1
  347. package/dist/sdk/testing.js +98 -2
  348. package/dist/sdk/testing.js.map +1 -1
  349. package/dist/types.d.ts +26 -0
  350. package/dist/types.js +2 -2
  351. package/dist/types.js.map +1 -1
  352. package/docs/AGENT_GUIDE.md +1 -0
  353. package/docs/ARCHITECTURE.md +126 -11
  354. package/docs/CLAUDE_CODE_PLUGIN.md +5 -13
  355. package/docs/COMMANDS.md +57 -5
  356. package/docs/CONFIGURATION.md +39 -0
  357. package/docs/EXTENSIONS.md +2 -2
  358. package/docs/MIGRATION_CLI_SIMPLIFICATION.md +9 -2
  359. package/docs/QUICKSTART.md +2 -0
  360. package/docs/README.md +2 -0
  361. package/docs/RELEASING.md +2 -1
  362. package/docs/SDK.md +80 -1
  363. package/docs/TESTING.md +18 -0
  364. package/package.json +4 -3
  365. package/packages/pm-command-kit/README.md +4 -0
  366. package/packages/pm-todos/extensions/todos/index.js +2 -0
  367. package/packages/pm-todos/extensions/todos/index.ts +2 -0
  368. package/packages/pm-todos/extensions/todos/runtime.js +192 -9
  369. package/packages/pm-todos/extensions/todos/runtime.ts +211 -8
  370. package/dist/cli-bundle/chunks/activity-62I26XZB.js +0 -18
  371. package/dist/cli-bundle/chunks/chunk-2RI3BDFK.js.map +0 -7
  372. package/dist/cli-bundle/chunks/chunk-4BFNWAMQ.js.map +0 -7
  373. package/dist/cli-bundle/chunks/chunk-5QLHQK7V.js.map +0 -7
  374. package/dist/cli-bundle/chunks/chunk-5SRID7DV.js.map +0 -1
  375. package/dist/cli-bundle/chunks/chunk-COHNQLB2.js.map +0 -1
  376. package/dist/cli-bundle/chunks/chunk-DVUU3AHP.js.map +0 -1
  377. package/dist/cli-bundle/chunks/chunk-EMFDZ7N3.js.map +0 -1
  378. package/dist/cli-bundle/chunks/chunk-FP7YLQ52.js.map +0 -1
  379. package/dist/cli-bundle/chunks/chunk-FPQA4ZMA.js.map +0 -7
  380. package/dist/cli-bundle/chunks/chunk-GDOTTY2S.js.map +0 -7
  381. package/dist/cli-bundle/chunks/chunk-I2VJM36R.js.map +0 -1
  382. package/dist/cli-bundle/chunks/chunk-K5TD74VU.js.map +0 -7
  383. package/dist/cli-bundle/chunks/chunk-MAKOMXVM.js.map +0 -1
  384. package/dist/cli-bundle/chunks/chunk-PSWSQHM7.js.map +0 -1
  385. package/dist/cli-bundle/chunks/chunk-QSH34DSQ.js.map +0 -7
  386. package/dist/cli-bundle/chunks/chunk-UA4ANRJY.js.map +0 -7
  387. package/dist/cli-bundle/chunks/chunk-UCNEPMPY.js.map +0 -7
  388. package/dist/cli-bundle/chunks/chunk-X26ODPU4.js.map +0 -7
  389. package/dist/cli-bundle/chunks/chunk-Y4IBSEQ2.js.map +0 -1
  390. package/dist/cli-bundle/chunks/chunk-YOGXK3ON.js.map +0 -7
  391. package/dist/cli-bundle/chunks/chunk-YWAYOWDQ.js.map +0 -1
  392. package/dist/cli-bundle/chunks/chunk-ZZEM2PH7.js.map +0 -1
  393. package/dist/cli-bundle/chunks/close-TQZK43Z5.js +0 -15
  394. package/dist/cli-bundle/chunks/config-UWF3KRP5.js.map +0 -7
  395. package/dist/cli-bundle/chunks/config-YICACPHA.js.map +0 -1
  396. package/dist/cli-bundle/chunks/contracts-7G553BHP.js.map +0 -1
  397. package/dist/cli-bundle/chunks/contracts-B2YWYUV5.js.map +0 -7
  398. package/dist/cli-bundle/chunks/create-UZLJXHLA.js +0 -30
  399. package/dist/cli-bundle/chunks/gc-5RTYXLQU.js +0 -211
  400. package/dist/cli-bundle/chunks/gc-5RTYXLQU.js.map +0 -7
  401. package/dist/cli-bundle/chunks/gc-FK5732EK.js +0 -208
  402. package/dist/cli-bundle/chunks/gc-FK5732EK.js.map +0 -1
  403. package/dist/cli-bundle/chunks/health-JEBESKID.js.map +0 -7
  404. package/dist/cli-bundle/chunks/health-PPTJ7XKW.js.map +0 -1
  405. package/dist/cli-bundle/chunks/history-TKTULEVE.js +0 -20
  406. package/dist/cli-bundle/chunks/history-redact-SPIFIXVY.js +0 -21
  407. package/dist/cli-bundle/chunks/init-A6FTZ5UC.js +0 -20
  408. package/dist/cli-bundle/chunks/plan-BK3OMLGP.js.map +0 -1
  409. package/dist/cli-bundle/chunks/plan-X3STKLED.js.map +0 -7
  410. package/dist/cli-bundle/chunks/register-list-query-FUMXPWZ4.js.map +0 -7
  411. package/dist/cli-bundle/chunks/register-list-query-XDLFESU7.js.map +0 -1
  412. package/dist/cli-bundle/chunks/register-operations-DGSHCS75.js.map +0 -1
  413. package/dist/cli-bundle/chunks/restore-PI3WM4GK.js +0 -19
  414. package/dist/cli-bundle/chunks/search-2JX776KX.js.map +0 -1
  415. package/dist/cli-bundle/chunks/search-AZR2JITW.js.map +0 -7
  416. package/dist/cli-bundle/chunks/update-2BZEAI7B.js +0 -24
  417. package/dist/cli-bundle/chunks/update-GCNLER2H.js +0 -28
  418. package/dist/cli-bundle/chunks/validate-ADKGN37M.js.map +0 -7
  419. package/dist/cli-bundle/chunks/validate-GVY4VE43.js.map +0 -1
  420. /package/dist/cli-bundle/chunks/{activity-62I26XZB.js.map → activity-CIHV64UC.js.map} +0 -0
  421. /package/dist/cli-bundle/chunks/{activity-XJ7WRNWS.js.map → activity-KW3IRP44.js.map} +0 -0
  422. /package/dist/cli-bundle/chunks/{aggregate-LBCWF6UJ.js.map → aggregate-3ZKQG5SW.js.map} +0 -0
  423. /package/dist/cli-bundle/chunks/{aggregate-OFT4YVB6.js.map → aggregate-FIGQEIL3.js.map} +0 -0
  424. /package/dist/cli-bundle/chunks/{append-BP2ARHOK.js.map → append-EWJYJ7CH.js.map} +0 -0
  425. /package/dist/cli-bundle/chunks/{append-5U3XR5SD.js.map → append-QEOLX3XB.js.map} +0 -0
  426. /package/dist/cli-bundle/chunks/{chunk-CSRFOIHG.js.map → chunk-2EIMPCE4.js.map} +0 -0
  427. /package/dist/cli-bundle/chunks/{chunk-II3WDRCM.js.map → chunk-37QXHBIB.js.map} +0 -0
  428. /package/dist/cli-bundle/chunks/{chunk-UMTPQMEG.js.map → chunk-3MDHREEV.js.map} +0 -0
  429. /package/dist/cli-bundle/chunks/{chunk-DJ3YYLHG.js.map → chunk-5FIEHKEX.js.map} +0 -0
  430. /package/dist/cli-bundle/chunks/{chunk-OURX4WVG.js.map → chunk-5IDI42VV.js.map} +0 -0
  431. /package/dist/cli-bundle/chunks/{chunk-RVG3WLY3.js.map → chunk-5QICDWMV.js.map} +0 -0
  432. /package/dist/cli-bundle/chunks/{chunk-LIVRA6VG.js.map → chunk-6CJWZAN7.js.map} +0 -0
  433. /package/dist/cli-bundle/chunks/{chunk-AVMSP2HH.js.map → chunk-6H4LF7AO.js.map} +0 -0
  434. /package/dist/cli-bundle/chunks/{chunk-B2NEFANY.js.map → chunk-6M4RJS4M.js.map} +0 -0
  435. /package/dist/cli-bundle/chunks/{chunk-A5JPMD4Q.js.map → chunk-76Z3YSHV.js.map} +0 -0
  436. /package/dist/cli-bundle/chunks/{chunk-5NS6TJ2F.js.map → chunk-7D3SAQXM.js.map} +0 -0
  437. /package/dist/cli-bundle/chunks/{chunk-2E3KSWFG.js.map → chunk-AIS5PGI3.js.map} +0 -0
  438. /package/dist/cli-bundle/chunks/{chunk-47BYN6CA.js.map → chunk-AZMOLBN3.js.map} +0 -0
  439. /package/dist/cli-bundle/chunks/{chunk-DYZKSIVX.js.map → chunk-B473GZWW.js.map} +0 -0
  440. /package/dist/cli-bundle/chunks/{chunk-RNWE2HZR.js.map → chunk-C3AZ5ZML.js.map} +0 -0
  441. /package/dist/cli-bundle/chunks/{chunk-S673PVHD.js.map → chunk-C4N5I7ZW.js.map} +0 -0
  442. /package/dist/cli-bundle/chunks/{chunk-TYQBGXH2.js.map → chunk-CF55WVVS.js.map} +0 -0
  443. /package/dist/cli-bundle/chunks/{chunk-4EOS35RL.js.map → chunk-CZPXY7UW.js.map} +0 -0
  444. /package/dist/cli-bundle/chunks/{chunk-MPEWACTG.js.map → chunk-CZUB4XWO.js.map} +0 -0
  445. /package/dist/cli-bundle/chunks/{chunk-22YA6GCA.js.map → chunk-D7FYS6PG.js.map} +0 -0
  446. /package/dist/cli-bundle/chunks/{chunk-UIMLAFOE.js.map → chunk-F5NZWFSW.js.map} +0 -0
  447. /package/dist/cli-bundle/chunks/{chunk-H3DP6OUM.js.map → chunk-FKBDKV7Y.js.map} +0 -0
  448. /package/dist/cli-bundle/chunks/{chunk-SO2IOH37.js.map → chunk-FRUFKWNK.js.map} +0 -0
  449. /package/dist/cli-bundle/chunks/{chunk-7R5YKVKO.js.map → chunk-FWPDUYEY.js.map} +0 -0
  450. /package/dist/cli-bundle/chunks/{chunk-USVOBLGB.js.map → chunk-GOZUHWFD.js.map} +0 -0
  451. /package/dist/cli-bundle/chunks/{chunk-ZBVYKWT2.js.map → chunk-H7NJZWG2.js.map} +0 -0
  452. /package/dist/cli-bundle/chunks/{chunk-4L7Q4OVT.js.map → chunk-HCU6WUSY.js.map} +0 -0
  453. /package/dist/cli-bundle/chunks/{chunk-PRUB7RI4.js.map → chunk-LUWW7L4B.js.map} +0 -0
  454. /package/dist/cli-bundle/chunks/{chunk-N7LDJRB4.js.map → chunk-M5XALUSL.js.map} +0 -0
  455. /package/dist/cli-bundle/chunks/{chunk-NPNNW5TU.js.map → chunk-N5ZJACJ6.js.map} +0 -0
  456. /package/dist/cli-bundle/chunks/{chunk-NNMBNN6Q.js.map → chunk-NB3PYLQ6.js.map} +0 -0
  457. /package/dist/cli-bundle/chunks/{chunk-USKTNI7P.js.map → chunk-NIGXMNNI.js.map} +0 -0
  458. /package/dist/cli-bundle/chunks/{chunk-GL3QKIET.js.map → chunk-NIUEXCFS.js.map} +0 -0
  459. /package/dist/cli-bundle/chunks/{chunk-OUG4CL4H.js.map → chunk-NJHMAJ6N.js.map} +0 -0
  460. /package/dist/cli-bundle/chunks/{chunk-2K56AWDP.js.map → chunk-NJN7LL7U.js.map} +0 -0
  461. /package/dist/cli-bundle/chunks/{chunk-5P2LQV2F.js.map → chunk-NSBZ2CKF.js.map} +0 -0
  462. /package/dist/cli-bundle/chunks/{chunk-XV6N3QND.js.map → chunk-O47BPOPC.js.map} +0 -0
  463. /package/dist/cli-bundle/chunks/{chunk-QCHNPN24.js.map → chunk-OTHIWLMB.js.map} +0 -0
  464. /package/dist/cli-bundle/chunks/{chunk-YVYJQR6S.js.map → chunk-PFSRUHDH.js.map} +0 -0
  465. /package/dist/cli-bundle/chunks/{chunk-M35R7LFX.js.map → chunk-PP2U4NMT.js.map} +0 -0
  466. /package/dist/cli-bundle/chunks/{chunk-ZGEWEO7B.js.map → chunk-PUH4D67U.js.map} +0 -0
  467. /package/dist/cli-bundle/chunks/{chunk-LEC57E7K.js.map → chunk-Q2J2HJUN.js.map} +0 -0
  468. /package/dist/cli-bundle/chunks/{chunk-QG6M6XQE.js.map → chunk-QQHISQBJ.js.map} +0 -0
  469. /package/dist/cli-bundle/chunks/{chunk-BQVESTOG.js.map → chunk-QZTWBFXJ.js.map} +0 -0
  470. /package/dist/cli-bundle/chunks/{chunk-5LZPDRFW.js.map → chunk-RLK7P77C.js.map} +0 -0
  471. /package/dist/cli-bundle/chunks/{chunk-QTWWHFI3.js.map → chunk-RVHYI7SL.js.map} +0 -0
  472. /package/dist/cli-bundle/chunks/{chunk-FVZSUIN5.js.map → chunk-SBGXDO7A.js.map} +0 -0
  473. /package/dist/cli-bundle/chunks/{chunk-VP4Z36GO.js.map → chunk-SRYYRVPB.js.map} +0 -0
  474. /package/dist/cli-bundle/chunks/{chunk-X7RKOJXL.js.map → chunk-SX75ADBK.js.map} +0 -0
  475. /package/dist/cli-bundle/chunks/{chunk-MTDEEUGV.js.map → chunk-TKBHZPKD.js.map} +0 -0
  476. /package/dist/cli-bundle/chunks/{chunk-HMQMRHQJ.js.map → chunk-UEMNL33O.js.map} +0 -0
  477. /package/dist/cli-bundle/chunks/{chunk-SDUZTZ55.js.map → chunk-UPE3CDD7.js.map} +0 -0
  478. /package/dist/cli-bundle/chunks/{chunk-VG3B2CYD.js.map → chunk-UV6IYE5U.js.map} +0 -0
  479. /package/dist/cli-bundle/chunks/{chunk-MTGMNUMJ.js.map → chunk-YCJX76PW.js.map} +0 -0
  480. /package/dist/cli-bundle/chunks/{claim-2BSYPJTJ.js.map → claim-RC56YUWG.js.map} +0 -0
  481. /package/dist/cli-bundle/chunks/{claim-EZ6JKY5X.js.map → claim-THOJ6CMV.js.map} +0 -0
  482. /package/dist/cli-bundle/chunks/{close-TQZK43Z5.js.map → close-DNMHGOCX.js.map} +0 -0
  483. /package/dist/cli-bundle/chunks/{close-3TSX6BYF.js.map → close-SG5MNCVQ.js.map} +0 -0
  484. /package/dist/cli-bundle/chunks/{close-many-HB4UFCS3.js.map → close-many-3L3AQZBU.js.map} +0 -0
  485. /package/dist/cli-bundle/chunks/{close-many-B7DBVA46.js.map → close-many-DKQLBGLV.js.map} +0 -0
  486. /package/dist/cli-bundle/chunks/{comments-M7PAINZT.js.map → comments-M7ZOZNBX.js.map} +0 -0
  487. /package/dist/cli-bundle/chunks/{comments-X7M7XM6I.js.map → comments-SKWMDWCW.js.map} +0 -0
  488. /package/dist/cli-bundle/chunks/{context-EGUGU4K7.js.map → context-OEC366TO.js.map} +0 -0
  489. /package/dist/cli-bundle/chunks/{context-V32ZAPL6.js.map → context-PUG2A7RC.js.map} +0 -0
  490. /package/dist/cli-bundle/chunks/{copy-NWFFRYWM.js.map → copy-6QLHILAC.js.map} +0 -0
  491. /package/dist/cli-bundle/chunks/{copy-RMLXLZOL.js.map → copy-JPX7VZCH.js.map} +0 -0
  492. /package/dist/cli-bundle/chunks/{create-UZLJXHLA.js.map → create-NNP6TSFO.js.map} +0 -0
  493. /package/dist/cli-bundle/chunks/{create-GK4MX2LJ.js.map → create-Y54ZVVZH.js.map} +0 -0
  494. /package/dist/cli-bundle/chunks/{delete-RMGNIL2S.js.map → delete-63PCMQAJ.js.map} +0 -0
  495. /package/dist/cli-bundle/chunks/{delete-EFQLVEAS.js.map → delete-GDSMGAGJ.js.map} +0 -0
  496. /package/dist/cli-bundle/chunks/{deps-WKHUPVD3.js.map → deps-T4ZF5G54.js.map} +0 -0
  497. /package/dist/cli-bundle/chunks/{deps-K2I7QV4Y.js.map → deps-XYQ73XRH.js.map} +0 -0
  498. /package/dist/cli-bundle/chunks/{extension-4LPTFTV4.js.map → extension-46NWACO3.js.map} +0 -0
  499. /package/dist/cli-bundle/chunks/{extension-ASUPTDCC.js.map → extension-D5FDZ2CO.js.map} +0 -0
  500. /package/dist/cli-bundle/chunks/{focus-UGROAMAX.js.map → focus-5STQXDPU.js.map} +0 -0
  501. /package/dist/cli-bundle/chunks/{focus-ELOPN2SR.js.map → focus-MDVPOWB5.js.map} +0 -0
  502. /package/dist/cli-bundle/chunks/{get-OTXPLWYW.js.map → get-J2IPJRZW.js.map} +0 -0
  503. /package/dist/cli-bundle/chunks/{get-IWZ7IMPS.js.map → get-NT7L2HUZ.js.map} +0 -0
  504. /package/dist/cli-bundle/chunks/{history-HNWBUDMI.js.map → history-RLH2QCRO.js.map} +0 -0
  505. /package/dist/cli-bundle/chunks/{history-TKTULEVE.js.map → history-WX3MGFS2.js.map} +0 -0
  506. /package/dist/cli-bundle/chunks/{history-compact-MMQC7MCW.js.map → history-compact-UXDIFC33.js.map} +0 -0
  507. /package/dist/cli-bundle/chunks/{history-compact-EQXVPK3V.js.map → history-compact-ZBEBGJM7.js.map} +0 -0
  508. /package/dist/cli-bundle/chunks/{history-redact-UBDHBEJE.js.map → history-redact-P7I7AWMB.js.map} +0 -0
  509. /package/dist/cli-bundle/chunks/{history-redact-SPIFIXVY.js.map → history-redact-YQGU3XRA.js.map} +0 -0
  510. /package/dist/cli-bundle/chunks/{history-repair-GMMHFAGH.js.map → history-repair-52KINVIK.js.map} +0 -0
  511. /package/dist/cli-bundle/chunks/{history-repair-7DSDRPMX.js.map → history-repair-RM5ITLU5.js.map} +0 -0
  512. /package/dist/cli-bundle/chunks/{init-QGK4JFF6.js.map → init-2BNZOO6F.js.map} +0 -0
  513. /package/dist/cli-bundle/chunks/{init-A6FTZ5UC.js.map → init-SC2P6CHP.js.map} +0 -0
  514. /package/dist/cli-bundle/chunks/{learnings-DR53TWRA.js.map → learnings-W6F3FIPL.js.map} +0 -0
  515. /package/dist/cli-bundle/chunks/{learnings-A6DGBMG3.js.map → learnings-YGYVHWJK.js.map} +0 -0
  516. /package/dist/cli-bundle/chunks/{list-LJ5DBXC3.js.map → list-K6FPV4J4.js.map} +0 -0
  517. /package/dist/cli-bundle/chunks/{list-KOJN23UL.js.map → list-KPAYU5KZ.js.map} +0 -0
  518. /package/dist/cli-bundle/chunks/{notes-7VLTHKPW.js.map → notes-PU74EPKV.js.map} +0 -0
  519. /package/dist/cli-bundle/chunks/{notes-K5I3TAYR.js.map → notes-YTD6ZILW.js.map} +0 -0
  520. /package/dist/cli-bundle/chunks/{register-mutation-RXRX4P7D.js.map → register-mutation-X3BUOQEL.js.map} +0 -0
  521. /package/dist/cli-bundle/chunks/{register-mutation-ZAGVKMBQ.js.map → register-mutation-YINRTS7L.js.map} +0 -0
  522. /package/dist/cli-bundle/chunks/{register-setup-LTJG52WP.js.map → register-setup-DYBFLEZX.js.map} +0 -0
  523. /package/dist/cli-bundle/chunks/{register-setup-2FEGSFLM.js.map → register-setup-EG2U6CEU.js.map} +0 -0
  524. /package/dist/cli-bundle/chunks/{restore-PI3WM4GK.js.map → restore-5KHR2YYQ.js.map} +0 -0
  525. /package/dist/cli-bundle/chunks/{restore-SJUWYOCK.js.map → restore-XQYSWBV7.js.map} +0 -0
  526. /package/dist/cli-bundle/chunks/{scheduling-shortcuts-VWAMP2S2.js.map → scheduling-shortcuts-HJMTEXKE.js.map} +0 -0
  527. /package/dist/cli-bundle/chunks/{scheduling-shortcuts-W4F2LKHW.js.map → scheduling-shortcuts-NNT5ASZR.js.map} +0 -0
  528. /package/dist/cli-bundle/chunks/{schema-N5ETYD4P.js.map → schema-263RI4WW.js.map} +0 -0
  529. /package/dist/cli-bundle/chunks/{schema-DXQRIEFM.js.map → schema-TZXW6XWD.js.map} +0 -0
  530. /package/dist/cli-bundle/chunks/{test-55RJCGRU.js.map → search-LECJYSXE.js.map} +0 -0
  531. /package/dist/cli-bundle/chunks/{test-47UQGEXE.js.map → search-SFRJOVDG.js.map} +0 -0
  532. /package/dist/cli-bundle/chunks/{stats-ZFESFVQC.js.map → stats-N7ER2PNK.js.map} +0 -0
  533. /package/dist/cli-bundle/chunks/{stats-J563NJBY.js.map → stats-VPBGSRQH.js.map} +0 -0
  534. /package/dist/cli-bundle/chunks/{telemetry-BNGLIFEM.js.map → telemetry-L6K3BUIQ.js.map} +0 -0
  535. /package/dist/cli-bundle/chunks/{telemetry-SC7HPRXX.js.map → telemetry-PP3FUNBU.js.map} +0 -0
  536. /package/dist/cli-bundle/chunks/{update-GCNLER2H.js.map → test-3EGNS3T5.js.map} +0 -0
  537. /package/dist/cli-bundle/chunks/{update-2BZEAI7B.js.map → test-RQCTSWUK.js.map} +0 -0
  538. /package/dist/cli-bundle/chunks/{test-all-DN6KG4FX.js.map → test-all-NJ5V2QFS.js.map} +0 -0
  539. /package/dist/cli-bundle/chunks/{test-all-5SFTTRNL.js.map → test-all-ZHFAWNC3.js.map} +0 -0
  540. /package/dist/cli-bundle/chunks/{test-runs-NHRBT7SN.js.map → test-runs-AJHR6IQF.js.map} +0 -0
  541. /package/dist/cli-bundle/chunks/{test-runs-DZEDJ4GH.js.map → test-runs-GN3C6GOI.js.map} +0 -0
  542. /package/dist/cli-bundle/chunks/{update-many-IRNV3OTG.js.map → update-many-N2AN6ZDH.js.map} +0 -0
  543. /package/dist/cli-bundle/chunks/{update-many-OKYWFCBO.js.map → update-many-RYQ64CFW.js.map} +0 -0
  544. /package/dist/cli-bundle/chunks/{upgrade-KP4I45V5.js.map → upgrade-TJXFLZIX.js.map} +0 -0
  545. /package/dist/cli-bundle/chunks/{upgrade-3RIAFQIK.js.map → upgrade-VPCIYAVW.js.map} +0 -0
@@ -4,7 +4,7 @@
4
4
  * Defines item parsing, formatting, and lifecycle helpers for Status Filter.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a820defb-5037-5162-aef4-7ac9d17cc596")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="68b0f67b-4043-5b34-a8ed-39f5d6baea42")}catch(e){}}();
8
8
  import { EXIT_CODE } from "../shared/constants.js";
9
9
  import { PmCliError } from "../shared/errors.js";
10
10
  import { levenshteinDistanceWithinLimit } from "../shared/levenshtein.js";
@@ -20,6 +20,18 @@ const STATUS_GROUP_ALIASES = {
20
20
  canceled: "canceled_status",
21
21
  cancelled: "canceled_status",
22
22
  };
23
+ const STATUS_ALL_SENTINEL = "all";
24
+ /** Return true when a status-filter input is the standalone all-status sentinel. */
25
+ export function isStatusAllFilterInput(raw) {
26
+ if (typeof raw !== "string") {
27
+ return false;
28
+ }
29
+ const tokens = raw
30
+ .split(",")
31
+ .map((entry) => entry.trim().toLowerCase())
32
+ .filter((entry) => entry.length > 0);
33
+ return tokens.length === 1 && tokens[0] === STATUS_ALL_SENTINEL;
34
+ }
23
35
  /**
24
36
  * Resolve a single status-filter token to a concrete runtime status id.
25
37
  *
@@ -66,9 +78,11 @@ function suggestClosestStatus(value, registry) {
66
78
  }
67
79
  /**
68
80
  * Parse a comma-separated status filter into a de-duplicated list of concrete
69
- * status ids. Shared by `pm list` (lenient) and `pm search` (strict, with a
70
- * did-you-mean hint on typos) so both surfaces resolve the open/closed/canceled
71
- * workflow-group aliases identically.
81
+ * status ids. A standalone `all` sentinel intentionally resolves to no filter
82
+ * so duplicate-discovery loops can search every lifecycle bucket with the same
83
+ * `--status` flag shape. Shared by `pm list` (lenient) and `pm search`
84
+ * (strict, with a did-you-mean hint on typos) so both surfaces resolve the
85
+ * open/closed/canceled workflow-group aliases identically.
72
86
  */
73
87
  export function parseStatusFilterCsv(raw, registry, options) {
74
88
  if (typeof raw !== "string") {
@@ -83,6 +97,13 @@ export function parseStatusFilterCsv(raw, registry, options) {
83
97
  if (tokens.length === 0) {
84
98
  return undefined;
85
99
  }
100
+ const allTokenCount = tokens.filter((token) => token.trim().toLowerCase() === STATUS_ALL_SENTINEL).length;
101
+ if (allTokenCount > 0) {
102
+ if (tokens.length === 1) {
103
+ return undefined;
104
+ }
105
+ throw new PmCliError(`Invalid ${flagLabel} value "all". Use it by itself, or omit ${flagLabel} to search every status.`, EXIT_CODE.USAGE);
106
+ }
86
107
  const resolved = [];
87
108
  for (const token of tokens) {
88
109
  const status = resolveStatusFilterToken(token, registry);
@@ -99,4 +120,4 @@ export function parseStatusFilterCsv(raw, registry, options) {
99
120
  return [...new Set(resolved)];
100
121
  }
101
122
  //# sourceMappingURL=status-filter.js.map
102
- //# debugId=a820defb-5037-5162-aef4-7ac9d17cc596
123
+ //# debugId=68b0f67b-4043-5b34-a8ed-39f5d6baea42
@@ -1 +1 @@
1
- {"version":3,"file":"status-filter.js","sources":["core/item/status-filter.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module core/item/status-filter\n *\n * Defines item parsing, formatting, and lifecycle helpers for Status Filter.\n */\nimport { EXIT_CODE } from \"../shared/constants.js\";\nimport { PmCliError } from \"../shared/errors.js\";\nimport { levenshteinDistanceWithinLimit } from \"../shared/levenshtein.js\";\nimport type { RuntimeStatusRegistry } from \"../schema/runtime-schema.js\";\nimport type { ItemStatus } from \"../../types/index.js\";\nimport { normalizeStatusInput } from \"./status.js\";\n\n// Workflow-group aliases shared by every status-filter consumer so that the\n// agent-friendly `open`/`closed`/`canceled` shorthands resolve to whatever the\n// runtime workflow has configured as its open/close/cancel anchors, rather than\n// hardcoding the built-in status ids. `cancelled` is accepted as a spelling\n// variant of `canceled`.\nconst STATUS_GROUP_ALIASES: Readonly<Record<string, \"open_status\" | \"close_status\" | \"canceled_status\">> = {\n open: \"open_status\",\n closed: \"close_status\",\n canceled: \"canceled_status\",\n cancelled: \"canceled_status\",\n};\n\n/**\n * Resolve a single status-filter token to a concrete runtime status id.\n *\n * Returns `undefined` when the token is not a recognized workflow-group alias\n * (open/closed/canceled) and does not normalize to a known registry status.\n * Callers decide whether an unrecognized token is an error (strict mode) or is\n * passed through verbatim (lenient mode) — see {@link parseStatusFilterCsv}.\n */\nexport function resolveStatusFilterToken(\n token: string,\n registry: RuntimeStatusRegistry,\n): ItemStatus | undefined {\n const trimmed = token.trim().toLowerCase();\n if (trimmed.length === 0) {\n return undefined;\n }\n const group = STATUS_GROUP_ALIASES[trimmed];\n if (group) {\n return registry[group] as ItemStatus;\n }\n return normalizeStatusInput(token, registry);\n}\n\nfunction collectStatusSuggestionCandidates(registry: RuntimeStatusRegistry): string[] {\n const candidates = new Set<string>(Object.keys(STATUS_GROUP_ALIASES));\n for (const definition of registry.definitions) {\n candidates.add(definition.id);\n for (const alias of definition.aliases) {\n candidates.add(alias);\n }\n }\n return [...candidates];\n}\n\nfunction suggestClosestStatus(value: string, registry: RuntimeStatusRegistry): string | undefined {\n // Only ever called for an unresolved, already-non-empty token (parseStatusFilterCsv\n // filters blanks first), so no empty-input guard is needed here.\n const normalized = value.trim().toLowerCase();\n let best: string | undefined;\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const candidate of collectStatusSuggestionCandidates(registry)) {\n const distance = levenshteinDistanceWithinLimit(normalized, candidate.toLowerCase(), 2);\n if (distance !== null && distance < bestDistance) {\n best = candidate;\n bestDistance = distance;\n }\n }\n return best;\n}\n\n/**\n * Documents the parse status filter options payload exchanged by command, SDK, and package integrations.\n */\nexport interface ParseStatusFilterOptions {\n /**\n * When true, an unrecognized token throws a USAGE error with a did-you-mean\n * hint. When false (default), an unrecognized token is passed through\n * verbatim so custom/unknown statuses still compare against item values by\n * their raw string — preserving the long-standing lenient `pm list` behavior.\n */\n strict?: boolean;\n /** Flag label used in error messages (default: \"--status\"). */\n flagLabel?: string;\n}\n\n/**\n * Parse a comma-separated status filter into a de-duplicated list of concrete\n * status ids. Shared by `pm list` (lenient) and `pm search` (strict, with a\n * did-you-mean hint on typos) so both surfaces resolve the open/closed/canceled\n * workflow-group aliases identically.\n */\nexport function parseStatusFilterCsv(\n raw: unknown,\n registry: RuntimeStatusRegistry,\n options?: ParseStatusFilterOptions,\n): ItemStatus[] | undefined {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const strict = options?.strict === true;\n const flagLabel = options?.flagLabel ?? \"--status\";\n const tokens = raw\n .split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n if (tokens.length === 0) {\n return undefined;\n }\n const resolved: ItemStatus[] = [];\n for (const token of tokens) {\n const status = resolveStatusFilterToken(token, registry);\n if (status === undefined) {\n if (strict) {\n const suggestion = suggestClosestStatus(token, registry);\n throw new PmCliError(\n `Invalid ${flagLabel} value \"${token}\". Use open|closed|canceled or a configured status id${\n suggestion ? `. Did you mean \"${suggestion}\"?` : \".\"\n }`,\n EXIT_CODE.USAGE,\n );\n }\n resolved.push(token as ItemStatus);\n continue;\n }\n resolved.push(status);\n }\n return [...new Set(resolved)];\n}\n"],"names":[],"mappings":"AAAA;;;;GAIG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,4EAA4E;AAC5E,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,yBAAyB;AACzB,MAAM,oBAAoB,GAAiF;IACzG,IAAI,EAAE,aAAa;IACnB,MAAM,EAAE,cAAc;IACtB,QAAQ,EAAE,iBAAiB;IAC3B,SAAS,EAAE,iBAAiB;CAC7B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAa,EACb,QAA+B;IAE/B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,QAAQ,CAAC,KAAK,CAAe,CAAC;IACvC,CAAC;IACD,OAAO,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,iCAAiC,CAAC,QAA+B;IACxE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACtE,KAAK,MAAM,UAAU,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC9C,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa,EAAE,QAA+B;IAC1E,oFAAoF;IACpF,iEAAiE;IACjE,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,IAAwB,CAAC;IAC7B,IAAI,YAAY,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC5C,KAAK,MAAM,SAAS,IAAI,iCAAiC,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpE,MAAM,QAAQ,GAAG,8BAA8B,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;QACxF,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;YACjD,IAAI,GAAG,SAAS,CAAC;YACjB,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAiBD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAY,EACZ,QAA+B,EAC/B,OAAkC;IAElC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,UAAU,CAAC;IACnD,MAAM,MAAM,GAAG,GAAG;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM,IAAI,UAAU,CAClB,WAAW,SAAS,WAAW,KAAK,wDAClC,UAAU,CAAC,CAAC,CAAC,mBAAmB,UAAU,IAAI,CAAC,CAAC,CAAC,GACnD,EAAE,EACF,SAAS,CAAC,KAAK,CAChB,CAAC;YACJ,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,KAAmB,CAAC,CAAC;YACnC,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChC,CAAC","debugId":"a820defb-5037-5162-aef4-7ac9d17cc596"}
1
+ {"version":3,"file":"status-filter.js","sources":["core/item/status-filter.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module core/item/status-filter\n *\n * Defines item parsing, formatting, and lifecycle helpers for Status Filter.\n */\nimport { EXIT_CODE } from \"../shared/constants.js\";\nimport { PmCliError } from \"../shared/errors.js\";\nimport { levenshteinDistanceWithinLimit } from \"../shared/levenshtein.js\";\nimport type { RuntimeStatusRegistry } from \"../schema/runtime-schema.js\";\nimport type { ItemStatus } from \"../../types/index.js\";\nimport { normalizeStatusInput } from \"./status.js\";\n\n// Workflow-group aliases shared by every status-filter consumer so that the\n// agent-friendly `open`/`closed`/`canceled` shorthands resolve to whatever the\n// runtime workflow has configured as its open/close/cancel anchors, rather than\n// hardcoding the built-in status ids. `cancelled` is accepted as a spelling\n// variant of `canceled`.\nconst STATUS_GROUP_ALIASES: Readonly<Record<string, \"open_status\" | \"close_status\" | \"canceled_status\">> = {\n open: \"open_status\",\n closed: \"close_status\",\n canceled: \"canceled_status\",\n cancelled: \"canceled_status\",\n};\n\nconst STATUS_ALL_SENTINEL = \"all\";\n\n/** Return true when a status-filter input is the standalone all-status sentinel. */\nexport function isStatusAllFilterInput(raw: unknown): boolean {\n if (typeof raw !== \"string\") {\n return false;\n }\n const tokens = raw\n .split(\",\")\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0);\n return tokens.length === 1 && tokens[0] === STATUS_ALL_SENTINEL;\n}\n\n/**\n * Resolve a single status-filter token to a concrete runtime status id.\n *\n * Returns `undefined` when the token is not a recognized workflow-group alias\n * (open/closed/canceled) and does not normalize to a known registry status.\n * Callers decide whether an unrecognized token is an error (strict mode) or is\n * passed through verbatim (lenient mode) — see {@link parseStatusFilterCsv}.\n */\nexport function resolveStatusFilterToken(\n token: string,\n registry: RuntimeStatusRegistry,\n): ItemStatus | undefined {\n const trimmed = token.trim().toLowerCase();\n if (trimmed.length === 0) {\n return undefined;\n }\n const group = STATUS_GROUP_ALIASES[trimmed];\n if (group) {\n return registry[group] as ItemStatus;\n }\n return normalizeStatusInput(token, registry);\n}\n\nfunction collectStatusSuggestionCandidates(registry: RuntimeStatusRegistry): string[] {\n const candidates = new Set<string>(Object.keys(STATUS_GROUP_ALIASES));\n for (const definition of registry.definitions) {\n candidates.add(definition.id);\n for (const alias of definition.aliases) {\n candidates.add(alias);\n }\n }\n return [...candidates];\n}\n\nfunction suggestClosestStatus(value: string, registry: RuntimeStatusRegistry): string | undefined {\n // Only ever called for an unresolved, already-non-empty token (parseStatusFilterCsv\n // filters blanks first), so no empty-input guard is needed here.\n const normalized = value.trim().toLowerCase();\n let best: string | undefined;\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const candidate of collectStatusSuggestionCandidates(registry)) {\n const distance = levenshteinDistanceWithinLimit(normalized, candidate.toLowerCase(), 2);\n if (distance !== null && distance < bestDistance) {\n best = candidate;\n bestDistance = distance;\n }\n }\n return best;\n}\n\n/**\n * Documents the parse status filter options payload exchanged by command, SDK, and package integrations.\n */\nexport interface ParseStatusFilterOptions {\n /**\n * When true, an unrecognized token throws a USAGE error with a did-you-mean\n * hint. When false (default), an unrecognized token is passed through\n * verbatim so custom/unknown statuses still compare against item values by\n * their raw string — preserving the long-standing lenient `pm list` behavior.\n */\n strict?: boolean;\n /** Flag label used in error messages (default: \"--status\"). */\n flagLabel?: string;\n}\n\n/**\n * Parse a comma-separated status filter into a de-duplicated list of concrete\n * status ids. A standalone `all` sentinel intentionally resolves to no filter\n * so duplicate-discovery loops can search every lifecycle bucket with the same\n * `--status` flag shape. Shared by `pm list` (lenient) and `pm search`\n * (strict, with a did-you-mean hint on typos) so both surfaces resolve the\n * open/closed/canceled workflow-group aliases identically.\n */\nexport function parseStatusFilterCsv(\n raw: unknown,\n registry: RuntimeStatusRegistry,\n options?: ParseStatusFilterOptions,\n): ItemStatus[] | undefined {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const strict = options?.strict === true;\n const flagLabel = options?.flagLabel ?? \"--status\";\n const tokens = raw\n .split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n if (tokens.length === 0) {\n return undefined;\n }\n const allTokenCount = tokens.filter((token) => token.trim().toLowerCase() === STATUS_ALL_SENTINEL).length;\n if (allTokenCount > 0) {\n if (tokens.length === 1) {\n return undefined;\n }\n throw new PmCliError(\n `Invalid ${flagLabel} value \"all\". Use it by itself, or omit ${flagLabel} to search every status.`,\n EXIT_CODE.USAGE,\n );\n }\n const resolved: ItemStatus[] = [];\n for (const token of tokens) {\n const status = resolveStatusFilterToken(token, registry);\n if (status === undefined) {\n if (strict) {\n const suggestion = suggestClosestStatus(token, registry);\n throw new PmCliError(\n `Invalid ${flagLabel} value \"${token}\". Use open|closed|canceled or a configured status id${\n suggestion ? `. Did you mean \"${suggestion}\"?` : \".\"\n }`,\n EXIT_CODE.USAGE,\n );\n }\n resolved.push(token as ItemStatus);\n continue;\n }\n resolved.push(status);\n }\n return [...new Set(resolved)];\n}\n"],"names":[],"mappings":"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","debugId":"68b0f67b-4043-5b34-a8ed-39f5d6baea42"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @module core/search/bm25
3
+ *
4
+ * Offline BM25 lexical retrieval (pm-75k9). Provides an Okapi BM25 ranker that
5
+ * scores the in-memory item corpus with no embedding service, vector store, or
6
+ * network dependency, so semantic/hybrid search degrades to a dense-quality
7
+ * lexical ranker — rather than the naive field-weighted scorer — in air-gapped,
8
+ * CI, and zero-setup environments.
9
+ *
10
+ * BM25 improves on raw term-frequency counting in three ways the legacy keyword
11
+ * scorer cannot: inverse document frequency down-weights terms common across the
12
+ * whole corpus, term-frequency saturation (`k1`) caps the contribution of a term
13
+ * repeated many times in one document, and length normalization (`b`) prevents
14
+ * long documents from dominating purely by size.
15
+ */
16
+ import type { PmSettings } from "../../types/index.js";
17
+ /**
18
+ * Default Okapi BM25 term-frequency saturation parameter. `1.2` is the widely
19
+ * used Lucene/Elasticsearch default: higher values reduce the diminishing
20
+ * returns of repeated terms more slowly (terms keep accruing weight), lower
21
+ * values saturate sooner.
22
+ */
23
+ export declare const DEFAULT_BM25_K1 = 1.2;
24
+ /**
25
+ * Default Okapi BM25 length-normalization parameter. `0.75` is the Lucene
26
+ * default: `0` disables length normalization entirely, `1` fully normalizes a
27
+ * document's score by its length relative to the corpus average.
28
+ */
29
+ export declare const DEFAULT_BM25_B = 0.75;
30
+ /**
31
+ * Resolved BM25 tuning parameters. Both are clamped to sane ranges by
32
+ * {@link resolveBm25Params} before scoring.
33
+ */
34
+ export interface Bm25Params {
35
+ /** Term-frequency saturation (`k1`); finite and `>= 0`. */
36
+ k1: number;
37
+ /** Length normalization (`b`); finite and within `[0, 1]`. */
38
+ b: number;
39
+ }
40
+ /**
41
+ * One corpus document supplied to {@link buildBm25Index}: a stable id and the
42
+ * already-concatenated searchable text. Callers build the text from whichever
43
+ * fields they want indexed (typically the resolved `search.corpus_fields`).
44
+ */
45
+ export interface Bm25Document {
46
+ id: string;
47
+ text: string;
48
+ }
49
+ /**
50
+ * Per-document statistics retained in a {@link Bm25Index}: the term-frequency
51
+ * map and the document length (total token count) used for length
52
+ * normalization.
53
+ */
54
+ interface Bm25DocumentStats {
55
+ id: string;
56
+ termFrequencies: Map<string, number>;
57
+ length: number;
58
+ }
59
+ /**
60
+ * An immutable BM25 index built from a document set. Carries the per-document
61
+ * term frequencies, per-term document frequencies, corpus size, and average
62
+ * document length needed to score any query. Build once per query batch with
63
+ * {@link buildBm25Index}, then score with {@link scoreBm25Query}.
64
+ */
65
+ export interface Bm25Index {
66
+ documents: Bm25DocumentStats[];
67
+ documentFrequency: Map<string, number>;
68
+ documentCount: number;
69
+ averageDocumentLength: number;
70
+ }
71
+ /**
72
+ * Tokenize a document/query string into BM25 terms. Shares
73
+ * {@link tokenizeAlphaNumeric} with the lexical scorer so the BM25 vocabulary is
74
+ * identical to the rest of search: lowercase, alphanumeric runs, no stop-word
75
+ * removal or stemming (kept deliberately simple and language-agnostic).
76
+ */
77
+ export declare function tokenizeBm25(text: string): string[];
78
+ /**
79
+ * Build a BM25 index from a corpus. Computes each document's term frequencies
80
+ * and length, the per-term document frequency, the corpus size, and the average
81
+ * document length. Empty documents (no tokens) are retained with length `0` so
82
+ * they can still be scored (they simply never match a query term). An empty
83
+ * corpus yields an index with `averageDocumentLength` `0`, which
84
+ * {@link scoreBm25Query} handles without dividing by zero.
85
+ */
86
+ export declare function buildBm25Index(documents: Bm25Document[]): Bm25Index;
87
+ /**
88
+ * Score every document in the index against a query and return an id → score
89
+ * map containing only documents with a strictly positive score (a document that
90
+ * matches no query term is omitted, mirroring the lexical scorer's "no match →
91
+ * no hit" contract). Query tokens are de-duplicated so a token repeated in the
92
+ * query does not multiply its own contribution. Returns an empty map for an
93
+ * empty index, an empty query, or a query whose terms appear in no document.
94
+ */
95
+ export declare function scoreBm25Query(index: Bm25Index, queryTokens: string[], params: Bm25Params): Map<string, number>;
96
+ /**
97
+ * Flatten a structured search-corpus record (the shape produced by
98
+ * `buildSearchCorpus`) into a single whitespace-joined string of its *values*
99
+ * only — field names/object keys are deliberately excluded so corpus-wide keys
100
+ * (`title`, `description`, …) never enter the BM25 vocabulary and skew IDF.
101
+ * Strings and numbers contribute their text; arrays and nested objects (tags,
102
+ * dependencies, the plan corpus) are walked recursively; everything else is
103
+ * dropped.
104
+ */
105
+ export declare function flattenSearchCorpusText(corpus: Record<string, unknown>): string;
106
+ /**
107
+ * Resolve the effective BM25 parameters from `settings.search.bm25`, applying
108
+ * {@link DEFAULT_BM25_K1}/{@link DEFAULT_BM25_B} when unset and clamping `k1` to
109
+ * `[0, 1000]` and `b` to `[0, 1]`. Tolerant of partial/invalid config: each
110
+ * knob is resolved independently so one bad value does not discard the other.
111
+ */
112
+ export declare function resolveBm25Params(settings: Pick<PmSettings, "search"> | undefined): Bm25Params;
113
+ export {};
@@ -0,0 +1,178 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="57c1eb74-63b5-5208-abe1-98da8308839d")}catch(e){}}();
3
+ import { tokenizeAlphaNumeric } from "../shared/text-normalization.js";
4
+ /**
5
+ * Default Okapi BM25 term-frequency saturation parameter. `1.2` is the widely
6
+ * used Lucene/Elasticsearch default: higher values reduce the diminishing
7
+ * returns of repeated terms more slowly (terms keep accruing weight), lower
8
+ * values saturate sooner.
9
+ */
10
+ export const DEFAULT_BM25_K1 = 1.2;
11
+ /**
12
+ * Default Okapi BM25 length-normalization parameter. `0.75` is the Lucene
13
+ * default: `0` disables length normalization entirely, `1` fully normalizes a
14
+ * document's score by its length relative to the corpus average.
15
+ */
16
+ export const DEFAULT_BM25_B = 0.75;
17
+ /**
18
+ * Tokenize a document/query string into BM25 terms. Shares
19
+ * {@link tokenizeAlphaNumeric} with the lexical scorer so the BM25 vocabulary is
20
+ * identical to the rest of search: lowercase, alphanumeric runs, no stop-word
21
+ * removal or stemming (kept deliberately simple and language-agnostic).
22
+ */
23
+ export function tokenizeBm25(text) {
24
+ return tokenizeAlphaNumeric(text);
25
+ }
26
+ /**
27
+ * Count occurrences of each token in a token list.
28
+ */
29
+ function buildTermFrequencies(tokens) {
30
+ const frequencies = new Map();
31
+ for (const token of tokens) {
32
+ frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
33
+ }
34
+ return frequencies;
35
+ }
36
+ /**
37
+ * Build a BM25 index from a corpus. Computes each document's term frequencies
38
+ * and length, the per-term document frequency, the corpus size, and the average
39
+ * document length. Empty documents (no tokens) are retained with length `0` so
40
+ * they can still be scored (they simply never match a query term). An empty
41
+ * corpus yields an index with `averageDocumentLength` `0`, which
42
+ * {@link scoreBm25Query} handles without dividing by zero.
43
+ */
44
+ export function buildBm25Index(documents) {
45
+ const stats = [];
46
+ const documentFrequency = new Map();
47
+ let totalLength = 0;
48
+ for (const document of documents) {
49
+ const tokens = tokenizeBm25(document.text);
50
+ const termFrequencies = buildTermFrequencies(tokens);
51
+ totalLength += tokens.length;
52
+ for (const term of termFrequencies.keys()) {
53
+ documentFrequency.set(term, (documentFrequency.get(term) ?? 0) + 1);
54
+ }
55
+ stats.push({ id: document.id, termFrequencies, length: tokens.length });
56
+ }
57
+ const documentCount = stats.length;
58
+ return {
59
+ documents: stats,
60
+ documentFrequency,
61
+ documentCount,
62
+ averageDocumentLength: documentCount > 0 ? totalLength / documentCount : 0,
63
+ };
64
+ }
65
+ /**
66
+ * Inverse document frequency for one term using the BM25 "plus-one" variant
67
+ * (`ln(1 + (N - n + 0.5) / (n + 0.5))`) used by Lucene. The `1 +` inside the
68
+ * logarithm keeps the IDF strictly non-negative even for a term that appears in
69
+ * every document, so a common term can never subtract from a document's score.
70
+ */
71
+ function inverseDocumentFrequency(documentCount, termDocumentFrequency) {
72
+ return Math.log(1 + (documentCount - termDocumentFrequency + 0.5) / (termDocumentFrequency + 0.5));
73
+ }
74
+ /**
75
+ * Score every document in the index against a query and return an id → score
76
+ * map containing only documents with a strictly positive score (a document that
77
+ * matches no query term is omitted, mirroring the lexical scorer's "no match →
78
+ * no hit" contract). Query tokens are de-duplicated so a token repeated in the
79
+ * query does not multiply its own contribution. Returns an empty map for an
80
+ * empty index, an empty query, or a query whose terms appear in no document.
81
+ */
82
+ export function scoreBm25Query(index, queryTokens, params) {
83
+ const scores = new Map();
84
+ // Empty corpus, or a corpus where every document is empty (avgdl 0): no term
85
+ // can match, so return early. Guarding here also guarantees a non-zero
86
+ // average document length below, keeping the length-normalization division
87
+ // safe without an unreachable fallback branch.
88
+ if (index.documentCount === 0 || index.averageDocumentLength === 0) {
89
+ return scores;
90
+ }
91
+ const distinctQueryTerms = [...new Set(queryTokens.filter((token) => token.length > 0))];
92
+ if (distinctQueryTerms.length === 0) {
93
+ return scores;
94
+ }
95
+ // Precompute the IDF for each query term once rather than per document.
96
+ const idfByTerm = new Map();
97
+ for (const term of distinctQueryTerms) {
98
+ const termDocumentFrequency = index.documentFrequency.get(term);
99
+ if (termDocumentFrequency === undefined) {
100
+ continue;
101
+ }
102
+ idfByTerm.set(term, inverseDocumentFrequency(index.documentCount, termDocumentFrequency));
103
+ }
104
+ if (idfByTerm.size === 0) {
105
+ return scores;
106
+ }
107
+ for (const document of index.documents) {
108
+ let score = 0;
109
+ for (const [term, idf] of idfByTerm) {
110
+ const termFrequency = document.termFrequencies.get(term);
111
+ if (termFrequency === undefined) {
112
+ continue;
113
+ }
114
+ const lengthNormalization = 1 - params.b + (params.b * document.length) / index.averageDocumentLength;
115
+ const denominator = termFrequency + params.k1 * lengthNormalization;
116
+ score += idf * ((termFrequency * (params.k1 + 1)) / denominator);
117
+ }
118
+ if (score > 0) {
119
+ scores.set(document.id, score);
120
+ }
121
+ }
122
+ return scores;
123
+ }
124
+ /**
125
+ * Coerce one persisted BM25 parameter to a finite number within `[min, max]`.
126
+ * A finite number is clamped to the range (preserving user intent — e.g.
127
+ * `k1=5000` becomes `1000` rather than being silently ignored); any non-number
128
+ * or non-finite value (string, `NaN`, `Infinity`, missing) falls back to
129
+ * `fallback` so a malformed `settings.json` can never crash or skew scoring.
130
+ */
131
+ function resolveClampedParam(candidate, fallback, min, max) {
132
+ if (typeof candidate !== "number" || !Number.isFinite(candidate)) {
133
+ return fallback;
134
+ }
135
+ return Math.min(max, Math.max(min, candidate));
136
+ }
137
+ /**
138
+ * Flatten a structured search-corpus record (the shape produced by
139
+ * `buildSearchCorpus`) into a single whitespace-joined string of its *values*
140
+ * only — field names/object keys are deliberately excluded so corpus-wide keys
141
+ * (`title`, `description`, …) never enter the BM25 vocabulary and skew IDF.
142
+ * Strings and numbers contribute their text; arrays and nested objects (tags,
143
+ * dependencies, the plan corpus) are walked recursively; everything else is
144
+ * dropped.
145
+ */
146
+ export function flattenSearchCorpusText(corpus) {
147
+ const collectValueText = (value) => {
148
+ if (typeof value === "string") {
149
+ return [value];
150
+ }
151
+ if (typeof value === "number") {
152
+ return [String(value)];
153
+ }
154
+ if (Array.isArray(value)) {
155
+ return value.flatMap(collectValueText);
156
+ }
157
+ if (typeof value === "object" && value !== null) {
158
+ return Object.values(value).flatMap(collectValueText);
159
+ }
160
+ return [];
161
+ };
162
+ return collectValueText(Object.values(corpus)).join(" ");
163
+ }
164
+ /**
165
+ * Resolve the effective BM25 parameters from `settings.search.bm25`, applying
166
+ * {@link DEFAULT_BM25_K1}/{@link DEFAULT_BM25_B} when unset and clamping `k1` to
167
+ * `[0, 1000]` and `b` to `[0, 1]`. Tolerant of partial/invalid config: each
168
+ * knob is resolved independently so one bad value does not discard the other.
169
+ */
170
+ export function resolveBm25Params(settings) {
171
+ const bm25 = settings?.search?.bm25;
172
+ return {
173
+ k1: resolveClampedParam(bm25?.k1, DEFAULT_BM25_K1, 0, 1_000),
174
+ b: resolveClampedParam(bm25?.b, DEFAULT_BM25_B, 0, 1),
175
+ };
176
+ }
177
+ //# sourceMappingURL=bm25.js.map
178
+ //# debugId=57c1eb74-63b5-5208-abe1-98da8308839d
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bm25.js","sources":["core/search/bm25.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module core/search/bm25\n *\n * Offline BM25 lexical retrieval (pm-75k9). Provides an Okapi BM25 ranker that\n * scores the in-memory item corpus with no embedding service, vector store, or\n * network dependency, so semantic/hybrid search degrades to a dense-quality\n * lexical ranker — rather than the naive field-weighted scorer — in air-gapped,\n * CI, and zero-setup environments.\n *\n * BM25 improves on raw term-frequency counting in three ways the legacy keyword\n * scorer cannot: inverse document frequency down-weights terms common across the\n * whole corpus, term-frequency saturation (`k1`) caps the contribution of a term\n * repeated many times in one document, and length normalization (`b`) prevents\n * long documents from dominating purely by size.\n */\nimport type { PmSettings } from \"../../types/index.js\";\nimport { tokenizeAlphaNumeric } from \"../shared/text-normalization.js\";\n\n/**\n * Default Okapi BM25 term-frequency saturation parameter. `1.2` is the widely\n * used Lucene/Elasticsearch default: higher values reduce the diminishing\n * returns of repeated terms more slowly (terms keep accruing weight), lower\n * values saturate sooner.\n */\nexport const DEFAULT_BM25_K1 = 1.2;\n\n/**\n * Default Okapi BM25 length-normalization parameter. `0.75` is the Lucene\n * default: `0` disables length normalization entirely, `1` fully normalizes a\n * document's score by its length relative to the corpus average.\n */\nexport const DEFAULT_BM25_B = 0.75;\n\n/**\n * Resolved BM25 tuning parameters. Both are clamped to sane ranges by\n * {@link resolveBm25Params} before scoring.\n */\nexport interface Bm25Params {\n /** Term-frequency saturation (`k1`); finite and `>= 0`. */\n k1: number;\n /** Length normalization (`b`); finite and within `[0, 1]`. */\n b: number;\n}\n\n/**\n * One corpus document supplied to {@link buildBm25Index}: a stable id and the\n * already-concatenated searchable text. Callers build the text from whichever\n * fields they want indexed (typically the resolved `search.corpus_fields`).\n */\nexport interface Bm25Document {\n id: string;\n text: string;\n}\n\n/**\n * Per-document statistics retained in a {@link Bm25Index}: the term-frequency\n * map and the document length (total token count) used for length\n * normalization.\n */\ninterface Bm25DocumentStats {\n id: string;\n termFrequencies: Map<string, number>;\n length: number;\n}\n\n/**\n * An immutable BM25 index built from a document set. Carries the per-document\n * term frequencies, per-term document frequencies, corpus size, and average\n * document length needed to score any query. Build once per query batch with\n * {@link buildBm25Index}, then score with {@link scoreBm25Query}.\n */\nexport interface Bm25Index {\n documents: Bm25DocumentStats[];\n documentFrequency: Map<string, number>;\n documentCount: number;\n averageDocumentLength: number;\n}\n\n/**\n * Tokenize a document/query string into BM25 terms. Shares\n * {@link tokenizeAlphaNumeric} with the lexical scorer so the BM25 vocabulary is\n * identical to the rest of search: lowercase, alphanumeric runs, no stop-word\n * removal or stemming (kept deliberately simple and language-agnostic).\n */\nexport function tokenizeBm25(text: string): string[] {\n return tokenizeAlphaNumeric(text);\n}\n\n/**\n * Count occurrences of each token in a token list.\n */\nfunction buildTermFrequencies(tokens: string[]): Map<string, number> {\n const frequencies = new Map<string, number>();\n for (const token of tokens) {\n frequencies.set(token, (frequencies.get(token) ?? 0) + 1);\n }\n return frequencies;\n}\n\n/**\n * Build a BM25 index from a corpus. Computes each document's term frequencies\n * and length, the per-term document frequency, the corpus size, and the average\n * document length. Empty documents (no tokens) are retained with length `0` so\n * they can still be scored (they simply never match a query term). An empty\n * corpus yields an index with `averageDocumentLength` `0`, which\n * {@link scoreBm25Query} handles without dividing by zero.\n */\nexport function buildBm25Index(documents: Bm25Document[]): Bm25Index {\n const stats: Bm25DocumentStats[] = [];\n const documentFrequency = new Map<string, number>();\n let totalLength = 0;\n for (const document of documents) {\n const tokens = tokenizeBm25(document.text);\n const termFrequencies = buildTermFrequencies(tokens);\n totalLength += tokens.length;\n for (const term of termFrequencies.keys()) {\n documentFrequency.set(term, (documentFrequency.get(term) ?? 0) + 1);\n }\n stats.push({ id: document.id, termFrequencies, length: tokens.length });\n }\n const documentCount = stats.length;\n return {\n documents: stats,\n documentFrequency,\n documentCount,\n averageDocumentLength: documentCount > 0 ? totalLength / documentCount : 0,\n };\n}\n\n/**\n * Inverse document frequency for one term using the BM25 \"plus-one\" variant\n * (`ln(1 + (N - n + 0.5) / (n + 0.5))`) used by Lucene. The `1 +` inside the\n * logarithm keeps the IDF strictly non-negative even for a term that appears in\n * every document, so a common term can never subtract from a document's score.\n */\nfunction inverseDocumentFrequency(documentCount: number, termDocumentFrequency: number): number {\n return Math.log(1 + (documentCount - termDocumentFrequency + 0.5) / (termDocumentFrequency + 0.5));\n}\n\n/**\n * Score every document in the index against a query and return an id → score\n * map containing only documents with a strictly positive score (a document that\n * matches no query term is omitted, mirroring the lexical scorer's \"no match →\n * no hit\" contract). Query tokens are de-duplicated so a token repeated in the\n * query does not multiply its own contribution. Returns an empty map for an\n * empty index, an empty query, or a query whose terms appear in no document.\n */\nexport function scoreBm25Query(index: Bm25Index, queryTokens: string[], params: Bm25Params): Map<string, number> {\n const scores = new Map<string, number>();\n // Empty corpus, or a corpus where every document is empty (avgdl 0): no term\n // can match, so return early. Guarding here also guarantees a non-zero\n // average document length below, keeping the length-normalization division\n // safe without an unreachable fallback branch.\n if (index.documentCount === 0 || index.averageDocumentLength === 0) {\n return scores;\n }\n const distinctQueryTerms = [...new Set(queryTokens.filter((token) => token.length > 0))];\n if (distinctQueryTerms.length === 0) {\n return scores;\n }\n // Precompute the IDF for each query term once rather than per document.\n const idfByTerm = new Map<string, number>();\n for (const term of distinctQueryTerms) {\n const termDocumentFrequency = index.documentFrequency.get(term);\n if (termDocumentFrequency === undefined) {\n continue;\n }\n idfByTerm.set(term, inverseDocumentFrequency(index.documentCount, termDocumentFrequency));\n }\n if (idfByTerm.size === 0) {\n return scores;\n }\n for (const document of index.documents) {\n let score = 0;\n for (const [term, idf] of idfByTerm) {\n const termFrequency = document.termFrequencies.get(term);\n if (termFrequency === undefined) {\n continue;\n }\n const lengthNormalization =\n 1 - params.b + (params.b * document.length) / index.averageDocumentLength;\n const denominator = termFrequency + params.k1 * lengthNormalization;\n score += idf * ((termFrequency * (params.k1 + 1)) / denominator);\n }\n if (score > 0) {\n scores.set(document.id, score);\n }\n }\n return scores;\n}\n\n/**\n * Coerce one persisted BM25 parameter to a finite number within `[min, max]`.\n * A finite number is clamped to the range (preserving user intent — e.g.\n * `k1=5000` becomes `1000` rather than being silently ignored); any non-number\n * or non-finite value (string, `NaN`, `Infinity`, missing) falls back to\n * `fallback` so a malformed `settings.json` can never crash or skew scoring.\n */\nfunction resolveClampedParam(candidate: unknown, fallback: number, min: number, max: number): number {\n if (typeof candidate !== \"number\" || !Number.isFinite(candidate)) {\n return fallback;\n }\n return Math.min(max, Math.max(min, candidate));\n}\n\n/**\n * Flatten a structured search-corpus record (the shape produced by\n * `buildSearchCorpus`) into a single whitespace-joined string of its *values*\n * only — field names/object keys are deliberately excluded so corpus-wide keys\n * (`title`, `description`, …) never enter the BM25 vocabulary and skew IDF.\n * Strings and numbers contribute their text; arrays and nested objects (tags,\n * dependencies, the plan corpus) are walked recursively; everything else is\n * dropped.\n */\nexport function flattenSearchCorpusText(corpus: Record<string, unknown>): string {\n const collectValueText = (value: unknown): string[] => {\n if (typeof value === \"string\") {\n return [value];\n }\n if (typeof value === \"number\") {\n return [String(value)];\n }\n if (Array.isArray(value)) {\n return value.flatMap(collectValueText);\n }\n if (typeof value === \"object\" && value !== null) {\n return Object.values(value).flatMap(collectValueText);\n }\n return [];\n };\n return collectValueText(Object.values(corpus)).join(\" \");\n}\n\n/**\n * Resolve the effective BM25 parameters from `settings.search.bm25`, applying\n * {@link DEFAULT_BM25_K1}/{@link DEFAULT_BM25_B} when unset and clamping `k1` to\n * `[0, 1000]` and `b` to `[0, 1]`. Tolerant of partial/invalid config: each\n * knob is resolved independently so one bad value does not discard the other.\n */\nexport function resolveBm25Params(settings: Pick<PmSettings, \"search\"> | undefined): Bm25Params {\n const bm25 = (settings?.search as { bm25?: { k1?: unknown; b?: unknown } } | undefined)?.bm25;\n return {\n k1: resolveClampedParam(bm25?.k1, DEFAULT_BM25_K1, 0, 1_000),\n b: resolveClampedParam(bm25?.b, DEFAULT_BM25_B, 0, 1),\n };\n}\n"],"names":[],"mappings":"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","debugId":"57c1eb74-63b5-5208-abe1-98da8308839d"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @module core/search/eval
3
+ *
4
+ * Search relevance evaluation primitives (pm-u8n5). Implements the standard
5
+ * offline ranking metrics — nDCG@k, MRR@k, precision@k, recall@k — over a
6
+ * human-curated golden-query set, plus the loader/validator for that set. The
7
+ * `pm eval` command (src/cli/commands/eval.ts) runs each golden query through
8
+ * the live retrieval path and feeds the ranked ids here, so relevance
9
+ * regressions from corpus changes, hybrid-weight changes, or provider swaps are
10
+ * measurable and gateable in CI.
11
+ *
12
+ * Relevance is binary: an id is either in a query's `relevant_ids` set or it is
13
+ * not. This keeps the golden set cheap to curate while still supporting the
14
+ * graded-discount behavior of DCG (earlier hits are worth more).
15
+ */
16
+ /**
17
+ * Retrieval mode a golden query is evaluated under. Mirrors the `pm search`
18
+ * mode surface; omitting it on a query defers to the eval run's default mode.
19
+ */
20
+ export type EvalSearchMode = "keyword" | "semantic" | "hybrid";
21
+ /** Default cutoff (`@k`) used for every metric when the caller does not override it. */
22
+ export declare const DEFAULT_EVAL_K = 10;
23
+ /**
24
+ * One curated golden query: the query text, the set of item ids considered
25
+ * relevant, an optional per-query retrieval mode override, and an optional
26
+ * human-readable description of the relevance intent.
27
+ */
28
+ export interface EvalQuery {
29
+ query: string;
30
+ relevant_ids: string[];
31
+ mode?: EvalSearchMode;
32
+ description?: string;
33
+ }
34
+ /** A parsed, validated golden-query set. */
35
+ export interface EvalQuerySet {
36
+ queries: EvalQuery[];
37
+ }
38
+ /**
39
+ * Thrown by {@link parseEvalQuerySet} when the raw golden-query payload is
40
+ * structurally invalid. Carries a precise, human-actionable message naming the
41
+ * offending query index/field so an author can fix the JSON directly.
42
+ */
43
+ export declare class EvalQuerySetError extends Error {
44
+ /** Construct an error describing why a golden-query set failed validation. */
45
+ constructor(message: string);
46
+ }
47
+ /**
48
+ * Compute Discounted Cumulative Gain at cutoff `k` for an ordered list of binary
49
+ * relevance gains (`1` relevant, `0` not). Position `i` (1-based) contributes
50
+ * `gain / log2(i + 1)`, so a relevant hit ranked first is worth `1` and later
51
+ * hits are progressively discounted. Gains beyond `k` are ignored.
52
+ */
53
+ export declare function dcgAtK(relevanceGains: number[], k: number): number;
54
+ /**
55
+ * Normalized DCG at cutoff `k`: the ranking's DCG divided by the DCG of the
56
+ * ideal ranking (all relevant ids first). Returns `0` when the query has no
57
+ * relevant ids (the ideal DCG is `0` and the score is undefined), giving a
58
+ * stable lower bound for aggregation. Result is in `[0, 1]`.
59
+ */
60
+ export declare function ndcgAtK(rankedIds: string[], relevantIds: ReadonlySet<string>, k: number): number;
61
+ /**
62
+ * Reciprocal rank of the first relevant id within the top `k` results: `1/rank`
63
+ * (rank is 1-based), or `0` when no relevant id appears in the cutoff. Averaged
64
+ * across queries this is Mean Reciprocal Rank (MRR@k).
65
+ */
66
+ export declare function reciprocalRankAtK(rankedIds: string[], relevantIds: ReadonlySet<string>, k: number): number;
67
+ /**
68
+ * Precision at cutoff `k`: relevant ids retrieved in the top `k` divided by `k`.
69
+ * Returns `0` for a non-positive `k`.
70
+ */
71
+ export declare function precisionAtK(rankedIds: string[], relevantIds: ReadonlySet<string>, k: number): number;
72
+ /**
73
+ * Recall at cutoff `k`: relevant ids retrieved in the top `k` divided by the
74
+ * total number of relevant ids. Returns `0` when the query has no relevant ids.
75
+ */
76
+ export declare function recallAtK(rankedIds: string[], relevantIds: ReadonlySet<string>, k: number): number;
77
+ /** The four ranking metrics computed for a single query, plus match counts. */
78
+ export interface QueryEvalMetrics {
79
+ ndcg: number;
80
+ mrr: number;
81
+ precision: number;
82
+ recall: number;
83
+ relevant_total: number;
84
+ retrieved_relevant: number;
85
+ }
86
+ /**
87
+ * Evaluate one ranking against its relevant-id set at cutoff `k`, returning all
88
+ * four metrics together so callers compute the document ranking once and read
89
+ * every score off the result.
90
+ */
91
+ export declare function evaluateRanking(rankedIds: string[], relevantIds: ReadonlySet<string>, k: number): QueryEvalMetrics;
92
+ /** Macro-averaged metrics across all evaluated queries. */
93
+ export interface AggregateEvalMetrics {
94
+ ndcg: number;
95
+ mrr: number;
96
+ precision: number;
97
+ recall: number;
98
+ query_count: number;
99
+ }
100
+ /**
101
+ * Macro-average per-query metrics (each query weighted equally). Returns all
102
+ * zeros with `query_count: 0` for an empty input so the gate path has a stable
103
+ * shape to compare against.
104
+ */
105
+ export declare function aggregateEvalMetrics(perQuery: QueryEvalMetrics[]): AggregateEvalMetrics;
106
+ /**
107
+ * Parse and validate a raw golden-query payload (typically `JSON.parse` of
108
+ * `.agents/pm/search/eval-queries.json`). Accepts either a bare array of query
109
+ * entries or an object with a `queries` array. Throws {@link EvalQuerySetError}
110
+ * with a precise message when the payload is not an array/object, when
111
+ * `queries` is missing/not an array, when the set is empty, or when any entry
112
+ * fails {@link parseEvalQueryEntry}.
113
+ */
114
+ export declare function parseEvalQuerySet(raw: unknown): EvalQuerySet;