panopticon-cli 0.6.5 → 0.6.7
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/README.md +2 -2
- package/dist/{agents-DfYify9s.js → agents-CfFDs52G.js} +14 -14
- package/dist/{agents-DfYify9s.js.map → agents-CfFDs52G.js.map} +1 -1
- package/dist/{agents-BKsVoIc9.js → agents-D_2oRFVf.js} +1 -1
- package/dist/{archive-planning-BJrZ3tmN.js → archive-planning-D97ziGec.js} +3 -3
- package/dist/{archive-planning-BJrZ3tmN.js.map → archive-planning-D97ziGec.js.map} +1 -1
- package/dist/{archive-planning-C3m3hfa5.js → archive-planning-DK90wn9Q.js} +1 -1
- package/dist/{browser-Cvdznzc0.js → browser-CX7jXfXX.js} +1 -1
- package/dist/{browser-Cvdznzc0.js.map → browser-CX7jXfXX.js.map} +1 -1
- package/dist/{clean-planning-DvhZAUv4.js → clean-planning-D_lz4aQq.js} +2 -2
- package/dist/{clean-planning-DvhZAUv4.js.map → clean-planning-D_lz4aQq.js.map} +1 -1
- package/dist/clean-planning-x1S-JdmO.js +2 -0
- package/dist/cli/index.js +291 -760
- package/dist/cli/index.js.map +1 -1
- package/dist/{close-issue-Dr7yZmrr.js → close-issue-CaFE0stN.js} +11 -7
- package/dist/close-issue-CaFE0stN.js.map +1 -0
- package/dist/close-issue-CjcfZI9s.js +2 -0
- package/dist/compact-beads-B0_qE1w3.js +2 -0
- package/dist/{compact-beads-BCOtIIRl.js → compact-beads-CjFkteSU.js} +2 -2
- package/dist/{compact-beads-BCOtIIRl.js.map → compact-beads-CjFkteSU.js.map} +1 -1
- package/dist/{config-CRzMQRgA.js → config-BQNKsi9G.js} +2 -2
- package/dist/{config-CRzMQRgA.js.map → config-BQNKsi9G.js.map} +1 -1
- package/dist/{config-BYgUzQ21.js → config-agyKgF5C.js} +1 -1
- package/dist/{config-yaml-BgOACZAB.js → config-yaml-DGbLSMCa.js} +1 -1
- package/dist/{config-yaml-BgOACZAB.js.map → config-yaml-DGbLSMCa.js.map} +1 -1
- package/dist/{config-yaml-fdyvyL0S.js → config-yaml-Dqt4FWQH.js} +1 -1
- package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js → acceptance-criteria-Dk9hhiYj.js} +1 -1
- package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js.map → acceptance-criteria-Dk9hhiYj.js.map} +1 -1
- package/dist/dashboard/{agent-enrichment-C67LJBgD.js → agent-enrichment-DdO7ZqjI.js} +11 -7
- package/dist/dashboard/agent-enrichment-DdO7ZqjI.js.map +1 -0
- package/dist/dashboard/{agent-enrichment-Cq0P1cNZ.js → agent-enrichment-dLeGE1fX.js} +1 -1
- package/dist/dashboard/{agents-YyO6t5Xa.js → agents-DCpQQ_W5.js} +14 -14
- package/dist/dashboard/{agents-YyO6t5Xa.js.map → agents-DCpQQ_W5.js.map} +1 -1
- package/dist/dashboard/{agents-BVBVCyat.js → agents-Dgh2TjSp.js} +1 -1
- package/dist/dashboard/{archive-planning-h-hAjk0P.js → archive-planning-BmW9UDTr.js} +3 -3
- package/dist/dashboard/{archive-planning-h-hAjk0P.js.map → archive-planning-BmW9UDTr.js.map} +1 -1
- package/dist/dashboard/{archive-planning-CScs1MOC.js → archive-planning-C3Ebf9yC.js} +1 -1
- package/dist/dashboard/{beads-qNB0yAHV.js → beads-Bv-AdX7G.js} +3 -3
- package/dist/dashboard/{beads-qNB0yAHV.js.map → beads-Bv-AdX7G.js.map} +1 -1
- package/dist/dashboard/{beads-D_FRedEJ.js → beads-By6-X07V.js} +1 -1
- package/dist/dashboard/clean-planning-D60L8rPY.js +2 -0
- package/dist/dashboard/{clean-planning-qafw99vY.js → clean-planning-VEJu5suh.js} +2 -2
- package/dist/dashboard/{clean-planning-qafw99vY.js.map → clean-planning-VEJu5suh.js.map} +1 -1
- package/dist/dashboard/close-issue-C2KeSKKJ.js +2 -0
- package/dist/dashboard/{close-issue-DfIggeZD.js → close-issue-DtKdsSTm.js} +11 -7
- package/dist/dashboard/close-issue-DtKdsSTm.js.map +1 -0
- package/dist/dashboard/compact-beads-C7BN5N11.js +2 -0
- package/dist/dashboard/{compact-beads-Dt0qTqsC.js → compact-beads-D8Vt3qyv.js} +2 -2
- package/dist/dashboard/{compact-beads-Dt0qTqsC.js.map → compact-beads-D8Vt3qyv.js.map} +1 -1
- package/dist/dashboard/{config-CUREjHP7.js → config-CDkGjnwy.js} +2 -2
- package/dist/dashboard/{config-CUREjHP7.js.map → config-CDkGjnwy.js.map} +1 -1
- package/dist/dashboard/{config-BeI3uy-8.js → config-CTXkBATQ.js} +1 -1
- package/dist/dashboard/{database-CozA13Wy.js → database-DhqASALP.js} +1 -1
- package/dist/dashboard/{database-C0y0hXBx.js → database-cxmQryoh.js} +2 -2
- package/dist/dashboard/{database-C0y0hXBx.js.map → database-cxmQryoh.js.map} +1 -1
- package/dist/dashboard/{dist-src-oG2iHzgI.js → dist-src-DTm11oQr.js} +1 -1
- package/dist/dashboard/{dist-src-oG2iHzgI.js.map → dist-src-DTm11oQr.js.map} +1 -1
- package/dist/dashboard/{event-store-D7kLBd07.js → event-store-VWWUmOfn.js} +1 -1
- package/dist/dashboard/{event-store-O9q0Gweh.js → event-store-vSmAA3Zp.js} +9 -4
- package/dist/dashboard/event-store-vSmAA3Zp.js.map +1 -0
- package/dist/dashboard/{factory-BnLdiQW-.js → factory-C8nhLGHB.js} +3 -3
- package/dist/dashboard/{factory-BnLdiQW-.js.map → factory-C8nhLGHB.js.map} +1 -1
- package/dist/dashboard/{feedback-writer-DyovUANg.js → feedback-writer-CudSe1WK.js} +2 -2
- package/dist/dashboard/{feedback-writer-DyovUANg.js.map → feedback-writer-CudSe1WK.js.map} +1 -1
- package/dist/dashboard/{feedback-writer-gSUv_W0h.js → feedback-writer-Wgv1cd1r.js} +1 -1
- package/dist/dashboard/{git-utils-BJRioREj.js → git-utils-C1m4SwAe.js} +1 -1
- package/dist/dashboard/{git-utils-BJRioREj.js.map → git-utils-C1m4SwAe.js.map} +1 -1
- package/dist/dashboard/{git-utils-BtCRddq3.js → git-utils-DQI8EYoj.js} +1 -1
- package/dist/dashboard/{github-app-XO-LBUGk.js → github-app-DClWjjHr.js} +1 -1
- package/dist/dashboard/{github-app-XO-LBUGk.js.map → github-app-DClWjjHr.js.map} +1 -1
- package/dist/dashboard/{health-events-db-584nYgJB.js → health-events-db-BMXQfInV.js} +1 -1
- package/dist/dashboard/{health-events-db-B3ChzN65.js → health-events-db-Do4NrOhC.js} +2 -2
- package/dist/dashboard/{health-events-db-B3ChzN65.js.map → health-events-db-Do4NrOhC.js.map} +1 -1
- package/dist/dashboard/{hooks-CKhs3N68.js → hooks-CB4T47NC.js} +1 -1
- package/dist/dashboard/{hooks-CErbP8Oq.js → hooks-CjqXOlNb.js} +2 -2
- package/dist/dashboard/{hooks-CErbP8Oq.js.map → hooks-CjqXOlNb.js.map} +1 -1
- package/dist/dashboard/hume-CA2pftu_.js +3 -0
- package/dist/dashboard/{hume-CX_U3Qha.js → hume-JsAlMOJC.js} +2 -2
- package/dist/dashboard/{hume-CX_U3Qha.js.map → hume-JsAlMOJC.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-B57kGDUV.js → inspect-agent-7eour7EA.js} +3 -3
- package/dist/dashboard/{inspect-agent-B57kGDUV.js.map → inspect-agent-7eour7EA.js.map} +1 -1
- package/dist/dashboard/{io-yGovuG4U.js → io-CWlFW78i.js} +1 -1
- package/dist/dashboard/{io-AJg-mzFi.js → io-DKS6359z.js} +1 -1
- package/dist/dashboard/{io-AJg-mzFi.js.map → io-DKS6359z.js.map} +1 -1
- package/dist/dashboard/issue-id-vwYJdsf8.js +62 -0
- package/dist/dashboard/issue-id-vwYJdsf8.js.map +1 -0
- package/dist/dashboard/{issue-service-singleton-DQK42EqH.js → issue-service-singleton-Co__-6kL.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js → issue-service-singleton-Wv4xBm3y.js} +7 -7
- package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js.map → issue-service-singleton-Wv4xBm3y.js.map} +1 -1
- package/dist/dashboard/{label-cleanup-CZEsbtq9.js → label-cleanup-nVKTmIIW.js} +7 -4
- package/dist/dashboard/label-cleanup-nVKTmIIW.js.map +1 -0
- package/dist/dashboard/lifecycle-BcUmtkR4.js +7 -0
- package/dist/dashboard/{merge-agent-GLtMEsTu.js → merge-agent-CGN3TT0a.js} +1 -1
- package/dist/dashboard/{merge-agent-twroFuAh.js → merge-agent-yudQOPZc.js} +148 -46
- package/dist/dashboard/merge-agent-yudQOPZc.js.map +1 -0
- package/dist/dashboard/{paths-COdEvoXR.js → paths-BDyJ7BiV.js} +19 -2
- package/dist/dashboard/{paths-COdEvoXR.js.map → paths-BDyJ7BiV.js.map} +1 -1
- package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js → pipeline-notifier-CCSN-jar.js} +1 -1
- package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js.map → pipeline-notifier-CCSN-jar.js.map} +1 -1
- package/dist/dashboard/{plan-utils-BkCIhn3B.js → plan-utils-Bkcsqr_s.js} +3 -3
- package/dist/dashboard/{plan-utils-BkCIhn3B.js.map → plan-utils-Bkcsqr_s.js.map} +1 -1
- package/dist/dashboard/{prd-draft-D09Afalc.js → prd-draft-BD8oMkZ1.js} +2 -2
- package/dist/dashboard/{prd-draft-D09Afalc.js.map → prd-draft-BD8oMkZ1.js.map} +1 -1
- package/dist/dashboard/{projection-cache-DQ9zegkK.js → projection-cache-C0EL8s8h.js} +1 -1
- package/dist/dashboard/{projection-cache-DQ9zegkK.js.map → projection-cache-C0EL8s8h.js.map} +1 -1
- package/dist/dashboard/{projects-DyT3vSy-.js → projects-C5ozxjwP.js} +1 -1
- package/dist/dashboard/{projects-Cq3TWdPS.js → projects-CFVl4oHn.js} +25 -13
- package/dist/dashboard/projects-CFVl4oHn.js.map +1 -0
- package/dist/dashboard/{providers-Ck2sQd_F.js → providers-B5Y4H2Mg.js} +4 -4
- package/dist/dashboard/providers-B5Y4H2Mg.js.map +1 -0
- package/dist/dashboard/{providers-DVQnDekG.js → providers-csVZVPkE.js} +1 -1
- package/dist/dashboard/public/assets/{dist-CCJbQrSB.js → dist-CXaO6nOE.js} +1 -1
- package/dist/dashboard/public/assets/index-CzFZIb87.js +212 -0
- package/dist/dashboard/public/assets/index-OEEbThNN.css +1 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/rally-6McpKKRa.js +3 -0
- package/dist/dashboard/{rally-Cwuae-4C.js → rally-YjFRxIiC.js} +2 -2
- package/dist/dashboard/{rally-Cwuae-4C.js.map → rally-YjFRxIiC.js.map} +1 -1
- package/dist/dashboard/{rally-api-DSUxm7EO.js → rally-api-C0WqCSkT.js} +1 -1
- package/dist/dashboard/{rally-api-DSUxm7EO.js.map → rally-api-C0WqCSkT.js.map} +1 -1
- package/dist/dashboard/{rally-api-CEH5KZi4.js → rally-api-DNttdCW4.js} +1 -1
- package/dist/dashboard/{remote-BHTTMpJJ.js → remote-Cigqjj3f.js} +2 -2
- package/dist/dashboard/{remote-BXo_iIku.js → remote-ObpNZ7hF.js} +2 -2
- package/dist/dashboard/{remote-BXo_iIku.js.map → remote-ObpNZ7hF.js.map} +1 -1
- package/dist/dashboard/{remote-agents-CTKVhFFY.js → remote-agents-Bf3GuM7t.js} +1 -1
- package/dist/dashboard/{remote-agents-C0_0LLNd.js → remote-agents-DFyjT1Le.js} +1 -1
- package/dist/dashboard/{remote-agents-C0_0LLNd.js.map → remote-agents-DFyjT1Le.js.map} +1 -1
- package/dist/dashboard/{review-status-CK3eBGyb.js → review-status-BtXqWBhS.js} +1 -1
- package/dist/dashboard/{review-status-CV55Tl-n.js → review-status-Bymwzh2i.js} +44 -4
- package/dist/dashboard/{review-status-CV55Tl-n.js.map → review-status-Bymwzh2i.js.map} +1 -1
- package/dist/dashboard/server.js +559 -253
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{settings-CuHV-wcv.js → settings-BHlDG7TK.js} +2 -2
- package/dist/dashboard/settings-BHlDG7TK.js.map +1 -0
- package/dist/dashboard/settings-XWvDcj-D.js +2 -0
- package/dist/dashboard/{shadow-engineering-BUeZunaE.js → shadow-engineering-lIn1W_95.js} +1 -1
- package/dist/dashboard/{shadow-engineering-BUeZunaE.js.map → shadow-engineering-lIn1W_95.js.map} +1 -1
- package/dist/dashboard/{shadow-state-DHQ-kASN.js → shadow-state-BIexcxkv.js} +1 -1
- package/dist/dashboard/{shadow-state-DHQ-kASN.js.map → shadow-state-BIexcxkv.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js → spawn-planning-session-33Jf-d5T.js} +6 -6
- package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js.map → spawn-planning-session-33Jf-d5T.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-U0Lqpjen.js → spawn-planning-session-D5hrVdWM.js} +1 -1
- package/dist/dashboard/{specialist-context-ColzlmGE.js → specialist-context-DGukHSn8.js} +6 -6
- package/dist/dashboard/{specialist-context-ColzlmGE.js.map → specialist-context-DGukHSn8.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-BhmDpFIq.js → specialist-logs-CIw4qfTy.js} +1 -1
- package/dist/dashboard/{specialists-C6s3U6tX.js → specialists-B_zrayaP.js} +37 -36
- package/dist/dashboard/specialists-B_zrayaP.js.map +1 -0
- package/dist/dashboard/{specialists-Cny632-T.js → specialists-Cp-PgspS.js} +1 -1
- package/dist/dashboard/{test-agent-queue-tqI4VDsu.js → test-agent-queue-ypF_ecHo.js} +4 -4
- package/dist/dashboard/{test-agent-queue-tqI4VDsu.js.map → test-agent-queue-ypF_ecHo.js.map} +1 -1
- package/dist/dashboard/{tldr-daemon-BNFyS7W_.js → tldr-daemon-B_oLRD8z.js} +2 -2
- package/dist/dashboard/{tldr-daemon-BNFyS7W_.js.map → tldr-daemon-B_oLRD8z.js.map} +1 -1
- package/dist/dashboard/{tldr-daemon-A6JqC59u.js → tldr-daemon-Cfs0bXTi.js} +1 -1
- package/dist/dashboard/{tmux-DYGAVJfb.js → tmux-BzxdKItf.js} +1 -1
- package/dist/dashboard/{tmux-IlN1Slv-.js → tmux-LwG0tHhU.js} +2 -2
- package/dist/dashboard/{tmux-IlN1Slv-.js.map → tmux-LwG0tHhU.js.map} +1 -1
- package/dist/dashboard/{tracker-config-BzNLnmcE.js → tracker-config-BP59uH4V.js} +1 -1
- package/dist/dashboard/{tracker-config-CNM_5rEf.js → tracker-config-e7ph1QqT.js} +2 -2
- package/dist/dashboard/{tracker-config-CNM_5rEf.js.map → tracker-config-e7ph1QqT.js.map} +1 -1
- package/dist/dashboard/{tunnel-D2BkwU7k.js → tunnel-0RzzuXPf.js} +1 -1
- package/dist/dashboard/{tunnel-Dub2hiAA.js → tunnel-DldbBPWL.js} +2 -2
- package/dist/dashboard/{tunnel-Dub2hiAA.js.map → tunnel-DldbBPWL.js.map} +1 -1
- package/dist/dashboard/{types-CWA-o4UN.js → types-RKZjGE5N.js} +1 -1
- package/dist/dashboard/{types-CWA-o4UN.js.map → types-RKZjGE5N.js.map} +1 -1
- package/dist/dashboard/{vtt-parser-BAXygRf0.js → vtt-parser-99vFekRQ.js} +1 -1
- package/dist/dashboard/{vtt-parser-BAXygRf0.js.map → vtt-parser-99vFekRQ.js.map} +1 -1
- package/dist/dashboard/{work-agent-prompt-JYq_OugP.js → work-agent-prompt-fCg67nyo.js} +65 -10
- package/dist/dashboard/{work-agent-prompt-JYq_OugP.js.map → work-agent-prompt-fCg67nyo.js.map} +1 -1
- package/dist/dashboard/{work-type-router-Cxp8_ur2.js → work-type-router-CWVW2Wk_.js} +1 -1
- package/dist/dashboard/{work-type-router-Cxp8_ur2.js.map → work-type-router-CWVW2Wk_.js.map} +1 -1
- package/dist/dashboard/{work-type-router-Com2amST.js → work-type-router-Di5gCQwh.js} +1 -1
- package/dist/dashboard/{workflows-N1UTipYl.js → workflows-BSMipN07.js} +35 -17
- package/dist/dashboard/workflows-BSMipN07.js.map +1 -0
- package/dist/dashboard/workflows-DaYWQIS2.js +2 -0
- package/dist/dashboard/{workspace-config-cmp5_ipD.js → workspace-config-DVDR-Ukh.js} +1 -1
- package/dist/dashboard/workspace-config-DVDR-Ukh.js.map +1 -0
- package/dist/dashboard/{workspace-manager-CjpWPgzL.js → workspace-manager-BYfzs_t2.js} +1 -1
- package/dist/dashboard/{workspace-manager-D_y9ZmW_.js → workspace-manager-C7OfT62A.js} +44 -24
- package/dist/dashboard/workspace-manager-C7OfT62A.js.map +1 -0
- package/dist/{dns-BKzHm-2q.js → dns-D_aKQJjb.js} +1 -1
- package/dist/{dns-DZwOWvVO.js → dns-Yxq4NNS7.js} +1 -1
- package/dist/{dns-DZwOWvVO.js.map → dns-Yxq4NNS7.js.map} +1 -1
- package/dist/{factory-DFu3IT4r.js → factory-BRBGw6OB.js} +1 -1
- package/dist/{factory-DfzczxN1.js → factory-DzsOiZVc.js} +3 -3
- package/dist/{factory-DfzczxN1.js.map → factory-DzsOiZVc.js.map} +1 -1
- package/dist/{feedback-writer-CwdnOkPO.js → feedback-writer-ygXN5F9N.js} +2 -2
- package/dist/{feedback-writer-CwdnOkPO.js.map → feedback-writer-ygXN5F9N.js.map} +1 -1
- package/dist/{github-app-CHKwxOeQ.js → github-app-DykduJ0X.js} +1 -1
- package/dist/{github-app-CHKwxOeQ.js.map → github-app-DykduJ0X.js.map} +1 -1
- package/dist/hume-9nv1VmMV.js +3 -0
- package/dist/{hume-DnV-tDsh.js → hume-DoCbph2h.js} +2 -2
- package/dist/{hume-DnV-tDsh.js.map → hume-DoCbph2h.js.map} +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/issue-id-CAcekoIw.js +62 -0
- package/dist/issue-id-CAcekoIw.js.map +1 -0
- package/dist/{label-cleanup-31ElPqqv.js → label-cleanup-C8R9Rspn.js} +7 -4
- package/dist/label-cleanup-C8R9Rspn.js.map +1 -0
- package/dist/{manifest-DL0oDbpv.js → manifest-B4ghOD-V.js} +1 -1
- package/dist/{manifest-DL0oDbpv.js.map → manifest-B4ghOD-V.js.map} +1 -1
- package/dist/{merge-agent-VQH9z9t8.js → merge-agent-DlUiUanN.js} +86 -33
- package/dist/merge-agent-DlUiUanN.js.map +1 -0
- package/dist/{paths-lMaxrYtT.js → paths-CDJ_HsbN.js} +19 -2
- package/dist/{paths-lMaxrYtT.js.map → paths-CDJ_HsbN.js.map} +1 -1
- package/dist/{pipeline-notifier-OJ-d3Y60.js → pipeline-notifier-XgDdCdvT.js} +1 -1
- package/dist/{pipeline-notifier-OJ-d3Y60.js.map → pipeline-notifier-XgDdCdvT.js.map} +1 -1
- package/dist/{projects-CvLepaxC.js → projects-Bk-5QhFQ.js} +25 -13
- package/dist/projects-Bk-5QhFQ.js.map +1 -0
- package/dist/{projects-DMWmPeIU.js → projects-DhU7rAVN.js} +1 -1
- package/dist/{providers-DcCPZ5K4.js → providers-DSU1vfQF.js} +4 -4
- package/dist/providers-DSU1vfQF.js.map +1 -0
- package/dist/rally-DdPvGa-w.js +3 -0
- package/dist/{rally-uUUZXp1h.js → rally-Dy00NElU.js} +1 -1
- package/dist/{rally-uUUZXp1h.js.map → rally-Dy00NElU.js.map} +1 -1
- package/dist/{remote-CkLBqLJc.js → remote-CYiOJg0q.js} +2 -2
- package/dist/{remote-CkLBqLJc.js.map → remote-CYiOJg0q.js.map} +1 -1
- package/dist/{remote-agents-C5Bd2fgt.js → remote-agents-CZXrUF4f.js} +1 -1
- package/dist/{remote-agents-C5Bd2fgt.js.map → remote-agents-CZXrUF4f.js.map} +1 -1
- package/dist/{remote-agents-BTzD-wMQ.js → remote-agents-ycHHVsgf.js} +1 -1
- package/dist/{remote-workspace-Dxghqiti.js → remote-workspace-CA33UuVI.js} +4 -4
- package/dist/{remote-workspace-Dxghqiti.js.map → remote-workspace-CA33UuVI.js.map} +1 -1
- package/dist/{review-status-2TdtHNcs.js → review-status-D6H2WOw8.js} +1 -1
- package/dist/{review-status-Bm1bWNEa.js → review-status-DEDvCKMP.js} +44 -4
- package/dist/{review-status-Bm1bWNEa.js.map → review-status-DEDvCKMP.js.map} +1 -1
- package/dist/{tracker-C_62ukEq.js → settings-BcWPTrua.js} +7 -199
- package/dist/settings-BcWPTrua.js.map +1 -0
- package/dist/shadow-state-BZzxfEGw.js +2 -0
- package/dist/{shadow-state-CFFHf05M.js → shadow-state-CE3dQfll.js} +1 -1
- package/dist/{shadow-state-CFFHf05M.js.map → shadow-state-CE3dQfll.js.map} +1 -1
- package/dist/{specialist-context-BdNFsfMG.js → specialist-context-BAUWL1Fl.js} +6 -6
- package/dist/{specialist-context-BdNFsfMG.js.map → specialist-context-BAUWL1Fl.js.map} +1 -1
- package/dist/{specialist-logs-CLztE_bE.js → specialist-logs-DQKKQV9B.js} +1 -1
- package/dist/{specialists-aUoUVWsN.js → specialists-Bfb9ATzw.js} +1 -1
- package/dist/{specialists-DEKqgkxp.js → specialists-D7Kj5o6s.js} +35 -34
- package/dist/specialists-D7Kj5o6s.js.map +1 -0
- package/dist/sync-DMfgd389.js +693 -0
- package/dist/sync-DMfgd389.js.map +1 -0
- package/dist/sync-TL6y-8K6.js +2 -0
- package/dist/{tldr-daemon-BCEFPItr.js → tldr-daemon-CFx4LXAl.js} +2 -2
- package/dist/{tldr-daemon-BCEFPItr.js.map → tldr-daemon-CFx4LXAl.js.map} +1 -1
- package/dist/{tldr-daemon-xBAx4cBE.js → tldr-daemon-D_EooADG.js} +1 -1
- package/dist/{tmux-DN6H886Y.js → tmux-CBtui_Cl.js} +1 -1
- package/dist/{tmux-CKdNxxJx.js → tmux-D6Ah4I8z.js} +2 -2
- package/dist/{tmux-CKdNxxJx.js.map → tmux-D6Ah4I8z.js.map} +1 -1
- package/dist/tracker-BhYYvU3p.js +198 -0
- package/dist/tracker-BhYYvU3p.js.map +1 -0
- package/dist/{tracker-utils-CVU2W1sX.js → tracker-utils-ChQyut8w.js} +34 -12
- package/dist/tracker-utils-ChQyut8w.js.map +1 -0
- package/dist/{traefik-DHgBoWXX.js → traefik-C80EbDu_.js} +4 -4
- package/dist/{traefik-DHgBoWXX.js.map → traefik-C80EbDu_.js.map} +1 -1
- package/dist/{traefik-BR-edbZv.js → traefik-CgHl7Bge.js} +1 -1
- package/dist/{tunnel-BZO9Q5oe.js → tunnel-DXOJ1wMM.js} +1 -1
- package/dist/{tunnel-Bl1qNSyQ.js → tunnel-DzXEPwIc.js} +2 -2
- package/dist/{tunnel-Bl1qNSyQ.js.map → tunnel-DzXEPwIc.js.map} +1 -1
- package/dist/{types-DewGdaIP.js → types-BhJj1SP1.js} +1 -1
- package/dist/{types-DewGdaIP.js.map → types-BhJj1SP1.js.map} +1 -1
- package/dist/{work-type-router-CS2BB1vS.js → work-type-router-CHjciPyS.js} +3 -3
- package/dist/{work-type-router-CS2BB1vS.js.map → work-type-router-CHjciPyS.js.map} +1 -1
- package/dist/{workspace-config-CNXOpKuj.js → workspace-config-fUafvYMp.js} +1 -1
- package/dist/workspace-config-fUafvYMp.js.map +1 -0
- package/dist/workspace-manager-B9jS4Dsq.js +3 -0
- package/dist/{workspace-manager-CncdZkIy.js → workspace-manager-DuLhnzJV.js} +112 -27
- package/dist/workspace-manager-DuLhnzJV.js.map +1 -0
- package/package.json +2 -1
- package/scripts/post-merge-deploy.sh +25 -5
- package/scripts/record-cost-event.js +57 -7
- package/scripts/record-cost-event.js.map +1 -1
- package/skills/pan-help/SKILL.md +1 -1
- package/skills/pan-sync/SKILL.md +6 -6
- package/skills/workspace-add-repo/skill.md +46 -0
- package/templates/claude-md/sections/warnings.md +15 -2
- package/dist/clean-planning-sZXvy3Y5.js +0 -2
- package/dist/close-issue-Dml437qV.js +0 -2
- package/dist/close-issue-Dr7yZmrr.js.map +0 -1
- package/dist/compact-beads-iu218JcO.js +0 -2
- package/dist/dashboard/agent-enrichment-C67LJBgD.js.map +0 -1
- package/dist/dashboard/clean-planning-DCu3cOTu.js +0 -2
- package/dist/dashboard/close-issue-DfIggeZD.js.map +0 -1
- package/dist/dashboard/close-issue-DwdwYtar.js +0 -2
- package/dist/dashboard/compact-beads-DXY2fK2s.js +0 -2
- package/dist/dashboard/event-store-O9q0Gweh.js.map +0 -1
- package/dist/dashboard/hume-MZndNDVU.js +0 -3
- package/dist/dashboard/label-cleanup-CZEsbtq9.js.map +0 -1
- package/dist/dashboard/lifecycle-ZTYdrr2O.js +0 -7
- package/dist/dashboard/merge-agent-twroFuAh.js.map +0 -1
- package/dist/dashboard/projects-Cq3TWdPS.js.map +0 -1
- package/dist/dashboard/providers-Ck2sQd_F.js.map +0 -1
- package/dist/dashboard/public/assets/index-CpSmB2ts.css +0 -1
- package/dist/dashboard/public/assets/index-yarWhi0M.js +0 -214
- package/dist/dashboard/rally-CQ1OBJrJ.js +0 -3
- package/dist/dashboard/settings-CuHV-wcv.js.map +0 -1
- package/dist/dashboard/settings-DMeGBRsk.js +0 -2
- package/dist/dashboard/specialists-C6s3U6tX.js.map +0 -1
- package/dist/dashboard/workflows-B2ARUpOa.js +0 -2
- package/dist/dashboard/workflows-N1UTipYl.js.map +0 -1
- package/dist/dashboard/workspace-config-cmp5_ipD.js.map +0 -1
- package/dist/dashboard/workspace-manager-D_y9ZmW_.js.map +0 -1
- package/dist/hume-BjmwmJ9E.js +0 -3
- package/dist/label-cleanup-31ElPqqv.js.map +0 -1
- package/dist/merge-agent-VQH9z9t8.js.map +0 -1
- package/dist/projects-CvLepaxC.js.map +0 -1
- package/dist/providers-DcCPZ5K4.js.map +0 -1
- package/dist/rally-DR9x8--6.js +0 -3
- package/dist/shadow-state-p3jpGRPJ.js +0 -2
- package/dist/specialists-DEKqgkxp.js.map +0 -1
- package/dist/tracker-C_62ukEq.js.map +0 -1
- package/dist/tracker-utils-CVU2W1sX.js.map +0 -1
- package/dist/workspace-config-CNXOpKuj.js.map +0 -1
- package/dist/workspace-manager-CncdZkIy.js.map +0 -1
- package/dist/workspace-manager-Cx0r2Jnv.js +0 -3
package/skills/pan-help/SKILL.md
CHANGED
|
@@ -80,7 +80,7 @@ Panopticon is a multi-agent orchestration framework for AI coding assistants. Th
|
|
|
80
80
|
| Command | Description | Example |
|
|
81
81
|
|---------|-------------|---------|
|
|
82
82
|
| `pan skills` | List available skills | `pan skills` |
|
|
83
|
-
| `pan sync` | Sync skills to AI tools (
|
|
83
|
+
| `pan sync` | Sync skills to AI tools (auto-runs on `pan up`) | `pan sync` |
|
|
84
84
|
| `pan sync --dry-run` | Preview skill sync without applying | `pan sync --dry-run` |
|
|
85
85
|
|
|
86
86
|
### Maintenance
|
package/skills/pan-sync/SKILL.md
CHANGED
|
@@ -18,13 +18,13 @@ allowed-tools:
|
|
|
18
18
|
|
|
19
19
|
This skill guides you through syncing Panopticon skills to AI coding assistants. The sync process creates symlinks from `~/.panopticon/skills/` to each tool's skill directory and copies workspace CLAUDE.md files to `~/.opencode/cl-aude-md/`.
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Auto-Sync
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
23
|
+
`pan up` runs a full sync automatically on every startup. You rarely need to run `pan sync` manually. Manual sync is useful when:
|
|
24
|
+
|
|
25
|
+
- You added new skills and want to sync without restarting the dashboard
|
|
26
|
+
- You want to preview changes with `--dry-run`
|
|
27
|
+
- You need to force-overwrite with `--force`
|
|
28
28
|
|
|
29
29
|
## How Sync Works
|
|
30
30
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workspace-add-repo
|
|
3
|
+
description: Add repositories to the current progressive polyrepo workspace
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add Repos to Workspace
|
|
7
|
+
|
|
8
|
+
Use this when you need to work on repos that aren't in the workspace yet.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
Run from the workspace root:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pan workspace add-repo <workspace-id> <repo-name> [repo-name...]
|
|
16
|
+
pan workspace add-repo <workspace-id> --group <group-name>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Add specific repos
|
|
23
|
+
pan workspace add-repo int-123 int-provider-a int-canonical-to-hs
|
|
24
|
+
|
|
25
|
+
# Add a named group
|
|
26
|
+
pan workspace add-repo int-123 --group simphony
|
|
27
|
+
|
|
28
|
+
# Add all repos
|
|
29
|
+
pan workspace add-repo int-123 --group all
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How to decide which repos you need
|
|
33
|
+
|
|
34
|
+
1. Read `repo-map.md` in the meta repo for architecture and repo descriptions
|
|
35
|
+
2. Read `repo-groups.yaml` for available groups
|
|
36
|
+
3. Add only the repos you need — you can always add more later
|
|
37
|
+
|
|
38
|
+
## After adding repos
|
|
39
|
+
|
|
40
|
+
The new repos appear as subdirectories in the workspace with feature branches ready.
|
|
41
|
+
Run git commands inside each repo subdirectory.
|
|
42
|
+
|
|
43
|
+
## Notes
|
|
44
|
+
|
|
45
|
+
- Meta repos (symlinked) are readonly — do NOT commit changes to them
|
|
46
|
+
- Use `--dry-run` first to see what would be added without making changes
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## Investigation First — NEVER Fix Inline
|
|
2
|
+
|
|
3
|
+
**Your default mode is INVESTIGATION, not implementation.** When you encounter a bug, unexpected behavior, or something that needs changing outside your current issue scope:
|
|
4
|
+
|
|
5
|
+
1. **Investigate** — understand the root cause fully
|
|
6
|
+
2. **Document** — write up the problem, root cause, and proposed solution
|
|
7
|
+
3. **File an issue** — use `bd create` or the issue tracker with your findings so the fix is ready to implement
|
|
8
|
+
4. **Do NOT fix it yourself** — unless it directly blocks your current issue
|
|
9
|
+
|
|
10
|
+
This prevents scope creep, keeps changes traceable, and ensures every fix goes through the proper workspace/review pipeline. A well-investigated issue with a clear solution is more valuable than a drive-by fix buried in an unrelated PR.
|
|
11
|
+
|
|
12
|
+
**Exception:** If the bug directly blocks your assigned issue, fix it — but document it as a separate commit with a clear message.
|
|
13
|
+
|
|
1
14
|
## Warnings
|
|
2
15
|
|
|
3
16
|
- **DO NOT** modify files outside this workspace without explicit permission
|
|
@@ -59,12 +72,12 @@ npm test # or: mvn test, cargo test, etc.
|
|
|
59
72
|
git add -A
|
|
60
73
|
git commit -m "feat: description (ISSUE-XXX)
|
|
61
74
|
|
|
62
|
-
Co-Authored-By:
|
|
75
|
+
Co-Authored-By: <your-exact-model-id-here> <noreply@anthropic.com>"
|
|
63
76
|
|
|
64
77
|
# Example for claude-sonnet-4-5-20250929:
|
|
65
78
|
# git commit -m "feat: add specialist completion API
|
|
66
79
|
#
|
|
67
|
-
# Co-Authored-By:
|
|
80
|
+
# Co-Authored-By: claude-sonnet-4-5-20250929 <noreply@anthropic.com>"
|
|
68
81
|
|
|
69
82
|
# 3. Push to remote
|
|
70
83
|
git push -u origin $(git branch --show-current)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"close-issue-Dr7yZmrr.js","names":[],"sources":["../src/lib/lifecycle/close-issue.ts"],"sourcesContent":["/**\n * close-issue — Transition issue to closed/done state + label management.\n *\n * Uses the IssueTracker abstraction when available, with fallback to\n * direct API calls for contexts where the tracker isn't set up (e.g.,\n * standalone CLI).\n *\n * Operations:\n * 1. Transition issue to closed state\n * 2. Add 'closed-out' label\n * 3. Remove workflow labels (in-progress, in-review, needs-close-out)\n * 4. Add completion comment\n */\n\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { IssueTracker } from '../tracker/interface.js';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\n\nconst execAsync = promisify(exec);\n\nconst CLOSED_OUT_LABEL = 'closed-out';\nconst CLOSED_OUT_COLOR = '1d4ed8';\nconst WORKFLOW_LABELS = ['in-progress', 'in-review', 'needs-close-out'];\n\n/** Options for close-issue */\nexport interface CloseIssueOptions {\n /** IssueTracker instance (preferred — uses abstraction layer) */\n tracker?: IssueTracker;\n /** Reason for closing */\n reason?: string;\n /** Comment to add when closing */\n comment?: string;\n /** Apply the closed-out label. Default: true */\n applyLabel?: boolean;\n /** Only apply label (skip state transition). Default: false */\n labelOnly?: boolean;\n}\n\n/**\n * Close an issue and manage labels.\n *\n * If a tracker is provided, uses the abstraction layer.\n * Otherwise, falls back to direct gh CLI (GitHub) or Linear SDK calls.\n */\nexport async function closeIssue(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Promise<StepResult[]> {\n const results: StepResult[] = [];\n const { applyLabel = true, labelOnly = false, comment } = opts;\n\n // Step 1: Transition to closed (unless labelOnly)\n if (!labelOnly) {\n const closeResult = opts.tracker\n ? await closeViaTracker(ctx, opts.tracker, comment)\n : await closeViaDirect(ctx, comment);\n results.push(closeResult);\n\n // If close failed, don't bother with labels\n if (!closeResult.success && !closeResult.skipped) {\n return results;\n }\n }\n\n // Step 2: Close any open PR for the feature branch (GitHub only)\n if (ctx.github) {\n const prResult = await closeGitHubPr(ctx);\n results.push(prResult);\n }\n\n // Step 3: Apply closed-out label + remove workflow labels\n if (applyLabel) {\n const labelResult = await applyClosedOutLabel(ctx, opts.tracker);\n results.push(labelResult);\n }\n\n return results;\n}\n\n/**\n * Close via IssueTracker abstraction.\n */\nasync function closeViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n try {\n await tracker.transitionIssue(ctx.issueId, 'closed');\n if (comment) {\n try {\n await tracker.addComment(ctx.issueId, comment);\n } catch {\n // Non-fatal — comment is best-effort\n }\n }\n return stepOk(step, [`Closed ${ctx.issueId} via ${tracker.name} tracker`]);\n } catch (err) {\n return stepFailed(step, `Failed to close via tracker: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close via direct API calls (fallback when no tracker configured).\n * Determines issue type from context and uses appropriate method.\n */\nasync function closeViaDirect(\n ctx: LifecycleContext,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n if (ctx.github) {\n return closeGitHubDirect(ctx, comment);\n }\n\n // Rally issue\n if (ctx.rally) {\n return closeRallyDirect(ctx);\n }\n\n // Try Linear\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return closeLinearDirect(ctx, linearApiKey);\n }\n\n return stepFailed(step, 'No tracker available and cannot determine issue type');\n}\n\n/**\n * Close a GitHub issue via gh CLI.\n */\nasync function closeGitHubDirect(ctx: LifecycleContext, comment?: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.github) {\n return stepFailed(step, 'GitHub config not provided');\n }\n const { owner, repo, number } = ctx.github;\n try {\n const commentArg = comment ? ` --comment \"${comment.replace(/\"/g, '\\\\\"')}\"` : '';\n await execAsync(\n `gh issue close ${number} --repo ${owner}/${repo}${commentArg}`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed GitHub issue #${number} on ${owner}/${repo}`]);\n } catch (err) {\n return stepFailed(step, `gh issue close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close any open GitHub PR for the feature branch.\n */\nasync function closeGitHubPr(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:close-pr';\n if (!ctx.github) {\n return stepSkipped(step, ['Not a GitHub issue']);\n }\n const { owner, repo } = ctx.github;\n const issueLower = ctx.issueId.toLowerCase();\n const branchName = `feature/${issueLower}`;\n\n try {\n const { stdout: prListRaw } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --head \"${branchName}\" --state open --json number --jq '.[0].number'`,\n { encoding: 'utf-8' },\n );\n const prNumber = prListRaw.trim();\n if (!prNumber) {\n return stepSkipped(step, ['No open PR found for branch']);\n }\n await execAsync(\n `gh pr close ${prNumber} --repo ${owner}/${repo} --comment \"Merged via Panopticon lifecycle\"`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed PR #${prNumber} on ${owner}/${repo}`]);\n } catch (err) {\n return stepSkipped(step, [`PR close failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n/**\n * Rate limit circuit breaker for Linear API.\n * After hitting a rate limit, stop all Linear API calls for COOLDOWN_MS.\n * This prevents the 24,626-call storm that exhausted Linear's 5000 req/hr limit (PAN-328).\n */\nlet _linearRateLimitUntil = 0;\nconst LINEAR_RATE_LIMIT_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour (matches Linear's 5000/hr window)\n\n/**\n * Close a Linear issue via SDK (find by identifier, transition to Done).\n */\nasync function closeLinearDirect(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n // Circuit breaker: if we recently hit a rate limit, fail fast without making API calls\n if (Date.now() < _linearRateLimitUntil) {\n const remainingMin = Math.ceil((_linearRateLimitUntil - Date.now()) / 60000);\n return stepFailed(step, `Linear rate limit cooldown active (${remainingMin}min remaining). Issue will be closed during close-out ceremony.`);\n }\n\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNumber = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const issuePrefix = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNumber },\n team: { key: { eq: issuePrefix } },\n },\n first: 1,\n });\n\n if (results.nodes.length === 0) {\n return stepFailed(step, `Issue ${ctx.issueId} not found in Linear`);\n }\n\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const doneState = states.nodes.find(s => s.name === 'Done') ||\n states.nodes.find(s => s.type === 'completed');\n if (doneState) {\n await issue.update({ stateId: doneState.id });\n }\n }\n\n return stepOk(step, [`Moved Linear issue ${ctx.issueId} to Done`]);\n } catch (err) {\n const message = (err as Error).message;\n\n // Detect rate limit errors and activate circuit breaker\n if (message.includes('Rate limit') || message.includes('rate limit') || message.includes('429')) {\n _linearRateLimitUntil = Date.now() + LINEAR_RATE_LIMIT_COOLDOWN_MS;\n console.warn(`[close-issue] Linear rate limit hit — circuit breaker activated for 1 hour`);\n return stepFailed(step, `Linear rate limit exceeded. Circuit breaker activated — no Linear API calls for 1 hour. Issue will be closed during close-out ceremony.`);\n }\n\n return stepFailed(step, `Linear close failed: ${message}`);\n }\n}\n\n/**\n * Close a Rally issue via RallyTracker.\n */\nasync function closeRallyDirect(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.rally) {\n return stepFailed(step, 'Rally config not provided');\n }\n try {\n const { RallyTracker } = await import('../tracker/rally.js');\n const tracker = new RallyTracker({\n apiKey: ctx.rally.apiKey,\n server: ctx.rally.server,\n workspace: ctx.rally.workspace,\n project: ctx.rally.project,\n });\n await tracker.transitionIssue(ctx.issueId, 'closed');\n return stepOk(step, [`Closed Rally issue ${ctx.issueId}`]);\n } catch (err) {\n return stepFailed(step, `Rally close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Apply 'closed-out' label and remove workflow labels.\n * Uses tracker if available, falls back to direct calls.\n */\nasync function applyClosedOutLabel(\n ctx: LifecycleContext,\n tracker?: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n\n if (tracker) {\n return applyLabelViaTracker(ctx, tracker);\n }\n\n if (ctx.github) {\n return applyLabelGitHub(ctx);\n }\n\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return applyLabelLinear(ctx, linearApiKey);\n }\n\n return stepSkipped(step, ['No tracker available for label management']);\n}\n\nasync function applyLabelViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const issue = await tracker.getIssue(ctx.issueId);\n const newLabels = issue.labels.filter(l => !WORKFLOW_LABELS.includes(l));\n if (!newLabels.includes(CLOSED_OUT_LABEL)) {\n newLabels.push(CLOSED_OUT_LABEL);\n }\n await tracker.updateIssue(ctx.issueId, { labels: newLabels });\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label via ${tracker.name} tracker`]);\n } catch (err) {\n // Label management is non-fatal\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelGitHub(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:label';\n if (!ctx.github) return stepSkipped(step);\n const { owner, repo, number } = ctx.github;\n\n try {\n // Ensure label exists\n await execAsync(\n `gh label create \"${CLOSED_OUT_LABEL}\" --repo ${owner}/${repo} --color \"${CLOSED_OUT_COLOR}\" --description \"Verified and closed out\" --force 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n // Add label\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"${CLOSED_OUT_LABEL}\"`,\n { encoding: 'utf-8' },\n );\n // Remove workflow labels\n for (const label of WORKFLOW_LABELS) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\" 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n }\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on GitHub`]);\n } catch (err) {\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelLinear(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNum = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const teamKey = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length === 0) {\n return stepSkipped(step, ['Issue not found for label management']);\n }\n\n const issue = results.nodes[0];\n\n // Find or create closed-out label\n const labels = await client.issueLabels({ filter: { name: { eq: CLOSED_OUT_LABEL } } });\n let labelId: string;\n if (labels.nodes.length > 0) {\n labelId = labels.nodes[0].id;\n } else {\n const created = await client.createIssueLabel({ name: CLOSED_OUT_LABEL, color: `#${CLOSED_OUT_COLOR}` });\n const createdLabel = await created.issueLabel;\n labelId = createdLabel ? createdLabel.id : '';\n }\n\n if (labelId) {\n const existingLabels = await issue.labels();\n const labelIds = existingLabels.nodes.map(l => l.id);\n if (!labelIds.includes(labelId)) {\n labelIds.push(labelId);\n await issue.update({ labelIds });\n }\n }\n\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on Linear`]);\n } catch (err) {\n return stepSkipped(step, [`Linear label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-enrichment-C67LJBgD.js","names":[],"sources":["../../src/lib/agent-enrichment.ts"],"sourcesContent":["/**\n * Agent enrichment utilities (PAN-440)\n *\n * Shared functions for computing enrichment fields:\n * agentPhase, hasPendingQuestion, pendingQuestionCount, resolution, resolutionCount\n *\n * Used by both the legacy REST /api/agents endpoint and the new\n * AgentEnrichmentService background poller.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport { homedir } from 'os'\nimport { join } from 'path'\nimport { promisify } from 'util'\nimport { exec } from 'child_process'\nimport { getAgentRuntimeState, getAgentDir } from './agents.js'\nimport { resolveProjectFromIssue } from './projects.js'\nimport { getGitHubConfig } from '../dashboard/server/services/tracker-config.js'\n\nconst execAsync = promisify(exec)\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface QuestionOption { label: string; description: string }\nexport interface Question { question: string; header: string; options: QuestionOption[]; multiSelect: boolean }\nexport interface PendingQuestion { toolId: string; timestamp: string; questions: Question[] }\n\nexport interface AgentEnrichment {\n agentPhase: 'planning' | 'exploration' | 'implementation' | 'testing' | 'documentation' | 'pre_push' | 'post_push'\n hasPendingQuestion: boolean\n pendingQuestionCount: number\n resolution: string\n resolutionCount: number\n}\n\n// ─── JSONL path helpers ───────────────────────────────────────────────────────\n\nexport function getClaudeProjectDir(workspacePath: string): string {\n const dirName = workspacePath.replace(/^\\//, '').replace(/\\//g, '-')\n return join(homedir(), '.claude', 'projects', `-${dirName}`)\n}\n\nexport function getActiveSessionPath(projectDir: string): string | null {\n if (!existsSync(projectDir)) return null\n try {\n const files = readdirSync(projectDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({\n name: f,\n path: join(projectDir, f),\n mtime: statSync(join(projectDir, f)).mtime.getTime(),\n }))\n .sort((a, b) => b.mtime - a.mtime)\n return files.length > 0 ? files[0].path : null\n } catch {\n return null\n }\n}\n\nfunction getProjectPathByPrefix(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`\n const resolved = resolveProjectFromIssue(issueId)\n if (resolved) return resolved.projectPath\n const config = getGitHubConfig()\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '')\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ]\n for (const path of possiblePaths) {\n if (existsSync(path)) return path\n }\n }\n }\n }\n return join(homedir(), 'Projects')\n}\n\nexport async function getAgentWorkspace(agentId: string): Promise<string | null> {\n const stateFile = join(getAgentDir(agentId), 'state.json')\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n if (state.workspace) return state.workspace\n } catch {}\n }\n try {\n const { stdout: paneCwd } = await execAsync(\n `tmux display-message -t ${agentId} -p '#{pane_current_path}' 2>/dev/null`,\n { encoding: 'utf-8' }\n )\n const trimmed = paneCwd.trim()\n if (trimmed && existsSync(trimmed)) return trimmed\n } catch {}\n const issueId = agentId.replace(/^(agent-|planning-)/, '').toUpperCase()\n const prefix = issueId.split('-')[0]\n try {\n const projectPath = getProjectPathByPrefix(prefix)\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`)\n if (existsSync(workspacePath)) return workspacePath\n return projectPath\n } catch {\n return null\n }\n}\n\nexport async function getAgentJsonlPath(agentId: string): Promise<string | null> {\n const workspace = await getAgentWorkspace(agentId)\n if (!workspace) return null\n const projectDir = getClaudeProjectDir(workspace)\n return getActiveSessionPath(projectDir)\n}\n\n// ─── JSONL scanning ───────────────────────────────────────────────────────────\n\nexport async function getPendingQuestions(jsonlPath: string): Promise<PendingQuestion[]> {\n if (!existsSync(jsonlPath)) return []\n try {\n const content = await readFile(jsonlPath, 'utf-8')\n const lines = content.split('\\n').filter(line => line.trim())\n const toolCalls = new Map<string, PendingQuestion>()\n const answeredIds = new Set<string>()\n for (const line of lines) {\n try {\n const entry = JSON.parse(line)\n const messageContent = entry.message?.content\n if (!Array.isArray(messageContent)) continue\n for (const item of messageContent) {\n if (item.type === 'tool_use' && item.name === 'AskUserQuestion') {\n toolCalls.set(item.id, {\n toolId: item.id,\n timestamp: entry.timestamp || new Date().toISOString(),\n questions: item.input?.questions || [],\n })\n }\n if (item.type === 'tool_result' && item.tool_use_id) {\n answeredIds.add(item.tool_use_id)\n }\n }\n } catch {}\n }\n return Array.from(toolCalls.entries())\n .filter(([id]) => !answeredIds.has(id))\n .map(([, question]) => question)\n } catch {\n return []\n }\n}\n\nexport async function getAgentPendingQuestions(agentId: string): Promise<PendingQuestion[]> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath) return []\n return getPendingQuestions(jsonlPath)\n}\n\n/**\n * Get the mtime (ms since epoch) of the agent's active JSONL session file.\n * Returns null if the file doesn't exist or the path can't be resolved.\n * Used by AgentEnrichmentService to skip JSONL scans when the file is unchanged.\n */\nexport async function getAgentJsonlMtime(agentId: string): Promise<number | null> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath || !existsSync(jsonlPath)) return null\n try {\n return statSync(jsonlPath).mtime.getTime()\n } catch {\n return null\n }\n}\n\n// ─── Enrichment computation ───────────────────────────────────────────────────\n\n/**\n * Compute the full enrichment snapshot for a single agent.\n *\n * @param agentId - Agent session name (e.g. 'agent-pan-440', 'planning-pan-440')\n * @param startedAt - ISO timestamp when the agent started (filters stale questions)\n * @param hasActiveSpecialist - Whether the agent's issue has an active specialist running\n * @param skipJsonlScan - Skip JSONL file scan (use when mtime is unchanged); still reads runtime state\n */\nexport async function computeAgentEnrichment(\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n skipJsonlScan?: boolean,\n): Promise<AgentEnrichment> {\n const isPlanning = agentId.startsWith('planning-')\n\n // Read state.json for current phase\n const stateFile = join(getAgentDir(agentId), 'state.json')\n let statePhase: string | undefined\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n statePhase = state.phase\n } catch {}\n }\n\n const agentPhase = (isPlanning ? 'planning' : (statePhase || 'implementation')) as AgentEnrichment['agentPhase']\n\n // Get runtime state for resolution + idle detection\n const runtimeState = getAgentRuntimeState(agentId)\n const isIdle = runtimeState?.state === 'idle' ||\n (runtimeState?.currentTool === 'AskUserQuestion')\n\n // Get pending questions, filtered by agent start time\n // Skip JSONL scan when mtime is unchanged (optimization for static TUI sessions)\n let pendingQuestions: PendingQuestion[] = []\n if (!skipJsonlScan) {\n pendingQuestions = await getAgentPendingQuestions(agentId)\n if (pendingQuestions.length > 0 && startedAt) {\n const agentStartTime = new Date(startedAt).getTime()\n pendingQuestions = pendingQuestions.filter(q => {\n const qTime = new Date(q.timestamp).getTime()\n return !isNaN(qTime) && qTime >= agentStartTime\n })\n }\n }\n\n const hasPendingQuestion =\n !hasActiveSpecialist &&\n (pendingQuestions.length > 0 || isIdle || runtimeState?.resolution === 'needs_input')\n\n return {\n agentPhase,\n hasPendingQuestion,\n pendingQuestionCount: pendingQuestions.length,\n resolution: runtimeState?.resolution || 'working',\n resolutionCount: runtimeState?.resolutionCount || 0,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"close-issue-DfIggeZD.js","names":[],"sources":["../../src/lib/lifecycle/close-issue.ts"],"sourcesContent":["/**\n * close-issue — Transition issue to closed/done state + label management.\n *\n * Uses the IssueTracker abstraction when available, with fallback to\n * direct API calls for contexts where the tracker isn't set up (e.g.,\n * standalone CLI).\n *\n * Operations:\n * 1. Transition issue to closed state\n * 2. Add 'closed-out' label\n * 3. Remove workflow labels (in-progress, in-review, needs-close-out)\n * 4. Add completion comment\n */\n\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { IssueTracker } from '../tracker/interface.js';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\n\nconst execAsync = promisify(exec);\n\nconst CLOSED_OUT_LABEL = 'closed-out';\nconst CLOSED_OUT_COLOR = '1d4ed8';\nconst WORKFLOW_LABELS = ['in-progress', 'in-review', 'needs-close-out'];\n\n/** Options for close-issue */\nexport interface CloseIssueOptions {\n /** IssueTracker instance (preferred — uses abstraction layer) */\n tracker?: IssueTracker;\n /** Reason for closing */\n reason?: string;\n /** Comment to add when closing */\n comment?: string;\n /** Apply the closed-out label. Default: true */\n applyLabel?: boolean;\n /** Only apply label (skip state transition). Default: false */\n labelOnly?: boolean;\n}\n\n/**\n * Close an issue and manage labels.\n *\n * If a tracker is provided, uses the abstraction layer.\n * Otherwise, falls back to direct gh CLI (GitHub) or Linear SDK calls.\n */\nexport async function closeIssue(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Promise<StepResult[]> {\n const results: StepResult[] = [];\n const { applyLabel = true, labelOnly = false, comment } = opts;\n\n // Step 1: Transition to closed (unless labelOnly)\n if (!labelOnly) {\n const closeResult = opts.tracker\n ? await closeViaTracker(ctx, opts.tracker, comment)\n : await closeViaDirect(ctx, comment);\n results.push(closeResult);\n\n // If close failed, don't bother with labels\n if (!closeResult.success && !closeResult.skipped) {\n return results;\n }\n }\n\n // Step 2: Close any open PR for the feature branch (GitHub only)\n if (ctx.github) {\n const prResult = await closeGitHubPr(ctx);\n results.push(prResult);\n }\n\n // Step 3: Apply closed-out label + remove workflow labels\n if (applyLabel) {\n const labelResult = await applyClosedOutLabel(ctx, opts.tracker);\n results.push(labelResult);\n }\n\n return results;\n}\n\n/**\n * Close via IssueTracker abstraction.\n */\nasync function closeViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n try {\n await tracker.transitionIssue(ctx.issueId, 'closed');\n if (comment) {\n try {\n await tracker.addComment(ctx.issueId, comment);\n } catch {\n // Non-fatal — comment is best-effort\n }\n }\n return stepOk(step, [`Closed ${ctx.issueId} via ${tracker.name} tracker`]);\n } catch (err) {\n return stepFailed(step, `Failed to close via tracker: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close via direct API calls (fallback when no tracker configured).\n * Determines issue type from context and uses appropriate method.\n */\nasync function closeViaDirect(\n ctx: LifecycleContext,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n if (ctx.github) {\n return closeGitHubDirect(ctx, comment);\n }\n\n // Rally issue\n if (ctx.rally) {\n return closeRallyDirect(ctx);\n }\n\n // Try Linear\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return closeLinearDirect(ctx, linearApiKey);\n }\n\n return stepFailed(step, 'No tracker available and cannot determine issue type');\n}\n\n/**\n * Close a GitHub issue via gh CLI.\n */\nasync function closeGitHubDirect(ctx: LifecycleContext, comment?: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.github) {\n return stepFailed(step, 'GitHub config not provided');\n }\n const { owner, repo, number } = ctx.github;\n try {\n const commentArg = comment ? ` --comment \"${comment.replace(/\"/g, '\\\\\"')}\"` : '';\n await execAsync(\n `gh issue close ${number} --repo ${owner}/${repo}${commentArg}`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed GitHub issue #${number} on ${owner}/${repo}`]);\n } catch (err) {\n return stepFailed(step, `gh issue close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close any open GitHub PR for the feature branch.\n */\nasync function closeGitHubPr(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:close-pr';\n if (!ctx.github) {\n return stepSkipped(step, ['Not a GitHub issue']);\n }\n const { owner, repo } = ctx.github;\n const issueLower = ctx.issueId.toLowerCase();\n const branchName = `feature/${issueLower}`;\n\n try {\n const { stdout: prListRaw } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --head \"${branchName}\" --state open --json number --jq '.[0].number'`,\n { encoding: 'utf-8' },\n );\n const prNumber = prListRaw.trim();\n if (!prNumber) {\n return stepSkipped(step, ['No open PR found for branch']);\n }\n await execAsync(\n `gh pr close ${prNumber} --repo ${owner}/${repo} --comment \"Merged via Panopticon lifecycle\"`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed PR #${prNumber} on ${owner}/${repo}`]);\n } catch (err) {\n return stepSkipped(step, [`PR close failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n/**\n * Rate limit circuit breaker for Linear API.\n * After hitting a rate limit, stop all Linear API calls for COOLDOWN_MS.\n * This prevents the 24,626-call storm that exhausted Linear's 5000 req/hr limit (PAN-328).\n */\nlet _linearRateLimitUntil = 0;\nconst LINEAR_RATE_LIMIT_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour (matches Linear's 5000/hr window)\n\n/**\n * Close a Linear issue via SDK (find by identifier, transition to Done).\n */\nasync function closeLinearDirect(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n // Circuit breaker: if we recently hit a rate limit, fail fast without making API calls\n if (Date.now() < _linearRateLimitUntil) {\n const remainingMin = Math.ceil((_linearRateLimitUntil - Date.now()) / 60000);\n return stepFailed(step, `Linear rate limit cooldown active (${remainingMin}min remaining). Issue will be closed during close-out ceremony.`);\n }\n\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNumber = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const issuePrefix = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNumber },\n team: { key: { eq: issuePrefix } },\n },\n first: 1,\n });\n\n if (results.nodes.length === 0) {\n return stepFailed(step, `Issue ${ctx.issueId} not found in Linear`);\n }\n\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const doneState = states.nodes.find(s => s.name === 'Done') ||\n states.nodes.find(s => s.type === 'completed');\n if (doneState) {\n await issue.update({ stateId: doneState.id });\n }\n }\n\n return stepOk(step, [`Moved Linear issue ${ctx.issueId} to Done`]);\n } catch (err) {\n const message = (err as Error).message;\n\n // Detect rate limit errors and activate circuit breaker\n if (message.includes('Rate limit') || message.includes('rate limit') || message.includes('429')) {\n _linearRateLimitUntil = Date.now() + LINEAR_RATE_LIMIT_COOLDOWN_MS;\n console.warn(`[close-issue] Linear rate limit hit — circuit breaker activated for 1 hour`);\n return stepFailed(step, `Linear rate limit exceeded. Circuit breaker activated — no Linear API calls for 1 hour. Issue will be closed during close-out ceremony.`);\n }\n\n return stepFailed(step, `Linear close failed: ${message}`);\n }\n}\n\n/**\n * Close a Rally issue via RallyTracker.\n */\nasync function closeRallyDirect(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.rally) {\n return stepFailed(step, 'Rally config not provided');\n }\n try {\n const { RallyTracker } = await import('../tracker/rally.js');\n const tracker = new RallyTracker({\n apiKey: ctx.rally.apiKey,\n server: ctx.rally.server,\n workspace: ctx.rally.workspace,\n project: ctx.rally.project,\n });\n await tracker.transitionIssue(ctx.issueId, 'closed');\n return stepOk(step, [`Closed Rally issue ${ctx.issueId}`]);\n } catch (err) {\n return stepFailed(step, `Rally close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Apply 'closed-out' label and remove workflow labels.\n * Uses tracker if available, falls back to direct calls.\n */\nasync function applyClosedOutLabel(\n ctx: LifecycleContext,\n tracker?: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n\n if (tracker) {\n return applyLabelViaTracker(ctx, tracker);\n }\n\n if (ctx.github) {\n return applyLabelGitHub(ctx);\n }\n\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return applyLabelLinear(ctx, linearApiKey);\n }\n\n return stepSkipped(step, ['No tracker available for label management']);\n}\n\nasync function applyLabelViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const issue = await tracker.getIssue(ctx.issueId);\n const newLabels = issue.labels.filter(l => !WORKFLOW_LABELS.includes(l));\n if (!newLabels.includes(CLOSED_OUT_LABEL)) {\n newLabels.push(CLOSED_OUT_LABEL);\n }\n await tracker.updateIssue(ctx.issueId, { labels: newLabels });\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label via ${tracker.name} tracker`]);\n } catch (err) {\n // Label management is non-fatal\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelGitHub(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:label';\n if (!ctx.github) return stepSkipped(step);\n const { owner, repo, number } = ctx.github;\n\n try {\n // Ensure label exists\n await execAsync(\n `gh label create \"${CLOSED_OUT_LABEL}\" --repo ${owner}/${repo} --color \"${CLOSED_OUT_COLOR}\" --description \"Verified and closed out\" --force 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n // Add label\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"${CLOSED_OUT_LABEL}\"`,\n { encoding: 'utf-8' },\n );\n // Remove workflow labels\n for (const label of WORKFLOW_LABELS) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\" 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n }\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on GitHub`]);\n } catch (err) {\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelLinear(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNum = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const teamKey = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length === 0) {\n return stepSkipped(step, ['Issue not found for label management']);\n }\n\n const issue = results.nodes[0];\n\n // Find or create closed-out label\n const labels = await client.issueLabels({ filter: { name: { eq: CLOSED_OUT_LABEL } } });\n let labelId: string;\n if (labels.nodes.length > 0) {\n labelId = labels.nodes[0].id;\n } else {\n const created = await client.createIssueLabel({ name: CLOSED_OUT_LABEL, color: `#${CLOSED_OUT_COLOR}` });\n const createdLabel = await created.issueLabel;\n labelId = createdLabel ? createdLabel.id : '';\n }\n\n if (labelId) {\n const existingLabels = await issue.labels();\n const labelIds = existingLabels.nodes.map(l => l.id);\n if (!labelIds.includes(labelId)) {\n labelIds.push(labelId);\n await issue.update({ labelIds });\n }\n }\n\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on Linear`]);\n } catch (err) {\n return stepSkipped(step, [`Linear label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-store-O9q0Gweh.js","names":[],"sources":["../../src/dashboard/server/event-store.ts"],"sourcesContent":["/**\n * Event Store — SQLite-backed append-only event log with PubSub (PAN-428)\n *\n * - Persists domain events to panopticon.db `events` table\n * - In-memory PubSub for live streaming to WebSocket clients\n * - Monotonic, gap-free sequence numbers (SQLite AUTOINCREMENT)\n * - 7-day retention with startup compaction\n * - Dual-runtime: bun:sqlite on Bun, better-sqlite3 on Node\n *\n * Usage:\n * const store = await initEventStore();\n * const seq = store.append({ type: 'agent.started', ... });\n * const past = store.readFrom(0);\n * const unsub = store.subscribe(event => console.log(event));\n */\n\nimport { EventEmitter } from 'node:events';\nimport { existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getPanopticonHome } from '../../lib/paths.js';\nimport type { DomainEvent } from '@panopticon/contracts';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface StoredEvent {\n sequence: number;\n type: string;\n timestamp: string;\n payload: unknown;\n}\n\nexport type EventSubscriber = (event: StoredEvent) => void;\nexport type Unsubscribe = () => void;\n\nexport interface EventStore {\n /** Append a domain event. Returns the assigned sequence number. */\n append(event: Omit<DomainEvent, 'sequence'>): number;\n /** Return all events with sequence > fromSequence (exclusive lower bound). */\n readFrom(fromSequence: number): StoredEvent[];\n /** Subscribe to live events. Returns an unsubscribe function. */\n subscribe(fn: EventSubscriber): Unsubscribe;\n /** Run 7-day retention compaction. Called at startup. */\n compact(): void;\n /** Return the highest sequence number in the store (0 if empty). */\n getLatestSequence(): number;\n}\n\n// ─── Minimal DB interface (compatible with bun:sqlite and better-sqlite3) ────\n//\n// Uses positional parameters (?) in all SQL to avoid runtime differences in\n// named-parameter binding syntax:\n// - bun:sqlite requires sigil in binding keys: { $name: value }\n// - better-sqlite3 requires no sigil: { name: value }\n// Positional parameters + arrays work identically in both runtimes.\n\ninterface PreparedStatement<R = Record<string, unknown>> {\n run(params?: unknown[]): { changes: number };\n get(params?: unknown[]): R | undefined | null;\n all(params?: unknown[]): R[];\n}\n\nexport interface DbAdapter {\n prepare<R = Record<string, unknown>>(sql: string): PreparedStatement<R>;\n exec(sql: string): void;\n}\n\n// ─── Row shape from SQLite ─────────────────────────────────────────────────────\n\ninterface EventRow {\n sequence: number;\n type: string;\n timestamp: string;\n payload: string;\n}\n\nfunction rowToStored(row: EventRow): StoredEvent {\n return {\n sequence: row.sequence,\n type: row.type,\n timestamp: row.timestamp,\n payload: JSON.parse(row.payload),\n };\n}\n\n// ─── Runtime-aware DB initializer ────────────────────────────────────────────\n\ndeclare const Bun: unknown;\n\n/**\n * Open the panopticon.db database using the appropriate driver for the runtime.\n * Under Bun: uses bun:sqlite (native, no native addons needed).\n * Under Node: uses the shared getDatabase() which applies migrations.\n */\nexport async function openEventDb(): Promise<DbAdapter> {\n const home = getPanopticonHome();\n if (!existsSync(home)) {\n mkdirSync(home, { recursive: true });\n }\n const dbPath = join(home, 'panopticon.db');\n\n if (typeof Bun !== 'undefined') {\n const { Database } = await import('bun:sqlite');\n const db = new Database(dbPath, { create: true });\n db.exec('PRAGMA journal_mode = WAL');\n db.exec('PRAGMA foreign_keys = ON');\n db.exec('PRAGMA synchronous = NORMAL');\n // Ensure required tables exist (Bun doesn't run the shared schema migrations)\n db.exec(`\n CREATE TABLE IF NOT EXISTS events (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n type TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n payload TEXT NOT NULL DEFAULT '{}'\n )\n `);\n db.exec(`CREATE INDEX IF NOT EXISTS events_timestamp_idx ON events (timestamp)`);\n db.exec(`\n CREATE TABLE IF NOT EXISTS projection_cache (\n key TEXT PRIMARY KEY,\n data TEXT NOT NULL,\n sequence INTEGER NOT NULL,\n updated_at TEXT NOT NULL\n )\n `);\n return db as unknown as DbAdapter;\n } else {\n // Node.js: use shared database connection — migrations run there\n const { getDatabase } = await import('../../lib/database/index.js');\n return getDatabase() as unknown as DbAdapter;\n }\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\n/**\n * Create an EventStore from a pre-opened DbAdapter.\n * Call openEventDb() first to get the adapter.\n */\nexport function createEventStore(db: DbAdapter): EventStore {\n const emitter = new EventEmitter();\n // Allow many subscribers (one per WebSocket connection)\n emitter.setMaxListeners(0);\n\n // Prepared statements for hot path performance.\n // All SQL uses positional parameters (?) — both bun:sqlite and better-sqlite3\n // accept arrays for positional bindings with no runtime-specific differences.\n const insertStmt = db.prepare<void>(\n `INSERT INTO events (type, timestamp, payload) VALUES (?, ?, ?)`,\n );\n const readFromStmt = db.prepare<EventRow>(\n `SELECT sequence, type, timestamp, payload FROM events WHERE sequence > ? ORDER BY sequence ASC`,\n );\n const compactStmt = db.prepare<void>(\n `DELETE FROM events WHERE timestamp < ?`,\n );\n const latestSeqStmt = db.prepare<{ seq: number | null }>(\n `SELECT MAX(sequence) AS seq FROM events`,\n );\n const lastRowIdStmt = db.prepare<{ sequence: number }>(\n `SELECT last_insert_rowid() AS sequence`,\n );\n\n function append(event: Omit<DomainEvent, 'sequence'>): number {\n const timestamp =\n (event as Record<string, unknown>)['timestamp'] as string ?? new Date().toISOString();\n const payload = JSON.stringify((event as Record<string, unknown>)['payload'] ?? {});\n\n insertStmt.run([event.type, timestamp, payload]);\n\n const row = lastRowIdStmt.get();\n const sequence = row?.sequence ?? 0;\n\n const stored: StoredEvent = {\n sequence,\n type: event.type,\n timestamp,\n payload: (event as Record<string, unknown>)['payload'] ?? {},\n };\n\n emitter.emit('event', stored);\n return sequence;\n }\n\n function readFrom(fromSequence: number): StoredEvent[] {\n const rows = readFromStmt.all([fromSequence]);\n return rows.map(rowToStored);\n }\n\n function subscribe(fn: EventSubscriber): Unsubscribe {\n emitter.on('event', fn);\n return () => emitter.off('event', fn);\n }\n\n function compact(): void {\n const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();\n const result = compactStmt.run([sevenDaysAgo]);\n if (result.changes > 0) {\n console.log(`[event-store] Compacted ${result.changes} events older than 7 days`);\n }\n }\n\n function getLatestSequence(): number {\n const row = latestSeqStmt.get();\n return row?.seq ?? 0;\n }\n\n return { append, readFrom, subscribe, compact, getLatestSequence };\n}\n\n// ─── Module-level singleton ───────────────────────────────────────────────────\n\nlet _store: EventStore | null = null;\nlet _db: DbAdapter | null = null;\nlet _initPromise: Promise<EventStore> | null = null;\n\n/**\n * Initialize and return the process-singleton EventStore (async, Bun-compatible).\n * Idempotent — returns the same store on subsequent calls.\n */\nexport async function initEventStore(): Promise<EventStore> {\n if (_store) return _store;\n if (_initPromise) return _initPromise;\n\n _initPromise = openEventDb().then((db) => {\n _db = db;\n const store = createEventStore(db);\n store.compact();\n _store = store;\n // Initialize projection cache with same DB connection\n import('./services/projection-cache.js').then(({ initProjectionCache }) => {\n initProjectionCache(db);\n }).catch(() => { /* module not available yet */ });\n return store;\n });\n\n return _initPromise;\n}\n\n/**\n * Return the shared DbAdapter after initEventStore() has resolved.\n * Used by services that need access to the same DB connection.\n */\nexport function getSharedDb(): DbAdapter {\n if (!_db) {\n throw new Error('[event-store] getSharedDb() called before initEventStore() resolved.');\n }\n return _db;\n}\n\n/**\n * Synchronous accessor — returns the store if already initialized, throws otherwise.\n * Used by legacy callers that expect a sync API (event-store unit tests, etc.)\n */\nexport function getEventStore(): EventStore {\n if (!_store) {\n throw new Error(\n '[event-store] getEventStore() called before initEventStore() resolved. ' +\n 'Use initEventStore() for async initialization.',\n );\n }\n return _store;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;YAmBuD;AAwDvD,SAAS,YAAY,KAA4B;AAC/C,QAAO;EACL,UAAU,IAAI;EACd,MAAM,IAAI;EACV,WAAW,IAAI;EACf,SAAS,KAAK,MAAM,IAAI,QAAQ;EACjC;;;;;;;AAYH,eAAsB,cAAkC;CACtD,MAAM,OAAO,mBAAmB;AAChC,KAAI,CAAC,WAAW,KAAK,CACnB,WAAU,MAAM,EAAE,WAAW,MAAM,CAAC;CAEtC,MAAM,SAAS,KAAK,MAAM,gBAAgB;AAE1C,KAAI,OAAO,QAAQ,aAAa;EAC9B,MAAM,EAAE,aAAa,MAAM,OAAO;EAClC,MAAM,KAAK,IAAI,SAAS,QAAQ,EAAE,QAAQ,MAAM,CAAC;AACjD,KAAG,KAAK,4BAA4B;AACpC,KAAG,KAAK,2BAA2B;AACnC,KAAG,KAAK,8BAA8B;AAEtC,KAAG,KAAK;;;;;;;MAON;AACF,KAAG,KAAK,wEAAwE;AAChF,KAAG,KAAK;;;;;;;MAON;AACF,SAAO;QACF;EAEL,MAAM,EAAE,gBAAgB,MAAM,OAAO;AACrC,SAAO,aAAa;;;;;;;AAUxB,SAAgB,iBAAiB,IAA2B;CAC1D,MAAM,UAAU,IAAI,cAAc;AAElC,SAAQ,gBAAgB,EAAE;CAK1B,MAAM,aAAa,GAAG,QACpB,iEACD;CACD,MAAM,eAAe,GAAG,QACtB,iGACD;CACD,MAAM,cAAc,GAAG,QACrB,yCACD;CACD,MAAM,gBAAgB,GAAG,QACvB,0CACD;CACD,MAAM,gBAAgB,GAAG,QACvB,yCACD;CAED,SAAS,OAAO,OAA8C;EAC5D,MAAM,YACH,MAAkC,iCAA0B,IAAI,MAAM,EAAC,aAAa;EACvF,MAAM,UAAU,KAAK,UAAW,MAAkC,cAAc,EAAE,CAAC;AAEnF,aAAW,IAAI;GAAC,MAAM;GAAM;GAAW;GAAQ,CAAC;EAGhD,MAAM,WADM,cAAc,KAAK,EACT,YAAY;EAElC,MAAM,SAAsB;GAC1B;GACA,MAAM,MAAM;GACZ;GACA,SAAU,MAAkC,cAAc,EAAE;GAC7D;AAED,UAAQ,KAAK,SAAS,OAAO;AAC7B,SAAO;;CAGT,SAAS,SAAS,cAAqC;AAErD,SADa,aAAa,IAAI,CAAC,aAAa,CAAC,CACjC,IAAI,YAAY;;CAG9B,SAAS,UAAU,IAAkC;AACnD,UAAQ,GAAG,SAAS,GAAG;AACvB,eAAa,QAAQ,IAAI,SAAS,GAAG;;CAGvC,SAAS,UAAgB;EACvB,MAAM,gCAAe,IAAI,KAAK,KAAK,KAAK,GAAG,QAAc,KAAK,IAAK,EAAC,aAAa;EACjF,MAAM,SAAS,YAAY,IAAI,CAAC,aAAa,CAAC;AAC9C,MAAI,OAAO,UAAU,EACnB,SAAQ,IAAI,2BAA2B,OAAO,QAAQ,2BAA2B;;CAIrF,SAAS,oBAA4B;AAEnC,SADY,cAAc,KAAK,EACnB,OAAO;;AAGrB,QAAO;EAAE;EAAQ;EAAU;EAAW;EAAS;EAAmB;;AAKpE,IAAI,SAA4B;AAChC,IAAI,MAAwB;AAC5B,IAAI,eAA2C;;;;;AAM/C,eAAsB,iBAAsC;AAC1D,KAAI,OAAQ,QAAO;AACnB,KAAI,aAAc,QAAO;AAEzB,gBAAe,aAAa,CAAC,MAAM,OAAO;AACxC,QAAM;EACN,MAAM,QAAQ,iBAAiB,GAAG;AAClC,QAAM,SAAS;AACf,WAAS;AAET,SAAO,kCAAkC,MAAM,EAAE,0BAA0B;AACzE,uBAAoB,GAAG;IACvB,CAAC,YAAY,GAAmC;AAClD,SAAO;GACP;AAEF,QAAO;;;;;;AAOT,SAAgB,cAAyB;AACvC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,uEAAuE;AAEzF,QAAO;;;;;;AAOT,SAAgB,gBAA4B;AAC1C,KAAI,CAAC,OACH,OAAM,IAAI,MACR,wHAED;AAEH,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"label-cleanup-CZEsbtq9.js","names":[],"sources":["../../src/lib/lifecycle/label-cleanup.ts"],"sourcesContent":["/**\n * label-cleanup — Remove workflow labels and apply 'merged' label after merge.\n *\n * Runs as part of postMergeLifecycle (step 3b), independently of close-issue.\n * Labels are cleaned even if the issue close step fails.\n *\n * Removes: in-review, in-progress, merge-agent\n * Adds: merged\n */\n\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\n\nconst execAsync = promisify(exec);\n\nconst MERGED_LABEL = 'merged';\nconst MERGED_COLOR = '0e8a16'; // green\nconst LABELS_TO_REMOVE = ['in-review', 'in-progress', 'merge-agent'];\n\n/**\n * Remove workflow labels and apply 'merged' label.\n * Non-fatal: label management failure does not block the merge lifecycle.\n */\nexport async function cleanupMergedLabels(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'label-cleanup:merged';\n\n if (ctx.github) {\n return cleanupLabelsGitHub(ctx);\n }\n\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return cleanupLabelsLinear(ctx, linearApiKey);\n }\n\n return stepSkipped(step, ['No tracker available for label cleanup']);\n}\n\nasync function cleanupLabelsGitHub(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'label-cleanup:merged';\n if (!ctx.github) return stepSkipped(step);\n const { owner, repo, number } = ctx.github;\n\n try {\n // Ensure merged label exists\n await execAsync(\n `gh label create \"${MERGED_LABEL}\" --repo ${owner}/${repo} --color \"${MERGED_COLOR}\" --description \"Merged to main\" --force 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n\n // Add merged label\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"${MERGED_LABEL}\"`,\n { encoding: 'utf-8' },\n );\n\n // Remove workflow labels (best-effort — skip if not present)\n for (const label of LABELS_TO_REMOVE) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\" 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n }\n\n return stepOk(step, [\n `Applied '${MERGED_LABEL}' label on GitHub #${number}`,\n `Removed: ${LABELS_TO_REMOVE.join(', ')}`,\n ]);\n } catch (err) {\n return stepFailed(step, `Label cleanup failed: ${(err as Error).message}`);\n }\n}\n\nasync function cleanupLabelsLinear(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'label-cleanup:merged';\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNum = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const teamKey = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n\n if (results.nodes.length === 0) {\n return stepSkipped(step, ['Issue not found for label cleanup']);\n }\n\n const issue = results.nodes[0];\n\n // Find or create merged label\n const labelSearch = await client.issueLabels({ filter: { name: { eq: MERGED_LABEL } } });\n let mergedLabelId: string;\n if (labelSearch.nodes.length > 0) {\n mergedLabelId = labelSearch.nodes[0].id;\n } else {\n const created = await client.createIssueLabel({ name: MERGED_LABEL, color: `#${MERGED_COLOR}` });\n const createdLabel = await created.issueLabel;\n mergedLabelId = createdLabel ? createdLabel.id : '';\n }\n\n if (mergedLabelId) {\n const existingLabels = await issue.labels();\n // Remove workflow labels, add merged\n const filteredIds = existingLabels.nodes\n .filter(l => !LABELS_TO_REMOVE.includes(l.name))\n .map(l => l.id);\n if (!filteredIds.includes(mergedLabelId)) {\n filteredIds.push(mergedLabelId);\n }\n await issue.update({ labelIds: filteredIds });\n }\n\n return stepOk(step, [\n `Applied '${MERGED_LABEL}' label on Linear ${ctx.issueId}`,\n `Removed: ${LABELS_TO_REMOVE.join(', ')}`,\n ]);\n } catch (err) {\n return stepFailed(step, `Linear label cleanup failed: ${(err as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAM,YAAY,UAAU,KAAK;AAEjC,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,mBAAmB;CAAC;CAAa;CAAe;CAAc;;;;;AAMpE,eAAsB,oBAAoB,KAA4C;CACpF,MAAM,OAAO;AAEb,KAAI,IAAI,OACN,QAAO,oBAAoB,IAAI;CAGjC,MAAM,eAAe,iBAAiB;AACtC,KAAI,aACF,QAAO,oBAAoB,KAAK,aAAa;AAG/C,QAAO,YAAY,MAAM,CAAC,yCAAyC,CAAC;;AAGtE,eAAe,oBAAoB,KAA4C;CAC7E,MAAM,OAAO;AACb,KAAI,CAAC,IAAI,OAAQ,QAAO,YAAY,KAAK;CACzC,MAAM,EAAE,OAAO,MAAM,WAAW,IAAI;AAEpC,KAAI;AAEF,QAAM,UACJ,oBAAoB,aAAa,WAAW,MAAM,GAAG,KAAK,YAAY,aAAa,+DACnF,EAAE,UAAU,SAAS,CACtB;AAGD,QAAM,UACJ,iBAAiB,OAAO,UAAU,MAAM,GAAG,KAAK,gBAAgB,aAAa,IAC7E,EAAE,UAAU,SAAS,CACtB;AAGD,OAAK,MAAM,SAAS,iBAClB,OAAM,UACJ,iBAAiB,OAAO,UAAU,MAAM,GAAG,KAAK,mBAAmB,MAAM,wBACzE,EAAE,UAAU,SAAS,CACtB;AAGH,SAAO,OAAO,MAAM,CAClB,YAAY,aAAa,qBAAqB,UAC9C,YAAY,iBAAiB,KAAK,KAAK,GACxC,CAAC;UACK,KAAK;AACZ,SAAO,WAAW,MAAM,yBAA0B,IAAc,UAAU;;;AAI9E,eAAe,oBAAoB,KAAuB,QAAqC;CAC7F,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,iBAAiB,MAAM,OAAO;EACtC,MAAM,SAAS,IAAI,aAAa,EAAE,QAAQ,CAAC;EAE3C,MAAM,WAAW,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG;EAClE,MAAM,UAAU,IAAI,QAAQ,MAAM,IAAI,CAAC,GAAG,aAAa;EACvD,MAAM,UAAU,MAAM,OAAO,OAAO;GAClC,QAAQ;IACN,QAAQ,EAAE,IAAI,UAAU;IACxB,MAAM,EAAE,KAAK,EAAE,IAAI,SAAS,EAAE;IAC/B;GACD,OAAO;GACR,CAAC;AAEF,MAAI,QAAQ,MAAM,WAAW,EAC3B,QAAO,YAAY,MAAM,CAAC,oCAAoC,CAAC;EAGjE,MAAM,QAAQ,QAAQ,MAAM;EAG5B,MAAM,cAAc,MAAM,OAAO,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,cAAc,EAAE,EAAE,CAAC;EACxF,IAAI;AACJ,MAAI,YAAY,MAAM,SAAS,EAC7B,iBAAgB,YAAY,MAAM,GAAG;OAChC;GAEL,MAAM,eAAe,OADL,MAAM,OAAO,iBAAiB;IAAE,MAAM;IAAc,OAAO,IAAI;IAAgB,CAAC,EAC7D;AACnC,mBAAgB,eAAe,aAAa,KAAK;;AAGnD,MAAI,eAAe;GAGjB,MAAM,eAFiB,MAAM,MAAM,QAAQ,EAER,MAChC,QAAO,MAAK,CAAC,iBAAiB,SAAS,EAAE,KAAK,CAAC,CAC/C,KAAI,MAAK,EAAE,GAAG;AACjB,OAAI,CAAC,YAAY,SAAS,cAAc,CACtC,aAAY,KAAK,cAAc;AAEjC,SAAM,MAAM,OAAO,EAAE,UAAU,aAAa,CAAC;;AAG/C,SAAO,OAAO,MAAM,CAClB,YAAY,aAAa,oBAAoB,IAAI,WACjD,YAAY,iBAAiB,KAAK,KAAK,GACxC,CAAC;UACK,KAAK;AACZ,SAAO,WAAW,MAAM,gCAAiC,IAAc,UAAU"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { i as stepSkipped, n as stepFailed, r as stepOk } from "./types-CWA-o4UN.js";
|
|
2
|
-
import { t as archivePlanning } from "./archive-planning-h-hAjk0P.js";
|
|
3
|
-
import { t as cleanPlanningArtifacts } from "./clean-planning-qafw99vY.js";
|
|
4
|
-
import { t as closeIssue } from "./close-issue-DfIggeZD.js";
|
|
5
|
-
import { t as compactBeads } from "./compact-beads-Dt0qTqsC.js";
|
|
6
|
-
import { a as teardownWorkspace, i as deepWipe, n as close, r as closeOut, t as approve } from "./workflows-N1UTipYl.js";
|
|
7
|
-
export { approve, archivePlanning, cleanPlanningArtifacts, close, closeIssue, closeOut, compactBeads, deepWipe, stepFailed, stepOk, stepSkipped, teardownWorkspace };
|