@unbrained/pm-cli 2026.7.1 → 2026.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/CHANGELOG.md +25 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +1 -1
- package/dist/cli/commander-usage.d.ts +2 -0
- package/dist/cli/commander-usage.js +110 -86
- package/dist/cli/commander-usage.js.map +1 -1
- package/dist/cli/commands/activity.js +79 -57
- package/dist/cli/commands/activity.js.map +1 -1
- package/dist/cli/commands/aggregate.js +102 -79
- package/dist/cli/commands/aggregate.js.map +1 -1
- package/dist/cli/commands/calendar.js +278 -243
- package/dist/cli/commands/calendar.js.map +1 -1
- package/dist/cli/commands/close-many.js +176 -114
- package/dist/cli/commands/close-many.js.map +1 -1
- package/dist/cli/commands/close.js +157 -129
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/comments-audit.js +50 -42
- package/dist/cli/commands/comments-audit.js.map +1 -1
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +443 -879
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/context.js +365 -287
- package/dist/cli/commands/context.js.map +1 -1
- package/dist/cli/commands/contracts.js +352 -238
- package/dist/cli/commands/contracts.js.map +1 -1
- package/dist/cli/commands/create.js +32 -17
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/dedupe-audit.js +46 -39
- package/dist/cli/commands/dedupe-audit.js.map +1 -1
- package/dist/cli/commands/dedupe-merge.js +88 -69
- package/dist/cli/commands/dedupe-merge.js.map +1 -1
- package/dist/cli/commands/extension/doctor.js +56 -67
- package/dist/cli/commands/extension/doctor.js.map +1 -1
- package/dist/cli/commands/extension/shared.js +21 -15
- package/dist/cli/commands/extension/shared.js.map +1 -1
- package/dist/cli/commands/get.js +114 -81
- package/dist/cli/commands/get.js.map +1 -1
- package/dist/cli/commands/health.d.ts +17 -0
- package/dist/cli/commands/health.js +545 -417
- package/dist/cli/commands/health.js.map +1 -1
- package/dist/cli/commands/history-compact.js +209 -140
- package/dist/cli/commands/history-compact.js.map +1 -1
- package/dist/cli/commands/history-redact.js +162 -119
- package/dist/cli/commands/history-redact.js.map +1 -1
- package/dist/cli/commands/history-repair.js +120 -85
- package/dist/cli/commands/history-repair.js.map +1 -1
- package/dist/cli/commands/init-agent-guidance.js +32 -19
- package/dist/cli/commands/init-agent-guidance.js.map +1 -1
- package/dist/cli/commands/init.d.ts +8 -2
- package/dist/cli/commands/init.js +263 -162
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/list.js +152 -102
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/next.js +60 -40
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/plan.js +373 -298
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/profile.js +23 -15
- package/dist/cli/commands/profile.js.map +1 -1
- package/dist/cli/commands/recurrence-parsers.js +43 -25
- package/dist/cli/commands/recurrence-parsers.js.map +1 -1
- package/dist/cli/commands/reindex.d.ts +21 -5
- package/dist/cli/commands/reindex.js +345 -205
- package/dist/cli/commands/reindex.js.map +1 -1
- package/dist/cli/commands/restore.d.ts +8 -7
- package/dist/cli/commands/restore.js +105 -69
- package/dist/cli/commands/restore.js.map +1 -1
- package/dist/cli/commands/schema.js +33 -30
- package/dist/cli/commands/schema.js.map +1 -1
- package/dist/cli/commands/search.d.ts +10 -2
- package/dist/cli/commands/search.js +503 -470
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/telemetry.js +98 -87
- package/dist/cli/commands/telemetry.js.map +1 -1
- package/dist/cli/commands/test-all.js +237 -168
- package/dist/cli/commands/test-all.js.map +1 -1
- package/dist/cli/commands/test.d.ts +6 -5
- package/dist/cli/commands/test.js +424 -344
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/commands/update-many.js +57 -42
- package/dist/cli/commands/update-many.js.map +1 -1
- package/dist/cli/commands/update.d.ts +1 -1
- package/dist/cli/commands/update.js +738 -671
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +20 -1
- package/dist/cli/commands/validate.js +478 -343
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/error-guidance.js +406 -361
- package/dist/cli/error-guidance.js.map +1 -1
- package/dist/cli/extension-command-help.js +27 -19
- package/dist/cli/extension-command-help.js.map +1 -1
- package/dist/cli/help-json-payload.js +57 -49
- package/dist/cli/help-json-payload.js.map +1 -1
- package/dist/cli/main.d.ts +47 -0
- package/dist/cli/main.js +474 -371
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/migration-gates.js +22 -45
- package/dist/cli/migration-gates.js.map +1 -1
- package/dist/cli/register-list-query.js +299 -310
- package/dist/cli/register-list-query.js.map +1 -1
- package/dist/cli/register-mutation.js +763 -779
- package/dist/cli/register-mutation.js.map +1 -1
- package/dist/cli/register-operations.js +386 -362
- package/dist/cli/register-operations.js.map +1 -1
- package/dist/cli/register-setup.js +120 -101
- package/dist/cli/register-setup.js.map +1 -1
- package/dist/cli/registration-helpers.d.ts +4 -0
- package/dist/cli/registration-helpers.js +45 -56
- package/dist/cli/registration-helpers.js.map +1 -1
- package/dist/cli-bundle/chunks/{activity-DBLQVIP6.js → activity-5YWLSPGE.js} +9 -9
- package/dist/cli-bundle/chunks/activity-BIML7K2D.js +16 -0
- package/dist/cli-bundle/chunks/{aggregate-SDF7DUOD.js → aggregate-2V2M3JZU.js} +107 -83
- package/dist/cli-bundle/chunks/{aggregate-SDF7DUOD.js.map → aggregate-2V2M3JZU.js.map} +2 -2
- package/dist/cli-bundle/chunks/{aggregate-JTEF4BP7.js → aggregate-EK7DXML7.js} +108 -84
- package/dist/cli-bundle/chunks/aggregate-EK7DXML7.js.map +1 -0
- package/dist/cli-bundle/chunks/{append-LOVPGVPT.js → append-EZESUVJT.js} +8 -8
- package/dist/cli-bundle/chunks/{append-QCFJSDFL.js → append-JFG4WQMG.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-TP5Q5ZD3.js → chunk-32IUNTG4.js} +158 -119
- package/dist/cli-bundle/chunks/chunk-32IUNTG4.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-BHO33VBQ.js → chunk-3W2HJKJX.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-VARHOHYV.js → chunk-4EXFFP7T.js} +86 -52
- package/dist/cli-bundle/chunks/chunk-4EXFFP7T.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-4DUVCVLZ.js → chunk-4PFVUUYV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JSU5P7VS.js → chunk-4WB6S4YX.js} +308 -184
- package/dist/cli-bundle/chunks/chunk-4WB6S4YX.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-HXTQJXWT.js → chunk-572FKBRL.js} +61 -102
- package/dist/cli-bundle/chunks/chunk-572FKBRL.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-IJYK2T43.js → chunk-5E7N4T2Z.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NKZAN7BD.js → chunk-5IZUBZBR.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-NOLPZMSV.js → chunk-5JIWFWOZ.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-FFMQB7WR.js → chunk-67LARBDL.js} +493 -404
- package/dist/cli-bundle/chunks/chunk-67LARBDL.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-6AQZPVHQ.js → chunk-6CDCWKSX.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-3K2M5OYU.js → chunk-6KRFL33E.js} +308 -184
- package/dist/cli-bundle/chunks/chunk-6KRFL33E.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-LB6TP6MC.js → chunk-6NYJGMTY.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SLMUGTK3.js → chunk-7CLLC75T.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-2466X6E4.js → chunk-7LYO6S5A.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-W4ULYO7Z.js → chunk-A3OQ7TFW.js} +8 -8
- package/dist/cli-bundle/chunks/{chunk-5CGRF3L5.js → chunk-AIKU3IIL.js} +153 -90
- package/dist/cli-bundle/chunks/chunk-AIKU3IIL.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-WYDHYJPO.js → chunk-AJHYQCBJ.js} +39 -5
- package/dist/cli-bundle/chunks/chunk-AJHYQCBJ.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-VZDBVEZR.js → chunk-B4O5S7I3.js} +79 -57
- package/dist/cli-bundle/chunks/{chunk-VZDBVEZR.js.map → chunk-B4O5S7I3.js.map} +2 -2
- package/dist/cli-bundle/chunks/{chunk-OTUHB4HS.js → chunk-BE2SR6QL.js} +92 -77
- package/dist/cli-bundle/chunks/chunk-BE2SR6QL.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-JLM7BHI7.js → chunk-BI4R7HI2.js} +75 -53
- package/dist/cli-bundle/chunks/chunk-BI4R7HI2.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-AXIDKKAT.js → chunk-CXR3CMBQ.js} +33 -6
- package/dist/cli-bundle/chunks/chunk-CXR3CMBQ.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-ZETXE4SE.js → chunk-ECU6MMTQ.js} +713 -613
- package/dist/cli-bundle/chunks/chunk-ECU6MMTQ.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-2D4HATP2.js → chunk-EJ2OCBHF.js} +54 -30
- package/dist/cli-bundle/chunks/chunk-EJ2OCBHF.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-BDTISOSD.js → chunk-EQURFOMH.js} +41 -26
- package/dist/cli-bundle/chunks/chunk-EQURFOMH.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-GJ326ZKX.js → chunk-ER4ME5U2.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-HZZEHPRG.js → chunk-ERG65A2L.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-IZBR4NRW.js → chunk-F6WNBITZ.js} +157 -113
- package/dist/cli-bundle/chunks/chunk-F6WNBITZ.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-L5OEFZQG.js → chunk-FH2H3CBD.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-JPECA746.js → chunk-FUFFPIYA.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-CF7RL6FR.js → chunk-FZJM2TXF.js} +110 -73
- package/dist/cli-bundle/chunks/chunk-FZJM2TXF.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-27BXPBAY.js → chunk-GUYNUXQT.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-MAUKX7JC.js → chunk-GVJE5T7Q.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-3J6JRJ3I.js → chunk-HB4FAT3O.js} +42 -60
- package/dist/cli-bundle/chunks/chunk-HB4FAT3O.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-55SNVXRQ.js → chunk-HQAF23Q2.js} +626 -484
- package/dist/cli-bundle/chunks/chunk-HQAF23Q2.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-ZRP4VG5U.js → chunk-JBZ2NTKX.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-RMMQ74TG.js → chunk-JPATY4SE.js} +54 -30
- package/dist/cli-bundle/chunks/chunk-JPATY4SE.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-IPYBJWTE.js → chunk-K5DT2YKM.js} +109 -72
- package/dist/cli-bundle/chunks/chunk-K5DT2YKM.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-EJUDVUYZ.js → chunk-KEZOIPCR.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-RVUALSJF.js → chunk-KHVGM5CS.js} +466 -365
- package/dist/cli-bundle/chunks/chunk-KHVGM5CS.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-YITK2PJ6.js → chunk-KS6ATO47.js} +288 -175
- package/dist/cli-bundle/chunks/chunk-KS6ATO47.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-7R4QEMZZ.js → chunk-L4SLHV6G.js} +463 -362
- package/dist/cli-bundle/chunks/chunk-L4SLHV6G.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-RLDHZHQS.js → chunk-LBE5QFMZ.js} +158 -114
- package/dist/cli-bundle/chunks/chunk-LBE5QFMZ.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-22WKIL3H.js → chunk-LCWQP6NM.js} +76 -54
- package/dist/cli-bundle/chunks/chunk-LCWQP6NM.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-KNPEF7QG.js → chunk-LOWVNZBZ.js} +91 -76
- package/dist/cli-bundle/chunks/chunk-LOWVNZBZ.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-HYTGA6GI.js → chunk-MDHWWBNZ.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-N74UMG5H.js → chunk-MEZZZBYX.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-V3NOKJF2.js → chunk-MHRS5FUX.js} +288 -175
- package/dist/cli-bundle/chunks/chunk-MHRS5FUX.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-3XZCOZ5J.js → chunk-NOIEVOK5.js} +391 -313
- package/dist/cli-bundle/chunks/chunk-NOIEVOK5.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-SDR5J34Q.js → chunk-NZ7QIUP5.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-UYUYDGIB.js → chunk-OGLGI22K.js} +115 -67
- package/dist/cli-bundle/chunks/{chunk-UYUYDGIB.js.map → chunk-OGLGI22K.js.map} +1 -1
- package/dist/cli-bundle/chunks/{chunk-DDPTYJIM.js → chunk-P5L7Z2O6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BZCEN4JA.js → chunk-PULUSQAV.js} +492 -403
- package/dist/cli-bundle/chunks/chunk-PULUSQAV.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-3VGN2UWE.js → chunk-QCDTQCMW.js} +22 -30
- package/dist/cli-bundle/chunks/chunk-QCDTQCMW.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-4A3BJ675.js → chunk-QNNQAAZ5.js} +24 -18
- package/dist/cli-bundle/chunks/{chunk-4A3BJ675.js.map → chunk-QNNQAAZ5.js.map} +1 -1
- package/dist/cli-bundle/chunks/{chunk-MDPEOIGN.js → chunk-RAHINBVP.js} +60 -101
- package/dist/cli-bundle/chunks/chunk-RAHINBVP.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-CWFNTKQJ.js → chunk-RJH5VQHW.js} +43 -61
- package/dist/cli-bundle/chunks/chunk-RJH5VQHW.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-OGXLACHC.js → chunk-SU2E4AWU.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-HPPFYYOV.js → chunk-SU3TB4NS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-JWXRETDF.js → chunk-SYE6WMV6.js} +626 -484
- package/dist/cli-bundle/chunks/chunk-SYE6WMV6.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-NWMZWYYB.js → chunk-T3P7PMZP.js} +42 -27
- package/dist/cli-bundle/chunks/chunk-T3P7PMZP.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-G7I7DEZA.js → chunk-T4XBKJ3F.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-RH5ZBYFD.js → chunk-T6AXEBOP.js} +23 -17
- package/dist/cli-bundle/chunks/{chunk-RH5ZBYFD.js.map → chunk-T6AXEBOP.js.map} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5QFDBI4F.js → chunk-TQ6AX5SQ.js} +58 -69
- package/dist/cli-bundle/chunks/{chunk-5QFDBI4F.js.map → chunk-TQ6AX5SQ.js.map} +2 -2
- package/dist/cli-bundle/chunks/{chunk-4XOIEKSD.js → chunk-U3S2B7TA.js} +86 -52
- package/dist/cli-bundle/chunks/chunk-U3S2B7TA.js.map +1 -0
- package/dist/cli-bundle/chunks/{chunk-VICZWYGZ.js → chunk-U5TR4N7J.js} +7 -7
- package/dist/cli-bundle/chunks/{chunk-EVMTBV6V.js → chunk-UMNQZS4H.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-A7FPBAQS.js → chunk-WJA3PXW7.js} +7 -7
- package/dist/cli-bundle/chunks/{chunk-RGEMWD6Q.js → chunk-WRYERRZX.js} +157 -118
- package/dist/cli-bundle/chunks/chunk-WRYERRZX.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-NUHHPVVS.js → chunk-WYOIIFSE.js} +712 -612
- package/dist/cli-bundle/chunks/chunk-WYOIIFSE.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-4DSGXSBL.js → chunk-XRYSCRZR.js} +116 -68
- package/dist/cli-bundle/chunks/{chunk-4DSGXSBL.js.map → chunk-XRYSCRZR.js.map} +3 -3
- package/dist/cli-bundle/chunks/{chunk-NU2UEROZ.js → chunk-YF746GTH.js} +59 -70
- package/dist/cli-bundle/chunks/{chunk-NU2UEROZ.js.map → chunk-YF746GTH.js.map} +1 -1
- package/dist/cli-bundle/chunks/{chunk-JMP4E6D7.js → chunk-YPWUSH72.js} +390 -312
- package/dist/cli-bundle/chunks/{chunk-JMP4E6D7.js.map → chunk-YPWUSH72.js.map} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QCWF6IYD.js → chunk-YSDFIY7N.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-UZIWVBE6.js → chunk-YSGTMDWC.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-NFDPYAIP.js → chunk-YVKZ3ANP.js} +21 -29
- package/dist/cli-bundle/chunks/chunk-YVKZ3ANP.js.map +7 -0
- package/dist/cli-bundle/chunks/{chunk-OKQ22KTH.js → chunk-Z4DGFQDH.js} +80 -58
- package/dist/cli-bundle/chunks/{chunk-OKQ22KTH.js.map → chunk-Z4DGFQDH.js.map} +1 -1
- package/dist/cli-bundle/chunks/{chunk-64HNBBFQ.js → chunk-Z5F6XZXE.js} +6 -6
- package/dist/cli-bundle/chunks/{chunk-AVEE5EHK.js → chunk-Z7RPDF2Y.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-3S2KT7NG.js → chunk-ZFTKYWIF.js} +152 -89
- package/dist/cli-bundle/chunks/chunk-ZFTKYWIF.js.map +7 -0
- package/dist/cli-bundle/chunks/{claim-T3GCKV7C.js → claim-2BGBGSVI.js} +8 -8
- package/dist/cli-bundle/chunks/{claim-EMQLYPRW.js → claim-5GU7OE67.js} +8 -8
- package/dist/cli-bundle/chunks/close-6YODBUIV.js +14 -0
- package/dist/cli-bundle/chunks/{close-TQYAAFJP.js → close-AFVKPR57.js} +7 -7
- package/dist/cli-bundle/chunks/{close-many-BG5QQIDQ.js → close-many-7KLOSYLP.js} +186 -122
- package/dist/cli-bundle/chunks/close-many-7KLOSYLP.js.map +7 -0
- package/dist/cli-bundle/chunks/{close-many-LDRVEQY2.js → close-many-JKUJMHLG.js} +187 -123
- package/dist/cli-bundle/chunks/close-many-JKUJMHLG.js.map +1 -0
- package/dist/cli-bundle/chunks/{comments-YUXJRZPQ.js → comments-DNWHEBOS.js} +9 -9
- package/dist/cli-bundle/chunks/{comments-QCKR2YY2.js → comments-RW23R3VE.js} +9 -9
- package/dist/cli-bundle/chunks/config-AUM5WBYA.js +1072 -0
- package/dist/cli-bundle/chunks/config-AUM5WBYA.js.map +7 -0
- package/dist/cli-bundle/chunks/config-OZWSA2NP.js +1062 -0
- package/dist/cli-bundle/chunks/config-OZWSA2NP.js.map +1 -0
- package/dist/cli-bundle/chunks/{context-B6275WPX.js → context-OYXRGY7S.js} +12 -12
- package/dist/cli-bundle/chunks/{context-OCZ3FJ4Z.js → context-PKCEHQY3.js} +12 -12
- package/dist/cli-bundle/chunks/{contracts-HN7ABKLP.js → contracts-L6ODXNUM.js} +320 -199
- package/dist/cli-bundle/chunks/contracts-L6ODXNUM.js.map +7 -0
- package/dist/cli-bundle/chunks/{contracts-W4FZVVD6.js → contracts-X74F2GSM.js} +321 -200
- package/dist/cli-bundle/chunks/contracts-X74F2GSM.js.map +1 -0
- package/dist/cli-bundle/chunks/{copy-DEM4DATS.js → copy-6XOR3HKN.js} +5 -5
- package/dist/cli-bundle/chunks/{copy-BTJOSKKT.js → copy-I4NCLBR4.js} +5 -5
- package/dist/cli-bundle/chunks/{create-RLRJMRTM.js → create-PM45M6PE.js} +12 -12
- package/dist/cli-bundle/chunks/create-Z7YCH3BE.js +28 -0
- package/dist/cli-bundle/chunks/{delete-BHK7VB2Z.js → delete-7YXHYG67.js} +5 -5
- package/dist/cli-bundle/chunks/{delete-LSVHAB4Y.js → delete-LPISP43T.js} +5 -5
- package/dist/cli-bundle/chunks/{deps-XHYXWEPT.js → deps-N2T26FME.js} +5 -5
- package/dist/cli-bundle/chunks/{deps-4UDFJRUA.js → deps-WATKQDE3.js} +5 -5
- package/dist/cli-bundle/chunks/{docs-GYZNVS26.js → docs-6LQ3GLM7.js} +7 -7
- package/dist/cli-bundle/chunks/{docs-26S44RXU.js → docs-Z3X6JZLT.js} +7 -7
- package/dist/cli-bundle/chunks/{eval-UYF3ZPVO.js → eval-6DCLG52D.js} +10 -10
- package/dist/cli-bundle/chunks/{eval-PGRVIJQU.js → eval-Y3ZCUTHK.js} +10 -10
- package/dist/cli-bundle/chunks/{extension-CPSLJQVE.js → extension-BNINIMT7.js} +11 -11
- package/dist/cli-bundle/chunks/{extension-CXJ6RELI.js → extension-QWE4YYK5.js} +11 -11
- package/dist/cli-bundle/chunks/{files-NQHEXUZD.js → files-6SGKHS6O.js} +7 -7
- package/dist/cli-bundle/chunks/{files-MBNICQFM.js → files-FUYPSOFJ.js} +7 -7
- package/dist/cli-bundle/chunks/{focus-DWYIG7GU.js → focus-2Q3K76G2.js} +6 -6
- package/dist/cli-bundle/chunks/{focus-XZW5423D.js → focus-ATLBWXXD.js} +6 -6
- package/dist/cli-bundle/chunks/{gc-LPVMKJQJ.js → gc-RUYEMNRF.js} +3 -3
- package/dist/cli-bundle/chunks/{gc-7GQYOS2G.js → gc-TDWSDR66.js} +3 -3
- package/dist/cli-bundle/chunks/{get-A7MAOHCU.js → get-2WGLX74N.js} +120 -80
- package/dist/cli-bundle/chunks/get-2WGLX74N.js.map +7 -0
- package/dist/cli-bundle/chunks/{get-DSDEKJ3L.js → get-LY6M2JIM.js} +121 -81
- package/dist/cli-bundle/chunks/get-LY6M2JIM.js.map +1 -0
- package/dist/cli-bundle/chunks/{health-3IPFLKXB.js → health-4RKHYS3T.js} +535 -371
- package/dist/cli-bundle/chunks/health-4RKHYS3T.js.map +1 -0
- package/dist/cli-bundle/chunks/{health-GZXGBZ36.js → health-TMFVNYRA.js} +534 -370
- package/dist/cli-bundle/chunks/health-TMFVNYRA.js.map +7 -0
- package/dist/cli-bundle/chunks/history-CGNQJXFE.js +18 -0
- package/dist/cli-bundle/chunks/{history-NERDJNUB.js → history-SGRRS72R.js} +8 -8
- package/dist/cli-bundle/chunks/{history-compact-2WRZ2WK4.js → history-compact-LOED43R3.js} +213 -134
- package/dist/cli-bundle/chunks/history-compact-LOED43R3.js.map +7 -0
- package/dist/cli-bundle/chunks/{history-compact-HR7BB4LR.js → history-compact-V5DNAQHH.js} +214 -135
- package/dist/cli-bundle/chunks/history-compact-V5DNAQHH.js.map +1 -0
- package/dist/cli-bundle/chunks/{history-redact-O3NBCNWC.js → history-redact-IRL7WJ3E.js} +9 -9
- package/dist/cli-bundle/chunks/history-redact-N3JK7VBW.js +19 -0
- package/dist/cli-bundle/chunks/{history-repair-XA3LC4GA.js → history-repair-4WVBVHFR.js} +124 -84
- package/dist/cli-bundle/chunks/history-repair-4WVBVHFR.js.map +1 -0
- package/dist/cli-bundle/chunks/{history-repair-LCMOS45X.js → history-repair-PGYQJMGD.js} +123 -83
- package/dist/cli-bundle/chunks/history-repair-PGYQJMGD.js.map +7 -0
- package/dist/cli-bundle/chunks/{init-64XDLQJB.js → init-4GESW7RM.js} +12 -12
- package/dist/cli-bundle/chunks/init-XIDPABA4.js +22 -0
- package/dist/cli-bundle/chunks/{learnings-LPKCSJX7.js → learnings-4BHWFFUV.js} +9 -9
- package/dist/cli-bundle/chunks/{learnings-JR6MA5TI.js → learnings-4FJY7WIW.js} +9 -9
- package/dist/cli-bundle/chunks/{list-G5Y65VF6.js → list-472TIOVK.js} +8 -8
- package/dist/cli-bundle/chunks/{list-DNBJVO3E.js → list-D7BF5CL2.js} +8 -8
- package/dist/cli-bundle/chunks/{next-FYTQF6HG.js → next-D5STGWI2.js} +71 -46
- package/dist/cli-bundle/chunks/next-D5STGWI2.js.map +1 -0
- package/dist/cli-bundle/chunks/{next-BCERKDUR.js → next-SFQZVX2B.js} +70 -45
- package/dist/cli-bundle/chunks/next-SFQZVX2B.js.map +7 -0
- package/dist/cli-bundle/chunks/{notes-2BMU3VVQ.js → notes-6KNOSLIT.js} +9 -9
- package/dist/cli-bundle/chunks/{notes-6KTGO2OJ.js → notes-W4UEICGF.js} +9 -9
- package/dist/cli-bundle/chunks/{plan-RKZHQVXR.js → plan-JO6V7BXO.js} +373 -279
- package/dist/cli-bundle/chunks/plan-JO6V7BXO.js.map +1 -0
- package/dist/cli-bundle/chunks/{plan-4WXEQCTN.js → plan-MJQW5XK3.js} +372 -278
- package/dist/cli-bundle/chunks/plan-MJQW5XK3.js.map +7 -0
- package/dist/cli-bundle/chunks/{profile-4OMM32CD.js → profile-3TOTMUR4.js} +27 -19
- package/dist/cli-bundle/chunks/profile-3TOTMUR4.js.map +7 -0
- package/dist/cli-bundle/chunks/{profile-QLER5ENE.js → profile-WIQY2KCV.js} +28 -20
- package/dist/cli-bundle/chunks/profile-WIQY2KCV.js.map +1 -0
- package/dist/cli-bundle/chunks/register-list-query-ELBHCCFI.js +416 -0
- package/dist/cli-bundle/chunks/register-list-query-ELBHCCFI.js.map +7 -0
- package/dist/cli-bundle/chunks/register-list-query-GAPQ6DM3.js +406 -0
- package/dist/cli-bundle/chunks/register-list-query-GAPQ6DM3.js.map +1 -0
- package/dist/cli-bundle/chunks/{register-mutation-KK7PHYYU.js → register-mutation-ROFE2YPC.js} +729 -702
- package/dist/cli-bundle/chunks/register-mutation-ROFE2YPC.js.map +7 -0
- package/dist/cli-bundle/chunks/{register-mutation-ENNDUAZG.js → register-mutation-SJWMVIJ2.js} +730 -703
- package/dist/cli-bundle/chunks/register-mutation-SJWMVIJ2.js.map +1 -0
- package/dist/cli-bundle/chunks/{register-operations-HZ6XERQG.js → register-operations-IADFVH53.js} +389 -365
- package/dist/cli-bundle/chunks/register-operations-IADFVH53.js.map +7 -0
- package/dist/cli-bundle/chunks/register-operations-MEKU3AKC.js +510 -0
- package/dist/cli-bundle/chunks/register-operations-MEKU3AKC.js.map +1 -0
- package/dist/cli-bundle/chunks/{register-setup-ZKJ53RS7.js → register-setup-3QJY43CY.js} +128 -103
- package/dist/cli-bundle/chunks/register-setup-3QJY43CY.js.map +7 -0
- package/dist/cli-bundle/chunks/{register-setup-P67EMBDJ.js → register-setup-AJWUSSQX.js} +129 -104
- package/dist/cli-bundle/chunks/register-setup-AJWUSSQX.js.map +1 -0
- package/dist/cli-bundle/chunks/{restore-4MHZFJ2E.js → restore-44IHMMZZ.js} +9 -9
- package/dist/cli-bundle/chunks/restore-EXHU34EU.js +17 -0
- package/dist/cli-bundle/chunks/{scheduling-shortcuts-LB6DOLRL.js → scheduling-shortcuts-OLHNLAZY.js} +12 -12
- package/dist/cli-bundle/chunks/{scheduling-shortcuts-UTOYKLXQ.js → scheduling-shortcuts-TSBK4AY5.js} +12 -12
- package/dist/cli-bundle/chunks/{schema-73HR2OJW.js → schema-IB2NZZWD.js} +7 -7
- package/dist/cli-bundle/chunks/{schema-U6LJKSIR.js → schema-VBCELP5G.js} +7 -7
- package/dist/cli-bundle/chunks/{search-AXPXKZER.js → search-7UBETML4.js} +10 -10
- package/dist/cli-bundle/chunks/{search-KILGT37L.js → search-R227JOL6.js} +10 -10
- package/dist/cli-bundle/chunks/{stats-W5KNVJWQ.js → stats-FAG7COEH.js} +6 -6
- package/dist/cli-bundle/chunks/{stats-PSYVVTNZ.js → stats-MCK7PLYS.js} +6 -6
- package/dist/cli-bundle/chunks/{telemetry-GAHHQTOS.js → telemetry-DRPDZMAO.js} +84 -70
- package/dist/cli-bundle/chunks/telemetry-DRPDZMAO.js.map +7 -0
- package/dist/cli-bundle/chunks/{telemetry-BZUF6H7G.js → telemetry-GVVUQSJQ.js} +85 -71
- package/dist/cli-bundle/chunks/telemetry-GVVUQSJQ.js.map +1 -0
- package/dist/cli-bundle/chunks/{test-PWDIZ255.js → test-QY44WAMR.js} +15 -15
- package/dist/cli-bundle/chunks/{test-ACD52Z3S.js → test-SY76DZTO.js} +15 -15
- package/dist/cli-bundle/chunks/test-all-QD6QAEG7.js +430 -0
- package/dist/cli-bundle/chunks/test-all-QD6QAEG7.js.map +7 -0
- package/dist/cli-bundle/chunks/test-all-USPJHXFR.js +427 -0
- package/dist/cli-bundle/chunks/test-all-USPJHXFR.js.map +1 -0
- package/dist/cli-bundle/chunks/{test-runs-UXIXPDKU.js → test-runs-MG5VIGSK.js} +185 -147
- package/dist/cli-bundle/chunks/test-runs-MG5VIGSK.js.map +1 -0
- package/dist/cli-bundle/chunks/{test-runs-PLRAZEFW.js → test-runs-QIR5MJPL.js} +184 -146
- package/dist/cli-bundle/chunks/test-runs-QIR5MJPL.js.map +7 -0
- package/dist/cli-bundle/chunks/update-A7EICML6.js +24 -0
- package/dist/cli-bundle/chunks/{update-KFX2SIAC.js → update-IKJZWLQC.js} +15 -15
- package/dist/cli-bundle/chunks/{update-many-RNSBX2TA.js → update-many-BEMTDA2X.js} +84 -69
- package/dist/cli-bundle/chunks/{update-many-RNSBX2TA.js.map → update-many-BEMTDA2X.js.map} +1 -1
- package/dist/cli-bundle/chunks/{update-many-Z5GJS5LC.js → update-many-RATAARHU.js} +83 -68
- package/dist/cli-bundle/chunks/{update-many-Z5GJS5LC.js.map → update-many-RATAARHU.js.map} +2 -2
- package/dist/cli-bundle/chunks/{upgrade-BRL6KCOJ.js → upgrade-NWMR3VQL.js} +11 -11
- package/dist/cli-bundle/chunks/{upgrade-HAK6JG2O.js → upgrade-VU46BLJZ.js} +11 -11
- package/dist/cli-bundle/chunks/{validate-X5W76YJL.js → validate-GAJ6FUSI.js} +451 -321
- package/dist/cli-bundle/chunks/validate-GAJ6FUSI.js.map +1 -0
- package/dist/cli-bundle/chunks/{validate-MTUVL5VC.js → validate-NRY6MFXN.js} +450 -320
- package/dist/cli-bundle/chunks/validate-NRY6MFXN.js.map +7 -0
- package/dist/cli-bundle/main.js +1107 -916
- package/dist/cli-bundle/main.js.map +3 -3
- package/dist/core/config/nested-settings.js +89 -67
- package/dist/core/config/nested-settings.js.map +1 -1
- package/dist/core/governance/metadata-coverage.js +20 -28
- package/dist/core/governance/metadata-coverage.js.map +1 -1
- package/dist/core/history/drift-scan.js +93 -81
- package/dist/core/history/drift-scan.js.map +1 -1
- package/dist/core/item/item-type-definition.js +14 -14
- package/dist/core/item/item-type-definition.js.map +1 -1
- package/dist/core/item/type-registry.js +114 -99
- package/dist/core/item/type-registry.js.map +1 -1
- package/dist/core/output/mutation-projection.js +42 -26
- package/dist/core/output/mutation-projection.js.map +1 -1
- package/dist/core/schema/runtime-schema.js +97 -80
- package/dist/core/schema/runtime-schema.js.map +1 -1
- package/dist/core/schema/status-defs-file.js +26 -3
- package/dist/core/schema/status-defs-file.js.map +1 -1
- package/dist/core/search/corpus.js +25 -19
- package/dist/core/search/corpus.js.map +1 -1
- package/dist/core/search/semantic-defaults.js +64 -37
- package/dist/core/search/semantic-defaults.js.map +1 -1
- package/dist/core/search/vector-stores.js +32 -17
- package/dist/core/search/vector-stores.js.map +1 -1
- package/dist/core/sentry/helpers.js +31 -28
- package/dist/core/sentry/helpers.js.map +1 -1
- package/dist/core/sentry/instrument.js +54 -43
- package/dist/core/sentry/instrument.js.map +1 -1
- package/dist/core/shared/memo.d.ts +12 -0
- package/dist/core/shared/memo.js +25 -0
- package/dist/core/shared/memo.js.map +1 -0
- package/dist/core/shared/serialization.js +24 -14
- package/dist/core/shared/serialization.js.map +1 -1
- package/dist/core/shared/time.js +26 -4
- package/dist/core/shared/time.js.map +1 -1
- package/dist/core/store/front-matter-cache.d.ts +5 -0
- package/dist/core/store/front-matter-cache.js +316 -171
- package/dist/core/store/front-matter-cache.js.map +1 -1
- package/dist/core/store/item-store.js +33 -26
- package/dist/core/store/item-store.js.map +1 -1
- package/dist/core/store/settings.js +247 -203
- package/dist/core/store/settings.js.map +1 -1
- package/dist/core/test/background-runs.js +189 -160
- package/dist/core/test/background-runs.js.map +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +71 -99
- package/dist/sdk/cli-contracts/flag-contracts.js.map +1 -1
- package/dist/sdk/compose.js +51 -42
- package/dist/sdk/compose.js.map +1 -1
- package/docs/AGENT_GUIDE.md +2 -2
- package/docs/ARCHITECTURE.md +2 -2
- package/docs/COMMANDS.md +8 -4
- package/docs/CONFIGURATION.md +1 -1
- package/docs/EXTENSIONS.md +12 -12
- package/docs/ONBOARDING.md +4 -2
- package/docs/README.md +2 -0
- package/docs/examples/starter-extension/README.md +11 -10
- package/marketplace.json +3 -3
- package/package.json +2 -2
- package/packages/pm-beads/extensions/beads/runtime.ts +240 -167
- package/packages/pm-guide-shell/extensions/guide-shell/runtime.ts +65 -58
- package/packages/pm-todos/extensions/todos/runtime.ts +65 -42
- package/plugins/pm-claude/.claude-plugin/plugin.json +2 -2
- package/plugins/pm-claude/README.md +6 -3
- package/plugins/pm-claude/commands/pm-init.md +1 -1
- package/dist/cli-bundle/chunks/activity-AW6XVFH6.js +0 -16
- package/dist/cli-bundle/chunks/aggregate-JTEF4BP7.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-22WKIL3H.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-2D4HATP2.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-3J6JRJ3I.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-3K2M5OYU.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-3S2KT7NG.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-3VGN2UWE.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-3XZCOZ5J.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-4XOIEKSD.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-55SNVXRQ.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-5CGRF3L5.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-7R4QEMZZ.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-AXIDKKAT.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-BDTISOSD.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-BZCEN4JA.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-CF7RL6FR.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-CWFNTKQJ.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-FFMQB7WR.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-HXTQJXWT.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-IPYBJWTE.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-IZBR4NRW.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-JLM7BHI7.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-JSU5P7VS.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-JWXRETDF.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-KNPEF7QG.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-MDPEOIGN.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-NFDPYAIP.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-NUHHPVVS.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-NWMZWYYB.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-OTUHB4HS.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-RGEMWD6Q.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-RLDHZHQS.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-RMMQ74TG.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-RVUALSJF.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-TP5Q5ZD3.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-V3NOKJF2.js.map +0 -1
- package/dist/cli-bundle/chunks/chunk-VARHOHYV.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-WYDHYJPO.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-YITK2PJ6.js.map +0 -7
- package/dist/cli-bundle/chunks/chunk-ZETXE4SE.js.map +0 -1
- package/dist/cli-bundle/chunks/close-E6ZZGCJY.js +0 -14
- package/dist/cli-bundle/chunks/close-many-BG5QQIDQ.js.map +0 -7
- package/dist/cli-bundle/chunks/close-many-LDRVEQY2.js.map +0 -1
- package/dist/cli-bundle/chunks/config-DMSU64QO.js +0 -1541
- package/dist/cli-bundle/chunks/config-DMSU64QO.js.map +0 -7
- package/dist/cli-bundle/chunks/config-QH2DICD6.js +0 -1531
- package/dist/cli-bundle/chunks/config-QH2DICD6.js.map +0 -1
- package/dist/cli-bundle/chunks/contracts-HN7ABKLP.js.map +0 -7
- package/dist/cli-bundle/chunks/contracts-W4FZVVD6.js.map +0 -1
- package/dist/cli-bundle/chunks/create-OH3BDGBB.js +0 -28
- package/dist/cli-bundle/chunks/get-A7MAOHCU.js.map +0 -7
- package/dist/cli-bundle/chunks/get-DSDEKJ3L.js.map +0 -1
- package/dist/cli-bundle/chunks/health-3IPFLKXB.js.map +0 -1
- package/dist/cli-bundle/chunks/health-GZXGBZ36.js.map +0 -7
- package/dist/cli-bundle/chunks/history-WSZHY3PN.js +0 -18
- package/dist/cli-bundle/chunks/history-compact-2WRZ2WK4.js.map +0 -7
- package/dist/cli-bundle/chunks/history-compact-HR7BB4LR.js.map +0 -1
- package/dist/cli-bundle/chunks/history-redact-MP6CH7U5.js +0 -19
- package/dist/cli-bundle/chunks/history-repair-LCMOS45X.js.map +0 -7
- package/dist/cli-bundle/chunks/history-repair-XA3LC4GA.js.map +0 -1
- package/dist/cli-bundle/chunks/init-PAX32BJA.js +0 -22
- package/dist/cli-bundle/chunks/next-BCERKDUR.js.map +0 -7
- package/dist/cli-bundle/chunks/next-FYTQF6HG.js.map +0 -1
- package/dist/cli-bundle/chunks/plan-4WXEQCTN.js.map +0 -7
- package/dist/cli-bundle/chunks/plan-RKZHQVXR.js.map +0 -1
- package/dist/cli-bundle/chunks/profile-4OMM32CD.js.map +0 -7
- package/dist/cli-bundle/chunks/profile-QLER5ENE.js.map +0 -1
- package/dist/cli-bundle/chunks/register-list-query-7LV46VAU.js +0 -400
- package/dist/cli-bundle/chunks/register-list-query-7LV46VAU.js.map +0 -1
- package/dist/cli-bundle/chunks/register-list-query-SNG5PGA4.js +0 -410
- package/dist/cli-bundle/chunks/register-list-query-SNG5PGA4.js.map +0 -7
- package/dist/cli-bundle/chunks/register-mutation-ENNDUAZG.js.map +0 -1
- package/dist/cli-bundle/chunks/register-mutation-KK7PHYYU.js.map +0 -7
- package/dist/cli-bundle/chunks/register-operations-C6OWLDSH.js +0 -486
- package/dist/cli-bundle/chunks/register-operations-C6OWLDSH.js.map +0 -1
- package/dist/cli-bundle/chunks/register-operations-HZ6XERQG.js.map +0 -7
- package/dist/cli-bundle/chunks/register-setup-P67EMBDJ.js.map +0 -1
- package/dist/cli-bundle/chunks/register-setup-ZKJ53RS7.js.map +0 -7
- package/dist/cli-bundle/chunks/restore-YALDGTQR.js +0 -17
- package/dist/cli-bundle/chunks/telemetry-BZUF6H7G.js.map +0 -1
- package/dist/cli-bundle/chunks/telemetry-GAHHQTOS.js.map +0 -7
- package/dist/cli-bundle/chunks/test-all-L2MDDUCA.js +0 -358
- package/dist/cli-bundle/chunks/test-all-L2MDDUCA.js.map +0 -1
- package/dist/cli-bundle/chunks/test-all-RBZ6DLU2.js +0 -361
- package/dist/cli-bundle/chunks/test-all-RBZ6DLU2.js.map +0 -7
- package/dist/cli-bundle/chunks/test-runs-PLRAZEFW.js.map +0 -7
- package/dist/cli-bundle/chunks/test-runs-UXIXPDKU.js.map +0 -1
- package/dist/cli-bundle/chunks/update-AMRSOIWP.js +0 -24
- package/dist/cli-bundle/chunks/validate-MTUVL5VC.js.map +0 -7
- package/dist/cli-bundle/chunks/validate-X5W76YJL.js.map +0 -1
- /package/dist/cli-bundle/chunks/{activity-DBLQVIP6.js.map → activity-5YWLSPGE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{activity-AW6XVFH6.js.map → activity-BIML7K2D.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{append-LOVPGVPT.js.map → append-EZESUVJT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{append-QCFJSDFL.js.map → append-JFG4WQMG.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-BHO33VBQ.js.map → chunk-3W2HJKJX.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-4DUVCVLZ.js.map → chunk-4PFVUUYV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-IJYK2T43.js.map → chunk-5E7N4T2Z.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-NKZAN7BD.js.map → chunk-5IZUBZBR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-NOLPZMSV.js.map → chunk-5JIWFWOZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-6AQZPVHQ.js.map → chunk-6CDCWKSX.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-LB6TP6MC.js.map → chunk-6NYJGMTY.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-SLMUGTK3.js.map → chunk-7CLLC75T.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-2466X6E4.js.map → chunk-7LYO6S5A.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-W4ULYO7Z.js.map → chunk-A3OQ7TFW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-GJ326ZKX.js.map → chunk-ER4ME5U2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-HZZEHPRG.js.map → chunk-ERG65A2L.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-L5OEFZQG.js.map → chunk-FH2H3CBD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-JPECA746.js.map → chunk-FUFFPIYA.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-27BXPBAY.js.map → chunk-GUYNUXQT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-MAUKX7JC.js.map → chunk-GVJE5T7Q.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-ZRP4VG5U.js.map → chunk-JBZ2NTKX.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-EJUDVUYZ.js.map → chunk-KEZOIPCR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-HYTGA6GI.js.map → chunk-MDHWWBNZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-N74UMG5H.js.map → chunk-MEZZZBYX.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-SDR5J34Q.js.map → chunk-NZ7QIUP5.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-DDPTYJIM.js.map → chunk-P5L7Z2O6.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-OGXLACHC.js.map → chunk-SU2E4AWU.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-HPPFYYOV.js.map → chunk-SU3TB4NS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-G7I7DEZA.js.map → chunk-T4XBKJ3F.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-VICZWYGZ.js.map → chunk-U5TR4N7J.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-EVMTBV6V.js.map → chunk-UMNQZS4H.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-A7FPBAQS.js.map → chunk-WJA3PXW7.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-QCWF6IYD.js.map → chunk-YSDFIY7N.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-UZIWVBE6.js.map → chunk-YSGTMDWC.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-64HNBBFQ.js.map → chunk-Z5F6XZXE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{chunk-AVEE5EHK.js.map → chunk-Z7RPDF2Y.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{claim-T3GCKV7C.js.map → claim-2BGBGSVI.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{claim-EMQLYPRW.js.map → claim-5GU7OE67.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-E6ZZGCJY.js.map → close-6YODBUIV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{close-TQYAAFJP.js.map → close-AFVKPR57.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{comments-YUXJRZPQ.js.map → comments-DNWHEBOS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{comments-QCKR2YY2.js.map → comments-RW23R3VE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{context-B6275WPX.js.map → context-OYXRGY7S.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{context-OCZ3FJ4Z.js.map → context-PKCEHQY3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{copy-DEM4DATS.js.map → copy-6XOR3HKN.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{copy-BTJOSKKT.js.map → copy-I4NCLBR4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{create-RLRJMRTM.js.map → create-PM45M6PE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{create-OH3BDGBB.js.map → create-Z7YCH3BE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{delete-BHK7VB2Z.js.map → delete-7YXHYG67.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{delete-LSVHAB4Y.js.map → delete-LPISP43T.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{deps-XHYXWEPT.js.map → deps-N2T26FME.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{deps-4UDFJRUA.js.map → deps-WATKQDE3.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{docs-GYZNVS26.js.map → docs-6LQ3GLM7.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{docs-26S44RXU.js.map → docs-Z3X6JZLT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{eval-UYF3ZPVO.js.map → eval-6DCLG52D.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{eval-PGRVIJQU.js.map → eval-Y3ZCUTHK.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{extension-CPSLJQVE.js.map → extension-BNINIMT7.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{extension-CXJ6RELI.js.map → extension-QWE4YYK5.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{files-NQHEXUZD.js.map → files-6SGKHS6O.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{files-MBNICQFM.js.map → files-FUYPSOFJ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{focus-DWYIG7GU.js.map → focus-2Q3K76G2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{focus-XZW5423D.js.map → focus-ATLBWXXD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{gc-LPVMKJQJ.js.map → gc-RUYEMNRF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{gc-7GQYOS2G.js.map → gc-TDWSDR66.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-WSZHY3PN.js.map → history-CGNQJXFE.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-NERDJNUB.js.map → history-SGRRS72R.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-redact-O3NBCNWC.js.map → history-redact-IRL7WJ3E.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{history-redact-MP6CH7U5.js.map → history-redact-N3JK7VBW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{init-64XDLQJB.js.map → init-4GESW7RM.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{init-PAX32BJA.js.map → init-XIDPABA4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{learnings-LPKCSJX7.js.map → learnings-4BHWFFUV.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{learnings-JR6MA5TI.js.map → learnings-4FJY7WIW.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{list-G5Y65VF6.js.map → list-472TIOVK.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{list-DNBJVO3E.js.map → list-D7BF5CL2.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{notes-2BMU3VVQ.js.map → notes-6KNOSLIT.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{notes-6KTGO2OJ.js.map → notes-W4UEICGF.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{restore-4MHZFJ2E.js.map → restore-44IHMMZZ.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{restore-YALDGTQR.js.map → restore-EXHU34EU.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{scheduling-shortcuts-LB6DOLRL.js.map → scheduling-shortcuts-OLHNLAZY.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{scheduling-shortcuts-UTOYKLXQ.js.map → scheduling-shortcuts-TSBK4AY5.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{schema-73HR2OJW.js.map → schema-IB2NZZWD.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{schema-U6LJKSIR.js.map → schema-VBCELP5G.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{search-AXPXKZER.js.map → search-7UBETML4.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{search-KILGT37L.js.map → search-R227JOL6.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{stats-W5KNVJWQ.js.map → stats-FAG7COEH.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{stats-PSYVVTNZ.js.map → stats-MCK7PLYS.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-PWDIZ255.js.map → test-QY44WAMR.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{test-ACD52Z3S.js.map → test-SY76DZTO.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-AMRSOIWP.js.map → update-A7EICML6.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{update-KFX2SIAC.js.map → update-IKJZWLQC.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{upgrade-BRL6KCOJ.js.map → upgrade-NWMR3VQL.js.map} +0 -0
- /package/dist/cli-bundle/chunks/{upgrade-HAK6JG2O.js.map → upgrade-VU46BLJZ.js.map} +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the pm dedupe audit command surface and its agent-facing runtime behavior.
|
|
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]="
|
|
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]="92659191-3659-5881-ae1a-f3751586ce41")}catch(e){}}();
|
|
8
8
|
import { isTerminalStatus, normalizeStatusInput } from "../../core/item/status.js";
|
|
9
9
|
import { resolveRuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
|
|
10
10
|
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
@@ -251,6 +251,47 @@ function compareClusters(left, right) {
|
|
|
251
251
|
}
|
|
252
252
|
return left.canonical.id.localeCompare(right.canonical.id);
|
|
253
253
|
}
|
|
254
|
+
function collectDedupeClusters(mode, prepared, fuzzyThreshold) {
|
|
255
|
+
if (mode === "title_exact") {
|
|
256
|
+
return collectExactTitleClusters(prepared);
|
|
257
|
+
}
|
|
258
|
+
if (mode === "parent_scope") {
|
|
259
|
+
return collectParentScopedClusters(prepared);
|
|
260
|
+
}
|
|
261
|
+
return collectFuzzyTitleClusters(prepared, fuzzyThreshold);
|
|
262
|
+
}
|
|
263
|
+
function toPreparedDedupeCandidate(item) {
|
|
264
|
+
return {
|
|
265
|
+
id: item.id,
|
|
266
|
+
title: item.title,
|
|
267
|
+
type: item.type,
|
|
268
|
+
status: item.status,
|
|
269
|
+
parent: item.parent ?? null,
|
|
270
|
+
priority: item.priority,
|
|
271
|
+
created_at: item.created_at,
|
|
272
|
+
updated_at: item.updated_at,
|
|
273
|
+
normalized_title: normalizeLowercaseWhitespace(item.title),
|
|
274
|
+
title_tokens: tokenizeAlphaNumeric(item.title),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function buildDedupeAuditFilters(params) {
|
|
278
|
+
return {
|
|
279
|
+
mode: params.mode,
|
|
280
|
+
status: params.status ?? null,
|
|
281
|
+
type: params.options.type ?? null,
|
|
282
|
+
tag: params.options.tag ?? null,
|
|
283
|
+
priority: params.options.priority ?? null,
|
|
284
|
+
deadline_before: params.options.deadlineBefore ?? null,
|
|
285
|
+
deadline_after: params.options.deadlineAfter ?? null,
|
|
286
|
+
assignee: params.options.assignee ?? null,
|
|
287
|
+
assignee_filter: params.options.assigneeFilter ?? null,
|
|
288
|
+
parent: params.options.parent ?? null,
|
|
289
|
+
sprint: params.options.sprint ?? null,
|
|
290
|
+
release: params.options.release ?? null,
|
|
291
|
+
limit: params.limit ?? null,
|
|
292
|
+
threshold: params.mode === "title_fuzzy" ? params.fuzzyThreshold : null,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
254
295
|
/**
|
|
255
296
|
* Implements run dedupe audit for the public runtime surface of this module.
|
|
256
297
|
*/
|
|
@@ -267,27 +308,8 @@ export async function runDedupeAudit(options, global) {
|
|
|
267
308
|
const threshold = parseThreshold(options.threshold);
|
|
268
309
|
const fuzzyThreshold = threshold ?? 0.8;
|
|
269
310
|
const listed = await runList(status, buildListQueryFilters(options), global);
|
|
270
|
-
const prepared = listed.items.map((item) => (
|
|
271
|
-
|
|
272
|
-
title: item.title,
|
|
273
|
-
type: item.type,
|
|
274
|
-
status: item.status,
|
|
275
|
-
parent: item.parent ?? null,
|
|
276
|
-
priority: item.priority,
|
|
277
|
-
created_at: item.created_at,
|
|
278
|
-
updated_at: item.updated_at,
|
|
279
|
-
normalized_title: normalizeLowercaseWhitespace(item.title),
|
|
280
|
-
title_tokens: tokenizeAlphaNumeric(item.title),
|
|
281
|
-
}));
|
|
282
|
-
const clusters = (() => {
|
|
283
|
-
if (mode === "title_exact") {
|
|
284
|
-
return collectExactTitleClusters(prepared);
|
|
285
|
-
}
|
|
286
|
-
if (mode === "parent_scope") {
|
|
287
|
-
return collectParentScopedClusters(prepared);
|
|
288
|
-
}
|
|
289
|
-
return collectFuzzyTitleClusters(prepared, fuzzyThreshold);
|
|
290
|
-
})();
|
|
311
|
+
const prepared = listed.items.map((item) => toPreparedDedupeCandidate(item));
|
|
312
|
+
const clusters = collectDedupeClusters(mode, prepared, fuzzyThreshold);
|
|
291
313
|
const sortedClusters = clusters.sort(compareClusters);
|
|
292
314
|
const limitedClusters = limit === undefined ? sortedClusters : sortedClusters.slice(0, limit);
|
|
293
315
|
const duplicateCandidates = limitedClusters.reduce((total, cluster) => total + cluster.cluster_size, 0);
|
|
@@ -303,22 +325,7 @@ export async function runDedupeAudit(options, global) {
|
|
|
303
325
|
duplicate_candidates: duplicateCandidates,
|
|
304
326
|
merge_suggestions: mergeSuggestions,
|
|
305
327
|
},
|
|
306
|
-
filters: {
|
|
307
|
-
mode,
|
|
308
|
-
status: status ?? null,
|
|
309
|
-
type: options.type ?? null,
|
|
310
|
-
tag: options.tag ?? null,
|
|
311
|
-
priority: options.priority ?? null,
|
|
312
|
-
deadline_before: options.deadlineBefore ?? null,
|
|
313
|
-
deadline_after: options.deadlineAfter ?? null,
|
|
314
|
-
assignee: options.assignee ?? null,
|
|
315
|
-
assignee_filter: options.assigneeFilter ?? null,
|
|
316
|
-
parent: options.parent ?? null,
|
|
317
|
-
sprint: options.sprint ?? null,
|
|
318
|
-
release: options.release ?? null,
|
|
319
|
-
limit: limit ?? null,
|
|
320
|
-
threshold: mode === "title_fuzzy" ? fuzzyThreshold : null,
|
|
321
|
-
},
|
|
328
|
+
filters: buildDedupeAuditFilters({ mode, status, options, limit, fuzzyThreshold }),
|
|
322
329
|
now: nowIso(),
|
|
323
330
|
/* c8 ignore next -- warnings are omitted when undefined to keep stable result payloads. */
|
|
324
331
|
...(warnings ? { warnings } : {}),
|
|
@@ -336,4 +343,4 @@ export const _testOnly = {
|
|
|
336
343
|
toMergeSuggestion,
|
|
337
344
|
};
|
|
338
345
|
//# sourceMappingURL=dedupe-audit.js.map
|
|
339
|
-
//# debugId=
|
|
346
|
+
//# debugId=92659191-3659-5881-ae1a-f3751586ce41
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dedupe-audit.js","sources":["cli/commands/dedupe-audit.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module cli/commands/dedupe-audit\n *\n * Implements the pm dedupe audit command surface and its agent-facing runtime behavior.\n */\nimport { isTerminalStatus, normalizeStatusInput } from \"../../core/item/status.js\";\nimport { resolveRuntimeStatusRegistry, type RuntimeStatusRegistry } from \"../../core/schema/runtime-schema.js\";\nimport { EXIT_CODE } from \"../../core/shared/constants.js\";\nimport type { GlobalOptions } from \"../../core/shared/command-types.js\";\nimport { PmCliError } from \"../../core/shared/errors.js\";\nimport { compareTimestampStrings, nowIso } from \"../../core/shared/time.js\";\nimport { jaccardSimilarity, normalizeLowercaseWhitespace, tokenizeAlphaNumeric } from \"../../core/shared/text-normalization.js\";\nimport { resolvePmRoot } from \"../../core/store/paths.js\";\nimport { readSettings } from \"../../core/store/settings.js\";\nimport { type ItemStatus } from \"../../types/index.js\";\nimport { parseIntegerLimit } from \"../shared-parsers.js\";\nimport { buildListQueryFilters } from \"./list-filter-shared.js\";\nimport { runList } from \"./list.js\";\n\nexport const DEDUPE_AUDIT_MODES = [\"title_exact\", \"title_fuzzy\", \"parent_scope\"] as const;\n\n/**\n * Restricts dedupe audit mode values accepted by command, SDK, and storage contracts.\n */\nexport type DedupeAuditMode = (typeof DEDUPE_AUDIT_MODES)[number];\n\ninterface DedupeAuditPreparedCandidate {\n id: string;\n title: string;\n type: string;\n status: ItemStatus;\n parent: string | null;\n priority: number;\n created_at: string;\n updated_at: string;\n normalized_title: string;\n title_tokens: string[];\n}\n\n/**\n * Documents the dedupe audit candidate payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditCandidate {\n id: string;\n title: string;\n type: string;\n status: ItemStatus;\n parent: string | null;\n priority: number;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * Documents the dedupe merge suggestion payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeSuggestion {\n duplicate_id: string;\n canonical_id: string;\n suggested_close_reason: string;\n suggested_message: string;\n suggested_command: string;\n}\n\n/**\n * Documents the dedupe audit cluster payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditCluster {\n mode: DedupeAuditMode;\n key: string;\n match_reason: string;\n cluster_size: number;\n canonical: DedupeAuditCandidate;\n duplicates: DedupeAuditCandidate[];\n merge_suggestions: DedupeMergeSuggestion[];\n similarity?: {\n metric: \"token_jaccard\";\n threshold: number;\n min: number;\n max: number;\n };\n}\n\n/**\n * Documents the dedupe audit options payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditOptions {\n mode?: string;\n status?: string;\n type?: string;\n tag?: string;\n priority?: string;\n deadlineBefore?: string;\n deadlineAfter?: string;\n assignee?: string;\n assigneeFilter?: string;\n parent?: string;\n sprint?: string;\n release?: string;\n limit?: string;\n threshold?: string;\n}\n\n/**\n * Documents the dedupe audit result payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditResult {\n mode: DedupeAuditMode;\n clusters: DedupeAuditCluster[];\n count: number;\n totals: {\n items_considered: number;\n duplicate_candidates: number;\n merge_suggestions: number;\n };\n filters: {\n mode: DedupeAuditMode;\n status: ItemStatus | null;\n type: string | null;\n tag: string | null;\n priority: string | null;\n deadline_before: string | null;\n deadline_after: string | null;\n assignee: string | null;\n assignee_filter: string | null;\n parent: string | null;\n sprint: string | null;\n release: string | null;\n limit: number | null;\n threshold: number | null;\n };\n now: string;\n warnings?: string[];\n}\n\nfunction parseMode(raw: string | undefined): DedupeAuditMode {\n const normalized = (raw ?? \"title_exact\").trim().toLowerCase();\n if (!DEDUPE_AUDIT_MODES.includes(normalized as DedupeAuditMode)) {\n throw new PmCliError(`Dedupe audit mode must be one of ${DEDUPE_AUDIT_MODES.join(\"|\")}`, EXIT_CODE.USAGE);\n }\n return normalized as DedupeAuditMode;\n}\n\nlet dedupeAllowedStatuses = new Set<string>([\"draft\", \"open\", \"in_progress\", \"blocked\", \"closed\", \"canceled\"]);\nlet dedupeTerminalStatuses = new Set<string>([\"closed\", \"canceled\"]);\nlet dedupeStatusRegistry: RuntimeStatusRegistry | null = null;\n\nfunction parseStatus(raw: string | undefined): ItemStatus | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const normalized = raw.trim().toLowerCase().replaceAll(\"-\", \"_\");\n if (!dedupeAllowedStatuses.has(normalized)) {\n throw new PmCliError(`Status filter must be one of ${[...dedupeAllowedStatuses].join(\"|\")}`, EXIT_CODE.USAGE);\n }\n return normalized as ItemStatus;\n}\n\nfunction parseThreshold(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {\n throw new PmCliError(\"--threshold must be a number between 0 and 1\", EXIT_CODE.USAGE);\n }\n return parsed;\n}\n\nfunction isTerminal(status: ItemStatus): boolean {\n if (dedupeStatusRegistry) {\n return isTerminalStatus(status, dedupeStatusRegistry);\n }\n const normalized = normalizeStatusInput(status) ?? status;\n return dedupeTerminalStatuses.has(normalized);\n}\n\nfunction compareCandidates(left: DedupeAuditPreparedCandidate, right: DedupeAuditPreparedCandidate): number {\n const leftTerminal = isTerminal(left.status);\n const rightTerminal = isTerminal(right.status);\n if (leftTerminal !== rightTerminal) {\n return leftTerminal ? 1 : -1;\n }\n const byPriority = left.priority - right.priority;\n if (byPriority !== 0) {\n return byPriority;\n }\n const byUpdated = compareTimestampStrings(right.updated_at, left.updated_at);\n if (byUpdated !== 0) {\n return byUpdated;\n }\n return left.id.localeCompare(right.id);\n}\n\nfunction toCandidate(candidate: DedupeAuditPreparedCandidate): DedupeAuditCandidate {\n return {\n id: candidate.id,\n title: candidate.title,\n type: candidate.type,\n status: candidate.status,\n parent: candidate.parent,\n priority: candidate.priority,\n created_at: candidate.created_at,\n updated_at: candidate.updated_at,\n };\n}\n\nfunction toMergeSuggestion(duplicate: DedupeAuditPreparedCandidate, canonical: DedupeAuditPreparedCandidate, mode: DedupeAuditMode): DedupeMergeSuggestion {\n const closeReason = `Duplicate of ${canonical.id}`;\n const message = `Close ${duplicate.id} as duplicate of ${canonical.id} from pm dedupe-audit (${mode}).`;\n const escapedReason = closeReason.replaceAll('\"', '\\\\\"');\n const escapedMessage = message.replaceAll('\"', '\\\\\"');\n return {\n duplicate_id: duplicate.id,\n canonical_id: canonical.id,\n suggested_close_reason: closeReason,\n suggested_message: message,\n suggested_command: `pm close ${duplicate.id} \"${escapedReason}\" --message \"${escapedMessage}\"`,\n };\n}\n\nfunction clusterFromMembers(\n mode: DedupeAuditMode,\n key: string,\n members: DedupeAuditPreparedCandidate[],\n matchReason: string,\n threshold: number | undefined,\n): DedupeAuditCluster {\n const sortedMembers = [...members].sort(compareCandidates);\n const canonical = sortedMembers[0];\n const duplicates = sortedMembers.slice(1);\n const mergeSuggestions = duplicates.map((candidate) => toMergeSuggestion(candidate, canonical, mode));\n const cluster: DedupeAuditCluster = {\n mode,\n key,\n match_reason: matchReason,\n cluster_size: sortedMembers.length,\n canonical: toCandidate(canonical),\n duplicates: duplicates.map((candidate) => toCandidate(candidate)),\n merge_suggestions: mergeSuggestions,\n };\n if (mode === \"title_fuzzy\") {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n for (let leftIndex = 0; leftIndex < sortedMembers.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < sortedMembers.length; rightIndex += 1) {\n const score = similarityScore(sortedMembers[leftIndex], sortedMembers[rightIndex]);\n min = Math.min(min, score);\n max = Math.max(max, score);\n }\n }\n if (!Number.isFinite(min)) {\n min = 1;\n }\n if (!Number.isFinite(max)) {\n max = 1;\n }\n cluster.similarity = {\n metric: \"token_jaccard\",\n threshold: threshold ?? 0.8,\n min,\n max,\n };\n }\n return cluster;\n}\n\nfunction similarityScore(left: DedupeAuditPreparedCandidate, right: DedupeAuditPreparedCandidate): number {\n if (left.normalized_title === right.normalized_title) {\n return 1;\n }\n return jaccardSimilarity(left.title_tokens, right.title_tokens);\n}\n\nfunction collectExactTitleClusters(items: DedupeAuditPreparedCandidate[]): DedupeAuditCluster[] {\n const byTitle = new Map<string, DedupeAuditPreparedCandidate[]>();\n for (const item of items) {\n if (item.normalized_title.length === 0) {\n continue;\n }\n const existing = byTitle.get(item.normalized_title);\n if (existing) {\n existing.push(item);\n } else {\n byTitle.set(item.normalized_title, [item]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const [title, members] of byTitle.entries()) {\n if (members.length <= 1) {\n continue;\n }\n clusters.push(clusterFromMembers(\"title_exact\", title, members, \"exact_normalized_title_match\", undefined));\n }\n return clusters;\n}\n\nfunction collectParentScopedClusters(items: DedupeAuditPreparedCandidate[]): DedupeAuditCluster[] {\n const byParentAndTitle = new Map<string, DedupeAuditPreparedCandidate[]>();\n for (const item of items) {\n if (!item.parent || item.normalized_title.length === 0) {\n continue;\n }\n const key = `${item.parent}|${item.normalized_title}`;\n const existing = byParentAndTitle.get(key);\n if (existing) {\n existing.push(item);\n } else {\n byParentAndTitle.set(key, [item]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const [key, members] of byParentAndTitle.entries()) {\n if (members.length <= 1) {\n continue;\n }\n clusters.push(clusterFromMembers(\"parent_scope\", key, members, \"same_parent_and_exact_normalized_title\", undefined));\n }\n return clusters;\n}\n\nfunction findRoot(parents: number[], index: number): number {\n let current = index;\n while (parents[current] !== current) {\n parents[current] = parents[parents[current]];\n current = parents[current];\n }\n return current;\n}\n\nfunction unionRoots(parents: number[], left: number, right: number): void {\n const leftRoot = findRoot(parents, left);\n const rightRoot = findRoot(parents, right);\n if (leftRoot === rightRoot) {\n return;\n }\n if (leftRoot < rightRoot) {\n parents[rightRoot] = leftRoot;\n } else {\n parents[leftRoot] = rightRoot;\n }\n}\n\nfunction collectFuzzyTitleClusters(items: DedupeAuditPreparedCandidate[], threshold: number): DedupeAuditCluster[] {\n if (items.length <= 1) {\n return [];\n }\n const parents = items.map((_item, index) => index);\n for (let leftIndex = 0; leftIndex < items.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < items.length; rightIndex += 1) {\n const score = similarityScore(items[leftIndex], items[rightIndex]);\n if (score >= threshold) {\n unionRoots(parents, leftIndex, rightIndex);\n }\n }\n }\n const groupedIndices = new Map<number, number[]>();\n for (let index = 0; index < items.length; index += 1) {\n const root = findRoot(parents, index);\n const existing = groupedIndices.get(root);\n if (existing) {\n existing.push(index);\n } else {\n groupedIndices.set(root, [index]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const indices of groupedIndices.values()) {\n if (indices.length <= 1) {\n continue;\n }\n const members = indices.map((index) => items[index]);\n /* c8 ignore next -- clusters with indices.length > 1 always produce at least one member id. */\n const canonicalKey = [...members].sort(compareCandidates)[0]?.id ?? `cluster-${clusters.length + 1}`;\n clusters.push(\n clusterFromMembers(\"title_fuzzy\", canonicalKey, members, \"title_token_jaccard_above_threshold\", threshold),\n );\n }\n return clusters;\n}\n\nfunction compareClusters(left: DedupeAuditCluster, right: DedupeAuditCluster): number {\n const bySize = right.cluster_size - left.cluster_size;\n if (bySize !== 0) {\n return bySize;\n }\n return left.canonical.id.localeCompare(right.canonical.id);\n}\n\n/**\n * Implements run dedupe audit for the public runtime surface of this module.\n */\nexport async function runDedupeAudit(options: DedupeAuditOptions, global: GlobalOptions): Promise<DedupeAuditResult> {\n const pmRoot = resolvePmRoot(process.cwd(), global.path);\n const settings = await readSettings(pmRoot);\n const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);\n dedupeStatusRegistry = statusRegistry;\n dedupeAllowedStatuses = new Set(statusRegistry.definitions.map((definition) => definition.id));\n dedupeTerminalStatuses = new Set(statusRegistry.terminal_statuses);\n const mode = parseMode(options.mode);\n const status = parseStatus(options.status);\n const limit = parseIntegerLimit(options.limit);\n const threshold = parseThreshold(options.threshold);\n const fuzzyThreshold = threshold ?? 0.8;\n\n const listed = await runList(\n status,\n buildListQueryFilters(options),\n global,\n );\n\n const prepared: DedupeAuditPreparedCandidate[] = listed.items.map((item) => ({\n id: item.id,\n title: item.title,\n type: item.type,\n status: item.status,\n parent: item.parent ?? null,\n priority: item.priority,\n created_at: item.created_at,\n updated_at: item.updated_at,\n normalized_title: normalizeLowercaseWhitespace(item.title),\n title_tokens: tokenizeAlphaNumeric(item.title),\n }));\n\n const clusters = (() => {\n if (mode === \"title_exact\") {\n return collectExactTitleClusters(prepared);\n }\n if (mode === \"parent_scope\") {\n return collectParentScopedClusters(prepared);\n }\n return collectFuzzyTitleClusters(prepared, fuzzyThreshold);\n })();\n\n const sortedClusters = clusters.sort(compareClusters);\n const limitedClusters = limit === undefined ? sortedClusters : sortedClusters.slice(0, limit);\n const duplicateCandidates = limitedClusters.reduce((total, cluster) => total + cluster.cluster_size, 0);\n const mergeSuggestions = limitedClusters.reduce((total, cluster) => total + cluster.merge_suggestions.length, 0);\n /* c8 ignore next -- list warnings are normalized upstream in command-level tests. */\n const warnings = listed.warnings && listed.warnings.length > 0 ? listed.warnings : undefined;\n\n return {\n mode,\n clusters: limitedClusters,\n count: limitedClusters.length,\n totals: {\n items_considered: prepared.length,\n duplicate_candidates: duplicateCandidates,\n merge_suggestions: mergeSuggestions,\n },\n filters: {\n mode,\n status: status ?? null,\n type: options.type ?? null,\n tag: options.tag ?? null,\n priority: options.priority ?? null,\n deadline_before: options.deadlineBefore ?? null,\n deadline_after: options.deadlineAfter ?? null,\n assignee: options.assignee ?? null,\n assignee_filter: options.assigneeFilter ?? null,\n parent: options.parent ?? null,\n sprint: options.sprint ?? null,\n release: options.release ?? null,\n limit: limit ?? null,\n threshold: mode === \"title_fuzzy\" ? fuzzyThreshold : null,\n },\n now: nowIso(),\n /* c8 ignore next -- warnings are omitted when undefined to keep stable result payloads. */\n ...(warnings ? { warnings } : {}),\n };\n}\n\nexport const _testOnly = {\n parseMode,\n parseStatus,\n parseThreshold,\n compareCandidates,\n collectExactTitleClusters,\n collectParentScopedClusters,\n collectFuzzyTitleClusters,\n clusterFromMembers,\n toMergeSuggestion,\n};\n"],"names":[],"mappings":"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","debugId":"e8a2ed4b-94a3-5fbc-83a1-44ceceae8ccb"}
|
|
1
|
+
{"version":3,"file":"dedupe-audit.js","sources":["cli/commands/dedupe-audit.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module cli/commands/dedupe-audit\n *\n * Implements the pm dedupe audit command surface and its agent-facing runtime behavior.\n */\nimport { isTerminalStatus, normalizeStatusInput } from \"../../core/item/status.js\";\nimport { resolveRuntimeStatusRegistry, type RuntimeStatusRegistry } from \"../../core/schema/runtime-schema.js\";\nimport { EXIT_CODE } from \"../../core/shared/constants.js\";\nimport type { GlobalOptions } from \"../../core/shared/command-types.js\";\nimport { PmCliError } from \"../../core/shared/errors.js\";\nimport { compareTimestampStrings, nowIso } from \"../../core/shared/time.js\";\nimport { jaccardSimilarity, normalizeLowercaseWhitespace, tokenizeAlphaNumeric } from \"../../core/shared/text-normalization.js\";\nimport { resolvePmRoot } from \"../../core/store/paths.js\";\nimport { readSettings } from \"../../core/store/settings.js\";\nimport { type ItemStatus } from \"../../types/index.js\";\nimport { parseIntegerLimit } from \"../shared-parsers.js\";\nimport { buildListQueryFilters } from \"./list-filter-shared.js\";\nimport { runList } from \"./list.js\";\n\nexport const DEDUPE_AUDIT_MODES = [\"title_exact\", \"title_fuzzy\", \"parent_scope\"] as const;\n\n/**\n * Restricts dedupe audit mode values accepted by command, SDK, and storage contracts.\n */\nexport type DedupeAuditMode = (typeof DEDUPE_AUDIT_MODES)[number];\n\ninterface DedupeAuditPreparedCandidate {\n id: string;\n title: string;\n type: string;\n status: ItemStatus;\n parent: string | null;\n priority: number;\n created_at: string;\n updated_at: string;\n normalized_title: string;\n title_tokens: string[];\n}\n\n/**\n * Documents the dedupe audit candidate payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditCandidate {\n id: string;\n title: string;\n type: string;\n status: ItemStatus;\n parent: string | null;\n priority: number;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * Documents the dedupe merge suggestion payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeSuggestion {\n duplicate_id: string;\n canonical_id: string;\n suggested_close_reason: string;\n suggested_message: string;\n suggested_command: string;\n}\n\n/**\n * Documents the dedupe audit cluster payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditCluster {\n mode: DedupeAuditMode;\n key: string;\n match_reason: string;\n cluster_size: number;\n canonical: DedupeAuditCandidate;\n duplicates: DedupeAuditCandidate[];\n merge_suggestions: DedupeMergeSuggestion[];\n similarity?: {\n metric: \"token_jaccard\";\n threshold: number;\n min: number;\n max: number;\n };\n}\n\n/**\n * Documents the dedupe audit options payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditOptions {\n mode?: string;\n status?: string;\n type?: string;\n tag?: string;\n priority?: string;\n deadlineBefore?: string;\n deadlineAfter?: string;\n assignee?: string;\n assigneeFilter?: string;\n parent?: string;\n sprint?: string;\n release?: string;\n limit?: string;\n threshold?: string;\n}\n\n/**\n * Documents the dedupe audit result payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeAuditResult {\n mode: DedupeAuditMode;\n clusters: DedupeAuditCluster[];\n count: number;\n totals: {\n items_considered: number;\n duplicate_candidates: number;\n merge_suggestions: number;\n };\n filters: {\n mode: DedupeAuditMode;\n status: ItemStatus | null;\n type: string | null;\n tag: string | null;\n priority: string | null;\n deadline_before: string | null;\n deadline_after: string | null;\n assignee: string | null;\n assignee_filter: string | null;\n parent: string | null;\n sprint: string | null;\n release: string | null;\n limit: number | null;\n threshold: number | null;\n };\n now: string;\n warnings?: string[];\n}\n\nfunction parseMode(raw: string | undefined): DedupeAuditMode {\n const normalized = (raw ?? \"title_exact\").trim().toLowerCase();\n if (!DEDUPE_AUDIT_MODES.includes(normalized as DedupeAuditMode)) {\n throw new PmCliError(`Dedupe audit mode must be one of ${DEDUPE_AUDIT_MODES.join(\"|\")}`, EXIT_CODE.USAGE);\n }\n return normalized as DedupeAuditMode;\n}\n\nlet dedupeAllowedStatuses = new Set<string>([\"draft\", \"open\", \"in_progress\", \"blocked\", \"closed\", \"canceled\"]);\nlet dedupeTerminalStatuses = new Set<string>([\"closed\", \"canceled\"]);\nlet dedupeStatusRegistry: RuntimeStatusRegistry | null = null;\n\nfunction parseStatus(raw: string | undefined): ItemStatus | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const normalized = raw.trim().toLowerCase().replaceAll(\"-\", \"_\");\n if (!dedupeAllowedStatuses.has(normalized)) {\n throw new PmCliError(`Status filter must be one of ${[...dedupeAllowedStatuses].join(\"|\")}`, EXIT_CODE.USAGE);\n }\n return normalized as ItemStatus;\n}\n\nfunction parseThreshold(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {\n throw new PmCliError(\"--threshold must be a number between 0 and 1\", EXIT_CODE.USAGE);\n }\n return parsed;\n}\n\nfunction isTerminal(status: ItemStatus): boolean {\n if (dedupeStatusRegistry) {\n return isTerminalStatus(status, dedupeStatusRegistry);\n }\n const normalized = normalizeStatusInput(status) ?? status;\n return dedupeTerminalStatuses.has(normalized);\n}\n\nfunction compareCandidates(left: DedupeAuditPreparedCandidate, right: DedupeAuditPreparedCandidate): number {\n const leftTerminal = isTerminal(left.status);\n const rightTerminal = isTerminal(right.status);\n if (leftTerminal !== rightTerminal) {\n return leftTerminal ? 1 : -1;\n }\n const byPriority = left.priority - right.priority;\n if (byPriority !== 0) {\n return byPriority;\n }\n const byUpdated = compareTimestampStrings(right.updated_at, left.updated_at);\n if (byUpdated !== 0) {\n return byUpdated;\n }\n return left.id.localeCompare(right.id);\n}\n\nfunction toCandidate(candidate: DedupeAuditPreparedCandidate): DedupeAuditCandidate {\n return {\n id: candidate.id,\n title: candidate.title,\n type: candidate.type,\n status: candidate.status,\n parent: candidate.parent,\n priority: candidate.priority,\n created_at: candidate.created_at,\n updated_at: candidate.updated_at,\n };\n}\n\nfunction toMergeSuggestion(duplicate: DedupeAuditPreparedCandidate, canonical: DedupeAuditPreparedCandidate, mode: DedupeAuditMode): DedupeMergeSuggestion {\n const closeReason = `Duplicate of ${canonical.id}`;\n const message = `Close ${duplicate.id} as duplicate of ${canonical.id} from pm dedupe-audit (${mode}).`;\n const escapedReason = closeReason.replaceAll('\"', '\\\\\"');\n const escapedMessage = message.replaceAll('\"', '\\\\\"');\n return {\n duplicate_id: duplicate.id,\n canonical_id: canonical.id,\n suggested_close_reason: closeReason,\n suggested_message: message,\n suggested_command: `pm close ${duplicate.id} \"${escapedReason}\" --message \"${escapedMessage}\"`,\n };\n}\n\nfunction clusterFromMembers(\n mode: DedupeAuditMode,\n key: string,\n members: DedupeAuditPreparedCandidate[],\n matchReason: string,\n threshold: number | undefined,\n): DedupeAuditCluster {\n const sortedMembers = [...members].sort(compareCandidates);\n const canonical = sortedMembers[0];\n const duplicates = sortedMembers.slice(1);\n const mergeSuggestions = duplicates.map((candidate) => toMergeSuggestion(candidate, canonical, mode));\n const cluster: DedupeAuditCluster = {\n mode,\n key,\n match_reason: matchReason,\n cluster_size: sortedMembers.length,\n canonical: toCandidate(canonical),\n duplicates: duplicates.map((candidate) => toCandidate(candidate)),\n merge_suggestions: mergeSuggestions,\n };\n if (mode === \"title_fuzzy\") {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n for (let leftIndex = 0; leftIndex < sortedMembers.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < sortedMembers.length; rightIndex += 1) {\n const score = similarityScore(sortedMembers[leftIndex], sortedMembers[rightIndex]);\n min = Math.min(min, score);\n max = Math.max(max, score);\n }\n }\n if (!Number.isFinite(min)) {\n min = 1;\n }\n if (!Number.isFinite(max)) {\n max = 1;\n }\n cluster.similarity = {\n metric: \"token_jaccard\",\n threshold: threshold ?? 0.8,\n min,\n max,\n };\n }\n return cluster;\n}\n\nfunction similarityScore(left: DedupeAuditPreparedCandidate, right: DedupeAuditPreparedCandidate): number {\n if (left.normalized_title === right.normalized_title) {\n return 1;\n }\n return jaccardSimilarity(left.title_tokens, right.title_tokens);\n}\n\nfunction collectExactTitleClusters(items: DedupeAuditPreparedCandidate[]): DedupeAuditCluster[] {\n const byTitle = new Map<string, DedupeAuditPreparedCandidate[]>();\n for (const item of items) {\n if (item.normalized_title.length === 0) {\n continue;\n }\n const existing = byTitle.get(item.normalized_title);\n if (existing) {\n existing.push(item);\n } else {\n byTitle.set(item.normalized_title, [item]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const [title, members] of byTitle.entries()) {\n if (members.length <= 1) {\n continue;\n }\n clusters.push(clusterFromMembers(\"title_exact\", title, members, \"exact_normalized_title_match\", undefined));\n }\n return clusters;\n}\n\nfunction collectParentScopedClusters(items: DedupeAuditPreparedCandidate[]): DedupeAuditCluster[] {\n const byParentAndTitle = new Map<string, DedupeAuditPreparedCandidate[]>();\n for (const item of items) {\n if (!item.parent || item.normalized_title.length === 0) {\n continue;\n }\n const key = `${item.parent}|${item.normalized_title}`;\n const existing = byParentAndTitle.get(key);\n if (existing) {\n existing.push(item);\n } else {\n byParentAndTitle.set(key, [item]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const [key, members] of byParentAndTitle.entries()) {\n if (members.length <= 1) {\n continue;\n }\n clusters.push(clusterFromMembers(\"parent_scope\", key, members, \"same_parent_and_exact_normalized_title\", undefined));\n }\n return clusters;\n}\n\nfunction findRoot(parents: number[], index: number): number {\n let current = index;\n while (parents[current] !== current) {\n parents[current] = parents[parents[current]];\n current = parents[current];\n }\n return current;\n}\n\nfunction unionRoots(parents: number[], left: number, right: number): void {\n const leftRoot = findRoot(parents, left);\n const rightRoot = findRoot(parents, right);\n if (leftRoot === rightRoot) {\n return;\n }\n if (leftRoot < rightRoot) {\n parents[rightRoot] = leftRoot;\n } else {\n parents[leftRoot] = rightRoot;\n }\n}\n\nfunction collectFuzzyTitleClusters(items: DedupeAuditPreparedCandidate[], threshold: number): DedupeAuditCluster[] {\n if (items.length <= 1) {\n return [];\n }\n const parents = items.map((_item, index) => index);\n for (let leftIndex = 0; leftIndex < items.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < items.length; rightIndex += 1) {\n const score = similarityScore(items[leftIndex], items[rightIndex]);\n if (score >= threshold) {\n unionRoots(parents, leftIndex, rightIndex);\n }\n }\n }\n const groupedIndices = new Map<number, number[]>();\n for (let index = 0; index < items.length; index += 1) {\n const root = findRoot(parents, index);\n const existing = groupedIndices.get(root);\n if (existing) {\n existing.push(index);\n } else {\n groupedIndices.set(root, [index]);\n }\n }\n const clusters: DedupeAuditCluster[] = [];\n for (const indices of groupedIndices.values()) {\n if (indices.length <= 1) {\n continue;\n }\n const members = indices.map((index) => items[index]);\n /* c8 ignore next -- clusters with indices.length > 1 always produce at least one member id. */\n const canonicalKey = [...members].sort(compareCandidates)[0]?.id ?? `cluster-${clusters.length + 1}`;\n clusters.push(\n clusterFromMembers(\"title_fuzzy\", canonicalKey, members, \"title_token_jaccard_above_threshold\", threshold),\n );\n }\n return clusters;\n}\n\nfunction compareClusters(left: DedupeAuditCluster, right: DedupeAuditCluster): number {\n const bySize = right.cluster_size - left.cluster_size;\n if (bySize !== 0) {\n return bySize;\n }\n return left.canonical.id.localeCompare(right.canonical.id);\n}\n\nfunction collectDedupeClusters(\n mode: DedupeAuditMode,\n prepared: DedupeAuditPreparedCandidate[],\n fuzzyThreshold: number,\n): DedupeAuditCluster[] {\n if (mode === \"title_exact\") {\n return collectExactTitleClusters(prepared);\n }\n if (mode === \"parent_scope\") {\n return collectParentScopedClusters(prepared);\n }\n return collectFuzzyTitleClusters(prepared, fuzzyThreshold);\n}\n\nfunction toPreparedDedupeCandidate(item: Awaited<ReturnType<typeof runList>>[\"items\"][number]): DedupeAuditPreparedCandidate {\n return {\n id: item.id,\n title: item.title,\n type: item.type,\n status: item.status,\n parent: item.parent ?? null,\n priority: item.priority,\n created_at: item.created_at,\n updated_at: item.updated_at,\n normalized_title: normalizeLowercaseWhitespace(item.title),\n title_tokens: tokenizeAlphaNumeric(item.title),\n };\n}\n\nfunction buildDedupeAuditFilters(params: {\n mode: DedupeAuditMode;\n status: ItemStatus | undefined;\n options: DedupeAuditOptions;\n limit: number | undefined;\n fuzzyThreshold: number;\n}): DedupeAuditResult[\"filters\"] {\n return {\n mode: params.mode,\n status: params.status ?? null,\n type: params.options.type ?? null,\n tag: params.options.tag ?? null,\n priority: params.options.priority ?? null,\n deadline_before: params.options.deadlineBefore ?? null,\n deadline_after: params.options.deadlineAfter ?? null,\n assignee: params.options.assignee ?? null,\n assignee_filter: params.options.assigneeFilter ?? null,\n parent: params.options.parent ?? null,\n sprint: params.options.sprint ?? null,\n release: params.options.release ?? null,\n limit: params.limit ?? null,\n threshold: params.mode === \"title_fuzzy\" ? params.fuzzyThreshold : null,\n };\n}\n\n/**\n * Implements run dedupe audit for the public runtime surface of this module.\n */\nexport async function runDedupeAudit(options: DedupeAuditOptions, global: GlobalOptions): Promise<DedupeAuditResult> {\n const pmRoot = resolvePmRoot(process.cwd(), global.path);\n const settings = await readSettings(pmRoot);\n const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);\n dedupeStatusRegistry = statusRegistry;\n dedupeAllowedStatuses = new Set(statusRegistry.definitions.map((definition) => definition.id));\n dedupeTerminalStatuses = new Set(statusRegistry.terminal_statuses);\n const mode = parseMode(options.mode);\n const status = parseStatus(options.status);\n const limit = parseIntegerLimit(options.limit);\n const threshold = parseThreshold(options.threshold);\n const fuzzyThreshold = threshold ?? 0.8;\n\n const listed = await runList(\n status,\n buildListQueryFilters(options),\n global,\n );\n\n const prepared = listed.items.map((item) => toPreparedDedupeCandidate(item));\n\n const clusters = collectDedupeClusters(mode, prepared, fuzzyThreshold);\n\n const sortedClusters = clusters.sort(compareClusters);\n const limitedClusters = limit === undefined ? sortedClusters : sortedClusters.slice(0, limit);\n const duplicateCandidates = limitedClusters.reduce((total, cluster) => total + cluster.cluster_size, 0);\n const mergeSuggestions = limitedClusters.reduce((total, cluster) => total + cluster.merge_suggestions.length, 0);\n /* c8 ignore next -- list warnings are normalized upstream in command-level tests. */\n const warnings = listed.warnings && listed.warnings.length > 0 ? listed.warnings : undefined;\n\n return {\n mode,\n clusters: limitedClusters,\n count: limitedClusters.length,\n totals: {\n items_considered: prepared.length,\n duplicate_candidates: duplicateCandidates,\n merge_suggestions: mergeSuggestions,\n },\n filters: buildDedupeAuditFilters({ mode, status, options, limit, fuzzyThreshold }),\n now: nowIso(),\n /* c8 ignore next -- warnings are omitted when undefined to keep stable result payloads. */\n ...(warnings ? { warnings } : {}),\n };\n}\n\nexport const _testOnly = {\n parseMode,\n parseStatus,\n parseThreshold,\n compareCandidates,\n collectExactTitleClusters,\n collectParentScopedClusters,\n collectFuzzyTitleClusters,\n clusterFromMembers,\n toMergeSuggestion,\n};\n"],"names":[],"mappings":"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","debugId":"92659191-3659-5881-ae1a-f3751586ce41"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* agent can act on a partial result.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
!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]="
|
|
14
|
+
!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]="6ecfeba5-55df-529b-bfa6-6338bbe64322")}catch(e){}}();
|
|
15
15
|
import { pathExists } from "../../core/fs/fs-utils.js";
|
|
16
16
|
import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
|
|
17
17
|
import { isTerminalStatus } from "../../core/item/status.js";
|
|
@@ -81,6 +81,81 @@ async function loadItem(pmRoot, settings, id) {
|
|
|
81
81
|
const loaded = await readLocatedItem(located, { schema: settings.schema });
|
|
82
82
|
return { id: located.id, title: loaded.document.metadata.title, metadata: loaded.document.metadata };
|
|
83
83
|
}
|
|
84
|
+
function buildChildrenByParentForDedupeMerge(items, statusRegistry) {
|
|
85
|
+
const childrenByParent = new Map();
|
|
86
|
+
for (const item of items) {
|
|
87
|
+
const parent = item.parent?.trim();
|
|
88
|
+
if (!parent) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const bucket = childrenByParent.get(parent) ?? [];
|
|
92
|
+
bucket.push({ id: item.id, title: item.title, terminal: isTerminalStatus(item.status, statusRegistry) });
|
|
93
|
+
childrenByParent.set(parent, bucket);
|
|
94
|
+
}
|
|
95
|
+
return childrenByParent;
|
|
96
|
+
}
|
|
97
|
+
async function applyDedupeMergeReparents(params) {
|
|
98
|
+
const reparentEnabled = params.options.reparentChildren !== false;
|
|
99
|
+
const reparentTargets = reparentEnabled ? params.children.filter((child) => !child.terminal) : [];
|
|
100
|
+
const skippedChildren = (reparentEnabled ? params.children.filter((child) => child.terminal) : []).map((child) => ({
|
|
101
|
+
child_id: child.id,
|
|
102
|
+
child_title: child.title,
|
|
103
|
+
reason: "terminal",
|
|
104
|
+
}));
|
|
105
|
+
const reparented = [];
|
|
106
|
+
for (const child of reparentTargets) {
|
|
107
|
+
const record = {
|
|
108
|
+
child_id: child.id,
|
|
109
|
+
child_title: child.title,
|
|
110
|
+
from_parent: params.duplicateId,
|
|
111
|
+
to_parent: params.keep,
|
|
112
|
+
applied: false,
|
|
113
|
+
};
|
|
114
|
+
if (params.apply) {
|
|
115
|
+
try {
|
|
116
|
+
await runUpdate(child.id, {
|
|
117
|
+
parent: params.keep,
|
|
118
|
+
author: params.options.author,
|
|
119
|
+
message: params.options.message ?? `dedupe-merge: re-parent from ${params.duplicateId} to ${params.keep}`,
|
|
120
|
+
}, params.global);
|
|
121
|
+
record.applied = true;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
params.warnings.push(`reparent_failed:${child.id}:${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
reparented.push(record);
|
|
128
|
+
}
|
|
129
|
+
return { reparented, skippedChildren };
|
|
130
|
+
}
|
|
131
|
+
async function applyDedupeMergeClose(params) {
|
|
132
|
+
const reason = `Duplicate of ${params.keep}`;
|
|
133
|
+
const close = { duplicate_of: params.keep, reason, applied: false };
|
|
134
|
+
if (isTerminalStatus(params.duplicate.metadata.status, params.statusRegistry)) {
|
|
135
|
+
close.skipped_reason = "already_terminal";
|
|
136
|
+
if (params.duplicate.metadata.duplicate_of?.trim() !== params.keep) {
|
|
137
|
+
params.warnings.push(`close_skipped_terminal:${params.duplicate.id}:already_closed`);
|
|
138
|
+
}
|
|
139
|
+
return close;
|
|
140
|
+
}
|
|
141
|
+
if (!params.apply) {
|
|
142
|
+
close.skipped_reason = "dry_run";
|
|
143
|
+
return close;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
await runClose(params.duplicate.id, undefined, {
|
|
147
|
+
duplicateOf: params.keep,
|
|
148
|
+
author: params.options.author,
|
|
149
|
+
message: params.options.message ?? `dedupe-merge: close ${params.duplicate.id} as duplicate of ${params.keep}`,
|
|
150
|
+
}, params.global);
|
|
151
|
+
close.applied = true;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
close.skipped_reason = "failed";
|
|
155
|
+
params.warnings.push(`close_failed:${params.duplicate.id}:${error instanceof Error ? error.message : String(error)}`);
|
|
156
|
+
}
|
|
157
|
+
return close;
|
|
158
|
+
}
|
|
84
159
|
/**
|
|
85
160
|
* Implements run dedupe merge for the public runtime surface of this module.
|
|
86
161
|
*/
|
|
@@ -93,7 +168,6 @@ export async function runDedupeMerge(options, global) {
|
|
|
93
168
|
const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
|
|
94
169
|
const keep = parseRequiredId(options.keep, "--keep");
|
|
95
170
|
const duplicateIds = parseDuplicateIds(options.close, keep);
|
|
96
|
-
const reparentChildren = options.reparentChildren !== false;
|
|
97
171
|
// Explicit dry-run always wins over apply so a preview can be forced safely.
|
|
98
172
|
const apply = options.apply === true && options.dryRun !== true;
|
|
99
173
|
const canonical = await loadItem(pmRoot, settings, keep);
|
|
@@ -106,16 +180,7 @@ export async function runDedupeMerge(options, global) {
|
|
|
106
180
|
// A single full listing supplies every child lookup without re-reading the
|
|
107
181
|
// corpus per duplicate.
|
|
108
182
|
const corpus = await runList(undefined, {}, global);
|
|
109
|
-
const childrenByParent =
|
|
110
|
-
for (const item of corpus.items) {
|
|
111
|
-
const parent = item.parent?.trim();
|
|
112
|
-
if (!parent) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const bucket = childrenByParent.get(parent) ?? [];
|
|
116
|
-
bucket.push({ id: item.id, title: item.title, terminal: isTerminalStatus(item.status, statusRegistry) });
|
|
117
|
-
childrenByParent.set(parent, bucket);
|
|
118
|
-
}
|
|
183
|
+
const childrenByParent = buildChildrenByParentForDedupeMerge(corpus.items, statusRegistry);
|
|
119
184
|
const warnings = [];
|
|
120
185
|
const outcomes = [];
|
|
121
186
|
for (const duplicateId of duplicateIds) {
|
|
@@ -132,65 +197,19 @@ export async function runDedupeMerge(options, global) {
|
|
|
132
197
|
nextSteps: ["Pass distinct ids: --keep <canonical> and --close <duplicate>."],
|
|
133
198
|
});
|
|
134
199
|
}
|
|
135
|
-
const reason = `Duplicate of ${keep}`;
|
|
136
200
|
// The canonical can never be re-parented onto itself; terminal children are
|
|
137
201
|
// historical and keep their frozen parent link, so only active children move.
|
|
138
202
|
const children = (childrenByParent.get(duplicate.id) ?? []).filter((child) => child.id !== keep);
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
child_title: child.title,
|
|
150
|
-
from_parent: duplicate.id,
|
|
151
|
-
to_parent: keep,
|
|
152
|
-
applied: false,
|
|
153
|
-
};
|
|
154
|
-
if (apply) {
|
|
155
|
-
try {
|
|
156
|
-
await runUpdate(child.id, {
|
|
157
|
-
parent: keep,
|
|
158
|
-
author: options.author,
|
|
159
|
-
message: options.message ?? `dedupe-merge: re-parent from ${duplicate.id} to ${keep}`,
|
|
160
|
-
}, global);
|
|
161
|
-
record.applied = true;
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
warnings.push(`reparent_failed:${child.id}:${error instanceof Error ? error.message : String(error)}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
reparented.push(record);
|
|
168
|
-
}
|
|
169
|
-
const alreadyTerminal = isTerminalStatus(duplicate.metadata.status, statusRegistry);
|
|
170
|
-
const close = { duplicate_of: keep, reason, applied: false };
|
|
171
|
-
if (alreadyTerminal) {
|
|
172
|
-
close.skipped_reason = "already_terminal";
|
|
173
|
-
if (duplicate.metadata.duplicate_of?.trim() !== keep) {
|
|
174
|
-
warnings.push(`close_skipped_terminal:${duplicate.id}:already_closed`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else if (!apply) {
|
|
178
|
-
close.skipped_reason = "dry_run";
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
try {
|
|
182
|
-
await runClose(duplicate.id, undefined, {
|
|
183
|
-
duplicateOf: keep,
|
|
184
|
-
author: options.author,
|
|
185
|
-
message: options.message ?? `dedupe-merge: close ${duplicate.id} as duplicate of ${keep}`,
|
|
186
|
-
}, global);
|
|
187
|
-
close.applied = true;
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
close.skipped_reason = "failed";
|
|
191
|
-
warnings.push(`close_failed:${duplicate.id}:${error instanceof Error ? error.message : String(error)}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
203
|
+
const { reparented, skippedChildren } = await applyDedupeMergeReparents({
|
|
204
|
+
children,
|
|
205
|
+
duplicateId: duplicate.id,
|
|
206
|
+
keep,
|
|
207
|
+
apply,
|
|
208
|
+
options,
|
|
209
|
+
global,
|
|
210
|
+
warnings,
|
|
211
|
+
});
|
|
212
|
+
const close = await applyDedupeMergeClose({ duplicate, keep, apply, options, global, statusRegistry, warnings });
|
|
194
213
|
outcomes.push({
|
|
195
214
|
duplicate_id: duplicate.id,
|
|
196
215
|
duplicate_title: duplicate.title,
|
|
@@ -223,4 +242,4 @@ export const _testOnly = {
|
|
|
223
242
|
parseDuplicateIds,
|
|
224
243
|
};
|
|
225
244
|
//# sourceMappingURL=dedupe-merge.js.map
|
|
226
|
-
//# debugId=
|
|
245
|
+
//# debugId=6ecfeba5-55df-529b-bfa6-6338bbe64322
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dedupe-merge.js","sources":["cli/commands/dedupe-merge.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module cli/commands/dedupe-merge\n *\n * Implements the guided dedupe-merge consolidation workflow (GH-163). Where\n * `pm dedupe-audit` only *detects* likely-duplicate clusters and emits suggested\n * close commands, this module *performs* the merge: it re-parents a duplicate's\n * still-active children onto the canonical item and then closes each duplicate\n * with structured `duplicate_of` metadata. The operation previews by default\n * (dry-run) and only mutates under `--apply`, and it never aborts the whole\n * batch on a single per-item failure — failures are recorded as warnings so an\n * agent can act on a partial result.\n */\nimport { pathExists } from \"../../core/fs/fs-utils.js\";\nimport { getActiveExtensionRegistrations } from \"../../core/extensions/index.js\";\nimport { isTerminalStatus } from \"../../core/item/status.js\";\nimport { resolveItemTypeRegistry } from \"../../core/item/type-registry.js\";\nimport { resolveRuntimeStatusRegistry } from \"../../core/schema/runtime-schema.js\";\nimport { EXIT_CODE } from \"../../core/shared/constants.js\";\nimport type { GlobalOptions } from \"../../core/shared/command-types.js\";\nimport { PmCliError } from \"../../core/shared/errors.js\";\nimport { splitCommaList } from \"../../core/shared/split-comma-list.js\";\nimport { nowIso } from \"../../core/shared/time.js\";\nimport { locateItem, readLocatedItem } from \"../../core/store/item-store.js\";\nimport { getSettingsPath, resolvePmRoot } from \"../../core/store/paths.js\";\nimport { readSettings } from \"../../core/store/settings.js\";\nimport type { ItemMetadata } from \"../../types/index.js\";\nimport { runClose } from \"./close.js\";\nimport { runList } from \"./list.js\";\nimport { runUpdate } from \"./update.js\";\n\n/**\n * Documents the dedupe-merge options payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeOptions {\n /** Canonical item id to keep (children move here, duplicates close against it). */\n keep?: string;\n /** Duplicate item id(s) to consolidate into the canonical item (csv-friendly, repeatable). */\n close?: string | string[];\n /** Apply the merge mutations; omitted/false performs a non-mutating preview. */\n apply?: boolean;\n /** Force a preview even if `apply` is also set (explicit dry-run wins). */\n dryRun?: boolean;\n /** Re-parent each duplicate's still-active children onto the canonical (default true). */\n reparentChildren?: boolean;\n /** Author recorded on every mutation. */\n author?: string;\n /** History message recorded on every mutation. */\n message?: string;\n}\n\n/**\n * Documents a single child re-parent action exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeChildReparent {\n child_id: string;\n child_title: string;\n from_parent: string;\n to_parent: string;\n applied: boolean;\n}\n\n/**\n * Documents the close action planned/performed for a duplicate item.\n */\nexport interface DedupeMergeCloseAction {\n duplicate_of: string;\n reason: string;\n applied: boolean;\n /** When the close was not applied, the reason it was skipped. */\n skipped_reason?: \"already_terminal\" | \"dry_run\" | \"failed\";\n}\n\n/**\n * Documents the per-duplicate merge outcome exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeDuplicateOutcome {\n duplicate_id: string;\n duplicate_title: string;\n canonical_id: string;\n reparented_children: DedupeMergeChildReparent[];\n skipped_children: { child_id: string; child_title: string; reason: \"terminal\" }[];\n close: DedupeMergeCloseAction;\n}\n\n/**\n * Documents the dedupe-merge result payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeResult {\n canonical_id: string;\n canonical_title: string;\n mode: \"dry_run\" | \"apply\";\n duplicates: DedupeMergeDuplicateOutcome[];\n totals: {\n duplicates: number;\n children_reparented: number;\n children_skipped: number;\n closed: number;\n };\n warnings: string[];\n now: string;\n}\n\ninterface ResolvedItem {\n id: string;\n title: string;\n metadata: ItemMetadata;\n}\n\n/**\n * Parses a single required item id, rejecting blank input with usage guidance.\n */\nfunction parseRequiredId(raw: string | undefined, flag: string): string {\n const trimmed = (raw ?? \"\").trim();\n if (trimmed.length === 0) {\n throw new PmCliError(`pm dedupe-merge requires ${flag} <id>`, EXIT_CODE.USAGE, {\n code: \"missing_required_option\",\n examples: ['pm dedupe-merge --keep pm-canonical --close pm-dup1 --apply'],\n });\n }\n return trimmed;\n}\n\n/**\n * Parses the duplicate id list, splitting csv values and de-duplicating while\n * preserving first-seen order so the result is deterministic.\n */\nfunction parseDuplicateIds(raw: string | string[] | undefined, keep: string): string[] {\n const collected = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n const ids: string[] = [];\n const seen = new Set<string>();\n for (const entry of collected) {\n for (const id of splitCommaList(entry, { unique: false })) {\n const trimmed = id.trim();\n if (trimmed.length === 0 || seen.has(trimmed)) {\n continue;\n }\n seen.add(trimmed);\n ids.push(trimmed);\n }\n }\n if (ids.length === 0) {\n throw new PmCliError(\"pm dedupe-merge requires at least one --close <id> duplicate\", EXIT_CODE.USAGE, {\n code: \"missing_required_option\",\n examples: ['pm dedupe-merge --keep pm-canonical --close pm-dup1,pm-dup2 --apply'],\n });\n }\n if (seen.has(keep)) {\n throw new PmCliError(`Cannot close the canonical item ${keep} as a duplicate of itself`, EXIT_CODE.USAGE, {\n code: \"invalid_option_combination\",\n nextSteps: [\"Pass distinct ids: --keep <canonical> and --close <duplicate>.\"],\n });\n }\n return ids;\n}\n\nasync function loadItem(\n pmRoot: string,\n settings: Awaited<ReturnType<typeof readSettings>>,\n id: string,\n): Promise<ResolvedItem | null> {\n const typeToFolder = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations()).type_to_folder;\n const located = await locateItem(pmRoot, id, settings.id_prefix, settings.item_format, typeToFolder);\n if (!located) {\n return null;\n }\n const loaded = await readLocatedItem(located, { schema: settings.schema });\n return { id: located.id, title: loaded.document.metadata.title, metadata: loaded.document.metadata };\n}\n\n/**\n * Implements run dedupe merge for the public runtime surface of this module.\n */\nexport async function runDedupeMerge(options: DedupeMergeOptions, global: GlobalOptions): Promise<DedupeMergeResult> {\n const pmRoot = resolvePmRoot(process.cwd(), global.path);\n if (!(await pathExists(getSettingsPath(pmRoot)))) {\n throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);\n }\n const settings = await readSettings(pmRoot);\n const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);\n const keep = parseRequiredId(options.keep, \"--keep\");\n const duplicateIds = parseDuplicateIds(options.close, keep);\n const reparentChildren = options.reparentChildren !== false;\n // Explicit dry-run always wins over apply so a preview can be forced safely.\n const apply = options.apply === true && options.dryRun !== true;\n\n const canonical = await loadItem(pmRoot, settings, keep);\n if (!canonical) {\n throw new PmCliError(`Canonical item ${keep} not found`, EXIT_CODE.NOT_FOUND, {\n code: \"item_not_found\",\n nextSteps: [`Verify the canonical id with: pm get ${keep}`],\n });\n }\n\n // A single full listing supplies every child lookup without re-reading the\n // corpus per duplicate.\n const corpus = await runList(undefined, {}, global);\n const childrenByParent = new Map<string, { id: string; title: string; terminal: boolean }[]>();\n for (const item of corpus.items) {\n const parent = item.parent?.trim();\n if (!parent) {\n continue;\n }\n const bucket = childrenByParent.get(parent) ?? [];\n bucket.push({ id: item.id, title: item.title, terminal: isTerminalStatus(item.status, statusRegistry) });\n childrenByParent.set(parent, bucket);\n }\n\n const warnings: string[] = [];\n const outcomes: DedupeMergeDuplicateOutcome[] = [];\n for (const duplicateId of duplicateIds) {\n const duplicate = await loadItem(pmRoot, settings, duplicateId);\n if (!duplicate) {\n throw new PmCliError(`Duplicate item ${duplicateId} not found`, EXIT_CODE.NOT_FOUND, {\n code: \"item_not_found\",\n nextSteps: [`Verify the duplicate id with: pm get ${duplicateId}`],\n });\n }\n if (duplicate.id === canonical.id) {\n throw new PmCliError(`Cannot close the canonical item ${canonical.id} as a duplicate of itself`, EXIT_CODE.USAGE, {\n code: \"invalid_option_combination\",\n nextSteps: [\"Pass distinct ids: --keep <canonical> and --close <duplicate>.\"],\n });\n }\n const reason = `Duplicate of ${keep}`;\n // The canonical can never be re-parented onto itself; terminal children are\n // historical and keep their frozen parent link, so only active children move.\n const children = (childrenByParent.get(duplicate.id) ?? []).filter((child) => child.id !== keep);\n const reparentTargets = reparentChildren ? children.filter((child) => !child.terminal) : [];\n const skippedChildren = (reparentChildren ? children.filter((child) => child.terminal) : []).map((child) => ({\n child_id: child.id,\n child_title: child.title,\n reason: \"terminal\" as const,\n }));\n\n const reparented: DedupeMergeChildReparent[] = [];\n for (const child of reparentTargets) {\n const record: DedupeMergeChildReparent = {\n child_id: child.id,\n child_title: child.title,\n from_parent: duplicate.id,\n to_parent: keep,\n applied: false,\n };\n if (apply) {\n try {\n await runUpdate(\n child.id,\n {\n parent: keep,\n author: options.author,\n message: options.message ?? `dedupe-merge: re-parent from ${duplicate.id} to ${keep}`,\n },\n global,\n );\n record.applied = true;\n } catch (error) {\n warnings.push(`reparent_failed:${child.id}:${error instanceof Error ? error.message : String(error)}`);\n }\n }\n reparented.push(record);\n }\n\n const alreadyTerminal = isTerminalStatus(duplicate.metadata.status, statusRegistry);\n const close: DedupeMergeCloseAction = { duplicate_of: keep, reason, applied: false };\n if (alreadyTerminal) {\n close.skipped_reason = \"already_terminal\";\n if (duplicate.metadata.duplicate_of?.trim() !== keep) {\n warnings.push(`close_skipped_terminal:${duplicate.id}:already_closed`);\n }\n } else if (!apply) {\n close.skipped_reason = \"dry_run\";\n } else {\n try {\n await runClose(\n duplicate.id,\n undefined,\n {\n duplicateOf: keep,\n author: options.author,\n message: options.message ?? `dedupe-merge: close ${duplicate.id} as duplicate of ${keep}`,\n },\n global,\n );\n close.applied = true;\n } catch (error) {\n close.skipped_reason = \"failed\";\n warnings.push(`close_failed:${duplicate.id}:${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n outcomes.push({\n duplicate_id: duplicate.id,\n duplicate_title: duplicate.title,\n canonical_id: keep,\n reparented_children: reparented,\n skipped_children: skippedChildren,\n close,\n });\n }\n\n const childrenReparented = outcomes.reduce(\n (total, outcome) => total + outcome.reparented_children.filter((child) => child.applied).length,\n 0,\n );\n const childrenSkipped = outcomes.reduce((total, outcome) => total + outcome.skipped_children.length, 0);\n const closed = outcomes.reduce((total, outcome) => total + (outcome.close.applied ? 1 : 0), 0);\n\n return {\n canonical_id: canonical.id,\n canonical_title: canonical.title,\n mode: apply ? \"apply\" : \"dry_run\",\n duplicates: outcomes,\n totals: {\n duplicates: outcomes.length,\n children_reparented: childrenReparented,\n children_skipped: childrenSkipped,\n closed,\n },\n warnings,\n now: nowIso(),\n };\n}\n\nexport const _testOnly = {\n parseRequiredId,\n parseDuplicateIds,\n};\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;;;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgFxC;;GAEG;AACH,SAAS,eAAe,CAAC,GAAuB,EAAE,IAAY;IAC5D,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,UAAU,CAAC,4BAA4B,IAAI,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE;YAC7E,IAAI,EAAE,yBAAyB;YAC/B,QAAQ,EAAE,CAAC,6DAA6D,CAAC;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,GAAkC,EAAE,IAAY;IACzE,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,KAAK,MAAM,EAAE,IAAI,cAAc,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC1D,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;YAC1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9C,SAAS;YACX,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,UAAU,CAAC,8DAA8D,EAAE,SAAS,CAAC,KAAK,EAAE;YACpG,IAAI,EAAE,yBAAyB;YAC/B,QAAQ,EAAE,CAAC,qEAAqE,CAAC;SAClF,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,UAAU,CAAC,mCAAmC,IAAI,2BAA2B,EAAE,SAAS,CAAC,KAAK,EAAE;YACxG,IAAI,EAAE,4BAA4B;YAClC,SAAS,EAAE,CAAC,gEAAgE,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAAc,EACd,QAAkD,EAClD,EAAU;IAEV,MAAM,YAAY,GAAG,uBAAuB,CAAC,QAAQ,EAAE,+BAA+B,EAAE,CAAC,CAAC,cAAc,CAAC;IACzG,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACrG,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;AACvG,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA2B,EAAE,MAAqB;IACrF,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,UAAU,CAAC,iCAAiC,MAAM,sBAAsB,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,4BAA4B,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrE,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC5D,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,KAAK,KAAK,CAAC;IAC5D,6EAA6E;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IAEhE,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAAC,kBAAkB,IAAI,YAAY,EAAE,SAAS,CAAC,SAAS,EAAE;YAC5E,IAAI,EAAE,gBAAgB;YACtB,SAAS,EAAE,CAAC,wCAAwC,IAAI,EAAE,CAAC;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,wBAAwB;IACxB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA8D,CAAC;IAC/F,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;QACzG,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAkC,EAAE,CAAC;IACnD,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,UAAU,CAAC,kBAAkB,WAAW,YAAY,EAAE,SAAS,CAAC,SAAS,EAAE;gBACnF,IAAI,EAAE,gBAAgB;gBACtB,SAAS,EAAE,CAAC,wCAAwC,WAAW,EAAE,CAAC;aACnE,CAAC,CAAC;QACL,CAAC;QACD,IAAI,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,UAAU,CAAC,mCAAmC,SAAS,CAAC,EAAE,2BAA2B,EAAE,SAAS,CAAC,KAAK,EAAE;gBAChH,IAAI,EAAE,4BAA4B;gBAClC,SAAS,EAAE,CAAC,gEAAgE,CAAC;aAC9E,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,gBAAgB,IAAI,EAAE,CAAC;QACtC,4EAA4E;QAC5E,8EAA8E;QAC9E,MAAM,QAAQ,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;QACjG,MAAM,eAAe,GAAG,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,MAAM,eAAe,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3G,QAAQ,EAAE,KAAK,CAAC,EAAE;YAClB,WAAW,EAAE,KAAK,CAAC,KAAK;YACxB,MAAM,EAAE,UAAmB;SAC5B,CAAC,CAAC,CAAC;QAEJ,MAAM,UAAU,GAA+B,EAAE,CAAC;QAClD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,MAAM,MAAM,GAA6B;gBACvC,QAAQ,EAAE,KAAK,CAAC,EAAE;gBAClB,WAAW,EAAE,KAAK,CAAC,KAAK;gBACxB,WAAW,EAAE,SAAS,CAAC,EAAE;gBACzB,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,KAAK;aACf,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC;oBACH,MAAM,SAAS,CACb,KAAK,CAAC,EAAE,EACR;wBACE,MAAM,EAAE,IAAI;wBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;wBACtB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,gCAAgC,SAAS,CAAC,EAAE,OAAO,IAAI,EAAE;qBACtF,EACD,MAAM,CACP,CAAC;oBACF,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACxB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,QAAQ,CAAC,IAAI,CAAC,mBAAmB,KAAK,CAAC,EAAE,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACzG,CAAC;YACH,CAAC;YACD,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QAED,MAAM,eAAe,GAAG,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACpF,MAAM,KAAK,GAA2B,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACrF,IAAI,eAAe,EAAE,CAAC;YACpB,KAAK,CAAC,cAAc,GAAG,kBAAkB,CAAC;YAC1C,IAAI,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBACrD,QAAQ,CAAC,IAAI,CAAC,0BAA0B,SAAS,CAAC,EAAE,iBAAiB,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAClB,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,MAAM,QAAQ,CACZ,SAAS,CAAC,EAAE,EACZ,SAAS,EACT;oBACE,WAAW,EAAE,IAAI;oBACjB,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,uBAAuB,SAAS,CAAC,EAAE,oBAAoB,IAAI,EAAE;iBAC1F,EACD,MAAM,CACP,CAAC;gBACF,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,SAAS,CAAC,EAAE,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC1G,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC;YACZ,YAAY,EAAE,SAAS,CAAC,EAAE;YAC1B,eAAe,EAAE,SAAS,CAAC,KAAK;YAChC,YAAY,EAAE,IAAI;YAClB,mBAAmB,EAAE,UAAU;YAC/B,gBAAgB,EAAE,eAAe;YACjC,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,CACxC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAC/F,CAAC,CACF,CAAC;IACF,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxG,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE/F,OAAO;QACL,YAAY,EAAE,SAAS,CAAC,EAAE;QAC1B,eAAe,EAAE,SAAS,CAAC,KAAK;QAChC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACjC,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE;YACN,UAAU,EAAE,QAAQ,CAAC,MAAM;YAC3B,mBAAmB,EAAE,kBAAkB;YACvC,gBAAgB,EAAE,eAAe;YACjC,MAAM;SACP;QACD,QAAQ;QACR,GAAG,EAAE,MAAM,EAAE;KACd,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,eAAe;IACf,iBAAiB;CAClB,CAAC","debugId":"243cb0fc-d422-5750-be45-ac8132fd617d"}
|
|
1
|
+
{"version":3,"file":"dedupe-merge.js","sources":["cli/commands/dedupe-merge.ts"],"sourceRoot":"/","sourcesContent":["/**\n * @module cli/commands/dedupe-merge\n *\n * Implements the guided dedupe-merge consolidation workflow (GH-163). Where\n * `pm dedupe-audit` only *detects* likely-duplicate clusters and emits suggested\n * close commands, this module *performs* the merge: it re-parents a duplicate's\n * still-active children onto the canonical item and then closes each duplicate\n * with structured `duplicate_of` metadata. The operation previews by default\n * (dry-run) and only mutates under `--apply`, and it never aborts the whole\n * batch on a single per-item failure — failures are recorded as warnings so an\n * agent can act on a partial result.\n */\nimport { pathExists } from \"../../core/fs/fs-utils.js\";\nimport { getActiveExtensionRegistrations } from \"../../core/extensions/index.js\";\nimport { isTerminalStatus } from \"../../core/item/status.js\";\nimport { resolveItemTypeRegistry } from \"../../core/item/type-registry.js\";\nimport { resolveRuntimeStatusRegistry } from \"../../core/schema/runtime-schema.js\";\nimport { EXIT_CODE } from \"../../core/shared/constants.js\";\nimport type { GlobalOptions } from \"../../core/shared/command-types.js\";\nimport { PmCliError } from \"../../core/shared/errors.js\";\nimport { splitCommaList } from \"../../core/shared/split-comma-list.js\";\nimport { nowIso } from \"../../core/shared/time.js\";\nimport { locateItem, readLocatedItem } from \"../../core/store/item-store.js\";\nimport { getSettingsPath, resolvePmRoot } from \"../../core/store/paths.js\";\nimport { readSettings } from \"../../core/store/settings.js\";\nimport type { ItemMetadata } from \"../../types/index.js\";\nimport { runClose } from \"./close.js\";\nimport { runList } from \"./list.js\";\nimport { runUpdate } from \"./update.js\";\n\n/**\n * Documents the dedupe-merge options payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeOptions {\n /** Canonical item id to keep (children move here, duplicates close against it). */\n keep?: string;\n /** Duplicate item id(s) to consolidate into the canonical item (csv-friendly, repeatable). */\n close?: string | string[];\n /** Apply the merge mutations; omitted/false performs a non-mutating preview. */\n apply?: boolean;\n /** Force a preview even if `apply` is also set (explicit dry-run wins). */\n dryRun?: boolean;\n /** Re-parent each duplicate's still-active children onto the canonical (default true). */\n reparentChildren?: boolean;\n /** Author recorded on every mutation. */\n author?: string;\n /** History message recorded on every mutation. */\n message?: string;\n}\n\n/**\n * Documents a single child re-parent action exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeChildReparent {\n child_id: string;\n child_title: string;\n from_parent: string;\n to_parent: string;\n applied: boolean;\n}\n\n/**\n * Documents the close action planned/performed for a duplicate item.\n */\nexport interface DedupeMergeCloseAction {\n duplicate_of: string;\n reason: string;\n applied: boolean;\n /** When the close was not applied, the reason it was skipped. */\n skipped_reason?: \"already_terminal\" | \"dry_run\" | \"failed\";\n}\n\n/**\n * Documents the per-duplicate merge outcome exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeDuplicateOutcome {\n duplicate_id: string;\n duplicate_title: string;\n canonical_id: string;\n reparented_children: DedupeMergeChildReparent[];\n skipped_children: { child_id: string; child_title: string; reason: \"terminal\" }[];\n close: DedupeMergeCloseAction;\n}\n\n/**\n * Documents the dedupe-merge result payload exchanged by command, SDK, and package integrations.\n */\nexport interface DedupeMergeResult {\n canonical_id: string;\n canonical_title: string;\n mode: \"dry_run\" | \"apply\";\n duplicates: DedupeMergeDuplicateOutcome[];\n totals: {\n duplicates: number;\n children_reparented: number;\n children_skipped: number;\n closed: number;\n };\n warnings: string[];\n now: string;\n}\n\ninterface ResolvedItem {\n id: string;\n title: string;\n metadata: ItemMetadata;\n}\n\n/**\n * Parses a single required item id, rejecting blank input with usage guidance.\n */\nfunction parseRequiredId(raw: string | undefined, flag: string): string {\n const trimmed = (raw ?? \"\").trim();\n if (trimmed.length === 0) {\n throw new PmCliError(`pm dedupe-merge requires ${flag} <id>`, EXIT_CODE.USAGE, {\n code: \"missing_required_option\",\n examples: ['pm dedupe-merge --keep pm-canonical --close pm-dup1 --apply'],\n });\n }\n return trimmed;\n}\n\n/**\n * Parses the duplicate id list, splitting csv values and de-duplicating while\n * preserving first-seen order so the result is deterministic.\n */\nfunction parseDuplicateIds(raw: string | string[] | undefined, keep: string): string[] {\n const collected = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n const ids: string[] = [];\n const seen = new Set<string>();\n for (const entry of collected) {\n for (const id of splitCommaList(entry, { unique: false })) {\n const trimmed = id.trim();\n if (trimmed.length === 0 || seen.has(trimmed)) {\n continue;\n }\n seen.add(trimmed);\n ids.push(trimmed);\n }\n }\n if (ids.length === 0) {\n throw new PmCliError(\"pm dedupe-merge requires at least one --close <id> duplicate\", EXIT_CODE.USAGE, {\n code: \"missing_required_option\",\n examples: ['pm dedupe-merge --keep pm-canonical --close pm-dup1,pm-dup2 --apply'],\n });\n }\n if (seen.has(keep)) {\n throw new PmCliError(`Cannot close the canonical item ${keep} as a duplicate of itself`, EXIT_CODE.USAGE, {\n code: \"invalid_option_combination\",\n nextSteps: [\"Pass distinct ids: --keep <canonical> and --close <duplicate>.\"],\n });\n }\n return ids;\n}\n\nasync function loadItem(\n pmRoot: string,\n settings: Awaited<ReturnType<typeof readSettings>>,\n id: string,\n): Promise<ResolvedItem | null> {\n const typeToFolder = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations()).type_to_folder;\n const located = await locateItem(pmRoot, id, settings.id_prefix, settings.item_format, typeToFolder);\n if (!located) {\n return null;\n }\n const loaded = await readLocatedItem(located, { schema: settings.schema });\n return { id: located.id, title: loaded.document.metadata.title, metadata: loaded.document.metadata };\n}\n\nfunction buildChildrenByParentForDedupeMerge(\n items: Awaited<ReturnType<typeof runList>>[\"items\"],\n statusRegistry: ReturnType<typeof resolveRuntimeStatusRegistry>,\n): Map<string, { id: string; title: string; terminal: boolean }[]> {\n const childrenByParent = new Map<string, { id: string; title: string; terminal: boolean }[]>();\n for (const item of items) {\n const parent = item.parent?.trim();\n if (!parent) {\n continue;\n }\n const bucket = childrenByParent.get(parent) ?? [];\n bucket.push({ id: item.id, title: item.title, terminal: isTerminalStatus(item.status, statusRegistry) });\n childrenByParent.set(parent, bucket);\n }\n return childrenByParent;\n}\n\nasync function applyDedupeMergeReparents(params: {\n children: { id: string; title: string; terminal: boolean }[];\n duplicateId: string;\n keep: string;\n apply: boolean;\n options: DedupeMergeOptions;\n global: GlobalOptions;\n warnings: string[];\n}): Promise<{\n reparented: DedupeMergeChildReparent[];\n skippedChildren: { child_id: string; child_title: string; reason: \"terminal\" }[];\n}> {\n const reparentEnabled = params.options.reparentChildren !== false;\n const reparentTargets = reparentEnabled ? params.children.filter((child) => !child.terminal) : [];\n const skippedChildren = (reparentEnabled ? params.children.filter((child) => child.terminal) : []).map((child) => ({\n child_id: child.id,\n child_title: child.title,\n reason: \"terminal\" as const,\n }));\n const reparented: DedupeMergeChildReparent[] = [];\n for (const child of reparentTargets) {\n const record: DedupeMergeChildReparent = {\n child_id: child.id,\n child_title: child.title,\n from_parent: params.duplicateId,\n to_parent: params.keep,\n applied: false,\n };\n if (params.apply) {\n try {\n await runUpdate(\n child.id,\n {\n parent: params.keep,\n author: params.options.author,\n message: params.options.message ?? `dedupe-merge: re-parent from ${params.duplicateId} to ${params.keep}`,\n },\n params.global,\n );\n record.applied = true;\n } catch (error) {\n params.warnings.push(`reparent_failed:${child.id}:${error instanceof Error ? error.message : String(error)}`);\n }\n }\n reparented.push(record);\n }\n return { reparented, skippedChildren };\n}\n\nasync function applyDedupeMergeClose(params: {\n duplicate: ResolvedItem;\n keep: string;\n apply: boolean;\n options: DedupeMergeOptions;\n global: GlobalOptions;\n statusRegistry: ReturnType<typeof resolveRuntimeStatusRegistry>;\n warnings: string[];\n}): Promise<DedupeMergeCloseAction> {\n const reason = `Duplicate of ${params.keep}`;\n const close: DedupeMergeCloseAction = { duplicate_of: params.keep, reason, applied: false };\n if (isTerminalStatus(params.duplicate.metadata.status, params.statusRegistry)) {\n close.skipped_reason = \"already_terminal\";\n if (params.duplicate.metadata.duplicate_of?.trim() !== params.keep) {\n params.warnings.push(`close_skipped_terminal:${params.duplicate.id}:already_closed`);\n }\n return close;\n }\n if (!params.apply) {\n close.skipped_reason = \"dry_run\";\n return close;\n }\n try {\n await runClose(\n params.duplicate.id,\n undefined,\n {\n duplicateOf: params.keep,\n author: params.options.author,\n message: params.options.message ?? `dedupe-merge: close ${params.duplicate.id} as duplicate of ${params.keep}`,\n },\n params.global,\n );\n close.applied = true;\n } catch (error) {\n close.skipped_reason = \"failed\";\n params.warnings.push(`close_failed:${params.duplicate.id}:${error instanceof Error ? error.message : String(error)}`);\n }\n return close;\n}\n\n/**\n * Implements run dedupe merge for the public runtime surface of this module.\n */\nexport async function runDedupeMerge(options: DedupeMergeOptions, global: GlobalOptions): Promise<DedupeMergeResult> {\n const pmRoot = resolvePmRoot(process.cwd(), global.path);\n if (!(await pathExists(getSettingsPath(pmRoot)))) {\n throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);\n }\n const settings = await readSettings(pmRoot);\n const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);\n const keep = parseRequiredId(options.keep, \"--keep\");\n const duplicateIds = parseDuplicateIds(options.close, keep);\n // Explicit dry-run always wins over apply so a preview can be forced safely.\n const apply = options.apply === true && options.dryRun !== true;\n\n const canonical = await loadItem(pmRoot, settings, keep);\n if (!canonical) {\n throw new PmCliError(`Canonical item ${keep} not found`, EXIT_CODE.NOT_FOUND, {\n code: \"item_not_found\",\n nextSteps: [`Verify the canonical id with: pm get ${keep}`],\n });\n }\n\n // A single full listing supplies every child lookup without re-reading the\n // corpus per duplicate.\n const corpus = await runList(undefined, {}, global);\n const childrenByParent = buildChildrenByParentForDedupeMerge(corpus.items, statusRegistry);\n\n const warnings: string[] = [];\n const outcomes: DedupeMergeDuplicateOutcome[] = [];\n for (const duplicateId of duplicateIds) {\n const duplicate = await loadItem(pmRoot, settings, duplicateId);\n if (!duplicate) {\n throw new PmCliError(`Duplicate item ${duplicateId} not found`, EXIT_CODE.NOT_FOUND, {\n code: \"item_not_found\",\n nextSteps: [`Verify the duplicate id with: pm get ${duplicateId}`],\n });\n }\n if (duplicate.id === canonical.id) {\n throw new PmCliError(`Cannot close the canonical item ${canonical.id} as a duplicate of itself`, EXIT_CODE.USAGE, {\n code: \"invalid_option_combination\",\n nextSteps: [\"Pass distinct ids: --keep <canonical> and --close <duplicate>.\"],\n });\n }\n // The canonical can never be re-parented onto itself; terminal children are\n // historical and keep their frozen parent link, so only active children move.\n const children = (childrenByParent.get(duplicate.id) ?? []).filter((child) => child.id !== keep);\n const { reparented, skippedChildren } = await applyDedupeMergeReparents({\n children,\n duplicateId: duplicate.id,\n keep,\n apply,\n options,\n global,\n warnings,\n });\n const close = await applyDedupeMergeClose({ duplicate, keep, apply, options, global, statusRegistry, warnings });\n\n outcomes.push({\n duplicate_id: duplicate.id,\n duplicate_title: duplicate.title,\n canonical_id: keep,\n reparented_children: reparented,\n skipped_children: skippedChildren,\n close,\n });\n }\n\n const childrenReparented = outcomes.reduce(\n (total, outcome) => total + outcome.reparented_children.filter((child) => child.applied).length,\n 0,\n );\n const childrenSkipped = outcomes.reduce((total, outcome) => total + outcome.skipped_children.length, 0);\n const closed = outcomes.reduce((total, outcome) => total + (outcome.close.applied ? 1 : 0), 0);\n\n return {\n canonical_id: canonical.id,\n canonical_title: canonical.title,\n mode: apply ? \"apply\" : \"dry_run\",\n duplicates: outcomes,\n totals: {\n duplicates: outcomes.length,\n children_reparented: childrenReparented,\n children_skipped: childrenSkipped,\n closed,\n },\n warnings,\n now: nowIso(),\n };\n}\n\nexport const _testOnly = {\n parseRequiredId,\n parseDuplicateIds,\n};\n"],"names":[],"mappings":"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","debugId":"6ecfeba5-55df-529b-bfa6-6338bbe64322"}
|