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
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as openEventDb, i as initEventStore, n as getEventStore, r as getSharedDb, t as createEventStore } from "./event-store-
|
|
1
|
+
import { a as openEventDb, i as initEventStore, n as getEventStore, r as getSharedDb, t as createEventStore } from "./event-store-vSmAA3Zp.js";
|
|
2
2
|
export { createEventStore, getEventStore, getSharedDb, initEventStore, openEventDb };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as init_paths, g as getPanopticonHome } from "./paths-BDyJ7BiV.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import { existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -61,7 +61,7 @@ async function openEventDb() {
|
|
|
61
61
|
`);
|
|
62
62
|
return db;
|
|
63
63
|
} else {
|
|
64
|
-
const { getDatabase } = await import("./database-
|
|
64
|
+
const { getDatabase } = await import("./database-DhqASALP.js");
|
|
65
65
|
return getDatabase();
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -77,6 +77,7 @@ function createEventStore(db) {
|
|
|
77
77
|
const compactStmt = db.prepare(`DELETE FROM events WHERE timestamp < ?`);
|
|
78
78
|
const latestSeqStmt = db.prepare(`SELECT MAX(sequence) AS seq FROM events`);
|
|
79
79
|
const lastRowIdStmt = db.prepare(`SELECT last_insert_rowid() AS sequence`);
|
|
80
|
+
const queryByTypeStmt = db.prepare(`SELECT sequence, type, timestamp, payload FROM events WHERE type = ? ORDER BY sequence DESC LIMIT ?`);
|
|
80
81
|
function append(event) {
|
|
81
82
|
const timestamp = event["timestamp"] ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
82
83
|
const payload = JSON.stringify(event["payload"] ?? {});
|
|
@@ -110,9 +111,13 @@ function createEventStore(db) {
|
|
|
110
111
|
function getLatestSequence() {
|
|
111
112
|
return latestSeqStmt.get()?.seq ?? 0;
|
|
112
113
|
}
|
|
114
|
+
function queryByType(type, limit = 100) {
|
|
115
|
+
return queryByTypeStmt.all([type, limit]).map(rowToStored).reverse();
|
|
116
|
+
}
|
|
113
117
|
return {
|
|
114
118
|
append,
|
|
115
119
|
readFrom,
|
|
120
|
+
queryByType,
|
|
116
121
|
subscribe,
|
|
117
122
|
compact,
|
|
118
123
|
getLatestSequence
|
|
@@ -133,7 +138,7 @@ async function initEventStore() {
|
|
|
133
138
|
const store = createEventStore(db);
|
|
134
139
|
store.compact();
|
|
135
140
|
_store = store;
|
|
136
|
-
import("./projection-cache-
|
|
141
|
+
import("./projection-cache-C0EL8s8h.js").then(({ initProjectionCache }) => {
|
|
137
142
|
initProjectionCache(db);
|
|
138
143
|
}).catch(() => {});
|
|
139
144
|
return store;
|
|
@@ -159,4 +164,4 @@ function getEventStore() {
|
|
|
159
164
|
//#endregion
|
|
160
165
|
export { openEventDb as a, initEventStore as i, getEventStore as n, getSharedDb as r, createEventStore as t };
|
|
161
166
|
|
|
162
|
-
//# sourceMappingURL=event-store-
|
|
167
|
+
//# sourceMappingURL=event-store-vSmAA3Zp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store-vSmAA3Zp.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 /** Return events of a given type, most recent first, capped at limit. */\n queryByType(type: string, limit?: 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 const queryByTypeStmt = db.prepare<EventRow>(\n `SELECT sequence, type, timestamp, payload FROM events WHERE type = ? ORDER BY sequence DESC LIMIT ?`,\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 function queryByType(type: string, limit = 100): StoredEvent[] {\n const rows = queryByTypeStmt.all([type, limit]);\n // Return most-recent-first (ORDER BY sequence DESC)\n return rows.map(rowToStored).reverse();\n }\n\n return { append, readFrom, queryByType, 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":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as __esmMin } from "./chunk-DORXReHP.js";
|
|
2
2
|
import { i as loadConfig, r as init_config_yaml } from "./config-yaml-DSfYpzN6.js";
|
|
3
|
-
import { n as init_dist_src, t as Octokit } from "./dist-src-
|
|
4
|
-
import { a as TrackerAuthError, i as NotImplementedError, n as init_rally, o as init_interface, r as IssueNotFoundError, t as RallyTracker } from "./rally-
|
|
3
|
+
import { n as init_dist_src, t as Octokit } from "./dist-src-DTm11oQr.js";
|
|
4
|
+
import { a as TrackerAuthError, i as NotImplementedError, n as init_rally, o as init_interface, r as IssueNotFoundError, t as RallyTracker } from "./rally-YjFRxIiC.js";
|
|
5
5
|
import { LinearClient } from "@linear/sdk";
|
|
6
6
|
//#region ../../lib/tracker/linear.ts
|
|
7
7
|
/**
|
|
@@ -498,4 +498,4 @@ var init_factory = __esmMin((() => {
|
|
|
498
498
|
//#endregion
|
|
499
499
|
export { createTrackerFromConfig as n, init_factory as r, createTracker as t };
|
|
500
500
|
|
|
501
|
-
//# sourceMappingURL=factory-
|
|
501
|
+
//# sourceMappingURL=factory-C8nhLGHB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory-BnLdiQW-.js","names":["loadYamlConfig"],"sources":["../../src/lib/tracker/linear.ts","../../src/lib/tracker/github.ts","../../src/lib/tracker/gitlab.ts","../../src/lib/tracker/factory.ts"],"sourcesContent":["/**\n * Linear Issue Tracker Adapter\n *\n * Implements IssueTracker interface for Linear.\n */\n\nimport { LinearClient } from '@linear/sdk';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n// Map Linear state types to our normalized states\nconst STATE_MAP: Record<string, IssueState> = {\n backlog: 'open',\n unstarted: 'open',\n started: 'in_progress',\n completed: 'closed',\n canceled: 'closed',\n};\n\nexport class LinearTracker implements IssueTracker {\n readonly name: TrackerType = 'linear';\n private client: LinearClient;\n private defaultTeam?: string;\n\n constructor(apiKey: string, options?: { team?: string }) {\n if (!apiKey) {\n throw new TrackerAuthError('linear', 'API key is required');\n }\n this.client = new LinearClient({ apiKey });\n this.defaultTeam = options?.team;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const team = filters?.team ?? this.defaultTeam;\n\n const result = await this.client.issues({\n first: filters?.limit ?? 50,\n filter: {\n team: team ? { key: { eq: team } } : undefined,\n state: filters?.state\n ? { type: { eq: this.reverseMapState(filters.state) } }\n : filters?.includeClosed\n ? undefined\n : { type: { neq: 'completed' } },\n labels: filters?.labels?.length\n ? { name: { in: filters.labels } }\n : undefined,\n assignee: filters?.assignee\n ? { name: { containsIgnoreCase: filters.assignee } }\n : undefined,\n },\n });\n\n const issues: Issue[] = [];\n for (const node of result.nodes) {\n issues.push(await this.normalizeIssue(node));\n }\n return issues;\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Check if it's a UUID (36 chars with hyphens)\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n\n if (isUuid) {\n // Fetch directly by UUID\n const issue = await this.client.issue(id);\n if (issue) {\n return this.normalizeIssue(issue);\n }\n } else {\n // Parse identifier (e.g., MIN-630) and search\n const match = id.match(/^([A-Z]+)-(\\d+)$/i);\n if (match) {\n const [, teamKey, number] = match;\n // Use searchIssues which supports identifier matching\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n return this.normalizeIssue(results.nodes[0]);\n }\n }\n }\n\n throw new IssueNotFoundError(id, 'linear');\n } catch (error) {\n if (error instanceof IssueNotFoundError) throw error;\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issue = await this.getIssue(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.description = update.description;\n }\n if (update.priority !== undefined) {\n updatePayload.priority = update.priority;\n }\n if (update.dueDate !== undefined) {\n updatePayload.dueDate = update.dueDate;\n }\n if (update.state !== undefined) {\n // Need to find the state ID - this is complex in Linear\n // For now, we'll use the transition method\n await this.transitionIssue(id, update.state);\n }\n if (update.labels !== undefined) {\n // Need to look up label IDs - complex operation\n // TODO: Implement label updates\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await this.client.updateIssue(issue.id, updatePayload);\n }\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const team = newIssue.team ?? this.defaultTeam;\n\n if (!team) {\n throw new Error('Team is required to create an issue');\n }\n\n // Get team ID from key\n const teams = await this.client.teams({\n filter: { key: { eq: team } },\n });\n\n if (teams.nodes.length === 0) {\n throw new Error(`Team not found: ${team}`);\n }\n\n const teamId = teams.nodes[0].id;\n\n const result = await this.client.createIssue({\n teamId,\n title: newIssue.title,\n description: newIssue.description,\n priority: newIssue.priority,\n dueDate: newIssue.dueDate,\n });\n\n const created = await result.issue;\n if (!created) {\n throw new Error('Failed to create issue');\n }\n\n return this.normalizeIssue(created);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issue = await this.client.issue(issueId);\n const comments = await issue.comments();\n\n return comments.nodes.map((c) => ({\n id: c.id,\n issueId,\n body: c.body,\n author: c.user?.then((u) => u?.name ?? 'Unknown') as unknown as string, // Simplified\n createdAt: c.createdAt.toISOString(),\n updatedAt: c.updatedAt.toISOString(),\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const result = await this.client.createComment({\n issueId,\n body,\n });\n\n const comment = await result.comment;\n if (!comment) {\n throw new Error('Failed to create comment');\n }\n\n return {\n id: comment.id,\n issueId,\n body: comment.body,\n author: 'Panopticon', // Simplified\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n // Resolve the Linear issue directly (avoid normalizeIssue which may fail on SDK edge cases)\n let linearIssue: any;\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n if (isUuid) {\n linearIssue = await this.client.issue(id);\n } else {\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n linearIssue = results.nodes[0];\n } else {\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n // Get workflow states for the issue's team\n const team = await linearIssue.team;\n if (!team) {\n throw new Error('Could not determine issue team');\n }\n\n const states = await team.states();\n\n let targetState: any;\n if (state === 'in_review') {\n // Find a state named \"In Review\" (case-insensitive) — more precise than matching by type,\n // since \"In Progress\" and \"In Review\" are both type \"started\" in Linear.\n targetState = states.nodes.find((s: any) => s.name.toLowerCase() === 'in review');\n if (!targetState) {\n // Fall back to lowest-position \"started\" state if no \"In Review\" state exists\n const startedStates = states.nodes\n .filter((s: any) => s.type === 'started')\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = startedStates[0];\n if (!targetState) {\n throw new Error('No \"In Review\" or \"started\" state found in Linear');\n }\n }\n } else {\n const targetStateType = this.reverseMapState(state);\n\n // Find a state matching the target type.\n // Multiple states can share the same type (e.g., \"In Planning\", \"In Progress\", \"In Review\"\n // are all type \"started\"). Prefer the one with the lowest position (most basic/default state\n // for that type), which matches Linear's convention.\n const matchingStates = states.nodes\n .filter((s: any) => s.type === targetStateType)\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = matchingStates[0];\n if (!targetState) {\n throw new Error(`No state found matching type: ${targetStateType}`);\n }\n }\n\n await this.client.updateIssue(linearIssue.id, {\n stateId: targetState.id,\n });\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n const issue = await this.getIssue(issueId);\n\n await this.client.createAttachment({\n issueId: issue.id,\n title: 'Pull Request',\n url: prUrl,\n });\n }\n\n private async normalizeIssue(linearIssue: any): Promise<Issue> {\n const state = await linearIssue.state;\n const assignee = await linearIssue.assignee;\n const labels = await linearIssue.labels();\n\n // Handle dueDate - can be Date, string, or undefined\n let dueDate: string | undefined;\n if (linearIssue.dueDate) {\n dueDate = linearIssue.dueDate instanceof Date\n ? linearIssue.dueDate.toISOString()\n : String(linearIssue.dueDate);\n }\n\n return {\n id: linearIssue.id,\n ref: linearIssue.identifier,\n title: linearIssue.title,\n description: linearIssue.description ?? '',\n state: this.mapState(state?.type ?? 'backlog'),\n labels: labels?.nodes?.map((l: any) => l.name) ?? [],\n assignee: assignee?.name,\n url: linearIssue.url,\n tracker: 'linear',\n priority: linearIssue.priority,\n dueDate,\n createdAt: linearIssue.createdAt instanceof Date\n ? linearIssue.createdAt.toISOString()\n : String(linearIssue.createdAt),\n updatedAt: linearIssue.updatedAt instanceof Date\n ? linearIssue.updatedAt.toISOString()\n : String(linearIssue.updatedAt),\n };\n }\n\n private mapState(linearState: string): IssueState {\n return STATE_MAP[linearState] ?? 'open';\n }\n\n private reverseMapState(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_progress':\n case 'in_review':\n return 'started';\n case 'closed':\n return 'completed';\n default:\n return 'unstarted';\n }\n }\n}\n","/**\n * GitHub Issues Tracker Adapter\n *\n * Implements IssueTracker interface for GitHub Issues.\n */\n\nimport { Octokit } from '@octokit/rest';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n/**\n * Extract issue number from various formats: \"300\", \"#300\", \"PAN-300\"\n */\nfunction parseIssueNumber(id: string): number {\n const match = id.match(/(\\d+)$/);\n return match ? parseInt(match[1], 10) : NaN;\n}\n\nexport class GitHubTracker implements IssueTracker {\n readonly name: TrackerType = 'github';\n private octokit: Octokit;\n private owner: string;\n private repo: string;\n\n constructor(token: string, owner: string, repo: string) {\n if (!token) {\n throw new TrackerAuthError('github', 'Token is required');\n }\n if (!owner || !repo) {\n throw new Error('GitHub owner and repo are required');\n }\n\n this.octokit = new Octokit({ auth: token });\n this.owner = owner;\n this.repo = repo;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const state = this.mapStateToGitHub(filters?.state);\n\n const response = await this.octokit.issues.listForRepo({\n owner: this.owner,\n repo: this.repo,\n state: filters?.includeClosed ? 'all' : state,\n labels: filters?.labels?.join(',') || undefined,\n assignee: filters?.assignee || undefined,\n per_page: filters?.limit ?? 50,\n });\n\n // Filter out pull requests (GitHub API returns both)\n const issues = response.data.filter((item) => !item.pull_request);\n\n return issues.map((issue) => this.normalizeIssue(issue));\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Parse the issue number from refs like \"#42\" or just \"42\"\n const issueNumber = parseIssueNumber(id);\n\n if (isNaN(issueNumber)) {\n throw new IssueNotFoundError(id, 'github');\n }\n\n const { data: issue } = await this.octokit.issues.get({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return this.normalizeIssue(issue);\n } catch (error: any) {\n if (error?.status === 404) {\n throw new IssueNotFoundError(id, 'github');\n }\n throw error;\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issueNumber = parseIssueNumber(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.body = update.description;\n }\n if (update.state !== undefined) {\n updatePayload.state = update.state === 'closed' ? 'closed' : 'open';\n }\n if (update.labels !== undefined) {\n updatePayload.labels = update.labels;\n }\n if (update.assignee !== undefined) {\n updatePayload.assignees = update.assignee ? [update.assignee] : [];\n }\n\n await this.octokit.issues.update({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n ...updatePayload,\n });\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const { data: issue } = await this.octokit.issues.create({\n owner: this.owner,\n repo: this.repo,\n title: newIssue.title,\n body: newIssue.description,\n labels: newIssue.labels,\n assignees: newIssue.assignee ? [newIssue.assignee] : undefined,\n });\n\n return this.normalizeIssue(issue);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comments } = await this.octokit.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return comments.map((c) => ({\n id: String(c.id),\n issueId,\n body: c.body ?? '',\n author: c.user?.login ?? 'Unknown',\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comment } = await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n body,\n });\n\n return {\n id: String(comment.id),\n issueId,\n body: comment.body ?? '',\n author: comment.user?.login ?? 'Unknown',\n createdAt: comment.created_at,\n updatedAt: comment.updated_at,\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n const issueNumber = parseIssueNumber(id);\n\n if (state === 'in_progress') {\n // GitHub has no native \"in progress\" state — use a label instead.\n await this.ensureLabelExists('in-progress', 'In progress', '0075ca');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-progress'],\n });\n } else if (state === 'in_review') {\n // Swap in-progress label for in-review label\n await this.ensureLabelExists('in-review', 'In review', 'e4e669');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-review'],\n });\n // Remove in-progress label if present\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: 'in-progress',\n }).catch(() => {/* label may not exist, ignore */});\n } else {\n // Remove in-progress and in-review labels when moving to open or closed\n const issue = await this.getIssue(id);\n for (const label of ['in-progress', 'in-review']) {\n if (issue.labels?.includes(label)) {\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: label,\n }).catch(() => {/* label may not exist, ignore */});\n }\n }\n await this.updateIssue(id, { state });\n }\n }\n\n /** Ensure a label exists in the repo, creating it if needed. */\n private async ensureLabelExists(name: string, description: string, color: string): Promise<void> {\n try {\n await this.octokit.issues.getLabel({ owner: this.owner, repo: this.repo, name });\n } catch {\n await this.octokit.issues.createLabel({\n owner: this.owner,\n repo: this.repo,\n name,\n description,\n color,\n }).catch(() => {/* race condition: another process created it first */});\n }\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n // GitHub auto-links PRs that mention issues\n // Add a comment with the PR link\n await this.addComment(\n issueId,\n `Linked Pull Request: ${prUrl}`\n );\n }\n\n private normalizeIssue(ghIssue: any): Issue {\n const labels: string[] = ghIssue.labels.map((l: any) =>\n typeof l === 'string' ? l : l.name\n );\n return {\n id: String(ghIssue.id),\n ref: `#${ghIssue.number}`,\n title: ghIssue.title,\n description: ghIssue.body ?? '',\n state: this.mapStateFromGitHub(ghIssue.state, labels),\n labels,\n assignee: ghIssue.assignee?.login,\n url: ghIssue.html_url,\n tracker: 'github',\n priority: undefined, // GitHub doesn't have priority\n dueDate: undefined, // GitHub doesn't have due dates on issues\n createdAt: ghIssue.created_at,\n updatedAt: ghIssue.updated_at,\n };\n }\n\n private mapStateFromGitHub(ghState: string, labels: string[] = []): IssueState {\n if (ghState === 'closed') return 'closed';\n if (labels.includes('in-progress')) return 'in_progress';\n return 'open';\n }\n\n private mapStateToGitHub(\n state?: IssueState\n ): 'open' | 'closed' | 'all' {\n if (!state) return 'open';\n if (state === 'closed') return 'closed';\n return 'open'; // Both 'open' and 'in_progress' map to 'open'\n }\n}\n","/**\n * GitLab Issues Tracker Adapter (Stub)\n *\n * Placeholder implementation for GitLab Issues support.\n * Full implementation will use @gitbeaker/rest.\n */\n\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { NotImplementedError } from './interface.js';\n\nexport class GitLabTracker implements IssueTracker {\n readonly name: TrackerType = 'gitlab';\n\n constructor(\n private token: string,\n private projectId: string\n ) {\n // Stub - will initialize @gitbeaker client when implemented\n }\n\n async listIssues(_filters?: IssueFilters): Promise<Issue[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getIssue(_id: string): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async updateIssue(_id: string, _update: IssueUpdate): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async createIssue(_issue: NewIssue): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getComments(_issueId: string): Promise<Comment[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async addComment(_issueId: string, _body: string): Promise<Comment> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async transitionIssue(_id: string, _state: IssueState): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async linkPR(_issueId: string, _prUrl: string): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n}\n","/**\n * Tracker Factory\n *\n * Creates appropriate tracker instances based on configuration.\n */\n\nimport type { IssueTracker, TrackerType } from './interface.js';\nimport { TrackerAuthError } from './interface.js';\nimport { LinearTracker } from './linear.js';\nimport { GitHubTracker } from './github.js';\nimport { GitLabTracker } from './gitlab.js';\nimport { RallyTracker } from './rally.js';\nimport type { TrackersConfig } from '../config.js';\nimport { loadConfig as loadYamlConfig } from '../config-yaml.js';\n\n// Configuration for a single tracker\nexport interface TrackerConfig {\n type: TrackerType;\n\n // Linear-specific\n apiKeyEnv?: string;\n team?: string;\n\n // GitHub-specific\n tokenEnv?: string;\n owner?: string;\n repo?: string;\n\n // GitLab-specific\n projectId?: string;\n\n // Rally-specific\n server?: string;\n workspace?: string;\n project?: string;\n}\n\n// Multi-tracker configuration (re-exported from config.ts)\n// Note: Use TrackersConfig from config.ts for full type with nested configs\n\n/**\n * Get tracker API key from config.yaml (Settings page).\n * This is checked FIRST — env vars are the fallback, not the other way around.\n */\nfunction getTrackerKeyFromConfig(trackerType: TrackerType): string | undefined {\n try {\n const { config: yamlConfig } = loadYamlConfig();\n return yamlConfig.trackerKeys[trackerType];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a tracker instance from configuration.\n * Priority: config.yaml (Settings) > environment variable > custom env var name\n */\nexport function createTracker(config: TrackerConfig): IssueTracker {\n switch (config.type) {\n case 'linear': {\n const configKey = getTrackerKeyFromConfig('linear');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.LINEAR_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'linear',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'LINEAR_API_KEY'} environment variable.`\n );\n }\n\n return new LinearTracker(apiKey, { team: config.team });\n }\n\n case 'github': {\n const configKey = getTrackerKeyFromConfig('github');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITHUB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'github',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITHUB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.owner || !config.repo) {\n throw new Error(\n 'GitHub tracker requires owner and repo configuration'\n );\n }\n\n return new GitHubTracker(token, config.owner, config.repo);\n }\n\n case 'gitlab': {\n const configKey = getTrackerKeyFromConfig('gitlab');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITLAB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'gitlab',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITLAB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.projectId) {\n throw new Error('GitLab tracker requires projectId configuration');\n }\n\n return new GitLabTracker(token, config.projectId);\n }\n\n case 'rally': {\n const configKey = getTrackerKeyFromConfig('rally');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.RALLY_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'rally',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'RALLY_API_KEY'} environment variable.`\n );\n }\n\n return new RallyTracker({\n apiKey,\n server: config.server,\n workspace: config.workspace,\n project: config.project,\n });\n }\n\n default:\n throw new Error(`Unknown tracker type: ${config.type}`);\n }\n}\n\n/**\n * Create tracker from trackers configuration section\n */\nexport function createTrackerFromConfig(\n trackersConfig: TrackersConfig,\n trackerType: TrackerType\n): IssueTracker {\n const config = trackersConfig[trackerType];\n\n if (!config) {\n throw new Error(\n `No configuration found for tracker: ${trackerType}. Add [trackers.${trackerType}] to config.`\n );\n }\n\n return createTracker({ ...config, type: trackerType });\n}\n\n/**\n * Get the primary tracker from configuration\n */\nexport function getPrimaryTracker(trackersConfig: TrackersConfig): IssueTracker {\n return createTrackerFromConfig(trackersConfig, trackersConfig.primary);\n}\n\n/**\n * Get the secondary tracker from configuration (if configured)\n */\nexport function getSecondaryTracker(\n trackersConfig: TrackersConfig\n): IssueTracker | null {\n if (!trackersConfig.secondary) {\n return null;\n }\n return createTrackerFromConfig(trackersConfig, trackersConfig.secondary);\n}\n\n/**\n * Get all configured trackers\n */\nexport function getAllTrackers(trackersConfig: TrackersConfig): IssueTracker[] {\n const trackers: IssueTracker[] = [getPrimaryTracker(trackersConfig)];\n\n const secondary = getSecondaryTracker(trackersConfig);\n if (secondary) {\n trackers.push(secondary);\n }\n\n return trackers;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"factory-C8nhLGHB.js","names":["loadYamlConfig"],"sources":["../../src/lib/tracker/linear.ts","../../src/lib/tracker/github.ts","../../src/lib/tracker/gitlab.ts","../../src/lib/tracker/factory.ts"],"sourcesContent":["/**\n * Linear Issue Tracker Adapter\n *\n * Implements IssueTracker interface for Linear.\n */\n\nimport { LinearClient } from '@linear/sdk';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n// Map Linear state types to our normalized states\nconst STATE_MAP: Record<string, IssueState> = {\n backlog: 'open',\n unstarted: 'open',\n started: 'in_progress',\n completed: 'closed',\n canceled: 'closed',\n};\n\nexport class LinearTracker implements IssueTracker {\n readonly name: TrackerType = 'linear';\n private client: LinearClient;\n private defaultTeam?: string;\n\n constructor(apiKey: string, options?: { team?: string }) {\n if (!apiKey) {\n throw new TrackerAuthError('linear', 'API key is required');\n }\n this.client = new LinearClient({ apiKey });\n this.defaultTeam = options?.team;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const team = filters?.team ?? this.defaultTeam;\n\n const result = await this.client.issues({\n first: filters?.limit ?? 50,\n filter: {\n team: team ? { key: { eq: team } } : undefined,\n state: filters?.state\n ? { type: { eq: this.reverseMapState(filters.state) } }\n : filters?.includeClosed\n ? undefined\n : { type: { neq: 'completed' } },\n labels: filters?.labels?.length\n ? { name: { in: filters.labels } }\n : undefined,\n assignee: filters?.assignee\n ? { name: { containsIgnoreCase: filters.assignee } }\n : undefined,\n },\n });\n\n const issues: Issue[] = [];\n for (const node of result.nodes) {\n issues.push(await this.normalizeIssue(node));\n }\n return issues;\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Check if it's a UUID (36 chars with hyphens)\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n\n if (isUuid) {\n // Fetch directly by UUID\n const issue = await this.client.issue(id);\n if (issue) {\n return this.normalizeIssue(issue);\n }\n } else {\n // Parse identifier (e.g., MIN-630) and search\n const match = id.match(/^([A-Z]+)-(\\d+)$/i);\n if (match) {\n const [, teamKey, number] = match;\n // Use searchIssues which supports identifier matching\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n return this.normalizeIssue(results.nodes[0]);\n }\n }\n }\n\n throw new IssueNotFoundError(id, 'linear');\n } catch (error) {\n if (error instanceof IssueNotFoundError) throw error;\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issue = await this.getIssue(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.description = update.description;\n }\n if (update.priority !== undefined) {\n updatePayload.priority = update.priority;\n }\n if (update.dueDate !== undefined) {\n updatePayload.dueDate = update.dueDate;\n }\n if (update.state !== undefined) {\n // Need to find the state ID - this is complex in Linear\n // For now, we'll use the transition method\n await this.transitionIssue(id, update.state);\n }\n if (update.labels !== undefined) {\n // Need to look up label IDs - complex operation\n // TODO: Implement label updates\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await this.client.updateIssue(issue.id, updatePayload);\n }\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const team = newIssue.team ?? this.defaultTeam;\n\n if (!team) {\n throw new Error('Team is required to create an issue');\n }\n\n // Get team ID from key\n const teams = await this.client.teams({\n filter: { key: { eq: team } },\n });\n\n if (teams.nodes.length === 0) {\n throw new Error(`Team not found: ${team}`);\n }\n\n const teamId = teams.nodes[0].id;\n\n const result = await this.client.createIssue({\n teamId,\n title: newIssue.title,\n description: newIssue.description,\n priority: newIssue.priority,\n dueDate: newIssue.dueDate,\n });\n\n const created = await result.issue;\n if (!created) {\n throw new Error('Failed to create issue');\n }\n\n return this.normalizeIssue(created);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issue = await this.client.issue(issueId);\n const comments = await issue.comments();\n\n return comments.nodes.map((c) => ({\n id: c.id,\n issueId,\n body: c.body,\n author: c.user?.then((u) => u?.name ?? 'Unknown') as unknown as string, // Simplified\n createdAt: c.createdAt.toISOString(),\n updatedAt: c.updatedAt.toISOString(),\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const result = await this.client.createComment({\n issueId,\n body,\n });\n\n const comment = await result.comment;\n if (!comment) {\n throw new Error('Failed to create comment');\n }\n\n return {\n id: comment.id,\n issueId,\n body: comment.body,\n author: 'Panopticon', // Simplified\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n // Resolve the Linear issue directly (avoid normalizeIssue which may fail on SDK edge cases)\n let linearIssue: any;\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n if (isUuid) {\n linearIssue = await this.client.issue(id);\n } else {\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n linearIssue = results.nodes[0];\n } else {\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n // Get workflow states for the issue's team\n const team = await linearIssue.team;\n if (!team) {\n throw new Error('Could not determine issue team');\n }\n\n const states = await team.states();\n\n let targetState: any;\n if (state === 'in_review') {\n // Find a state named \"In Review\" (case-insensitive) — more precise than matching by type,\n // since \"In Progress\" and \"In Review\" are both type \"started\" in Linear.\n targetState = states.nodes.find((s: any) => s.name.toLowerCase() === 'in review');\n if (!targetState) {\n // Fall back to lowest-position \"started\" state if no \"In Review\" state exists\n const startedStates = states.nodes\n .filter((s: any) => s.type === 'started')\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = startedStates[0];\n if (!targetState) {\n throw new Error('No \"In Review\" or \"started\" state found in Linear');\n }\n }\n } else {\n const targetStateType = this.reverseMapState(state);\n\n // Find a state matching the target type.\n // Multiple states can share the same type (e.g., \"In Planning\", \"In Progress\", \"In Review\"\n // are all type \"started\"). Prefer the one with the lowest position (most basic/default state\n // for that type), which matches Linear's convention.\n const matchingStates = states.nodes\n .filter((s: any) => s.type === targetStateType)\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = matchingStates[0];\n if (!targetState) {\n throw new Error(`No state found matching type: ${targetStateType}`);\n }\n }\n\n await this.client.updateIssue(linearIssue.id, {\n stateId: targetState.id,\n });\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n const issue = await this.getIssue(issueId);\n\n await this.client.createAttachment({\n issueId: issue.id,\n title: 'Pull Request',\n url: prUrl,\n });\n }\n\n private async normalizeIssue(linearIssue: any): Promise<Issue> {\n const state = await linearIssue.state;\n const assignee = await linearIssue.assignee;\n const labels = await linearIssue.labels();\n\n // Handle dueDate - can be Date, string, or undefined\n let dueDate: string | undefined;\n if (linearIssue.dueDate) {\n dueDate = linearIssue.dueDate instanceof Date\n ? linearIssue.dueDate.toISOString()\n : String(linearIssue.dueDate);\n }\n\n return {\n id: linearIssue.id,\n ref: linearIssue.identifier,\n title: linearIssue.title,\n description: linearIssue.description ?? '',\n state: this.mapState(state?.type ?? 'backlog'),\n labels: labels?.nodes?.map((l: any) => l.name) ?? [],\n assignee: assignee?.name,\n url: linearIssue.url,\n tracker: 'linear',\n priority: linearIssue.priority,\n dueDate,\n createdAt: linearIssue.createdAt instanceof Date\n ? linearIssue.createdAt.toISOString()\n : String(linearIssue.createdAt),\n updatedAt: linearIssue.updatedAt instanceof Date\n ? linearIssue.updatedAt.toISOString()\n : String(linearIssue.updatedAt),\n };\n }\n\n private mapState(linearState: string): IssueState {\n return STATE_MAP[linearState] ?? 'open';\n }\n\n private reverseMapState(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_progress':\n case 'in_review':\n return 'started';\n case 'closed':\n return 'completed';\n default:\n return 'unstarted';\n }\n }\n}\n","/**\n * GitHub Issues Tracker Adapter\n *\n * Implements IssueTracker interface for GitHub Issues.\n */\n\nimport { Octokit } from '@octokit/rest';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n/**\n * Extract issue number from various formats: \"300\", \"#300\", \"PAN-300\"\n */\nfunction parseIssueNumber(id: string): number {\n const match = id.match(/(\\d+)$/);\n return match ? parseInt(match[1], 10) : NaN;\n}\n\nexport class GitHubTracker implements IssueTracker {\n readonly name: TrackerType = 'github';\n private octokit: Octokit;\n private owner: string;\n private repo: string;\n\n constructor(token: string, owner: string, repo: string) {\n if (!token) {\n throw new TrackerAuthError('github', 'Token is required');\n }\n if (!owner || !repo) {\n throw new Error('GitHub owner and repo are required');\n }\n\n this.octokit = new Octokit({ auth: token });\n this.owner = owner;\n this.repo = repo;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const state = this.mapStateToGitHub(filters?.state);\n\n const response = await this.octokit.issues.listForRepo({\n owner: this.owner,\n repo: this.repo,\n state: filters?.includeClosed ? 'all' : state,\n labels: filters?.labels?.join(',') || undefined,\n assignee: filters?.assignee || undefined,\n per_page: filters?.limit ?? 50,\n });\n\n // Filter out pull requests (GitHub API returns both)\n const issues = response.data.filter((item) => !item.pull_request);\n\n return issues.map((issue) => this.normalizeIssue(issue));\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Parse the issue number from refs like \"#42\" or just \"42\"\n const issueNumber = parseIssueNumber(id);\n\n if (isNaN(issueNumber)) {\n throw new IssueNotFoundError(id, 'github');\n }\n\n const { data: issue } = await this.octokit.issues.get({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return this.normalizeIssue(issue);\n } catch (error: any) {\n if (error?.status === 404) {\n throw new IssueNotFoundError(id, 'github');\n }\n throw error;\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issueNumber = parseIssueNumber(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.body = update.description;\n }\n if (update.state !== undefined) {\n updatePayload.state = update.state === 'closed' ? 'closed' : 'open';\n }\n if (update.labels !== undefined) {\n updatePayload.labels = update.labels;\n }\n if (update.assignee !== undefined) {\n updatePayload.assignees = update.assignee ? [update.assignee] : [];\n }\n\n await this.octokit.issues.update({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n ...updatePayload,\n });\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const { data: issue } = await this.octokit.issues.create({\n owner: this.owner,\n repo: this.repo,\n title: newIssue.title,\n body: newIssue.description,\n labels: newIssue.labels,\n assignees: newIssue.assignee ? [newIssue.assignee] : undefined,\n });\n\n return this.normalizeIssue(issue);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comments } = await this.octokit.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return comments.map((c) => ({\n id: String(c.id),\n issueId,\n body: c.body ?? '',\n author: c.user?.login ?? 'Unknown',\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comment } = await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n body,\n });\n\n return {\n id: String(comment.id),\n issueId,\n body: comment.body ?? '',\n author: comment.user?.login ?? 'Unknown',\n createdAt: comment.created_at,\n updatedAt: comment.updated_at,\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n const issueNumber = parseIssueNumber(id);\n\n if (state === 'in_progress') {\n // GitHub has no native \"in progress\" state — use a label instead.\n await this.ensureLabelExists('in-progress', 'In progress', '0075ca');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-progress'],\n });\n } else if (state === 'in_review') {\n // Swap in-progress label for in-review label\n await this.ensureLabelExists('in-review', 'In review', 'e4e669');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-review'],\n });\n // Remove in-progress label if present\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: 'in-progress',\n }).catch(() => {/* label may not exist, ignore */});\n } else {\n // Remove in-progress and in-review labels when moving to open or closed\n const issue = await this.getIssue(id);\n for (const label of ['in-progress', 'in-review']) {\n if (issue.labels?.includes(label)) {\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: label,\n }).catch(() => {/* label may not exist, ignore */});\n }\n }\n await this.updateIssue(id, { state });\n }\n }\n\n /** Ensure a label exists in the repo, creating it if needed. */\n private async ensureLabelExists(name: string, description: string, color: string): Promise<void> {\n try {\n await this.octokit.issues.getLabel({ owner: this.owner, repo: this.repo, name });\n } catch {\n await this.octokit.issues.createLabel({\n owner: this.owner,\n repo: this.repo,\n name,\n description,\n color,\n }).catch(() => {/* race condition: another process created it first */});\n }\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n // GitHub auto-links PRs that mention issues\n // Add a comment with the PR link\n await this.addComment(\n issueId,\n `Linked Pull Request: ${prUrl}`\n );\n }\n\n private normalizeIssue(ghIssue: any): Issue {\n const labels: string[] = ghIssue.labels.map((l: any) =>\n typeof l === 'string' ? l : l.name\n );\n return {\n id: String(ghIssue.id),\n ref: `#${ghIssue.number}`,\n title: ghIssue.title,\n description: ghIssue.body ?? '',\n state: this.mapStateFromGitHub(ghIssue.state, labels),\n labels,\n assignee: ghIssue.assignee?.login,\n url: ghIssue.html_url,\n tracker: 'github',\n priority: undefined, // GitHub doesn't have priority\n dueDate: undefined, // GitHub doesn't have due dates on issues\n createdAt: ghIssue.created_at,\n updatedAt: ghIssue.updated_at,\n };\n }\n\n private mapStateFromGitHub(ghState: string, labels: string[] = []): IssueState {\n if (ghState === 'closed') return 'closed';\n if (labels.includes('in-progress')) return 'in_progress';\n return 'open';\n }\n\n private mapStateToGitHub(\n state?: IssueState\n ): 'open' | 'closed' | 'all' {\n if (!state) return 'open';\n if (state === 'closed') return 'closed';\n return 'open'; // Both 'open' and 'in_progress' map to 'open'\n }\n}\n","/**\n * GitLab Issues Tracker Adapter (Stub)\n *\n * Placeholder implementation for GitLab Issues support.\n * Full implementation will use @gitbeaker/rest.\n */\n\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { NotImplementedError } from './interface.js';\n\nexport class GitLabTracker implements IssueTracker {\n readonly name: TrackerType = 'gitlab';\n\n constructor(\n private token: string,\n private projectId: string\n ) {\n // Stub - will initialize @gitbeaker client when implemented\n }\n\n async listIssues(_filters?: IssueFilters): Promise<Issue[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getIssue(_id: string): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async updateIssue(_id: string, _update: IssueUpdate): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async createIssue(_issue: NewIssue): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getComments(_issueId: string): Promise<Comment[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async addComment(_issueId: string, _body: string): Promise<Comment> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async transitionIssue(_id: string, _state: IssueState): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async linkPR(_issueId: string, _prUrl: string): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n}\n","/**\n * Tracker Factory\n *\n * Creates appropriate tracker instances based on configuration.\n */\n\nimport type { IssueTracker, TrackerType } from './interface.js';\nimport { TrackerAuthError } from './interface.js';\nimport { LinearTracker } from './linear.js';\nimport { GitHubTracker } from './github.js';\nimport { GitLabTracker } from './gitlab.js';\nimport { RallyTracker } from './rally.js';\nimport type { TrackersConfig } from '../config.js';\nimport { loadConfig as loadYamlConfig } from '../config-yaml.js';\n\n// Configuration for a single tracker\nexport interface TrackerConfig {\n type: TrackerType;\n\n // Linear-specific\n apiKeyEnv?: string;\n team?: string;\n\n // GitHub-specific\n tokenEnv?: string;\n owner?: string;\n repo?: string;\n\n // GitLab-specific\n projectId?: string;\n\n // Rally-specific\n server?: string;\n workspace?: string;\n project?: string;\n}\n\n// Multi-tracker configuration (re-exported from config.ts)\n// Note: Use TrackersConfig from config.ts for full type with nested configs\n\n/**\n * Get tracker API key from config.yaml (Settings page).\n * This is checked FIRST — env vars are the fallback, not the other way around.\n */\nfunction getTrackerKeyFromConfig(trackerType: TrackerType): string | undefined {\n try {\n const { config: yamlConfig } = loadYamlConfig();\n return yamlConfig.trackerKeys[trackerType];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a tracker instance from configuration.\n * Priority: config.yaml (Settings) > environment variable > custom env var name\n */\nexport function createTracker(config: TrackerConfig): IssueTracker {\n switch (config.type) {\n case 'linear': {\n const configKey = getTrackerKeyFromConfig('linear');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.LINEAR_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'linear',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'LINEAR_API_KEY'} environment variable.`\n );\n }\n\n return new LinearTracker(apiKey, { team: config.team });\n }\n\n case 'github': {\n const configKey = getTrackerKeyFromConfig('github');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITHUB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'github',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITHUB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.owner || !config.repo) {\n throw new Error(\n 'GitHub tracker requires owner and repo configuration'\n );\n }\n\n return new GitHubTracker(token, config.owner, config.repo);\n }\n\n case 'gitlab': {\n const configKey = getTrackerKeyFromConfig('gitlab');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITLAB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'gitlab',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITLAB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.projectId) {\n throw new Error('GitLab tracker requires projectId configuration');\n }\n\n return new GitLabTracker(token, config.projectId);\n }\n\n case 'rally': {\n const configKey = getTrackerKeyFromConfig('rally');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.RALLY_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'rally',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'RALLY_API_KEY'} environment variable.`\n );\n }\n\n return new RallyTracker({\n apiKey,\n server: config.server,\n workspace: config.workspace,\n project: config.project,\n });\n }\n\n default:\n throw new Error(`Unknown tracker type: ${config.type}`);\n }\n}\n\n/**\n * Create tracker from trackers configuration section\n */\nexport function createTrackerFromConfig(\n trackersConfig: TrackersConfig,\n trackerType: TrackerType\n): IssueTracker {\n const config = trackersConfig[trackerType];\n\n if (!config) {\n throw new Error(\n `No configuration found for tracker: ${trackerType}. Add [trackers.${trackerType}] to config.`\n );\n }\n\n return createTracker({ ...config, type: trackerType });\n}\n\n/**\n * Get the primary tracker from configuration\n */\nexport function getPrimaryTracker(trackersConfig: TrackersConfig): IssueTracker {\n return createTrackerFromConfig(trackersConfig, trackersConfig.primary);\n}\n\n/**\n * Get the secondary tracker from configuration (if configured)\n */\nexport function getSecondaryTracker(\n trackersConfig: TrackersConfig\n): IssueTracker | null {\n if (!trackersConfig.secondary) {\n return null;\n }\n return createTrackerFromConfig(trackersConfig, trackersConfig.secondary);\n}\n\n/**\n * Get all configured trackers\n */\nexport function getAllTrackers(trackersConfig: TrackersConfig): IssueTracker[] {\n const trackers: IssueTracker[] = [getPrimaryTracker(trackersConfig)];\n\n const secondary = getSecondaryTracker(trackersConfig);\n if (secondary) {\n trackers.push(secondary);\n }\n\n return trackers;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __esmMin } from "./chunk-DORXReHP.js";
|
|
2
|
-
import { p as init_projects, y as resolveProjectFromIssue } from "./projects-
|
|
2
|
+
import { p as init_projects, y as resolveProjectFromIssue } from "./projects-CFVl4oHn.js";
|
|
3
3
|
import { existsSync } from "fs";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
@@ -122,4 +122,4 @@ var init_feedback_writer = __esmMin((() => {
|
|
|
122
122
|
//#endregion
|
|
123
123
|
export { writeFeedbackFile as n, init_feedback_writer as t };
|
|
124
124
|
|
|
125
|
-
//# sourceMappingURL=feedback-writer-
|
|
125
|
+
//# sourceMappingURL=feedback-writer-CudSe1WK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feedback-writer-
|
|
1
|
+
{"version":3,"file":"feedback-writer-CudSe1WK.js","names":[],"sources":["../../src/lib/cloister/feedback-writer.ts"],"sourcesContent":["/**\n * Feedback Writer — writes specialist feedback to workspace files.\n *\n * All specialist feedback (review, test, merge) is written to\n * .planning/feedback/ in the workspace, with a breadcrumb in STATE.md.\n * The work agent reads these on startup or after crash recovery.\n *\n * All I/O is async (fs/promises) — never execSync.\n */\n\nimport { writeFile, readFile, mkdir, readdir } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { resolveProjectFromIssue } from '../projects.js';\n\nexport interface WriteFeedbackOptions {\n issueId: string;\n workspacePath?: string;\n specialist: 'verification-gate' | 'review-agent' | 'test-agent' | 'inspect-agent' | 'uat-agent' | 'merge-agent';\n outcome: string;\n summary: string;\n markdownBody: string;\n}\n\nexport interface WriteFeedbackResult {\n success: boolean;\n /** Relative path from workspace root */\n relativePath?: string;\n /** Absolute path */\n filePath?: string;\n error?: string;\n}\n\n/**\n * Resolve workspace path from an issue ID.\n */\nfunction resolveWorkspacePath(issueId: string): string | null {\n const resolved = resolveProjectFromIssue(issueId);\n if (!resolved) return null;\n\n const wsPath = join(resolved.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`);\n return existsSync(wsPath) ? wsPath : null;\n}\n\n/**\n * Get the next sequence number from existing files in the feedback directory.\n */\nasync function getNextSequenceNumber(feedbackDir: string): Promise<number> {\n try {\n const files = await readdir(feedbackDir);\n let max = 0;\n for (const file of files) {\n const match = file.match(/^(\\d{3})-/);\n if (match) {\n const n = parseInt(match[1], 10);\n if (n > max) max = n;\n }\n }\n return max + 1;\n } catch {\n return 1;\n }\n}\n\n/**\n * Append a feedback entry to STATE.md's \"Specialist Feedback\" section.\n * Creates the section if it doesn't exist. Creates STATE.md if it doesn't exist.\n */\nasync function appendToStateMd(\n planningDir: string,\n entry: { timestamp: string; specialist: string; outcome: string; relativePath: string; issueId: string }\n): Promise<void> {\n const statePath = join(planningDir, 'STATE.md');\n const line = `- **[${entry.timestamp}] ${entry.specialist} → ${entry.outcome.toUpperCase()}** — \\`${entry.relativePath}\\``;\n\n let content: string;\n try {\n content = await readFile(statePath, 'utf-8');\n } catch {\n // STATE.md doesn't exist — create a minimal one\n content = `# Agent State: ${entry.issueId}\\n`;\n }\n\n const sectionHeader = '## Specialist Feedback';\n const sectionIndex = content.indexOf(sectionHeader);\n\n if (sectionIndex >= 0) {\n // Find the end of the section (next ## or EOF)\n const afterHeader = sectionIndex + sectionHeader.length;\n const nextSection = content.indexOf('\\n## ', afterHeader);\n const insertPos = nextSection >= 0 ? nextSection : content.length;\n content = content.slice(0, insertPos).trimEnd() + '\\n' + line + '\\n' + content.slice(insertPos);\n } else {\n // Append the section at the end\n content = content.trimEnd() + '\\n\\n' + sectionHeader + '\\n\\n' + line + '\\n';\n }\n\n await writeFile(statePath, content, 'utf-8');\n}\n\n/**\n * Write specialist feedback to a file in the workspace and update STATE.md.\n */\nexport async function writeFeedbackFile(opts: WriteFeedbackOptions): Promise<WriteFeedbackResult> {\n // Validate workspacePath — reject project roots (must contain /workspaces/ or have .planning dir)\n let providedPath = opts.workspacePath;\n if (providedPath && !existsSync(join(providedPath, '.planning')) && !providedPath.includes('/workspaces/')) {\n // Looks like a project root, not a workspace — fall back to resolution\n providedPath = undefined;\n }\n const workspacePath = providedPath || resolveWorkspacePath(opts.issueId);\n if (!workspacePath) {\n return { success: false, error: `Workspace not found for ${opts.issueId}` };\n }\n\n const planningDir = join(workspacePath, '.planning');\n const feedbackDir = join(planningDir, 'feedback');\n\n try {\n await mkdir(feedbackDir, { recursive: true });\n\n const seq = await getNextSequenceNumber(feedbackDir);\n const seqStr = String(seq).padStart(3, '0');\n const filename = `${seqStr}-${opts.specialist}-${opts.outcome}.md`;\n const filePath = join(feedbackDir, filename);\n const relativePath = `.planning/feedback/${filename}`;\n\n const timestamp = new Date().toISOString().replace(/\\.\\d+Z$/, 'Z');\n const shortTimestamp = timestamp.replace(/:\\d{2}Z$/, 'Z');\n\n const content = [\n '---',\n `specialist: ${opts.specialist}`,\n `issueId: ${opts.issueId}`,\n `outcome: ${opts.outcome}`,\n `timestamp: ${timestamp}`,\n '---',\n '',\n opts.markdownBody,\n '',\n ].join('\\n');\n\n await writeFile(filePath, content, 'utf-8');\n\n // Update STATE.md with breadcrumb\n await appendToStateMd(planningDir, {\n timestamp: shortTimestamp,\n specialist: opts.specialist,\n outcome: opts.outcome,\n relativePath,\n issueId: opts.issueId,\n });\n\n console.log(`[feedback-writer] Wrote ${relativePath} for ${opts.issueId}`);\n return { success: true, relativePath, filePath };\n } catch (error: any) {\n console.error(`[feedback-writer] Failed to write feedback for ${opts.issueId}:`, error);\n return { success: false, error: error.message };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-utils-
|
|
1
|
+
{"version":3,"file":"git-utils-C1m4SwAe.js","names":[],"sources":["../../src/lib/git-utils.ts"],"sourcesContent":["/**\n * Git utilities for handling common git operations and recovery\n */\n\nimport { existsSync, unlinkSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\n/**\n * Check if any git processes are currently running in a specific repository\n *\n * This checks if there are git processes with the repository path in their command line.\n * If we can't determine repository-specific processes, we conservatively return false\n * (no processes detected) to allow cleanup to proceed.\n */\nasync function hasRunningGitProcesses(repoPath: string): Promise<boolean> {\n try {\n // Try to find git processes that reference this specific repository\n // Use fuser to check if any process has the .git directory open (more reliable)\n try {\n const gitDir = join(repoPath, '.git');\n const { stdout } = await execAsync(`fuser \"${gitDir}\" 2>/dev/null`, {\n encoding: 'utf-8',\n });\n // fuser returns PIDs if any process has the directory open\n return stdout.trim().length > 0;\n } catch {\n // fuser not available or no processes found\n // Fall back to checking ps for git processes in this directory\n try {\n const { stdout } = await execAsync(\n `ps aux | grep -E \"git.*${repoPath.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\" | grep -v grep`,\n { encoding: 'utf-8' }\n );\n return stdout.trim().length > 0;\n } catch {\n // No git processes found for this repo\n return false;\n }\n }\n } catch {\n // Error checking - conservatively assume no processes\n return false;\n }\n}\n\n/**\n * Find all git lock files in a repository\n */\nfunction findGitLockFiles(repoPath: string): string[] {\n const lockFiles: string[] = [];\n\n // Check for index.lock in .git directory\n const indexLock = join(repoPath, '.git', 'index.lock');\n if (existsSync(indexLock)) {\n lockFiles.push(indexLock);\n }\n\n // Check for ref locks in .git/refs\n const refsDir = join(repoPath, '.git', 'refs');\n if (existsSync(refsDir)) {\n const findLocksRecursive = (dir: string) => {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n findLocksRecursive(fullPath);\n } else if (entry.name.endsWith('.lock')) {\n lockFiles.push(fullPath);\n }\n }\n };\n try {\n findLocksRecursive(refsDir);\n } catch {\n // Ignore errors reading refs directory\n }\n }\n\n return lockFiles;\n}\n\n/**\n * Check for and clean up stale git lock files\n *\n * A lock file is considered stale if:\n * 1. It exists\n * 2. No git processes are currently running\n *\n * @param repoPath - Path to the git repository\n * @returns Object with cleanup results\n */\nexport async function cleanupStaleLocks(repoPath: string): Promise<{\n found: string[];\n removed: string[];\n errors: Array<{ file: string; error: string }>;\n}> {\n const result = {\n found: [] as string[],\n removed: [] as string[],\n errors: [] as Array<{ file: string; error: string }>,\n };\n\n // Find all lock files\n const lockFiles = findGitLockFiles(repoPath);\n result.found = lockFiles;\n\n if (lockFiles.length === 0) {\n return result;\n }\n\n // Check if git processes are running for this repository\n const hasGitProcesses = await hasRunningGitProcesses(repoPath);\n\n if (hasGitProcesses) {\n // Don't remove locks if git is actively running\n result.errors.push({\n file: 'N/A',\n error: 'Git processes are running - not safe to remove locks',\n });\n return result;\n }\n\n // Remove stale lock files\n for (const lockFile of lockFiles) {\n try {\n unlinkSync(lockFile);\n result.removed.push(lockFile);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n result.errors.push({ file: lockFile, error: msg });\n }\n }\n\n return result;\n}\n\n/**\n * Result of getWorkspaceGitInfo.\n * Note: `branch` is the branch name (not a hash) despite the parent function name.\n */\nexport interface WorkspaceCommitInfo {\n /** Full SHA of the HEAD commit */\n HEAD: string;\n /** Current branch name (e.g. \"feature/pan-342\") */\n branch: string;\n}\n\n/**\n * Get the current HEAD commit SHA and branch name for a workspace.\n *\n * Note: the return value includes `branch` (a name, not a hash) alongside `HEAD` (a SHA).\n * The function name reflects its primary use-case of snapshotting commit state for review.\n *\n * @param workspacePath - Path to the git workspace\n * @returns WorkspaceCommitInfo with HEAD SHA and branch name\n * @throws Error if git commands fail (e.g. path is not a git repository)\n */\nexport async function getWorkspaceGitInfo(workspacePath: string): Promise<WorkspaceCommitInfo> {\n try {\n const [headResult, branchResult] = await Promise.all([\n execAsync('git rev-parse HEAD', { cwd: workspacePath }),\n execAsync('git rev-parse --abbrev-ref HEAD', { cwd: workspacePath }),\n ]);\n return {\n HEAD: headResult.stdout.trim(),\n branch: branchResult.stdout.trim(),\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`getWorkspaceGitInfo failed for ${workspacePath}: ${msg}`);\n }\n}\n\n/**\n * Check if a repository has stale lock files\n *\n * @param repoPath - Path to the git repository\n * @returns True if stale locks exist\n */\nexport async function hasStaleLocks(repoPath: string): Promise<boolean> {\n const lockFiles = findGitLockFiles(repoPath);\n if (lockFiles.length === 0) {\n return false;\n }\n\n const hasGitProcesses = await hasRunningGitProcesses(repoPath);\n return !hasGitProcesses;\n}\n"],"mappings":";;;;;;;;AASA,MAAM,YAAY,UAAU,KAAK;;;;;;;;AASjC,eAAe,uBAAuB,UAAoC;AACxE,KAAI;AAGF,MAAI;GAEF,MAAM,EAAE,WAAW,MAAM,UAAU,UADpB,KAAK,UAAU,OAAO,CACe,gBAAgB,EAClE,UAAU,SACX,CAAC;AAEF,UAAO,OAAO,MAAM,CAAC,SAAS;UACxB;AAGN,OAAI;IACF,MAAM,EAAE,WAAW,MAAM,UACvB,0BAA0B,SAAS,QAAQ,uBAAuB,OAAO,CAAC,mBAC1E,EAAE,UAAU,SAAS,CACtB;AACD,WAAO,OAAO,MAAM,CAAC,SAAS;WACxB;AAEN,WAAO;;;SAGL;AAEN,SAAO;;;;;;AAOX,SAAS,iBAAiB,UAA4B;CACpD,MAAM,YAAsB,EAAE;CAG9B,MAAM,YAAY,KAAK,UAAU,QAAQ,aAAa;AACtD,KAAI,WAAW,UAAU,CACvB,WAAU,KAAK,UAAU;CAI3B,MAAM,UAAU,KAAK,UAAU,QAAQ,OAAO;AAC9C,KAAI,WAAW,QAAQ,EAAE;EACvB,MAAM,sBAAsB,QAAgB;GAC1C,MAAM,UAAU,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC;AACzD,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK;AACtC,QAAI,MAAM,aAAa,CACrB,oBAAmB,SAAS;aACnB,MAAM,KAAK,SAAS,QAAQ,CACrC,WAAU,KAAK,SAAS;;;AAI9B,MAAI;AACF,sBAAmB,QAAQ;UACrB;;AAKV,QAAO;;;;;;;;;;;;AAaT,eAAsB,kBAAkB,UAIrC;CACD,MAAM,SAAS;EACb,OAAO,EAAE;EACT,SAAS,EAAE;EACX,QAAQ,EAAE;EACX;CAGD,MAAM,YAAY,iBAAiB,SAAS;AAC5C,QAAO,QAAQ;AAEf,KAAI,UAAU,WAAW,EACvB,QAAO;AAMT,KAFwB,MAAM,uBAAuB,SAAS,EAEzC;AAEnB,SAAO,OAAO,KAAK;GACjB,MAAM;GACN,OAAO;GACR,CAAC;AACF,SAAO;;AAIT,MAAK,MAAM,YAAY,UACrB,KAAI;AACF,aAAW,SAAS;AACpB,SAAO,QAAQ,KAAK,SAAS;UACtB,OAAgB;EACvB,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAClE,SAAO,OAAO,KAAK;GAAE,MAAM;GAAU,OAAO;GAAK,CAAC;;AAItD,QAAO;;;;;;;;;;;;AAwBT,eAAsB,oBAAoB,eAAqD;AAC7F,KAAI;EACF,MAAM,CAAC,YAAY,gBAAgB,MAAM,QAAQ,IAAI,CACnD,UAAU,sBAAsB,EAAE,KAAK,eAAe,CAAC,EACvD,UAAU,mCAAmC,EAAE,KAAK,eAAe,CAAC,CACrE,CAAC;AACF,SAAO;GACL,MAAM,WAAW,OAAO,MAAM;GAC9B,QAAQ,aAAa,OAAO,MAAM;GACnC;UACM,KAAc;EACrB,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,QAAM,IAAI,MAAM,kCAAkC,cAAc,IAAI,MAAM;;;;;;;;;AAU9E,eAAsB,cAAc,UAAoC;AAEtE,KADkB,iBAAiB,SAAS,CAC9B,WAAW,EACvB,QAAO;AAIT,QAAO,CADiB,MAAM,uBAAuB,SAAS"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as getWorkspaceGitInfo, r as hasStaleLocks, t as cleanupStaleLocks } from "./git-utils-
|
|
1
|
+
import { n as getWorkspaceGitInfo, r as hasStaleLocks, t as cleanupStaleLocks } from "./git-utils-C1m4SwAe.js";
|
|
2
2
|
export { cleanupStaleLocks, getWorkspaceGitInfo, hasStaleLocks };
|
|
@@ -189,4 +189,4 @@ __esmMin((() => {
|
|
|
189
189
|
}))();
|
|
190
190
|
export { configureWorkspaceForBot, generateInstallationToken, getAppStatus, getBotIdentity, isGitHubAppConfigured, loadGitHubAppConfig, refreshWorkspaceToken, reportCommitStatus };
|
|
191
191
|
|
|
192
|
-
//# sourceMappingURL=github-app-
|
|
192
|
+
//# sourceMappingURL=github-app-DClWjjHr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-app-XO-LBUGk.js","names":[],"sources":["../../src/lib/github-app.ts"],"sourcesContent":["/**\n * GitHub App Integration (PAN-536)\n *\n * Generates short-lived installation access tokens for the panopticon-agent GitHub App.\n * Agents push via HTTPS with these tokens instead of the user's SSH key, so commits\n * show as `panopticon-agent[bot]` with a verified badge.\n *\n * Credentials stored at: ~/.panopticon/github-app/\n * - app-id, private-key.pem, installation-id\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { createSign } from 'crypto';\n\nconst APP_DIR = join(homedir(), '.panopticon', 'github-app');\n\nexport interface GitHubAppConfig {\n appId: string;\n installationId: string;\n privateKey: string;\n}\n\nexport interface InstallationToken {\n token: string;\n expiresAt: string; // ISO timestamp\n}\n\n/**\n * Check if the GitHub App is configured (credentials exist)\n */\nexport function isGitHubAppConfigured(): boolean {\n return (\n existsSync(join(APP_DIR, 'app-id')) &&\n existsSync(join(APP_DIR, 'private-key.pem')) &&\n existsSync(join(APP_DIR, 'installation-id'))\n );\n}\n\n/**\n * Load GitHub App credentials from ~/.panopticon/github-app/\n */\nexport function loadGitHubAppConfig(): GitHubAppConfig | null {\n if (!isGitHubAppConfigured()) return null;\n try {\n return {\n appId: readFileSync(join(APP_DIR, 'app-id'), 'utf-8').trim(),\n installationId: readFileSync(join(APP_DIR, 'installation-id'), 'utf-8').trim(),\n privateKey: readFileSync(join(APP_DIR, 'private-key.pem'), 'utf-8'),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Generate a JWT for GitHub App authentication\n */\nfunction generateJWT(appId: string, privateKey: string): string {\n const now = Math.floor(Date.now() / 1000);\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(JSON.stringify({\n iat: now - 60, // 60s clock drift allowance\n exp: now + 600, // 10 minute expiry\n iss: appId,\n })).toString('base64url');\n\n const signer = createSign('SHA256');\n signer.update(`${header}.${payload}`);\n const signature = signer.sign(privateKey, 'base64url');\n\n return `${header}.${payload}.${signature}`;\n}\n\n/**\n * Generate a short-lived installation access token (~1 hour TTL).\n * Used for git push and PR operations by agent workspaces.\n */\nexport async function generateInstallationToken(\n config?: GitHubAppConfig\n): Promise<InstallationToken> {\n const appConfig = config || loadGitHubAppConfig();\n if (!appConfig) {\n throw new Error('GitHub App not configured. Run: node scripts/create-github-app.mjs');\n }\n\n const jwt = generateJWT(appConfig.appId, appConfig.privateKey);\n\n const response = await fetch(\n `https://api.github.com/app/installations/${appConfig.installationId}/access_tokens`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Accept': 'application/vnd.github+json',\n 'User-Agent': 'panopticon-cli',\n },\n }\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Failed to generate installation token: ${response.status} ${text}`);\n }\n\n const data = await response.json() as { token: string; expires_at: string };\n return {\n token: data.token,\n expiresAt: data.expires_at,\n };\n}\n\n/**\n * Get the bot identity for git config\n */\nexport function getBotIdentity(appConfig?: GitHubAppConfig): { name: string; email: string } {\n const config = appConfig || loadGitHubAppConfig();\n const appId = config?.appId || '0';\n return {\n name: 'panopticon-agent[bot]',\n email: `${appId}+panopticon-agent[bot]@users.noreply.github.com`,\n };\n}\n\n/**\n * Configure a workspace to push as the bot identity.\n * Sets git remote to HTTPS with token auth and configures bot user.\n *\n * @param workspacePath - Path to the git workspace\n * @param owner - GitHub repo owner\n * @param repo - GitHub repo name\n * @param token - Installation access token\n */\nexport async function configureWorkspaceForBot(\n workspacePath: string,\n owner: string,\n repo: string,\n token: string,\n): Promise<void> {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const { writeFileSync } = await import('fs');\n const execAsync = promisify(exec);\n const { name, email } = getBotIdentity();\n\n // Set git user identity for this workspace\n await execAsync(`git config user.name \"${name}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n await execAsync(`git config user.email \"${email}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n\n // Use git credential store with a workspace-local credential file.\n // Token is refreshed at workspace creation (~1hr TTL). For long sessions,\n // call refreshWorkspaceToken() to get a fresh one.\n const credFile = join(workspacePath, '.git', 'pan-credentials');\n writeFileSync(credFile, `https://x-access-token:${token}@github.com\\n`, { mode: 0o600 });\n\n // Set remote to HTTPS and configure credential store\n const httpsUrl = `https://github.com/${owner}/${repo}.git`;\n await execAsync(`git remote set-url origin \"${httpsUrl}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n await execAsync(`git config credential.helper \"store --file=${credFile}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n}\n\n/**\n * Report a check status on a commit (replaces the need for CI)\n *\n * @param owner - Repo owner\n * @param repo - Repo name\n * @param sha - Commit SHA\n * @param status - Check status\n * @param context - Check name (e.g. \"panopticon/review\", \"panopticon/test\")\n * @param description - Short description\n */\nexport async function reportCommitStatus(\n owner: string,\n repo: string,\n sha: string,\n status: 'pending' | 'success' | 'failure' | 'error',\n context: string,\n description: string,\n): Promise<void> {\n const config = loadGitHubAppConfig();\n if (!config) return; // Silently skip in fallback mode\n\n const { token } = await generateInstallationToken(config);\n\n const response = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/statuses/${sha}`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `token ${token}`,\n 'Accept': 'application/vnd.github+json',\n 'User-Agent': 'panopticon-cli',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ state: status, context, description }),\n }\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.warn(`[github-app] Failed to report status: ${response.status} ${text}`);\n }\n}\n\n/**\n * Refresh the installation token for a workspace (call when token expires).\n * Updates the credential file in-place.\n */\nexport async function refreshWorkspaceToken(\n workspacePath: string,\n): Promise<void> {\n const config = loadGitHubAppConfig();\n if (!config) throw new Error('GitHub App not configured');\n\n const { token } = await generateInstallationToken(config);\n const { writeFileSync } = await import('fs');\n const credFile = join(workspacePath, '.git', 'pan-credentials');\n writeFileSync(credFile, `https://x-access-token:${token}@github.com\\n`, { mode: 0o600 });\n}\n\n/**\n * Get GitHub App status for `pan status` display\n */\nexport function getAppStatus(): {\n configured: boolean;\n appId?: string;\n installationId?: string;\n mode: 'app' | 'fallback';\n} {\n const config = loadGitHubAppConfig();\n if (config) {\n return {\n configured: true,\n appId: config.appId,\n installationId: config.installationId,\n mode: 'app',\n };\n }\n return { configured: false, mode: 'fallback' };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github-app-DClWjjHr.js","names":[],"sources":["../../src/lib/github-app.ts"],"sourcesContent":["/**\n * GitHub App Integration (PAN-536)\n *\n * Generates short-lived installation access tokens for the panopticon-agent GitHub App.\n * Agents push via HTTPS with these tokens instead of the user's SSH key, so commits\n * show as `panopticon-agent[bot]` with a verified badge.\n *\n * Credentials stored at: ~/.panopticon/github-app/\n * - app-id, private-key.pem, installation-id\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { createSign } from 'crypto';\n\nconst APP_DIR = join(homedir(), '.panopticon', 'github-app');\n\nexport interface GitHubAppConfig {\n appId: string;\n installationId: string;\n privateKey: string;\n}\n\nexport interface InstallationToken {\n token: string;\n expiresAt: string; // ISO timestamp\n}\n\n/**\n * Check if the GitHub App is configured (credentials exist)\n */\nexport function isGitHubAppConfigured(): boolean {\n return (\n existsSync(join(APP_DIR, 'app-id')) &&\n existsSync(join(APP_DIR, 'private-key.pem')) &&\n existsSync(join(APP_DIR, 'installation-id'))\n );\n}\n\n/**\n * Load GitHub App credentials from ~/.panopticon/github-app/\n */\nexport function loadGitHubAppConfig(): GitHubAppConfig | null {\n if (!isGitHubAppConfigured()) return null;\n try {\n return {\n appId: readFileSync(join(APP_DIR, 'app-id'), 'utf-8').trim(),\n installationId: readFileSync(join(APP_DIR, 'installation-id'), 'utf-8').trim(),\n privateKey: readFileSync(join(APP_DIR, 'private-key.pem'), 'utf-8'),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Generate a JWT for GitHub App authentication\n */\nfunction generateJWT(appId: string, privateKey: string): string {\n const now = Math.floor(Date.now() / 1000);\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(JSON.stringify({\n iat: now - 60, // 60s clock drift allowance\n exp: now + 600, // 10 minute expiry\n iss: appId,\n })).toString('base64url');\n\n const signer = createSign('SHA256');\n signer.update(`${header}.${payload}`);\n const signature = signer.sign(privateKey, 'base64url');\n\n return `${header}.${payload}.${signature}`;\n}\n\n/**\n * Generate a short-lived installation access token (~1 hour TTL).\n * Used for git push and PR operations by agent workspaces.\n */\nexport async function generateInstallationToken(\n config?: GitHubAppConfig\n): Promise<InstallationToken> {\n const appConfig = config || loadGitHubAppConfig();\n if (!appConfig) {\n throw new Error('GitHub App not configured. Run: node scripts/create-github-app.mjs');\n }\n\n const jwt = generateJWT(appConfig.appId, appConfig.privateKey);\n\n const response = await fetch(\n `https://api.github.com/app/installations/${appConfig.installationId}/access_tokens`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Accept': 'application/vnd.github+json',\n 'User-Agent': 'panopticon-cli',\n },\n }\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Failed to generate installation token: ${response.status} ${text}`);\n }\n\n const data = await response.json() as { token: string; expires_at: string };\n return {\n token: data.token,\n expiresAt: data.expires_at,\n };\n}\n\n/**\n * Get the bot identity for git config\n */\nexport function getBotIdentity(appConfig?: GitHubAppConfig): { name: string; email: string } {\n const config = appConfig || loadGitHubAppConfig();\n const appId = config?.appId || '0';\n return {\n name: 'panopticon-agent[bot]',\n email: `${appId}+panopticon-agent[bot]@users.noreply.github.com`,\n };\n}\n\n/**\n * Configure a workspace to push as the bot identity.\n * Sets git remote to HTTPS with token auth and configures bot user.\n *\n * @param workspacePath - Path to the git workspace\n * @param owner - GitHub repo owner\n * @param repo - GitHub repo name\n * @param token - Installation access token\n */\nexport async function configureWorkspaceForBot(\n workspacePath: string,\n owner: string,\n repo: string,\n token: string,\n): Promise<void> {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const { writeFileSync } = await import('fs');\n const execAsync = promisify(exec);\n const { name, email } = getBotIdentity();\n\n // Set git user identity for this workspace\n await execAsync(`git config user.name \"${name}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n await execAsync(`git config user.email \"${email}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n\n // Use git credential store with a workspace-local credential file.\n // Token is refreshed at workspace creation (~1hr TTL). For long sessions,\n // call refreshWorkspaceToken() to get a fresh one.\n const credFile = join(workspacePath, '.git', 'pan-credentials');\n writeFileSync(credFile, `https://x-access-token:${token}@github.com\\n`, { mode: 0o600 });\n\n // Set remote to HTTPS and configure credential store\n const httpsUrl = `https://github.com/${owner}/${repo}.git`;\n await execAsync(`git remote set-url origin \"${httpsUrl}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n await execAsync(`git config credential.helper \"store --file=${credFile}\"`, { cwd: workspacePath, encoding: 'utf-8' });\n}\n\n/**\n * Report a check status on a commit (replaces the need for CI)\n *\n * @param owner - Repo owner\n * @param repo - Repo name\n * @param sha - Commit SHA\n * @param status - Check status\n * @param context - Check name (e.g. \"panopticon/review\", \"panopticon/test\")\n * @param description - Short description\n */\nexport async function reportCommitStatus(\n owner: string,\n repo: string,\n sha: string,\n status: 'pending' | 'success' | 'failure' | 'error',\n context: string,\n description: string,\n): Promise<void> {\n const config = loadGitHubAppConfig();\n if (!config) return; // Silently skip in fallback mode\n\n const { token } = await generateInstallationToken(config);\n\n const response = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/statuses/${sha}`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `token ${token}`,\n 'Accept': 'application/vnd.github+json',\n 'User-Agent': 'panopticon-cli',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ state: status, context, description }),\n }\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.warn(`[github-app] Failed to report status: ${response.status} ${text}`);\n }\n}\n\n/**\n * Refresh the installation token for a workspace (call when token expires).\n * Updates the credential file in-place.\n */\nexport async function refreshWorkspaceToken(\n workspacePath: string,\n): Promise<void> {\n const config = loadGitHubAppConfig();\n if (!config) throw new Error('GitHub App not configured');\n\n const { token } = await generateInstallationToken(config);\n const { writeFileSync } = await import('fs');\n const credFile = join(workspacePath, '.git', 'pan-credentials');\n writeFileSync(credFile, `https://x-access-token:${token}@github.com\\n`, { mode: 0o600 });\n}\n\n/**\n * Get GitHub App status for `pan status` display\n */\nexport function getAppStatus(): {\n configured: boolean;\n appId?: string;\n installationId?: string;\n mode: 'app' | 'fallback';\n} {\n const config = loadGitHubAppConfig();\n if (config) {\n return {\n configured: true,\n appId: config.appId,\n installationId: config.installationId,\n mode: 'app',\n };\n }\n return { configured: false, mode: 'fallback' };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as getHealthHistory, c as writeHealthEvent, i as getAllHealthHistory, l as writeHealthEvents, n as deleteAgentHealthHistory, o as getLatestHealthEvent, r as getAgentsWithHistory, s as getRecentHealthHistory, t as cleanupOldHealthEvents } from "./health-events-db-
|
|
1
|
+
import { a as getHealthHistory, c as writeHealthEvent, i as getAllHealthHistory, l as writeHealthEvents, n as deleteAgentHealthHistory, o as getLatestHealthEvent, r as getAgentsWithHistory, s as getRecentHealthHistory, t as cleanupOldHealthEvents } from "./health-events-db-Do4NrOhC.js";
|
|
2
2
|
export { cleanupOldHealthEvents, deleteAgentHealthHistory, getAgentsWithHistory, getAllHealthHistory, getHealthHistory, getLatestHealthEvent, getRecentHealthHistory, writeHealthEvent, writeHealthEvents };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as init_database, n as getDatabase } from "./database-
|
|
1
|
+
import { i as init_database, n as getDatabase } from "./database-cxmQryoh.js";
|
|
2
2
|
//#region ../../lib/database/health-events-db.ts
|
|
3
3
|
init_database();
|
|
4
4
|
const RETENTION_DAYS = 7;
|
|
@@ -81,4 +81,4 @@ function parseMetadata(event) {
|
|
|
81
81
|
//#endregion
|
|
82
82
|
export { getHealthHistory as a, writeHealthEvent as c, getAllHealthHistory as i, writeHealthEvents as l, deleteAgentHealthHistory as n, getLatestHealthEvent as o, getAgentsWithHistory as r, getRecentHealthHistory as s, cleanupOldHealthEvents as t };
|
|
83
83
|
|
|
84
|
-
//# sourceMappingURL=health-events-db-
|
|
84
|
+
//# sourceMappingURL=health-events-db-Do4NrOhC.js.map
|
package/dist/dashboard/{health-events-db-B3ChzN65.js.map → health-events-db-Do4NrOhC.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-events-db-
|
|
1
|
+
{"version":3,"file":"health-events-db-Do4NrOhC.js","names":[],"sources":["../../src/lib/database/health-events-db.ts"],"sourcesContent":["/**\n * Health Events SQLite Storage (panopticon.db)\n *\n * Provides the same function signatures as src/lib/cloister/database.ts but\n * stores events in health_events table within panopticon.db instead of\n * the separate cloister.db. This allows gradual migration — callers can\n * import from either module without changing call sites.\n */\n\nimport { getDatabase } from './index.js';\nimport type { HealthState } from '../runtimes/types.js';\n\nconst RETENTION_DAYS = 7;\n\n// ============== Types (re-exported to match cloister/database.ts) ==============\n\nexport interface HealthEvent {\n id?: number;\n agentId: string;\n timestamp: string;\n state: HealthState;\n previousState?: string;\n source?: string;\n metadata?: string;\n}\n\nexport interface HealthEventWithMetadata extends Omit<HealthEvent, 'metadata'> {\n metadata?: Record<string, unknown>;\n}\n\n// ============== Write operations ==============\n\nexport function writeHealthEvent(event: Omit<HealthEvent, 'id'>): number {\n const db = getDatabase();\n const result = db.prepare(`\n INSERT INTO health_events (agent_id, timestamp, state, previous_state, source, metadata)\n VALUES (?, ?, ?, ?, ?, ?)\n `).run(\n event.agentId,\n event.timestamp,\n event.state,\n event.previousState ?? null,\n event.source ?? null,\n event.metadata ?? null,\n );\n return result.lastInsertRowid as number;\n}\n\nexport function writeHealthEvents(events: Omit<HealthEvent, 'id'>[]): number {\n const db = getDatabase();\n const insert = db.prepare(`\n INSERT INTO health_events (agent_id, timestamp, state, previous_state, source, metadata)\n VALUES (?, ?, ?, ?, ?, ?)\n `);\n const insertMany = db.transaction((evs: Omit<HealthEvent, 'id'>[]) => {\n for (const ev of evs) {\n insert.run(ev.agentId, ev.timestamp, ev.state, ev.previousState ?? null, ev.source ?? null, ev.metadata ?? null);\n }\n return evs.length;\n });\n return insertMany(events);\n}\n\n// ============== Read operations ==============\n\nexport function getHealthHistory(\n agentId: string,\n startTime: string,\n endTime: string,\n): HealthEventWithMetadata[] {\n const db = getDatabase();\n const rows = db.prepare(`\n SELECT id, agent_id as agentId, timestamp, state, previous_state as previousState,\n source, metadata\n FROM health_events\n WHERE agent_id = ? AND timestamp >= ? AND timestamp <= ?\n ORDER BY timestamp ASC\n `).all(agentId, startTime, endTime) as HealthEvent[];\n return rows.map(parseMetadata);\n}\n\nexport function getRecentHealthHistory(agentId: string, limit = 100): HealthEventWithMetadata[] {\n const db = getDatabase();\n const rows = db.prepare(`\n SELECT id, agent_id as agentId, timestamp, state, previous_state as previousState,\n source, metadata\n FROM health_events\n WHERE agent_id = ?\n ORDER BY timestamp DESC\n LIMIT ?\n `).all(agentId, limit) as HealthEvent[];\n return rows.map(parseMetadata).reverse();\n}\n\nexport function getAllHealthHistory(startTime: string, endTime: string): HealthEventWithMetadata[] {\n const db = getDatabase();\n const rows = db.prepare(`\n SELECT id, agent_id as agentId, timestamp, state, previous_state as previousState,\n source, metadata\n FROM health_events\n WHERE timestamp >= ? AND timestamp <= ?\n ORDER BY timestamp ASC\n `).all(startTime, endTime) as HealthEvent[];\n return rows.map(parseMetadata);\n}\n\nexport function getLatestHealthEvent(agentId: string): HealthEventWithMetadata | null {\n const db = getDatabase();\n const row = db.prepare(`\n SELECT id, agent_id as agentId, timestamp, state, previous_state as previousState,\n source, metadata\n FROM health_events\n WHERE agent_id = ?\n ORDER BY timestamp DESC\n LIMIT 1\n `).get(agentId) as HealthEvent | undefined;\n return row ? parseMetadata(row) : null;\n}\n\nexport function getAgentsWithHistory(): string[] {\n const db = getDatabase();\n const results = db.prepare(`\n SELECT DISTINCT agent_id as agentId FROM health_events ORDER BY agent_id ASC\n `).all() as { agentId: string }[];\n return results.map(r => r.agentId);\n}\n\n// ============== Maintenance ==============\n\nexport function cleanupOldHealthEvents(retentionDays = RETENTION_DAYS): number {\n const db = getDatabase();\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - retentionDays);\n const result = db.prepare(\n 'DELETE FROM health_events WHERE timestamp < ?'\n ).run(cutoff.toISOString());\n return result.changes;\n}\n\nexport function deleteAgentHealthHistory(agentId: string): number {\n const db = getDatabase();\n const result = db.prepare('DELETE FROM health_events WHERE agent_id = ?').run(agentId);\n return result.changes;\n}\n\n// ============== Helpers ==============\n\nfunction parseMetadata(event: HealthEvent): HealthEventWithMetadata {\n return {\n ...event,\n metadata: event.metadata ? JSON.parse(event.metadata) : undefined,\n };\n}\n"],"mappings":";;eASyC;AAGzC,MAAM,iBAAiB;AAoBvB,SAAgB,iBAAiB,OAAwC;AAavE,QAZW,aAAa,CACN,QAAQ;;;IAGxB,CAAC,IACD,MAAM,SACN,MAAM,WACN,MAAM,OACN,MAAM,iBAAiB,MACvB,MAAM,UAAU,MAChB,MAAM,YAAY,KACnB,CACa;;AAGhB,SAAgB,kBAAkB,QAA2C;CAC3E,MAAM,KAAK,aAAa;CACxB,MAAM,SAAS,GAAG,QAAQ;;;IAGxB;AAOF,QANmB,GAAG,aAAa,QAAmC;AACpE,OAAK,MAAM,MAAM,IACf,QAAO,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,iBAAiB,MAAM,GAAG,UAAU,MAAM,GAAG,YAAY,KAAK;AAElH,SAAO,IAAI;GACX,CACgB,OAAO;;AAK3B,SAAgB,iBACd,SACA,WACA,SAC2B;AAS3B,QARW,aAAa,CACR,QAAQ;;;;;;IAMtB,CAAC,IAAI,SAAS,WAAW,QAAQ,CACvB,IAAI,cAAc;;AAGhC,SAAgB,uBAAuB,SAAiB,QAAQ,KAAgC;AAU9F,QATW,aAAa,CACR,QAAQ;;;;;;;IAOtB,CAAC,IAAI,SAAS,MAAM,CACV,IAAI,cAAc,CAAC,SAAS;;AAG1C,SAAgB,oBAAoB,WAAmB,SAA4C;AASjG,QARW,aAAa,CACR,QAAQ;;;;;;IAMtB,CAAC,IAAI,WAAW,QAAQ,CACd,IAAI,cAAc;;AAGhC,SAAgB,qBAAqB,SAAiD;CAEpF,MAAM,MADK,aAAa,CACT,QAAQ;;;;;;;IAOrB,CAAC,IAAI,QAAQ;AACf,QAAO,MAAM,cAAc,IAAI,GAAG;;AAGpC,SAAgB,uBAAiC;AAK/C,QAJW,aAAa,CACL,QAAQ;;IAEzB,CAAC,KAAK,CACO,KAAI,MAAK,EAAE,QAAQ;;AAKpC,SAAgB,uBAAuB,gBAAgB,gBAAwB;CAC7E,MAAM,KAAK,aAAa;CACxB,MAAM,yBAAS,IAAI,MAAM;AACzB,QAAO,QAAQ,OAAO,SAAS,GAAG,cAAc;AAIhD,QAHe,GAAG,QAChB,gDACD,CAAC,IAAI,OAAO,aAAa,CAAC,CACb;;AAGhB,SAAgB,yBAAyB,SAAyB;AAGhE,QAFW,aAAa,CACN,QAAQ,+CAA+C,CAAC,IAAI,QAAQ,CACxE;;AAKhB,SAAS,cAAc,OAA6C;AAClE,QAAO;EACL,GAAG;EACH,UAAU,MAAM,WAAW,KAAK,MAAM,MAAM,SAAS,GAAG,KAAA;EACzD"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as getHook, c as popFromHook, d as sendMail, i as generateFixedPointPrompt, l as pushToHook, n as clearHook, o as initHook, r as collectMail, s as init_hooks, t as checkHook, u as reorderHookItems } from "./hooks-
|
|
1
|
+
import { a as getHook, c as popFromHook, d as sendMail, i as generateFixedPointPrompt, l as pushToHook, n as clearHook, o as initHook, r as collectMail, s as init_hooks, t as checkHook, u as reorderHookItems } from "./hooks-CjqXOlNb.js";
|
|
2
2
|
init_hooks();
|
|
3
3
|
export { checkHook, clearHook, collectMail, generateFixedPointPrompt, getHook, initHook, popFromHook, pushToHook, reorderHookItems, sendMail };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __esmMin } from "./chunk-DORXReHP.js";
|
|
2
|
-
import {
|
|
2
|
+
import { _ as init_paths, t as AGENTS_DIR } from "./paths-BDyJ7BiV.js";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
//#region ../../lib/hooks.ts
|
|
@@ -239,4 +239,4 @@ var init_hooks = __esmMin((() => {
|
|
|
239
239
|
//#endregion
|
|
240
240
|
export { getHook as a, popFromHook as c, sendMail as d, generateFixedPointPrompt as i, pushToHook as l, clearHook as n, initHook as o, collectMail as r, init_hooks as s, checkHook as t, reorderHookItems as u };
|
|
241
241
|
|
|
242
|
-
//# sourceMappingURL=hooks-
|
|
242
|
+
//# sourceMappingURL=hooks-CjqXOlNb.js.map
|