panopticon-cli 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{agents-DfYify9s.js → agents-CfFDs52G.js} +14 -14
- package/dist/{agents-DfYify9s.js.map → agents-CfFDs52G.js.map} +1 -1
- package/dist/{agents-BKsVoIc9.js → agents-D_2oRFVf.js} +1 -1
- package/dist/{archive-planning-BJrZ3tmN.js → archive-planning-D97ziGec.js} +3 -3
- package/dist/{archive-planning-BJrZ3tmN.js.map → archive-planning-D97ziGec.js.map} +1 -1
- package/dist/{archive-planning-C3m3hfa5.js → archive-planning-DK90wn9Q.js} +1 -1
- package/dist/{browser-Cvdznzc0.js → browser-CX7jXfXX.js} +1 -1
- package/dist/{browser-Cvdznzc0.js.map → browser-CX7jXfXX.js.map} +1 -1
- package/dist/{clean-planning-DvhZAUv4.js → clean-planning-D_lz4aQq.js} +2 -2
- package/dist/{clean-planning-DvhZAUv4.js.map → clean-planning-D_lz4aQq.js.map} +1 -1
- package/dist/clean-planning-x1S-JdmO.js +2 -0
- package/dist/cli/index.js +291 -760
- package/dist/cli/index.js.map +1 -1
- package/dist/{close-issue-Dr7yZmrr.js → close-issue-CaFE0stN.js} +11 -7
- package/dist/close-issue-CaFE0stN.js.map +1 -0
- package/dist/close-issue-CjcfZI9s.js +2 -0
- package/dist/compact-beads-B0_qE1w3.js +2 -0
- package/dist/{compact-beads-BCOtIIRl.js → compact-beads-CjFkteSU.js} +2 -2
- package/dist/{compact-beads-BCOtIIRl.js.map → compact-beads-CjFkteSU.js.map} +1 -1
- package/dist/{config-CRzMQRgA.js → config-BQNKsi9G.js} +2 -2
- package/dist/{config-CRzMQRgA.js.map → config-BQNKsi9G.js.map} +1 -1
- package/dist/{config-BYgUzQ21.js → config-agyKgF5C.js} +1 -1
- package/dist/{config-yaml-BgOACZAB.js → config-yaml-DGbLSMCa.js} +1 -1
- package/dist/{config-yaml-BgOACZAB.js.map → config-yaml-DGbLSMCa.js.map} +1 -1
- package/dist/{config-yaml-fdyvyL0S.js → config-yaml-Dqt4FWQH.js} +1 -1
- package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js → acceptance-criteria-Dk9hhiYj.js} +1 -1
- package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js.map → acceptance-criteria-Dk9hhiYj.js.map} +1 -1
- package/dist/dashboard/{agent-enrichment-C67LJBgD.js → agent-enrichment-DdO7ZqjI.js} +11 -7
- package/dist/dashboard/agent-enrichment-DdO7ZqjI.js.map +1 -0
- package/dist/dashboard/{agent-enrichment-Cq0P1cNZ.js → agent-enrichment-dLeGE1fX.js} +1 -1
- package/dist/dashboard/{agents-YyO6t5Xa.js → agents-DCpQQ_W5.js} +14 -14
- package/dist/dashboard/{agents-YyO6t5Xa.js.map → agents-DCpQQ_W5.js.map} +1 -1
- package/dist/dashboard/{agents-BVBVCyat.js → agents-Dgh2TjSp.js} +1 -1
- package/dist/dashboard/{archive-planning-h-hAjk0P.js → archive-planning-BmW9UDTr.js} +3 -3
- package/dist/dashboard/{archive-planning-h-hAjk0P.js.map → archive-planning-BmW9UDTr.js.map} +1 -1
- package/dist/dashboard/{archive-planning-CScs1MOC.js → archive-planning-C3Ebf9yC.js} +1 -1
- package/dist/dashboard/{beads-qNB0yAHV.js → beads-Bv-AdX7G.js} +3 -3
- package/dist/dashboard/{beads-qNB0yAHV.js.map → beads-Bv-AdX7G.js.map} +1 -1
- package/dist/dashboard/{beads-D_FRedEJ.js → beads-By6-X07V.js} +1 -1
- package/dist/dashboard/clean-planning-D60L8rPY.js +2 -0
- package/dist/dashboard/{clean-planning-qafw99vY.js → clean-planning-VEJu5suh.js} +2 -2
- package/dist/dashboard/{clean-planning-qafw99vY.js.map → clean-planning-VEJu5suh.js.map} +1 -1
- package/dist/dashboard/close-issue-C2KeSKKJ.js +2 -0
- package/dist/dashboard/{close-issue-DfIggeZD.js → close-issue-DtKdsSTm.js} +11 -7
- package/dist/dashboard/close-issue-DtKdsSTm.js.map +1 -0
- package/dist/dashboard/compact-beads-C7BN5N11.js +2 -0
- package/dist/dashboard/{compact-beads-Dt0qTqsC.js → compact-beads-D8Vt3qyv.js} +2 -2
- package/dist/dashboard/{compact-beads-Dt0qTqsC.js.map → compact-beads-D8Vt3qyv.js.map} +1 -1
- package/dist/dashboard/{config-CUREjHP7.js → config-CDkGjnwy.js} +2 -2
- package/dist/dashboard/{config-CUREjHP7.js.map → config-CDkGjnwy.js.map} +1 -1
- package/dist/dashboard/{config-BeI3uy-8.js → config-CTXkBATQ.js} +1 -1
- package/dist/dashboard/{database-CozA13Wy.js → database-DhqASALP.js} +1 -1
- package/dist/dashboard/{database-C0y0hXBx.js → database-cxmQryoh.js} +2 -2
- package/dist/dashboard/{database-C0y0hXBx.js.map → database-cxmQryoh.js.map} +1 -1
- package/dist/dashboard/{dist-src-oG2iHzgI.js → dist-src-DTm11oQr.js} +1 -1
- package/dist/dashboard/{dist-src-oG2iHzgI.js.map → dist-src-DTm11oQr.js.map} +1 -1
- package/dist/dashboard/{event-store-D7kLBd07.js → event-store-VWWUmOfn.js} +1 -1
- package/dist/dashboard/{event-store-O9q0Gweh.js → event-store-vSmAA3Zp.js} +9 -4
- package/dist/dashboard/event-store-vSmAA3Zp.js.map +1 -0
- package/dist/dashboard/{factory-BnLdiQW-.js → factory-C8nhLGHB.js} +3 -3
- package/dist/dashboard/{factory-BnLdiQW-.js.map → factory-C8nhLGHB.js.map} +1 -1
- package/dist/dashboard/{feedback-writer-DyovUANg.js → feedback-writer-CudSe1WK.js} +2 -2
- package/dist/dashboard/{feedback-writer-DyovUANg.js.map → feedback-writer-CudSe1WK.js.map} +1 -1
- package/dist/dashboard/{feedback-writer-gSUv_W0h.js → feedback-writer-Wgv1cd1r.js} +1 -1
- package/dist/dashboard/{git-utils-BJRioREj.js → git-utils-C1m4SwAe.js} +1 -1
- package/dist/dashboard/{git-utils-BJRioREj.js.map → git-utils-C1m4SwAe.js.map} +1 -1
- package/dist/dashboard/{git-utils-BtCRddq3.js → git-utils-DQI8EYoj.js} +1 -1
- package/dist/dashboard/{github-app-XO-LBUGk.js → github-app-DClWjjHr.js} +1 -1
- package/dist/dashboard/{github-app-XO-LBUGk.js.map → github-app-DClWjjHr.js.map} +1 -1
- package/dist/dashboard/{health-events-db-584nYgJB.js → health-events-db-BMXQfInV.js} +1 -1
- package/dist/dashboard/{health-events-db-B3ChzN65.js → health-events-db-Do4NrOhC.js} +2 -2
- package/dist/dashboard/{health-events-db-B3ChzN65.js.map → health-events-db-Do4NrOhC.js.map} +1 -1
- package/dist/dashboard/{hooks-CKhs3N68.js → hooks-CB4T47NC.js} +1 -1
- package/dist/dashboard/{hooks-CErbP8Oq.js → hooks-CjqXOlNb.js} +2 -2
- package/dist/dashboard/{hooks-CErbP8Oq.js.map → hooks-CjqXOlNb.js.map} +1 -1
- package/dist/dashboard/hume-CA2pftu_.js +3 -0
- package/dist/dashboard/{hume-CX_U3Qha.js → hume-JsAlMOJC.js} +2 -2
- package/dist/dashboard/{hume-CX_U3Qha.js.map → hume-JsAlMOJC.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-B57kGDUV.js → inspect-agent-7eour7EA.js} +3 -3
- package/dist/dashboard/{inspect-agent-B57kGDUV.js.map → inspect-agent-7eour7EA.js.map} +1 -1
- package/dist/dashboard/{io-yGovuG4U.js → io-CWlFW78i.js} +1 -1
- package/dist/dashboard/{io-AJg-mzFi.js → io-DKS6359z.js} +1 -1
- package/dist/dashboard/{io-AJg-mzFi.js.map → io-DKS6359z.js.map} +1 -1
- package/dist/dashboard/issue-id-vwYJdsf8.js +62 -0
- package/dist/dashboard/issue-id-vwYJdsf8.js.map +1 -0
- package/dist/dashboard/{issue-service-singleton-DQK42EqH.js → issue-service-singleton-Co__-6kL.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js → issue-service-singleton-Wv4xBm3y.js} +7 -7
- package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js.map → issue-service-singleton-Wv4xBm3y.js.map} +1 -1
- package/dist/dashboard/{label-cleanup-CZEsbtq9.js → label-cleanup-nVKTmIIW.js} +7 -4
- package/dist/dashboard/label-cleanup-nVKTmIIW.js.map +1 -0
- package/dist/dashboard/lifecycle-BcUmtkR4.js +7 -0
- package/dist/dashboard/{merge-agent-GLtMEsTu.js → merge-agent-CGN3TT0a.js} +1 -1
- package/dist/dashboard/{merge-agent-twroFuAh.js → merge-agent-yudQOPZc.js} +148 -46
- package/dist/dashboard/merge-agent-yudQOPZc.js.map +1 -0
- package/dist/dashboard/{paths-COdEvoXR.js → paths-BDyJ7BiV.js} +19 -2
- package/dist/dashboard/{paths-COdEvoXR.js.map → paths-BDyJ7BiV.js.map} +1 -1
- package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js → pipeline-notifier-CCSN-jar.js} +1 -1
- package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js.map → pipeline-notifier-CCSN-jar.js.map} +1 -1
- package/dist/dashboard/{plan-utils-BkCIhn3B.js → plan-utils-Bkcsqr_s.js} +3 -3
- package/dist/dashboard/{plan-utils-BkCIhn3B.js.map → plan-utils-Bkcsqr_s.js.map} +1 -1
- package/dist/dashboard/{prd-draft-D09Afalc.js → prd-draft-BD8oMkZ1.js} +2 -2
- package/dist/dashboard/{prd-draft-D09Afalc.js.map → prd-draft-BD8oMkZ1.js.map} +1 -1
- package/dist/dashboard/{projection-cache-DQ9zegkK.js → projection-cache-C0EL8s8h.js} +1 -1
- package/dist/dashboard/{projection-cache-DQ9zegkK.js.map → projection-cache-C0EL8s8h.js.map} +1 -1
- package/dist/dashboard/{projects-DyT3vSy-.js → projects-C5ozxjwP.js} +1 -1
- package/dist/dashboard/{projects-Cq3TWdPS.js → projects-CFVl4oHn.js} +25 -13
- package/dist/dashboard/projects-CFVl4oHn.js.map +1 -0
- package/dist/dashboard/{providers-Ck2sQd_F.js → providers-B5Y4H2Mg.js} +4 -4
- package/dist/dashboard/providers-B5Y4H2Mg.js.map +1 -0
- package/dist/dashboard/{providers-DVQnDekG.js → providers-csVZVPkE.js} +1 -1
- package/dist/dashboard/public/assets/{dist-CCJbQrSB.js → dist-CXaO6nOE.js} +1 -1
- package/dist/dashboard/public/assets/index-CzFZIb87.js +212 -0
- package/dist/dashboard/public/assets/index-OEEbThNN.css +1 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/rally-6McpKKRa.js +3 -0
- package/dist/dashboard/{rally-Cwuae-4C.js → rally-YjFRxIiC.js} +2 -2
- package/dist/dashboard/{rally-Cwuae-4C.js.map → rally-YjFRxIiC.js.map} +1 -1
- package/dist/dashboard/{rally-api-DSUxm7EO.js → rally-api-C0WqCSkT.js} +1 -1
- package/dist/dashboard/{rally-api-DSUxm7EO.js.map → rally-api-C0WqCSkT.js.map} +1 -1
- package/dist/dashboard/{rally-api-CEH5KZi4.js → rally-api-DNttdCW4.js} +1 -1
- package/dist/dashboard/{remote-BHTTMpJJ.js → remote-Cigqjj3f.js} +2 -2
- package/dist/dashboard/{remote-BXo_iIku.js → remote-ObpNZ7hF.js} +2 -2
- package/dist/dashboard/{remote-BXo_iIku.js.map → remote-ObpNZ7hF.js.map} +1 -1
- package/dist/dashboard/{remote-agents-CTKVhFFY.js → remote-agents-Bf3GuM7t.js} +1 -1
- package/dist/dashboard/{remote-agents-C0_0LLNd.js → remote-agents-DFyjT1Le.js} +1 -1
- package/dist/dashboard/{remote-agents-C0_0LLNd.js.map → remote-agents-DFyjT1Le.js.map} +1 -1
- package/dist/dashboard/{review-status-CK3eBGyb.js → review-status-BtXqWBhS.js} +1 -1
- package/dist/dashboard/{review-status-CV55Tl-n.js → review-status-Bymwzh2i.js} +44 -4
- package/dist/dashboard/{review-status-CV55Tl-n.js.map → review-status-Bymwzh2i.js.map} +1 -1
- package/dist/dashboard/server.js +559 -253
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{settings-CuHV-wcv.js → settings-BHlDG7TK.js} +2 -2
- package/dist/dashboard/settings-BHlDG7TK.js.map +1 -0
- package/dist/dashboard/settings-XWvDcj-D.js +2 -0
- package/dist/dashboard/{shadow-engineering-BUeZunaE.js → shadow-engineering-lIn1W_95.js} +1 -1
- package/dist/dashboard/{shadow-engineering-BUeZunaE.js.map → shadow-engineering-lIn1W_95.js.map} +1 -1
- package/dist/dashboard/{shadow-state-DHQ-kASN.js → shadow-state-BIexcxkv.js} +1 -1
- package/dist/dashboard/{shadow-state-DHQ-kASN.js.map → shadow-state-BIexcxkv.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js → spawn-planning-session-33Jf-d5T.js} +6 -6
- package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js.map → spawn-planning-session-33Jf-d5T.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-U0Lqpjen.js → spawn-planning-session-D5hrVdWM.js} +1 -1
- package/dist/dashboard/{specialist-context-ColzlmGE.js → specialist-context-DGukHSn8.js} +6 -6
- package/dist/dashboard/{specialist-context-ColzlmGE.js.map → specialist-context-DGukHSn8.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-BhmDpFIq.js → specialist-logs-CIw4qfTy.js} +1 -1
- package/dist/dashboard/{specialists-C6s3U6tX.js → specialists-B_zrayaP.js} +37 -36
- package/dist/dashboard/specialists-B_zrayaP.js.map +1 -0
- package/dist/dashboard/{specialists-Cny632-T.js → specialists-Cp-PgspS.js} +1 -1
- package/dist/dashboard/{test-agent-queue-tqI4VDsu.js → test-agent-queue-ypF_ecHo.js} +4 -4
- package/dist/dashboard/{test-agent-queue-tqI4VDsu.js.map → test-agent-queue-ypF_ecHo.js.map} +1 -1
- package/dist/dashboard/{tldr-daemon-BNFyS7W_.js → tldr-daemon-B_oLRD8z.js} +2 -2
- package/dist/dashboard/{tldr-daemon-BNFyS7W_.js.map → tldr-daemon-B_oLRD8z.js.map} +1 -1
- package/dist/dashboard/{tldr-daemon-A6JqC59u.js → tldr-daemon-Cfs0bXTi.js} +1 -1
- package/dist/dashboard/{tmux-DYGAVJfb.js → tmux-BzxdKItf.js} +1 -1
- package/dist/dashboard/{tmux-IlN1Slv-.js → tmux-LwG0tHhU.js} +2 -2
- package/dist/dashboard/{tmux-IlN1Slv-.js.map → tmux-LwG0tHhU.js.map} +1 -1
- package/dist/dashboard/{tracker-config-BzNLnmcE.js → tracker-config-BP59uH4V.js} +1 -1
- package/dist/dashboard/{tracker-config-CNM_5rEf.js → tracker-config-e7ph1QqT.js} +2 -2
- package/dist/dashboard/{tracker-config-CNM_5rEf.js.map → tracker-config-e7ph1QqT.js.map} +1 -1
- package/dist/dashboard/{tunnel-D2BkwU7k.js → tunnel-0RzzuXPf.js} +1 -1
- package/dist/dashboard/{tunnel-Dub2hiAA.js → tunnel-DldbBPWL.js} +2 -2
- package/dist/dashboard/{tunnel-Dub2hiAA.js.map → tunnel-DldbBPWL.js.map} +1 -1
- package/dist/dashboard/{types-CWA-o4UN.js → types-RKZjGE5N.js} +1 -1
- package/dist/dashboard/{types-CWA-o4UN.js.map → types-RKZjGE5N.js.map} +1 -1
- package/dist/dashboard/{vtt-parser-BAXygRf0.js → vtt-parser-99vFekRQ.js} +1 -1
- package/dist/dashboard/{vtt-parser-BAXygRf0.js.map → vtt-parser-99vFekRQ.js.map} +1 -1
- package/dist/dashboard/{work-agent-prompt-JYq_OugP.js → work-agent-prompt-fCg67nyo.js} +65 -10
- package/dist/dashboard/{work-agent-prompt-JYq_OugP.js.map → work-agent-prompt-fCg67nyo.js.map} +1 -1
- package/dist/dashboard/{work-type-router-Cxp8_ur2.js → work-type-router-CWVW2Wk_.js} +1 -1
- package/dist/dashboard/{work-type-router-Cxp8_ur2.js.map → work-type-router-CWVW2Wk_.js.map} +1 -1
- package/dist/dashboard/{work-type-router-Com2amST.js → work-type-router-Di5gCQwh.js} +1 -1
- package/dist/dashboard/{workflows-N1UTipYl.js → workflows-BSMipN07.js} +35 -17
- package/dist/dashboard/workflows-BSMipN07.js.map +1 -0
- package/dist/dashboard/workflows-DaYWQIS2.js +2 -0
- package/dist/dashboard/{workspace-config-cmp5_ipD.js → workspace-config-DVDR-Ukh.js} +1 -1
- package/dist/dashboard/workspace-config-DVDR-Ukh.js.map +1 -0
- package/dist/dashboard/{workspace-manager-CjpWPgzL.js → workspace-manager-BYfzs_t2.js} +1 -1
- package/dist/dashboard/{workspace-manager-D_y9ZmW_.js → workspace-manager-C7OfT62A.js} +44 -24
- package/dist/dashboard/workspace-manager-C7OfT62A.js.map +1 -0
- package/dist/{dns-BKzHm-2q.js → dns-D_aKQJjb.js} +1 -1
- package/dist/{dns-DZwOWvVO.js → dns-Yxq4NNS7.js} +1 -1
- package/dist/{dns-DZwOWvVO.js.map → dns-Yxq4NNS7.js.map} +1 -1
- package/dist/{factory-DFu3IT4r.js → factory-BRBGw6OB.js} +1 -1
- package/dist/{factory-DfzczxN1.js → factory-DzsOiZVc.js} +3 -3
- package/dist/{factory-DfzczxN1.js.map → factory-DzsOiZVc.js.map} +1 -1
- package/dist/{feedback-writer-CwdnOkPO.js → feedback-writer-ygXN5F9N.js} +2 -2
- package/dist/{feedback-writer-CwdnOkPO.js.map → feedback-writer-ygXN5F9N.js.map} +1 -1
- package/dist/{github-app-CHKwxOeQ.js → github-app-DykduJ0X.js} +1 -1
- package/dist/{github-app-CHKwxOeQ.js.map → github-app-DykduJ0X.js.map} +1 -1
- package/dist/hume-9nv1VmMV.js +3 -0
- package/dist/{hume-DnV-tDsh.js → hume-DoCbph2h.js} +2 -2
- package/dist/{hume-DnV-tDsh.js.map → hume-DoCbph2h.js.map} +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/issue-id-CAcekoIw.js +62 -0
- package/dist/issue-id-CAcekoIw.js.map +1 -0
- package/dist/{label-cleanup-31ElPqqv.js → label-cleanup-C8R9Rspn.js} +7 -4
- package/dist/label-cleanup-C8R9Rspn.js.map +1 -0
- package/dist/{manifest-DL0oDbpv.js → manifest-B4ghOD-V.js} +1 -1
- package/dist/{manifest-DL0oDbpv.js.map → manifest-B4ghOD-V.js.map} +1 -1
- package/dist/{merge-agent-VQH9z9t8.js → merge-agent-DlUiUanN.js} +86 -33
- package/dist/merge-agent-DlUiUanN.js.map +1 -0
- package/dist/{paths-lMaxrYtT.js → paths-CDJ_HsbN.js} +19 -2
- package/dist/{paths-lMaxrYtT.js.map → paths-CDJ_HsbN.js.map} +1 -1
- package/dist/{pipeline-notifier-OJ-d3Y60.js → pipeline-notifier-XgDdCdvT.js} +1 -1
- package/dist/{pipeline-notifier-OJ-d3Y60.js.map → pipeline-notifier-XgDdCdvT.js.map} +1 -1
- package/dist/{projects-CvLepaxC.js → projects-Bk-5QhFQ.js} +25 -13
- package/dist/projects-Bk-5QhFQ.js.map +1 -0
- package/dist/{projects-DMWmPeIU.js → projects-DhU7rAVN.js} +1 -1
- package/dist/{providers-DcCPZ5K4.js → providers-DSU1vfQF.js} +4 -4
- package/dist/providers-DSU1vfQF.js.map +1 -0
- package/dist/rally-DdPvGa-w.js +3 -0
- package/dist/{rally-uUUZXp1h.js → rally-Dy00NElU.js} +1 -1
- package/dist/{rally-uUUZXp1h.js.map → rally-Dy00NElU.js.map} +1 -1
- package/dist/{remote-CkLBqLJc.js → remote-CYiOJg0q.js} +2 -2
- package/dist/{remote-CkLBqLJc.js.map → remote-CYiOJg0q.js.map} +1 -1
- package/dist/{remote-agents-C5Bd2fgt.js → remote-agents-CZXrUF4f.js} +1 -1
- package/dist/{remote-agents-C5Bd2fgt.js.map → remote-agents-CZXrUF4f.js.map} +1 -1
- package/dist/{remote-agents-BTzD-wMQ.js → remote-agents-ycHHVsgf.js} +1 -1
- package/dist/{remote-workspace-Dxghqiti.js → remote-workspace-CA33UuVI.js} +4 -4
- package/dist/{remote-workspace-Dxghqiti.js.map → remote-workspace-CA33UuVI.js.map} +1 -1
- package/dist/{review-status-2TdtHNcs.js → review-status-D6H2WOw8.js} +1 -1
- package/dist/{review-status-Bm1bWNEa.js → review-status-DEDvCKMP.js} +44 -4
- package/dist/{review-status-Bm1bWNEa.js.map → review-status-DEDvCKMP.js.map} +1 -1
- package/dist/{tracker-C_62ukEq.js → settings-BcWPTrua.js} +7 -199
- package/dist/settings-BcWPTrua.js.map +1 -0
- package/dist/shadow-state-BZzxfEGw.js +2 -0
- package/dist/{shadow-state-CFFHf05M.js → shadow-state-CE3dQfll.js} +1 -1
- package/dist/{shadow-state-CFFHf05M.js.map → shadow-state-CE3dQfll.js.map} +1 -1
- package/dist/{specialist-context-BdNFsfMG.js → specialist-context-BAUWL1Fl.js} +6 -6
- package/dist/{specialist-context-BdNFsfMG.js.map → specialist-context-BAUWL1Fl.js.map} +1 -1
- package/dist/{specialist-logs-CLztE_bE.js → specialist-logs-DQKKQV9B.js} +1 -1
- package/dist/{specialists-aUoUVWsN.js → specialists-Bfb9ATzw.js} +1 -1
- package/dist/{specialists-DEKqgkxp.js → specialists-D7Kj5o6s.js} +35 -34
- package/dist/specialists-D7Kj5o6s.js.map +1 -0
- package/dist/sync-DMfgd389.js +693 -0
- package/dist/sync-DMfgd389.js.map +1 -0
- package/dist/sync-TL6y-8K6.js +2 -0
- package/dist/{tldr-daemon-BCEFPItr.js → tldr-daemon-CFx4LXAl.js} +2 -2
- package/dist/{tldr-daemon-BCEFPItr.js.map → tldr-daemon-CFx4LXAl.js.map} +1 -1
- package/dist/{tldr-daemon-xBAx4cBE.js → tldr-daemon-D_EooADG.js} +1 -1
- package/dist/{tmux-DN6H886Y.js → tmux-CBtui_Cl.js} +1 -1
- package/dist/{tmux-CKdNxxJx.js → tmux-D6Ah4I8z.js} +2 -2
- package/dist/{tmux-CKdNxxJx.js.map → tmux-D6Ah4I8z.js.map} +1 -1
- package/dist/tracker-BhYYvU3p.js +198 -0
- package/dist/tracker-BhYYvU3p.js.map +1 -0
- package/dist/{tracker-utils-CVU2W1sX.js → tracker-utils-ChQyut8w.js} +34 -12
- package/dist/tracker-utils-ChQyut8w.js.map +1 -0
- package/dist/{traefik-DHgBoWXX.js → traefik-C80EbDu_.js} +4 -4
- package/dist/{traefik-DHgBoWXX.js.map → traefik-C80EbDu_.js.map} +1 -1
- package/dist/{traefik-BR-edbZv.js → traefik-CgHl7Bge.js} +1 -1
- package/dist/{tunnel-BZO9Q5oe.js → tunnel-DXOJ1wMM.js} +1 -1
- package/dist/{tunnel-Bl1qNSyQ.js → tunnel-DzXEPwIc.js} +2 -2
- package/dist/{tunnel-Bl1qNSyQ.js.map → tunnel-DzXEPwIc.js.map} +1 -1
- package/dist/{types-DewGdaIP.js → types-BhJj1SP1.js} +1 -1
- package/dist/{types-DewGdaIP.js.map → types-BhJj1SP1.js.map} +1 -1
- package/dist/{work-type-router-CS2BB1vS.js → work-type-router-CHjciPyS.js} +3 -3
- package/dist/{work-type-router-CS2BB1vS.js.map → work-type-router-CHjciPyS.js.map} +1 -1
- package/dist/{workspace-config-CNXOpKuj.js → workspace-config-fUafvYMp.js} +1 -1
- package/dist/workspace-config-fUafvYMp.js.map +1 -0
- package/dist/workspace-manager-B9jS4Dsq.js +3 -0
- package/dist/{workspace-manager-CncdZkIy.js → workspace-manager-DuLhnzJV.js} +112 -27
- package/dist/workspace-manager-DuLhnzJV.js.map +1 -0
- package/package.json +2 -1
- package/scripts/post-merge-deploy.sh +25 -5
- package/scripts/record-cost-event.js +57 -7
- package/scripts/record-cost-event.js.map +1 -1
- package/skills/pan-help/SKILL.md +1 -1
- package/skills/pan-sync/SKILL.md +6 -6
- package/skills/workspace-add-repo/skill.md +46 -0
- package/templates/claude-md/sections/warnings.md +15 -2
- package/dist/clean-planning-sZXvy3Y5.js +0 -2
- package/dist/close-issue-Dml437qV.js +0 -2
- package/dist/close-issue-Dr7yZmrr.js.map +0 -1
- package/dist/compact-beads-iu218JcO.js +0 -2
- package/dist/dashboard/agent-enrichment-C67LJBgD.js.map +0 -1
- package/dist/dashboard/clean-planning-DCu3cOTu.js +0 -2
- package/dist/dashboard/close-issue-DfIggeZD.js.map +0 -1
- package/dist/dashboard/close-issue-DwdwYtar.js +0 -2
- package/dist/dashboard/compact-beads-DXY2fK2s.js +0 -2
- package/dist/dashboard/event-store-O9q0Gweh.js.map +0 -1
- package/dist/dashboard/hume-MZndNDVU.js +0 -3
- package/dist/dashboard/label-cleanup-CZEsbtq9.js.map +0 -1
- package/dist/dashboard/lifecycle-ZTYdrr2O.js +0 -7
- package/dist/dashboard/merge-agent-twroFuAh.js.map +0 -1
- package/dist/dashboard/projects-Cq3TWdPS.js.map +0 -1
- package/dist/dashboard/providers-Ck2sQd_F.js.map +0 -1
- package/dist/dashboard/public/assets/index-CpSmB2ts.css +0 -1
- package/dist/dashboard/public/assets/index-yarWhi0M.js +0 -214
- package/dist/dashboard/rally-CQ1OBJrJ.js +0 -3
- package/dist/dashboard/settings-CuHV-wcv.js.map +0 -1
- package/dist/dashboard/settings-DMeGBRsk.js +0 -2
- package/dist/dashboard/specialists-C6s3U6tX.js.map +0 -1
- package/dist/dashboard/workflows-B2ARUpOa.js +0 -2
- package/dist/dashboard/workflows-N1UTipYl.js.map +0 -1
- package/dist/dashboard/workspace-config-cmp5_ipD.js.map +0 -1
- package/dist/dashboard/workspace-manager-D_y9ZmW_.js.map +0 -1
- package/dist/hume-BjmwmJ9E.js +0 -3
- package/dist/label-cleanup-31ElPqqv.js.map +0 -1
- package/dist/merge-agent-VQH9z9t8.js.map +0 -1
- package/dist/projects-CvLepaxC.js.map +0 -1
- package/dist/providers-DcCPZ5K4.js.map +0 -1
- package/dist/rally-DR9x8--6.js +0 -3
- package/dist/shadow-state-p3jpGRPJ.js +0 -2
- package/dist/specialists-DEKqgkxp.js.map +0 -1
- package/dist/tracker-C_62ukEq.js.map +0 -1
- package/dist/tracker-utils-CVU2W1sX.js.map +0 -1
- package/dist/workspace-config-CNXOpKuj.js.map +0 -1
- package/dist/workspace-manager-CncdZkIy.js.map +0 -1
- package/dist/workspace-manager-Cx0r2Jnv.js +0 -3
package/dist/dashboard/{work-agent-prompt-JYq_OugP.js.map → work-agent-prompt-fCg67nyo.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-agent-prompt-JYq_OugP.js","names":[],"sources":["../../src/lib/template.ts","../../src/lib/cloister/work-agent-prompt.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { CLAUDE_MD_TEMPLATES } from './paths.js';\n\nexport interface TemplateVariables {\n FEATURE_FOLDER: string;\n BRANCH_NAME: string;\n ISSUE_ID: string;\n WORKSPACE_PATH: string;\n FRONTEND_URL?: string;\n API_URL?: string;\n PROJECT_NAME?: string;\n PROJECT_DOMAIN?: string;\n [key: string]: string | undefined;\n}\n\nexport function loadTemplate(templatePath: string): string {\n if (!existsSync(templatePath)) {\n throw new Error(`Template not found: ${templatePath}`);\n }\n return readFileSync(templatePath, 'utf8');\n}\n\n/**\n * Process {{#env LOCAL}}...{{/env}} and {{#env REMOTE}}...{{/env}} blocks.\n * Keeps content for matching env, strips blocks for non-matching env.\n */\nexport function processEnvBlocks(template: string, env: 'LOCAL' | 'REMOTE'): string {\n // Keep matching env blocks (strip the tags, keep content)\n let result = template.replace(\n new RegExp(`\\\\{\\\\{#env ${env}\\\\}\\\\}([\\\\s\\\\S]*?)\\\\{\\\\{/env\\\\}\\\\}`, 'g'),\n '$1'\n );\n\n // Remove non-matching env blocks entirely\n const otherEnv = env === 'LOCAL' ? 'REMOTE' : 'LOCAL';\n result = result.replace(\n new RegExp(`\\\\{\\\\{#env ${otherEnv}\\\\}\\\\}[\\\\s\\\\S]*?\\\\{\\\\{/env\\\\}\\\\}`, 'g'),\n ''\n );\n\n return result;\n}\n\n/**\n * Process {{#if varName}}...{{/if}} blocks.\n * Keeps content when the variable is truthy (non-empty string), strips otherwise.\n */\nexport function processIfBlocks(template: string, vars: Record<string, string | undefined>): string {\n return template.replace(\n /\\{\\{#if (\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/if\\}\\}/g,\n (_match, varName: string, content: string) => {\n const value = vars[varName];\n return value && value.trim() ? content : '';\n }\n );\n}\n\nexport function substituteVariables(\n template: string,\n variables: TemplateVariables\n): string {\n let result = template;\n\n for (const [key, value] of Object.entries(variables)) {\n if (value !== undefined) {\n // Replace {{KEY}} and ${KEY} patterns\n result = result.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value);\n result = result.replace(new RegExp(`\\\\$\\\\{${key}\\\\}`, 'g'), value);\n }\n }\n\n return result;\n}\n\nexport function generateClaudeMd(\n projectPath: string,\n variables: TemplateVariables\n): string {\n const sections: string[] = [];\n\n // Layer 1: Panopticon default sections\n const defaultOrder = [\n 'workspace-info.md',\n 'beads.md',\n 'commands-skills.md',\n 'warnings.md',\n ];\n\n for (const section of defaultOrder) {\n const sectionPath = join(CLAUDE_MD_TEMPLATES, section);\n if (existsSync(sectionPath)) {\n const content = loadTemplate(sectionPath);\n sections.push(substituteVariables(content, variables));\n }\n }\n\n // Layer 2: Project-specific sections\n const projectSections = join(projectPath, '.panopticon', 'claude-md', 'sections');\n if (existsSync(projectSections)) {\n const projectFiles = readdirSync(projectSections)\n .filter((f) => f.endsWith('.md'))\n .sort();\n\n for (const file of projectFiles) {\n const content = loadTemplate(join(projectSections, file));\n sections.push(substituteVariables(content, variables));\n }\n }\n\n // If no sections found, return minimal CLAUDE.md\n if (sections.length === 0) {\n return `# Workspace: ${variables.FEATURE_FOLDER}\n\n**Issue:** ${variables.ISSUE_ID}\n**Branch:** ${variables.BRANCH_NAME}\n**Path:** ${variables.WORKSPACE_PATH}\n\n## Getting Started\n\nThis workspace was created by Panopticon. Use \\`bd\\` commands to track your work.\n`;\n }\n\n return sections.join('\\n\\n---\\n\\n');\n}\n","import { existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { processEnvBlocks, processIfBlocks, substituteVariables } from '../template.js';\nimport { extractTeamPrefix, findProjectByTeam } from '../projects.js';\nimport { readWorkspacePlan } from '../vbrief/io.js';\nimport { extractACFromDocument } from '../vbrief/acceptance-criteria.js';\nimport { loadConfig } from '../config.js';\nimport { createTrackerFromConfig } from '../tracker/factory.js';\nimport { NotImplementedError } from '../tracker/interface.js';\nimport type { TrackerType } from '../tracker/interface.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Resolve the prompts directory, handling both dev (src/) and bundled (dist/) layouts.\n */\nfunction resolvePromptsDir(): string {\n // Try direct sibling path first (works in dev: src/lib/cloister/prompts/)\n const direct = join(__dirname, 'prompts');\n if (existsSync(direct) && existsSync(join(direct, 'work-agent.md'))) {\n return direct;\n }\n\n // Fallback: resolve from package root (works when bundled into dist/)\n let packageRoot = __dirname;\n if (packageRoot.includes('/src/')) {\n packageRoot = packageRoot.replace(/\\/src\\/.*$/, '');\n } else {\n // dist/cli/ or dist/dashboard/ → go up to package root\n packageRoot = join(packageRoot, '..', '..');\n }\n const fromRoot = join(packageRoot, 'src', 'lib', 'cloister', 'prompts');\n if (existsSync(fromRoot)) {\n return fromRoot;\n }\n\n return direct; // Let it fail with a clear error below\n}\n\nexport interface WorkAgentPromptContext {\n issueId: string;\n env: 'LOCAL' | 'REMOTE';\n workspacePath: string;\n projectRoot?: string;\n /** Skip dynamic context gathering (filesystem reads). True for REMOTE/dashboard. */\n skipDynamicContext?: boolean;\n /** Pre-fetched tracker context (new comments, status). Injected by callers. */\n trackerContext?: string;\n}\n\n/**\n * Build the unified work agent prompt from the template.\n */\nexport function buildWorkAgentPrompt(ctx: WorkAgentPromptContext): string {\n const templatePath = join(resolvePromptsDir(), 'work-agent.md');\n\n if (!existsSync(templatePath)) {\n throw new Error(`Work agent prompt template not found at ${templatePath}`);\n }\n\n let template = readFileSync(templatePath, 'utf-8');\n\n // Gather dynamic context (only for LOCAL when filesystem is accessible)\n let beadsTasksStr = '';\n let stitchDesignsStr = '';\n let polyrepoContextStr = '';\n let pendingFeedbackStr = '';\n\n if (!ctx.skipDynamicContext && ctx.projectRoot) {\n const planningContent = readPlanningContext(ctx.workspacePath);\n\n const beadsTasks = readBeadsTasks(ctx.workspacePath, ctx.projectRoot, ctx.issueId);\n if (beadsTasks.length > 0) {\n beadsTasksStr = beadsTasks.join('\\n');\n }\n\n const stitchDesigns = extractStitchDesigns(planningContent);\n if (stitchDesigns) {\n stitchDesignsStr = stitchDesigns;\n }\n\n polyrepoContextStr = buildPolyrepoContext(ctx.issueId, ctx.workspacePath);\n\n // Check for pending specialist feedback\n pendingFeedbackStr = readPendingFeedback(ctx.workspacePath);\n }\n\n // Build variables map\n const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || '3011'}`;\n const vars: Record<string, string | undefined> = {\n ISSUE_ID: ctx.issueId,\n ISSUE_ID_LOWER: ctx.issueId.toLowerCase(),\n WORKSPACE_PATH: ctx.workspacePath,\n PROJECT_ROOT: ctx.projectRoot || '',\n API_URL: apiUrl,\n BEADS_TASKS: beadsTasksStr,\n STITCH_DESIGNS: stitchDesignsStr,\n POLYREPO_CONTEXT: polyrepoContextStr,\n PENDING_FEEDBACK: pendingFeedbackStr,\n NEW_TRACKER_CONTEXT: ctx.trackerContext || '',\n };\n\n // Processing pipeline: env blocks → if blocks → variable substitution\n template = processEnvBlocks(template, ctx.env);\n template = processIfBlocks(template, vars);\n template = substituteVariables(template, vars as any);\n\n // Wrap in orchestration markers to distinguish Panopticon's injected instructions\n // from the agent's actual reasoning. Log parsers and context compressors can\n // recognize these markers and handle orchestration context separately.\n return `<!-- panopticon:orchestration-context-start -->\n<!-- This is Panopticon orchestration context injected automatically.\n It contains workspace setup instructions, not agent reasoning.\n Session summarizers should SKIP this block and focus on the agent's\n actual work, decisions, and tradeoffs that follow. -->\n\n${template}\n\n<!-- panopticon:orchestration-context-end -->`;\n}\n\n/**\n * Read pending specialist feedback from .planning/feedback/.\n * Returns a summary of the latest feedback file(s) for injection into the prompt.\n */\nfunction readPendingFeedback(workspacePath: string): string {\n const feedbackDir = join(workspacePath, '.planning', 'feedback');\n if (!existsSync(feedbackDir)) return '';\n\n try {\n const files = readdirSync(feedbackDir)\n .filter(f => f.endsWith('.md'))\n .sort(); // NNN-prefixed, so sort gives chronological order\n\n if (files.length === 0) return '';\n\n // Show the latest feedback file path (agent will read it)\n const latest = files[files.length - 1];\n const lines: string[] = [\n `**${files.length} feedback file(s) in \\`.planning/feedback/\\`:**`,\n '',\n ];\n\n // List all files (most recent last)\n for (const file of files) {\n const marker = file === latest ? ' ← **latest, read this first**' : '';\n lines.push(`- \\`.planning/feedback/${file}\\`${marker}`);\n }\n\n lines.push('');\n lines.push('Read the latest feedback file and address any issues before continuing other work.');\n\n return lines.join('\\n');\n } catch {\n return '';\n }\n}\n\nconst COMMENT_BODY_LIMIT = 500;\nconst TOTAL_CONTEXT_LIMIT = 2000;\n\n/**\n * Fetch tracker context (new comments + issue status) since STATE.md was last modified.\n * Returns a formatted markdown string for injection into the work agent prompt.\n */\nexport async function getTrackerContext(\n issueId: string,\n workspacePath: string\n): Promise<string> {\n let stateMtime: Date | null = null;\n const statePath = join(workspacePath, '.planning', 'STATE.md');\n if (existsSync(statePath)) {\n try {\n stateMtime = statSync(statePath).mtime;\n } catch {\n // Ignore stat errors\n }\n }\n\n let config: ReturnType<typeof loadConfig>;\n try {\n config = loadConfig();\n } catch {\n return '_Tracker unavailable: could not load configuration. Check tracker settings manually._';\n }\n\n const trackersConfig = config.trackers;\n if (!trackersConfig) {\n return '';\n }\n\n // Try each configured tracker until one can resolve the issue\n const trackerTypes: TrackerType[] = [trackersConfig.primary];\n if (trackersConfig.secondary) {\n trackerTypes.push(trackersConfig.secondary);\n }\n\n for (const trackerType of trackerTypes) {\n try {\n const tracker = createTrackerFromConfig(trackersConfig, trackerType);\n\n // Fetch issue and comments in parallel\n const [issue, allComments] = await Promise.all([\n tracker.getIssue(issueId),\n tracker.getComments(issueId).catch((err: unknown) => {\n // GitLab throws NotImplementedError; treat as no comments\n if (err instanceof NotImplementedError) return [];\n throw err;\n }),\n ]);\n\n // Filter to comments newer than STATE.md mtime\n const newComments = stateMtime\n ? allComments.filter((c) => new Date(c.createdAt) > stateMtime!)\n : allComments;\n\n // Detect reopened: STATE.md exists (has completion history) but issue is open\n const isReopened =\n stateMtime !== null &&\n (issue.state === 'open' || issue.state === 'in_progress');\n\n // Build the section\n const lines: string[] = [];\n\n lines.push('## Tracker Status (Live)');\n lines.push('');\n\n const stateLabel = issue.rawState ?? issue.state;\n if (isReopened) {\n lines.push(\n `> **ISSUE REOPENED** — Current state: **${stateLabel}**. This issue was previously worked on. Review the tracker for new instructions before fast-pathing to done.`\n );\n } else {\n lines.push(`**Current state:** ${stateLabel}`);\n }\n\n if (newComments.length > 0) {\n const sinceLabel = stateMtime\n ? `since STATE.md was last updated (${stateMtime.toISOString().slice(0, 10)})`\n : 'all comments';\n lines.push('');\n lines.push(`**New comments ${sinceLabel}:**`);\n lines.push('');\n\n let totalChars = lines.join('\\n').length;\n let truncatedAny = false;\n\n for (const comment of newComments) {\n let body = comment.body;\n let commentTruncated = false;\n if (body.length > COMMENT_BODY_LIMIT) {\n body = body.slice(0, COMMENT_BODY_LIMIT) + ' [truncated — read full comment on tracker]';\n commentTruncated = true;\n truncatedAny = true;\n }\n\n const commentBlock = [\n `**${comment.author}** (${comment.createdAt.slice(0, 10)}):`,\n `> ${body.replace(/\\n/g, '\\n> ')}`,\n '',\n ].join('\\n');\n\n if (totalChars + commentBlock.length > TOTAL_CONTEXT_LIMIT) {\n lines.push('_[Additional comments truncated — check tracker for full history]_');\n truncatedAny = true;\n break;\n }\n\n lines.push(commentBlock);\n totalChars += commentBlock.length;\n }\n\n if (truncatedAny) {\n lines.push('');\n lines.push(`_Check the tracker directly for full comment content: ${issue.url}_`);\n }\n } else if (stateMtime) {\n lines.push('');\n lines.push('_No new comments since last STATE.md update._');\n }\n\n const result = lines.join('\\n').trim();\n // If only \"no new comments\" and not reopened, return empty to suppress the section\n if (!isReopened && newComments.length === 0) {\n return '';\n }\n return result;\n } catch (err: unknown) {\n // Issue not found in this tracker — try next\n const message = err instanceof Error ? err.message : String(err);\n if (\n message.toLowerCase().includes('not found') ||\n message.toLowerCase().includes('404') ||\n message.toLowerCase().includes('no configuration')\n ) {\n continue;\n }\n // Unexpected error (auth failure, network, etc.) — warn in prompt\n return `_Tracker unavailable: ${message}. Check tracker status and review any new comments manually._`;\n }\n }\n\n // No tracker resolved the issue\n return '';\n}\n\n/**\n * Read planning artifacts for an issue (STATE.md, etc.)\n */\nexport function readPlanningContext(workspacePath: string): string | null {\n const statePath = join(workspacePath, '.planning', 'STATE.md');\n if (existsSync(statePath)) {\n return readFileSync(statePath, 'utf-8');\n }\n return null;\n}\n\n/**\n * Check if STATE.md contains Stitch design information.\n * Returns the Stitch section if found, null otherwise.\n */\nexport function extractStitchDesigns(stateContent: string | null): string | null {\n if (!stateContent) return null;\n\n // Look for Stitch-related sections in STATE.md\n const stitchPatterns = [\n /## UI Designs[\\s\\S]*?(?=\\n## |$)/i,\n /### Stitch Assets[\\s\\S]*?(?=\\n### |\\n## |$)/i,\n /## Stitch[\\s\\S]*?(?=\\n## |$)/i,\n ];\n\n for (const pattern of stitchPatterns) {\n const match = stateContent.match(pattern);\n if (match) {\n return match[0].trim();\n }\n }\n\n // Also check for Stitch project/screen IDs mentioned anywhere\n if (\n stateContent.includes('Stitch') &&\n (stateContent.includes('Project ID') || stateContent.includes('Screen ID'))\n ) {\n const lines = stateContent.split('\\n');\n const stitchLines: string[] = [];\n let inStitchSection = false;\n\n for (const line of lines) {\n if (line.toLowerCase().includes('stitch')) {\n inStitchSection = true;\n }\n if (inStitchSection) {\n stitchLines.push(line);\n if (line.trim() === '' && stitchLines.length > 3) {\n break;\n }\n }\n }\n\n if (stitchLines.length > 0) {\n return stitchLines.join('\\n').trim();\n }\n }\n\n return null;\n}\n\n/**\n * Extract beads IDs from STATE.md content.\n * Looks for patterns like `panopticon-1dg` in backticks or tables.\n */\nexport function extractBeadsIdsFromState(stateContent: string): string[] {\n const ids: string[] = [];\n\n const backtickMatches = stateContent.match(/`([a-z]+-[a-z0-9]+)`/g) || [];\n for (const match of backtickMatches) {\n const id = match.replace(/`/g, '');\n if (id.match(/^[a-z]+-[a-z0-9]{2,4}$/)) {\n ids.push(id);\n }\n }\n\n return [...new Set(ids)];\n}\n\n/**\n * Read beads tasks for an issue from both workspace and project root.\n * Uses STATE.md to find the associated beads IDs.\n */\nexport function readBeadsTasks(\n workspacePath: string,\n projectRoot: string,\n issueId: string\n): string[] {\n const tasks: string[] = [];\n const normalizedId = issueId.toLowerCase();\n\n const stateContent = readPlanningContext(workspacePath);\n const beadsIds = stateContent ? extractBeadsIdsFromState(stateContent) : [];\n\n // Load vBRIEF AC indexed by item title (lowercase) for per-bead injection\n const acByTitle = buildACLookupByTitle(workspacePath);\n\n const beadsPaths = [\n join(workspacePath, '.beads', 'issues.jsonl'),\n join(projectRoot, '.beads', 'issues.jsonl'),\n ];\n\n const seenIds = new Set<string>();\n\n for (const beadsPath of beadsPaths) {\n if (!existsSync(beadsPath)) continue;\n\n try {\n const content = readFileSync(beadsPath, 'utf-8');\n const lines = content.split('\\n').filter((line) => line.trim());\n\n for (const line of lines) {\n try {\n const task = JSON.parse(line);\n if (seenIds.has(task.id)) continue;\n\n const labels = task.labels || task.tags || [];\n const isMatch =\n beadsIds.includes(task.id) ||\n labels.some((t: string) => t.toLowerCase().includes(normalizedId)) ||\n task.title?.toLowerCase().includes(normalizedId);\n\n if (isMatch) {\n seenIds.add(task.id);\n tasks.push(`- [${task.status || 'open'}] ${task.title} (${task.id})`);\n\n // Inject per-bead AC from vBRIEF plan\n const beadAC = matchBeadToAC(task.title, acByTitle);\n for (const ac of beadAC) {\n const check = ac.status === 'completed' ? 'x' : ' ';\n tasks.push(` - [${check}] AC: ${ac.title}`);\n }\n }\n } catch {\n // Skip malformed lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return tasks;\n}\n\n/**\n * Build a lookup map from item title (lowercase) → AC sub-items.\n * Used to match beads to their vBRIEF acceptance criteria.\n */\nfunction buildACLookupByTitle(workspacePath: string): Map<string, Array<{ title: string; status: string }>> {\n const lookup = new Map<string, Array<{ title: string; status: string }>>();\n const doc = readWorkspacePlan(workspacePath);\n if (!doc) return lookup;\n\n const criteria = extractACFromDocument(doc);\n for (const ac of criteria) {\n const key = ac.itemTitle.toLowerCase();\n let list = lookup.get(key);\n if (!list) {\n list = [];\n lookup.set(key, list);\n }\n list.push({ title: ac.title, status: ac.status });\n }\n\n return lookup;\n}\n\n/**\n * Match a bead title to its AC. Bead titles may have a plan ID prefix\n * (e.g., \"PAN-408: Create module\") — strip it before matching.\n */\nfunction matchBeadToAC(\n beadTitle: string,\n acByTitle: Map<string, Array<{ title: string; status: string }>>\n): Array<{ title: string; status: string }> {\n if (acByTitle.size === 0 || !beadTitle) return [];\n\n // Strip \"PAN-XXX: \" prefix if present\n const stripped = beadTitle.replace(/^[A-Z]+-\\d+:\\s*/, '');\n return acByTitle.get(stripped.toLowerCase()) || [];\n}\n\n/**\n * Generate polyrepo context section if applicable.\n */\nexport function buildPolyrepoContext(issueId: string, workspacePath: string): string {\n const teamPrefix = extractTeamPrefix(issueId);\n const projectConfig = teamPrefix ? findProjectByTeam(teamPrefix) : null;\n\n if (\n !projectConfig?.workspace?.type ||\n projectConfig.workspace.type !== 'polyrepo' ||\n !projectConfig.workspace.repos\n ) {\n return '';\n }\n\n const repos = projectConfig.workspace.repos;\n const lines: string[] = [\n '## Project Structure (Polyrepo)',\n '',\n '**IMPORTANT:** This project uses a **polyrepo** structure. The workspace root is NOT a git repository.',\n 'Each subdirectory is a separate git worktree:',\n '',\n '| Directory | Purpose |',\n '|-----------|---------|',\n ];\n\n for (const repo of repos) {\n lines.push(`| \\`${repo.name}/\\` | Git worktree for ${repo.path} |`);\n }\n\n lines.push('');\n lines.push('**Git operations:**');\n lines.push(\n '- Run `git status`, `git log`, etc. INSIDE the subdirectories (e.g., `cd fe && git status`)'\n );\n lines.push(\n `- The workspace root (\\`${workspacePath}\\`) has no \\`.git\\` directory`\n );\n lines.push(\n `- Each subdirectory has its own branch: \\`${repos[0]?.branch_prefix || 'feature/'}${issueId.toLowerCase()}\\``\n );\n lines.push('');\n\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"work-agent-prompt-fCg67nyo.js","names":[],"sources":["../../src/lib/template.ts","../../src/lib/cloister/work-agent-prompt.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { CLAUDE_MD_TEMPLATES } from './paths.js';\n\nexport interface TemplateVariables {\n FEATURE_FOLDER: string;\n BRANCH_NAME: string;\n ISSUE_ID: string;\n WORKSPACE_PATH: string;\n FRONTEND_URL?: string;\n API_URL?: string;\n PROJECT_NAME?: string;\n PROJECT_DOMAIN?: string;\n [key: string]: string | undefined;\n}\n\nexport function loadTemplate(templatePath: string): string {\n if (!existsSync(templatePath)) {\n throw new Error(`Template not found: ${templatePath}`);\n }\n return readFileSync(templatePath, 'utf8');\n}\n\n/**\n * Process {{#env LOCAL}}...{{/env}} and {{#env REMOTE}}...{{/env}} blocks.\n * Keeps content for matching env, strips blocks for non-matching env.\n */\nexport function processEnvBlocks(template: string, env: 'LOCAL' | 'REMOTE'): string {\n // Keep matching env blocks (strip the tags, keep content)\n let result = template.replace(\n new RegExp(`\\\\{\\\\{#env ${env}\\\\}\\\\}([\\\\s\\\\S]*?)\\\\{\\\\{/env\\\\}\\\\}`, 'g'),\n '$1'\n );\n\n // Remove non-matching env blocks entirely\n const otherEnv = env === 'LOCAL' ? 'REMOTE' : 'LOCAL';\n result = result.replace(\n new RegExp(`\\\\{\\\\{#env ${otherEnv}\\\\}\\\\}[\\\\s\\\\S]*?\\\\{\\\\{/env\\\\}\\\\}`, 'g'),\n ''\n );\n\n return result;\n}\n\n/**\n * Process {{#if varName}}...{{/if}} blocks.\n * Keeps content when the variable is truthy (non-empty string), strips otherwise.\n */\nexport function processIfBlocks(template: string, vars: Record<string, string | undefined>): string {\n return template.replace(\n /\\{\\{#if (\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/if\\}\\}/g,\n (_match, varName: string, content: string) => {\n const value = vars[varName];\n return value && value.trim() ? content : '';\n }\n );\n}\n\nexport function substituteVariables(\n template: string,\n variables: TemplateVariables\n): string {\n let result = template;\n\n for (const [key, value] of Object.entries(variables)) {\n if (value !== undefined) {\n // Replace {{KEY}} and ${KEY} patterns\n result = result.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value);\n result = result.replace(new RegExp(`\\\\$\\\\{${key}\\\\}`, 'g'), value);\n }\n }\n\n return result;\n}\n\nexport function generateClaudeMd(\n projectPath: string,\n variables: TemplateVariables\n): string {\n const sections: string[] = [];\n\n // Layer 1: Panopticon default sections\n const defaultOrder = [\n 'workspace-info.md',\n 'beads.md',\n 'commands-skills.md',\n 'warnings.md',\n ];\n\n for (const section of defaultOrder) {\n const sectionPath = join(CLAUDE_MD_TEMPLATES, section);\n if (existsSync(sectionPath)) {\n const content = loadTemplate(sectionPath);\n sections.push(substituteVariables(content, variables));\n }\n }\n\n // Layer 2: Project-specific sections\n const projectSections = join(projectPath, '.panopticon', 'claude-md', 'sections');\n if (existsSync(projectSections)) {\n const projectFiles = readdirSync(projectSections)\n .filter((f) => f.endsWith('.md'))\n .sort();\n\n for (const file of projectFiles) {\n const content = loadTemplate(join(projectSections, file));\n sections.push(substituteVariables(content, variables));\n }\n }\n\n // If no sections found, return minimal CLAUDE.md\n if (sections.length === 0) {\n return `# Workspace: ${variables.FEATURE_FOLDER}\n\n**Issue:** ${variables.ISSUE_ID}\n**Branch:** ${variables.BRANCH_NAME}\n**Path:** ${variables.WORKSPACE_PATH}\n\n## Getting Started\n\nThis workspace was created by Panopticon. Use \\`bd\\` commands to track your work.\n`;\n }\n\n return sections.join('\\n\\n---\\n\\n');\n}\n","import { existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { processEnvBlocks, processIfBlocks, substituteVariables } from '../template.js';\nimport { extractTeamPrefix, findProjectByTeam } from '../projects.js';\nimport { readWorkspacePlan } from '../vbrief/io.js';\nimport { extractACFromDocument } from '../vbrief/acceptance-criteria.js';\nimport { loadConfig } from '../config.js';\nimport { createTrackerFromConfig } from '../tracker/factory.js';\nimport { NotImplementedError } from '../tracker/interface.js';\nimport type { TrackerType } from '../tracker/interface.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Resolve the prompts directory, handling both dev (src/) and bundled (dist/) layouts.\n */\nfunction resolvePromptsDir(): string {\n // Try direct sibling path first (works in dev: src/lib/cloister/prompts/)\n const direct = join(__dirname, 'prompts');\n if (existsSync(direct) && existsSync(join(direct, 'work-agent.md'))) {\n return direct;\n }\n\n // Fallback: resolve from package root (works when bundled into dist/)\n let packageRoot = __dirname;\n if (packageRoot.includes('/src/')) {\n packageRoot = packageRoot.replace(/\\/src\\/.*$/, '');\n } else {\n // dist/cli/ or dist/dashboard/ → go up to package root\n packageRoot = join(packageRoot, '..', '..');\n }\n const fromRoot = join(packageRoot, 'src', 'lib', 'cloister', 'prompts');\n if (existsSync(fromRoot)) {\n return fromRoot;\n }\n\n return direct; // Let it fail with a clear error below\n}\n\nexport interface WorkAgentPromptContext {\n issueId: string;\n env: 'LOCAL' | 'REMOTE';\n workspacePath: string;\n projectRoot?: string;\n /** Skip dynamic context gathering (filesystem reads). True for REMOTE/dashboard. */\n skipDynamicContext?: boolean;\n /** Pre-fetched tracker context (new comments, status). Injected by callers. */\n trackerContext?: string;\n}\n\n/**\n * Build the unified work agent prompt from the template.\n */\nexport function buildWorkAgentPrompt(ctx: WorkAgentPromptContext): string {\n const templatePath = join(resolvePromptsDir(), 'work-agent.md');\n\n if (!existsSync(templatePath)) {\n throw new Error(`Work agent prompt template not found at ${templatePath}`);\n }\n\n let template = readFileSync(templatePath, 'utf-8');\n\n // Gather dynamic context (only for LOCAL when filesystem is accessible)\n let beadsTasksStr = '';\n let stitchDesignsStr = '';\n let polyrepoContextStr = '';\n let pendingFeedbackStr = '';\n\n if (!ctx.skipDynamicContext && ctx.projectRoot) {\n const planningContent = readPlanningContext(ctx.workspacePath);\n\n const beadsTasks = readBeadsTasks(ctx.workspacePath, ctx.projectRoot, ctx.issueId);\n if (beadsTasks.length > 0) {\n beadsTasksStr = beadsTasks.join('\\n');\n }\n\n const stitchDesigns = extractStitchDesigns(planningContent);\n if (stitchDesigns) {\n stitchDesignsStr = stitchDesigns;\n }\n\n polyrepoContextStr = buildPolyrepoContext(ctx.issueId, ctx.workspacePath);\n\n // Check for pending specialist feedback\n pendingFeedbackStr = readPendingFeedback(ctx.workspacePath);\n }\n\n // Build variables map\n const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || '3011'}`;\n const vars: Record<string, string | undefined> = {\n ISSUE_ID: ctx.issueId,\n ISSUE_ID_LOWER: ctx.issueId.toLowerCase(),\n WORKSPACE_PATH: ctx.workspacePath,\n PROJECT_ROOT: ctx.projectRoot || '',\n API_URL: apiUrl,\n BEADS_TASKS: beadsTasksStr,\n STITCH_DESIGNS: stitchDesignsStr,\n POLYREPO_CONTEXT: polyrepoContextStr,\n PENDING_FEEDBACK: pendingFeedbackStr,\n NEW_TRACKER_CONTEXT: ctx.trackerContext || '',\n };\n\n // Processing pipeline: env blocks → if blocks → variable substitution\n template = processEnvBlocks(template, ctx.env);\n template = processIfBlocks(template, vars);\n template = substituteVariables(template, vars as any);\n\n // Wrap in orchestration markers to distinguish Panopticon's injected instructions\n // from the agent's actual reasoning. Log parsers and context compressors can\n // recognize these markers and handle orchestration context separately.\n return `<!-- panopticon:orchestration-context-start -->\n<!-- This is Panopticon orchestration context injected automatically.\n It contains workspace setup instructions, not agent reasoning.\n Session summarizers should SKIP this block and focus on the agent's\n actual work, decisions, and tradeoffs that follow. -->\n\n${template}\n\n<!-- panopticon:orchestration-context-end -->`;\n}\n\n/**\n * Read pending specialist feedback from .planning/feedback/.\n * Returns a summary of the latest feedback file(s) for injection into the prompt.\n */\nfunction readPendingFeedback(workspacePath: string): string {\n const feedbackDir = join(workspacePath, '.planning', 'feedback');\n if (!existsSync(feedbackDir)) return '';\n\n try {\n const files = readdirSync(feedbackDir)\n .filter(f => f.endsWith('.md'))\n .sort(); // NNN-prefixed, so sort gives chronological order\n\n if (files.length === 0) return '';\n\n // Show the latest feedback file path (agent will read it)\n const latest = files[files.length - 1];\n const lines: string[] = [\n `**${files.length} feedback file(s) in \\`.planning/feedback/\\`:**`,\n '',\n ];\n\n // List all files (most recent last)\n for (const file of files) {\n const marker = file === latest ? ' ← **latest, read this first**' : '';\n lines.push(`- \\`.planning/feedback/${file}\\`${marker}`);\n }\n\n lines.push('');\n lines.push('Read the latest feedback file and address any issues before continuing other work.');\n\n return lines.join('\\n');\n } catch {\n return '';\n }\n}\n\nconst COMMENT_BODY_LIMIT = 500;\nconst TOTAL_CONTEXT_LIMIT = 2000;\n\n/**\n * Fetch tracker context (new comments + issue status) since STATE.md was last modified.\n * Returns a formatted markdown string for injection into the work agent prompt.\n */\nexport async function getTrackerContext(\n issueId: string,\n workspacePath: string\n): Promise<string> {\n let stateMtime: Date | null = null;\n const statePath = join(workspacePath, '.planning', 'STATE.md');\n if (existsSync(statePath)) {\n try {\n stateMtime = statSync(statePath).mtime;\n } catch {\n // Ignore stat errors\n }\n }\n\n let config: ReturnType<typeof loadConfig>;\n try {\n config = loadConfig();\n } catch {\n return '_Tracker unavailable: could not load configuration. Check tracker settings manually._';\n }\n\n const trackersConfig = config.trackers;\n if (!trackersConfig) {\n return '';\n }\n\n // Try each configured tracker until one can resolve the issue\n const trackerTypes: TrackerType[] = [trackersConfig.primary];\n if (trackersConfig.secondary) {\n trackerTypes.push(trackersConfig.secondary);\n }\n\n for (const trackerType of trackerTypes) {\n try {\n const tracker = createTrackerFromConfig(trackersConfig, trackerType);\n\n // Fetch issue and comments in parallel\n const [issue, allComments] = await Promise.all([\n tracker.getIssue(issueId),\n tracker.getComments(issueId).catch((err: unknown) => {\n // GitLab throws NotImplementedError; treat as no comments\n if (err instanceof NotImplementedError) return [];\n throw err;\n }),\n ]);\n\n // Filter to comments newer than STATE.md mtime\n const newComments = stateMtime\n ? allComments.filter((c) => new Date(c.createdAt) > stateMtime!)\n : allComments;\n\n // Detect reopened: STATE.md exists (has completion history) but issue is open\n const isReopened =\n stateMtime !== null &&\n (issue.state === 'open' || issue.state === 'in_progress');\n\n // Build the section\n const lines: string[] = [];\n\n lines.push('## Tracker Status (Live)');\n lines.push('');\n\n const stateLabel = issue.rawState ?? issue.state;\n if (isReopened) {\n lines.push(\n `> **ISSUE REOPENED** — Current state: **${stateLabel}**. This issue was previously worked on. Review the tracker for new instructions before fast-pathing to done.`\n );\n } else {\n lines.push(`**Current state:** ${stateLabel}`);\n }\n\n if (newComments.length > 0) {\n const sinceLabel = stateMtime\n ? `since STATE.md was last updated (${stateMtime.toISOString().slice(0, 10)})`\n : 'all comments';\n lines.push('');\n lines.push(`**New comments ${sinceLabel}:**`);\n lines.push('');\n\n let totalChars = lines.join('\\n').length;\n let truncatedAny = false;\n\n for (const comment of newComments) {\n let body = comment.body;\n let commentTruncated = false;\n if (body.length > COMMENT_BODY_LIMIT) {\n body = body.slice(0, COMMENT_BODY_LIMIT) + ' [truncated — read full comment on tracker]';\n commentTruncated = true;\n truncatedAny = true;\n }\n\n const commentBlock = [\n `**${comment.author}** (${comment.createdAt.slice(0, 10)}):`,\n `> ${body.replace(/\\n/g, '\\n> ')}`,\n '',\n ].join('\\n');\n\n if (totalChars + commentBlock.length > TOTAL_CONTEXT_LIMIT) {\n lines.push('_[Additional comments truncated — check tracker for full history]_');\n truncatedAny = true;\n break;\n }\n\n lines.push(commentBlock);\n totalChars += commentBlock.length;\n }\n\n if (truncatedAny) {\n lines.push('');\n lines.push(`_Check the tracker directly for full comment content: ${issue.url}_`);\n }\n } else if (stateMtime) {\n lines.push('');\n lines.push('_No new comments since last STATE.md update._');\n }\n\n const result = lines.join('\\n').trim();\n // If only \"no new comments\" and not reopened, return empty to suppress the section\n if (!isReopened && newComments.length === 0) {\n return '';\n }\n return result;\n } catch (err: unknown) {\n // Issue not found in this tracker — try next\n const message = err instanceof Error ? err.message : String(err);\n if (\n message.toLowerCase().includes('not found') ||\n message.toLowerCase().includes('404') ||\n message.toLowerCase().includes('no configuration')\n ) {\n continue;\n }\n // Unexpected error (auth failure, network, etc.) — warn in prompt\n return `_Tracker unavailable: ${message}. Check tracker status and review any new comments manually._`;\n }\n }\n\n // No tracker resolved the issue\n return '';\n}\n\n/**\n * Read planning artifacts for an issue (STATE.md, etc.)\n */\nexport function readPlanningContext(workspacePath: string): string | null {\n const statePath = join(workspacePath, '.planning', 'STATE.md');\n if (existsSync(statePath)) {\n return readFileSync(statePath, 'utf-8');\n }\n return null;\n}\n\n/**\n * Check if STATE.md contains Stitch design information.\n * Returns the Stitch section if found, null otherwise.\n */\nexport function extractStitchDesigns(stateContent: string | null): string | null {\n if (!stateContent) return null;\n\n // Look for Stitch-related sections in STATE.md\n const stitchPatterns = [\n /## UI Designs[\\s\\S]*?(?=\\n## |$)/i,\n /### Stitch Assets[\\s\\S]*?(?=\\n### |\\n## |$)/i,\n /## Stitch[\\s\\S]*?(?=\\n## |$)/i,\n ];\n\n for (const pattern of stitchPatterns) {\n const match = stateContent.match(pattern);\n if (match) {\n return match[0].trim();\n }\n }\n\n // Also check for Stitch project/screen IDs mentioned anywhere\n if (\n stateContent.includes('Stitch') &&\n (stateContent.includes('Project ID') || stateContent.includes('Screen ID'))\n ) {\n const lines = stateContent.split('\\n');\n const stitchLines: string[] = [];\n let inStitchSection = false;\n\n for (const line of lines) {\n if (line.toLowerCase().includes('stitch')) {\n inStitchSection = true;\n }\n if (inStitchSection) {\n stitchLines.push(line);\n if (line.trim() === '' && stitchLines.length > 3) {\n break;\n }\n }\n }\n\n if (stitchLines.length > 0) {\n return stitchLines.join('\\n').trim();\n }\n }\n\n return null;\n}\n\n/**\n * Extract beads IDs from STATE.md content.\n * Looks for patterns like `panopticon-1dg` in backticks or tables.\n */\nexport function extractBeadsIdsFromState(stateContent: string): string[] {\n const ids: string[] = [];\n\n const backtickMatches = stateContent.match(/`([a-z]+-[a-z0-9]+)`/g) || [];\n for (const match of backtickMatches) {\n const id = match.replace(/`/g, '');\n if (id.match(/^[a-z]+-[a-z0-9]{2,4}$/)) {\n ids.push(id);\n }\n }\n\n return [...new Set(ids)];\n}\n\n/**\n * Read beads tasks for an issue from both workspace and project root.\n * Uses STATE.md to find the associated beads IDs.\n */\nexport function readBeadsTasks(\n workspacePath: string,\n projectRoot: string,\n issueId: string\n): string[] {\n const tasks: string[] = [];\n const normalizedId = issueId.toLowerCase();\n\n const stateContent = readPlanningContext(workspacePath);\n const beadsIds = stateContent ? extractBeadsIdsFromState(stateContent) : [];\n\n // Load vBRIEF AC indexed by item title (lowercase) for per-bead injection\n const acByTitle = buildACLookupByTitle(workspacePath);\n\n const beadsPaths = [\n join(workspacePath, '.beads', 'issues.jsonl'),\n join(projectRoot, '.beads', 'issues.jsonl'),\n ];\n\n const seenIds = new Set<string>();\n\n for (const beadsPath of beadsPaths) {\n if (!existsSync(beadsPath)) continue;\n\n try {\n const content = readFileSync(beadsPath, 'utf-8');\n const lines = content.split('\\n').filter((line) => line.trim());\n\n for (const line of lines) {\n try {\n const task = JSON.parse(line);\n if (seenIds.has(task.id)) continue;\n\n const labels = task.labels || task.tags || [];\n const isMatch =\n beadsIds.includes(task.id) ||\n labels.some((t: string) => t.toLowerCase().includes(normalizedId)) ||\n task.title?.toLowerCase().includes(normalizedId);\n\n if (isMatch) {\n seenIds.add(task.id);\n tasks.push(`- [${task.status || 'open'}] ${task.title} (${task.id})`);\n\n // Inject per-bead AC from vBRIEF plan\n const beadAC = matchBeadToAC(task.title, acByTitle);\n for (const ac of beadAC) {\n const check = ac.status === 'completed' ? 'x' : ' ';\n tasks.push(` - [${check}] AC: ${ac.title}`);\n }\n }\n } catch {\n // Skip malformed lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return tasks;\n}\n\n/**\n * Build a lookup map from item title (lowercase) → AC sub-items.\n * Used to match beads to their vBRIEF acceptance criteria.\n */\nfunction buildACLookupByTitle(workspacePath: string): Map<string, Array<{ title: string; status: string }>> {\n const lookup = new Map<string, Array<{ title: string; status: string }>>();\n const doc = readWorkspacePlan(workspacePath);\n if (!doc) return lookup;\n\n const criteria = extractACFromDocument(doc);\n for (const ac of criteria) {\n const key = ac.itemTitle.toLowerCase();\n let list = lookup.get(key);\n if (!list) {\n list = [];\n lookup.set(key, list);\n }\n list.push({ title: ac.title, status: ac.status });\n }\n\n return lookup;\n}\n\n/**\n * Match a bead title to its AC. Bead titles may have a plan ID prefix\n * (e.g., \"PAN-408: Create module\") — strip it before matching.\n */\nfunction matchBeadToAC(\n beadTitle: string,\n acByTitle: Map<string, Array<{ title: string; status: string }>>\n): Array<{ title: string; status: string }> {\n if (acByTitle.size === 0 || !beadTitle) return [];\n\n // Strip \"PAN-XXX: \" prefix if present\n const stripped = beadTitle.replace(/^[A-Z]+-\\d+:\\s*/, '');\n return acByTitle.get(stripped.toLowerCase()) || [];\n}\n\n/**\n * Generate polyrepo context section if applicable.\n */\nexport function buildPolyrepoContext(issueId: string, workspacePath: string): string {\n const teamPrefix = extractTeamPrefix(issueId);\n const projectConfig = teamPrefix ? findProjectByTeam(teamPrefix) : null;\n\n if (\n !projectConfig?.workspace?.type ||\n projectConfig.workspace.type !== 'polyrepo' ||\n !projectConfig.workspace.repos\n ) {\n return '';\n }\n\n const wsConfig = projectConfig.workspace;\n const repos = wsConfig.repos;\n\n // In progressive mode, only show repos that exist in the workspace\n const isProgressive = wsConfig.progressive && wsConfig.always_include;\n let visibleRepos = repos;\n\n if (isProgressive) {\n // Check which repos actually exist in the workspace\n const existingRepos = readdirSync(workspacePath).filter(f => {\n const fullPath = join(workspacePath, f);\n return f !== '.planning' && f !== '.claude' && f !== '.pan' && f !== '.beads' && existsSync(fullPath);\n });\n visibleRepos = repos.filter(r => existingRepos.includes(r.name));\n }\n\n const lines: string[] = [\n '## Project Structure (Polyrepo)',\n '',\n '**IMPORTANT:** This project uses a **polyrepo** structure. The workspace root is NOT a git repository.',\n 'Each subdirectory is a separate git worktree:',\n '',\n '| Directory | Purpose |',\n '|-----------|---------|',\n ];\n\n for (const repo of visibleRepos) {\n const notes: string[] = [];\n if (repo.readonly) notes.push('readonly');\n if (repo.link_type === 'symlink') notes.push('symlink');\n const noteStr = notes.length > 0 ? ` (${notes.join(', ')})` : '';\n lines.push(`| \\`${repo.name}/\\` | ${repo.path}${noteStr} |`);\n }\n\n lines.push('');\n lines.push('**Git operations:**');\n lines.push(\n '- Run `git status`, `git log`, etc. INSIDE the subdirectories (e.g., `cd fe && git status`)'\n );\n lines.push(\n `- The workspace root (\\`${workspacePath}\\`) has no \\`.git\\` directory`\n );\n lines.push(\n `- Each subdirectory has its own branch: \\`${repos[0]?.branch_prefix || 'feature/'}${issueId.toLowerCase()}\\``\n );\n\n // Add PR target info if specified\n const prTargets = new Set<string>();\n for (const repo of visibleRepos) {\n const prTarget = repo.pr_target || wsConfig.pr_target;\n if (prTarget) prTargets.add(prTarget);\n }\n if (prTargets.size > 0) {\n lines.push('');\n lines.push(`**PR target branch:** \\`${[...prTargets].join('` or `')}\\` (NOT main/master)`);\n }\n\n // Progressive workspace: add instructions for adding repos\n if (isProgressive) {\n lines.push('');\n lines.push('## Adding Repositories');\n lines.push('');\n lines.push('This is a **progressive** workspace. Only essential repos are included.');\n lines.push('Use the `/workspace-add-repo` skill to add more repos when needed:');\n lines.push('');\n lines.push('```bash');\n lines.push(`pan workspace add-repo ${issueId.toLowerCase()} <repo-name> [repo-name...]`);\n lines.push('# Or add all repos in a group:');\n lines.push(`pan workspace add-repo ${issueId.toLowerCase()} --group <group-name>`);\n lines.push('```');\n lines.push('');\n lines.push('Available repos not yet in workspace:');\n\n const existingRepoNames = visibleRepos.map(r => r.name);\n const missingRepos = repos.filter(r => !existingRepoNames.includes(r.name));\n\n for (const repo of missingRepos) {\n const notes: string[] = [];\n if (repo.readonly) notes.push('readonly');\n if (repo.link_type === 'symlink') notes.push('symlink');\n const noteStr = notes.length > 0 ? ` (${notes.join(', ')})` : '';\n lines.push(`- \\`${repo.name}\\`${noteStr} — ${repo.path}`);\n }\n\n // List readonly/symlink repos\n const readonlyRepos = visibleRepos.filter(r => r.readonly || r.link_type === 'symlink');\n if (readonlyRepos.length > 0) {\n lines.push('');\n lines.push('**Readonly repos** (do NOT commit changes):');\n for (const repo of readonlyRepos) {\n lines.push(`- \\`${repo.name}/\\` — ${repo.path}`);\n }\n }\n }\n\n lines.push('');\n\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
|
@@ -758,4 +758,4 @@ var init_work_type_router = __esmMin((() => {
|
|
|
758
758
|
//#endregion
|
|
759
759
|
export { getModelId as a, reloadGlobalRouter as c, getModel as i, resetGlobalRouter as l, getDebugInfo as n, hasOverride as o, getGlobalRouter as r, init_work_type_router as s, WorkTypeRouter as t };
|
|
760
760
|
|
|
761
|
-
//# sourceMappingURL=work-type-router-
|
|
761
|
+
//# sourceMappingURL=work-type-router-CWVW2Wk_.js.map
|
package/dist/dashboard/{work-type-router-Cxp8_ur2.js.map → work-type-router-CWVW2Wk_.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-type-router-Cxp8_ur2.js","names":[],"sources":["../../src/lib/work-types.ts","../../src/lib/model-fallback.ts","../../src/lib/smart-model-selector.ts","../../src/lib/work-type-router.ts"],"sourcesContent":["/**\n * Work Type Registry\n *\n * Central registry of all work type IDs used for model routing.\n * Each work type represents a specific context where AI agents operate,\n * allowing fine-grained control over which models handle which tasks.\n */\n\n/**\n * Metadata for each work type\n */\nexport interface WorkTypeMetadata {\n /** Broad category this work type belongs to */\n category: 'issue-agent' | 'specialist' | 'subagent' | 'convoy' | 'pre-work' | 'cli';\n /** Optional phase within the category (e.g., for issue-agent phases) */\n phase?: string;\n /** Human-readable description */\n description: string;\n}\n\n/**\n * Complete registry of all 23 work types with metadata\n */\nexport const WORK_TYPES = {\n // Issue agent phases (6)\n 'issue-agent:exploration': {\n phase: 'exploration',\n category: 'issue-agent',\n description: 'Exploring codebase and understanding requirements',\n },\n 'issue-agent:implementation': {\n phase: 'implementation',\n category: 'issue-agent',\n description: 'Writing code to implement features or fixes',\n },\n 'issue-agent:testing': {\n phase: 'testing',\n category: 'issue-agent',\n description: 'Running tests and verifying functionality',\n },\n 'issue-agent:documentation': {\n phase: 'documentation',\n category: 'issue-agent',\n description: 'Writing documentation and updating docs',\n },\n 'issue-agent:review-response': {\n phase: 'review-response',\n category: 'issue-agent',\n description: 'Responding to code review feedback',\n },\n\n // Specialist agents (3)\n 'specialist-review-agent': {\n category: 'specialist',\n description: 'Comprehensive code review specialist',\n },\n 'specialist-test-agent': {\n category: 'specialist',\n description: 'Test generation and verification specialist',\n },\n 'specialist-merge-agent': {\n category: 'specialist',\n description: 'Merge request finalization specialist',\n },\n\n // Subagents (4)\n 'subagent:explore': {\n category: 'subagent',\n description: 'Fast codebase exploration subagent',\n },\n 'subagent:plan': {\n category: 'subagent',\n description: 'Implementation planning subagent',\n },\n 'subagent:bash': {\n category: 'subagent',\n description: 'Command execution specialist subagent',\n },\n 'subagent:general-purpose': {\n category: 'subagent',\n description: 'General-purpose task subagent',\n },\n\n // Convoy members (5)\n 'convoy:security-reviewer': {\n category: 'convoy',\n description: 'Security-focused code reviewer in convoy',\n },\n 'convoy:performance-reviewer': {\n category: 'convoy',\n description: 'Performance-focused code reviewer in convoy',\n },\n 'convoy:correctness-reviewer': {\n category: 'convoy',\n description: 'Correctness-focused code reviewer in convoy',\n },\n 'convoy:requirements-reviewer': {\n category: 'convoy',\n description: 'Verifies code changes satisfy the original issue requirements and vBRIEF acceptance criteria',\n },\n 'convoy:synthesis-agent': {\n category: 'convoy',\n description: 'Synthesizes findings from convoy reviewers',\n },\n\n // Pre-work agents (5)\n 'planning-agent': {\n category: 'pre-work',\n description: 'Interactive planning and discovery agent',\n },\n // CLI contexts (2)\n 'cli:interactive': {\n category: 'cli',\n description: 'Interactive CLI session',\n },\n 'cli:quick-command': {\n category: 'cli',\n description: 'Quick one-off CLI commands',\n },\n} as const;\n\n/**\n * Type-safe work type IDs\n */\nexport type WorkTypeId = keyof typeof WORK_TYPES;\n\n/**\n * Valid work type categories\n */\nexport type WorkTypeCategory = WorkTypeMetadata['category'];\n\n/**\n * Get all work type IDs\n */\nexport function getAllWorkTypes(): WorkTypeId[] {\n return Object.keys(WORK_TYPES) as WorkTypeId[];\n}\n\n/**\n * Get all work types in a specific category\n */\nexport function getWorkTypesByCategory(category: WorkTypeCategory): WorkTypeId[] {\n return getAllWorkTypes().filter((id) => WORK_TYPES[id].category === category);\n}\n\n/**\n * Check if a string is a valid work type ID\n */\nexport function isValidWorkType(id: string): id is WorkTypeId {\n return id in WORK_TYPES;\n}\n\n/**\n * Get metadata for a work type\n */\nexport function getWorkTypeMetadata(id: WorkTypeId): WorkTypeMetadata {\n return WORK_TYPES[id];\n}\n\n/**\n * Get human-readable name for a work type\n */\nexport function getWorkTypeName(id: WorkTypeId): string {\n const metadata = WORK_TYPES[id];\n if ('phase' in metadata && metadata.phase) {\n return `${metadata.category} (${metadata.phase})`;\n }\n return id;\n}\n\n/**\n * Validate work type ID and throw if invalid\n */\nexport function validateWorkType(id: string): asserts id is WorkTypeId {\n if (!isValidWorkType(id)) {\n throw new Error(\n `Invalid work type ID: ${id}. Valid types: ${getAllWorkTypes().join(', ')}`\n );\n }\n}\n","/**\n * Model Fallback Strategy\n *\n * When a non-Anthropic model is selected but its API key is missing,\n * automatically fallback to an equivalent Anthropic model. This ensures\n * Panopticon always works even without configuring external providers.\n */\n\nimport { ModelId, AnthropicModel, OpenAIModel, GoogleModel, ZAIModel } from './settings.js';\n\n/**\n * AI model provider types\n */\nexport type ModelProvider = 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi' | 'minimax' | 'openrouter';\n\n/**\n * Map of model ID to provider\n */\nconst MODEL_PROVIDERS: Record<ModelId, ModelProvider> = {\n // Anthropic models\n 'claude-opus-4-6': 'anthropic',\n 'claude-sonnet-4-6': 'anthropic',\n 'claude-sonnet-4-5': 'anthropic',\n 'claude-haiku-4-5': 'anthropic',\n\n // OpenAI models\n 'gpt-5.2-codex': 'openai',\n 'o3-deep-research': 'openai',\n 'gpt-4o': 'openai',\n 'gpt-4o-mini': 'openai',\n\n // Google models\n 'gemini-3-pro-preview': 'google',\n 'gemini-3-flash-preview': 'google',\n 'gemini-2.5-pro': 'google',\n 'gemini-2.5-flash': 'google',\n\n // Z.AI models\n 'glm-4.7': 'zai',\n 'glm-4.7-flash': 'zai',\n\n // Kimi models\n 'kimi-k2': 'kimi',\n 'kimi-k2.5': 'kimi',\n\n // MiniMax models\n 'minimax-m2.7': 'minimax',\n 'minimax-m2.7-highspeed': 'minimax',\n};\n\n/**\n * Fallback mapping: non-Anthropic model → Anthropic equivalent\n *\n * Mapping strategy:\n * - Premium models (GPT-5.2, O3, Gemini Pro) → Sonnet 4.6 (good balance)\n * - Economy models (GPT-4o-mini, Gemini Flash, GLM Flash) → Haiku 4.5\n * - GPT-4o → Sonnet 4.6 (similar tier)\n * - GLM-4.7 → Haiku 4.5 (economy tier)\n *\n * Note: We intentionally avoid Opus 4.6 as default fallback to keep costs reasonable.\n * Users who want Opus can explicitly set it in their config.\n */\nconst FALLBACK_MAP: Record<string, AnthropicModel> = {\n // OpenAI → Anthropic\n 'gpt-5.2-codex': 'claude-sonnet-4-6', // Premium code model → Sonnet\n 'o3-deep-research': 'claude-sonnet-4-6', // Premium research model → Sonnet\n 'gpt-4o': 'claude-sonnet-4-6', // Flagship model → Sonnet\n 'gpt-4o-mini': 'claude-haiku-4-5', // Economy model → Haiku\n\n // Google → Anthropic\n 'gemini-3-pro-preview': 'claude-sonnet-4-6', // Premium model → Sonnet\n 'gemini-3-flash-preview': 'claude-haiku-4-5', // Fast model → Haiku\n\n // Z.AI → Anthropic\n 'glm-4.7': 'claude-haiku-4-5', // Standard model → Haiku\n 'glm-4.7-flash': 'claude-haiku-4-5', // Fast model → Haiku\n\n // Kimi → Anthropic\n 'kimi-k2': 'claude-sonnet-4-6', // Good balance model → Sonnet\n 'kimi-k2.5': 'claude-sonnet-4-6', // Premium model → Sonnet\n\n // MiniMax → Anthropic\n 'minimax-m2.7': 'claude-sonnet-4-6', // Near-Opus performance → Sonnet\n 'minimax-m2.7-highspeed': 'claude-sonnet-4-6', // Same quality, faster → Sonnet\n};\n\n/**\n * Default fallback when model not in explicit mapping\n */\nconst DEFAULT_FALLBACK: AnthropicModel = 'claude-sonnet-4-6';\n\n/**\n * Check if a model ID is an OpenRouter model\n *\n * OpenRouter model IDs use the format \"organization/model-name\" (e.g., \"qwen/qwen3.6-plus:free\").\n * This is distinct from all other providers which use simple identifiers without slashes.\n */\nexport function isOpenRouterModel(modelId: string): boolean {\n return modelId.includes('/');\n}\n\n/**\n * Get the provider for a model ID\n */\nexport function getModelProvider(modelId: ModelId | string): ModelProvider {\n if (isOpenRouterModel(modelId)) return 'openrouter';\n return (MODEL_PROVIDERS as Record<string, ModelProvider>)[modelId] ?? 'anthropic';\n}\n\n/**\n * Check if a model requires an external API key\n */\nexport function requiresExternalKey(modelId: ModelId | string): boolean {\n return getModelProvider(modelId) !== 'anthropic';\n}\n\n/**\n * Get all models for a specific provider\n */\nexport function getModelsByProvider(provider: ModelProvider): ModelId[] {\n return Object.entries(MODEL_PROVIDERS)\n .filter(([_, p]) => p === provider)\n .map(([modelId]) => modelId as ModelId);\n}\n\n/**\n * Check if a provider is enabled (has API key configured)\n *\n * @param provider Provider to check\n * @param enabledProviders Set of enabled provider names\n * @returns true if provider is enabled or is Anthropic (always enabled)\n */\nexport function isProviderEnabled(\n provider: ModelProvider,\n enabledProviders: Set<ModelProvider>\n): boolean {\n // Anthropic is always enabled (required)\n if (provider === 'anthropic') return true;\n\n return enabledProviders.has(provider);\n}\n\n/**\n * Apply fallback strategy for a model\n *\n * If the model's provider is disabled (no API key), return an Anthropic equivalent.\n * Otherwise, return the original model.\n *\n * @param modelId Requested model\n * @param enabledProviders Set of enabled provider names\n * @returns Original model if provider enabled, otherwise Anthropic fallback\n */\nexport function applyFallback(\n modelId: ModelId,\n enabledProviders: Set<ModelProvider>\n): ModelId {\n const provider = getModelProvider(modelId);\n\n // If provider is enabled, use the requested model\n if (isProviderEnabled(provider, enabledProviders)) {\n return modelId;\n }\n\n // Provider disabled — fall back to the equivalent Anthropic model\n const fallback = getFallbackModel(modelId);\n console.warn(`Model ${modelId} requires ${provider} API key which is not configured, falling back to ${fallback}`);\n return fallback;\n}\n\n/**\n * Get the fallback model for a given model (useful for preview/display)\n *\n * @param modelId Model to get fallback for\n * @returns Anthropic fallback model\n */\nexport function getFallbackModel(modelId: ModelId): AnthropicModel {\n // Anthropic models fallback to themselves\n if (getModelProvider(modelId) === 'anthropic') {\n return modelId as AnthropicModel;\n }\n\n return FALLBACK_MAP[modelId] || DEFAULT_FALLBACK;\n}\n\n/**\n * Detect enabled providers from API keys configuration\n *\n * @param apiKeys API keys object from settings\n * @returns Set of enabled provider names\n */\nexport function detectEnabledProviders(apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n}): Set<ModelProvider> {\n const enabled = new Set<ModelProvider>(['anthropic']); // Always enabled\n\n // Check each optional provider\n if (apiKeys.openai && apiKeys.openai.trim()) {\n enabled.add('openai');\n }\n if (apiKeys.google && apiKeys.google.trim()) {\n enabled.add('google');\n }\n if (apiKeys.zai && apiKeys.zai.trim()) {\n enabled.add('zai');\n }\n if (apiKeys.kimi && apiKeys.kimi.trim()) {\n enabled.add('kimi');\n }\n\n return enabled;\n}\n\n/**\n * Filter a list of models to only those available with enabled providers\n *\n * @param models List of models to filter\n * @param enabledProviders Set of enabled provider names\n * @returns Filtered list of models\n */\nexport function filterAvailableModels(\n models: ModelId[],\n enabledProviders: Set<ModelProvider>\n): ModelId[] {\n return models.filter((modelId) => {\n const provider = getModelProvider(modelId);\n return isProviderEnabled(provider, enabledProviders);\n });\n}\n\n/**\n * Get all available models (across all enabled providers)\n *\n * @param enabledProviders Set of enabled provider names\n * @returns List of available model IDs\n */\nexport function getAvailableModels(enabledProviders: Set<ModelProvider>): ModelId[] {\n return Object.keys(MODEL_PROVIDERS).filter((modelId) => {\n const provider = MODEL_PROVIDERS[modelId as ModelId];\n return isProviderEnabled(provider, enabledProviders);\n }) as ModelId[];\n}\n","/**\n * Smart Model Selector\n *\n * Intelligently selects the best model for each work type based on:\n * 1. What models the user has enabled (API keys configured)\n * 2. Capability scores for the required skills\n *\n * This is an opinionated system - always pick the BEST model for each job.\n * Users control cost by which providers they enable, not a sensitivity slider.\n */\n\nimport { ModelId } from './settings.js';\nimport { WorkTypeId } from './work-types.js';\nimport {\n MODEL_CAPABILITIES,\n SkillDimension,\n ModelCapability,\n getModelCapability,\n} from './model-capabilities.js';\n\n/**\n * Skill requirements for a work type\n * Higher weight = more important for this task\n */\nexport interface SkillRequirement {\n skill: SkillDimension;\n weight: number; // 0-1, how important this skill is\n}\n\n/**\n * Work type to skill mapping\n * Defines what skills each work type needs\n */\nexport const WORK_TYPE_REQUIREMENTS: Record<WorkTypeId, SkillRequirement[]> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ISSUE AGENT PHASES\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'issue-agent:exploration': [\n { skill: 'speed', weight: 0.4 }, // Need fast exploration\n { skill: 'context-length', weight: 0.3 }, // Large codebases\n { skill: 'synthesis', weight: 0.3 }, // Understanding structure\n ],\n\n 'issue-agent:implementation': [\n { skill: 'code-generation', weight: 0.6 }, // Primary skill\n { skill: 'debugging', weight: 0.2 }, // Avoiding bugs\n { skill: 'testing', weight: 0.2 }, // Writing testable code\n ],\n\n 'issue-agent:testing': [\n { skill: 'testing', weight: 0.5 }, // Primary skill\n { skill: 'code-generation', weight: 0.3 }, // Writing test code\n { skill: 'debugging', weight: 0.2 }, // Finding edge cases\n ],\n\n 'issue-agent:documentation': [\n { skill: 'documentation', weight: 0.6 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Summarizing\n { skill: 'speed', weight: 0.1 }, // Fast iteration\n ],\n\n 'issue-agent:review-response': [\n { skill: 'code-review', weight: 0.4 }, // Understanding feedback\n { skill: 'code-generation', weight: 0.3 }, // Making fixes\n { skill: 'debugging', weight: 0.3 }, // Finding issues\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SPECIALIST AGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'specialist-review-agent': [\n { skill: 'code-review', weight: 0.5 }, // Primary skill\n { skill: 'security', weight: 0.25 }, // Security awareness\n { skill: 'performance', weight: 0.25 }, // Performance awareness\n ],\n\n 'specialist-test-agent': [\n { skill: 'testing', weight: 0.5 }, // Primary skill\n { skill: 'code-generation', weight: 0.3 }, // Writing tests\n { skill: 'debugging', weight: 0.2 }, // Finding issues\n ],\n\n 'specialist-merge-agent': [\n { skill: 'code-review', weight: 0.4 }, // Understanding conflicts\n { skill: 'synthesis', weight: 0.3 }, // Merging changes\n { skill: 'debugging', weight: 0.3 }, // Resolving issues\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SUBAGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'subagent:explore': [\n { skill: 'speed', weight: 0.5 }, // Need speed\n { skill: 'context-length', weight: 0.3 }, // Large scope\n { skill: 'synthesis', weight: 0.2 }, // Quick understanding\n ],\n\n 'subagent:plan': [\n { skill: 'planning', weight: 0.5 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Combining info\n { skill: 'speed', weight: 0.2 }, // Quick iteration\n ],\n\n 'subagent:bash': [\n { skill: 'speed', weight: 0.6 }, // Fast execution\n { skill: 'code-generation', weight: 0.3 }, // Command generation\n { skill: 'debugging', weight: 0.1 }, // Error handling\n ],\n\n 'subagent:general-purpose': [\n { skill: 'speed', weight: 0.3 }, // Balanced\n { skill: 'synthesis', weight: 0.3 }, // General understanding\n { skill: 'code-generation', weight: 0.4 }, // General tasks\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // CONVOY MEMBERS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'convoy:security-reviewer': [\n { skill: 'security', weight: 0.7 }, // PRIMARY - never compromise\n { skill: 'code-review', weight: 0.2 }, // Code understanding\n { skill: 'debugging', weight: 0.1 }, // Finding vulnerabilities\n ],\n\n 'convoy:performance-reviewer': [\n { skill: 'performance', weight: 0.6 }, // Primary skill\n { skill: 'code-review', weight: 0.3 }, // Code understanding\n { skill: 'debugging', weight: 0.1 }, // Finding bottlenecks\n ],\n\n 'convoy:correctness-reviewer': [\n { skill: 'code-review', weight: 0.4 }, // Primary skill\n { skill: 'debugging', weight: 0.4 }, // Finding bugs\n { skill: 'testing', weight: 0.2 }, // Test coverage\n ],\n\n 'convoy:requirements-reviewer': [\n { skill: 'planning', weight: 0.4 }, // Mapping requirements to code\n { skill: 'code-review', weight: 0.4 }, // Understanding what code does\n { skill: 'documentation', weight: 0.2 }, // Reading vBRIEF structure\n ],\n\n 'convoy:synthesis-agent': [\n { skill: 'synthesis', weight: 0.6 }, // Primary skill\n { skill: 'documentation', weight: 0.2 }, // Clear writing\n { skill: 'planning', weight: 0.2 }, // Organizing findings\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // PRE-WORK AGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'planning-agent': [\n { skill: 'planning', weight: 0.5 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Combining requirements\n { skill: 'documentation', weight: 0.2 }, // Documenting decisions\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // CLI CONTEXTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'cli:interactive': [\n { skill: 'speed', weight: 0.4 }, // Responsive\n { skill: 'synthesis', weight: 0.3 }, // Understanding context\n { skill: 'code-generation', weight: 0.3 }, // Quick code\n ],\n\n 'cli:quick-command': [\n { skill: 'speed', weight: 0.7 }, // Must be fast\n { skill: 'code-generation', weight: 0.2 }, // Simple generation\n { skill: 'synthesis', weight: 0.1 }, // Quick understanding\n ],\n};\n\n/**\n * Selection result with explanation\n */\nexport interface ModelSelectionResult {\n /** Selected model */\n model: ModelId;\n /** Score that led to selection (0-100) */\n score: number;\n /** Why this model was selected */\n reason: string;\n /** All candidates that were considered */\n candidates: Array<{\n model: ModelId;\n score: number;\n available: boolean;\n }>;\n}\n\n/**\n * Selection options\n */\nexport interface SelectionOptions {\n /**\n * Minimum capability threshold (0-100)\n * Models below this score are excluded\n * Default: 50\n */\n minCapability?: number;\n\n /**\n * Force a specific model (bypass selection)\n */\n forceModel?: ModelId;\n}\n\n/**\n * Calculate weighted skill score for a model given requirements\n */\nfunction calculateSkillScore(\n model: ModelId,\n requirements: SkillRequirement[]\n): number {\n const cap = getModelCapability(model);\n let totalScore = 0;\n let totalWeight = 0;\n\n for (const req of requirements) {\n totalScore += cap.skills[req.skill] * req.weight;\n totalWeight += req.weight;\n }\n\n return totalWeight > 0 ? totalScore / totalWeight : 0;\n}\n\n/**\n * Calculate final selection score - pure capability based\n *\n * We're opinionated: always pick the BEST model for the job.\n * Users control cost by which providers they enable.\n */\nfunction calculateSelectionScore(\n model: ModelId,\n skillScore: number\n): number {\n // Pure quality - just return the skill score\n // Cost control is done by which providers the user enables\n return skillScore;\n}\n\n/**\n * Select the best model for a work type from available models\n */\nexport function selectModel(\n workType: WorkTypeId,\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): ModelSelectionResult {\n const { minCapability = 50, forceModel } = options;\n\n // Force model if specified and available\n if (forceModel) {\n if (availableModels.includes(forceModel)) {\n return {\n model: forceModel,\n score: 100,\n reason: `Forced selection: ${forceModel}`,\n candidates: [{ model: forceModel, score: 100, available: true }],\n };\n }\n // Fall through to normal selection if forced model not available\n }\n\n const requirements = WORK_TYPE_REQUIREMENTS[workType];\n const allModels = Object.keys(MODEL_CAPABILITIES) as ModelId[];\n\n // Calculate scores for all models - pure capability based\n // Users control cost by which providers they enable\n const candidates = allModels.map((model) => {\n const skillScore = calculateSkillScore(model, requirements);\n const selectionScore = calculateSelectionScore(model, skillScore);\n const available = availableModels.includes(model);\n\n return {\n model,\n skillScore,\n score: selectionScore,\n available,\n };\n });\n\n // Filter to available models with minimum capability\n const eligible = candidates.filter(\n (c) => c.available && c.skillScore >= minCapability\n );\n\n // Sort by selection score (descending)\n eligible.sort((a, b) => b.score - a.score);\n\n // Fallback: if no eligible models, use best available regardless of threshold\n if (eligible.length === 0) {\n const fallback = candidates\n .filter((c) => c.available)\n .sort((a, b) => b.score - a.score)[0];\n\n if (!fallback) {\n // No available models at all - use Anthropic default\n return {\n model: 'claude-sonnet-4-6',\n score: 0,\n reason: 'No models available, falling back to default',\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n }\n\n return {\n model: fallback.model,\n score: fallback.score,\n reason: `Best available (below min threshold): ${fallback.model}`,\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n }\n\n const selected = eligible[0];\n const cap = getModelCapability(selected.model);\n\n // Generate reason\n const topSkills = requirements\n .sort((a, b) => b.weight - a.weight)\n .slice(0, 2)\n .map((r) => r.skill);\n\n const reason = `Best for ${workType}: ${cap.displayName} (${topSkills.join(', ')}: ${Math.round(selected.skillScore)}, cost: $${cap.costPer1MTokens}/1M)`;\n\n return {\n model: selected.model,\n score: selected.score,\n reason,\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n}\n\n/**\n * Select models for all work types at once\n */\nexport function selectAllModels(\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): Record<WorkTypeId, ModelSelectionResult> {\n const workTypes = Object.keys(WORK_TYPE_REQUIREMENTS) as WorkTypeId[];\n const results: Record<WorkTypeId, ModelSelectionResult> = {} as Record<\n WorkTypeId,\n ModelSelectionResult\n >;\n\n for (const workType of workTypes) {\n results[workType] = selectModel(workType, availableModels, options);\n }\n\n return results;\n}\n\n/**\n * Get simple model mapping (for backward compatibility with presets)\n */\nexport function getSimpleModelMapping(\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): Record<WorkTypeId, ModelId> {\n const results = selectAllModels(availableModels, options);\n const mapping: Record<WorkTypeId, ModelId> = {} as Record<WorkTypeId, ModelId>;\n\n for (const [workType, result] of Object.entries(results)) {\n mapping[workType as WorkTypeId] = result.model;\n }\n\n return mapping;\n}\n\n/**\n * Pretty print selection results for debugging\n */\nexport function formatSelectionResults(\n results: Record<WorkTypeId, ModelSelectionResult>\n): string {\n const lines: string[] = ['Model Selection Results', '='.repeat(60)];\n\n for (const [workType, result] of Object.entries(results)) {\n lines.push(`${workType}: ${result.model}`);\n lines.push(` Reason: ${result.reason}`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n","/**\n * Work Type Router\n *\n * Routes work types to appropriate models using smart (capability-based) selection.\n * Picks the best model for each job based on:\n * 1. What models the user has enabled (API keys configured)\n * 2. Capability scores for the required skills\n * 3. Cost optimization (configurable)\n */\n\nimport { WorkTypeId, isValidWorkType, validateWorkType, getAllWorkTypes } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { applyFallback, ModelProvider, getModelsByProvider } from './model-fallback.js';\nimport { loadConfig, NormalizedConfig } from './config-yaml.js';\nimport { selectModel, ModelSelectionResult } from './smart-model-selector.js';\n\n// Re-export WorkTypeId for backward compatibility\nexport type { WorkTypeId } from './work-types.js';\n\n/**\n * Model resolution result with debugging info\n */\nexport interface ModelResolutionResult {\n /** Final model to use */\n model: ModelId;\n /** Work type that was resolved */\n workType: WorkTypeId;\n /** How the model was determined */\n source: 'override' | 'smart';\n /** Whether fallback was applied (provider disabled) */\n usedFallback: boolean;\n /** Original model before fallback */\n originalModel?: ModelId;\n /** Smart selection details */\n selection: {\n score: number;\n reason: string;\n };\n}\n\n/**\n * Work Type Router\n *\n * Main router class for resolving work types to models.\n */\nexport class WorkTypeRouter {\n private config: NormalizedConfig;\n private availableModels: ModelId[] | null = null;\n\n constructor(config?: NormalizedConfig) {\n this.config = config || loadConfig().config;\n }\n\n /**\n * Get list of available models based on enabled providers\n */\n private getAvailableModels(): ModelId[] {\n if (this.availableModels) {\n return this.availableModels;\n }\n\n const available: ModelId[] = [];\n for (const provider of this.config.enabledProviders) {\n available.push(...getModelsByProvider(provider));\n }\n this.availableModels = available;\n return available;\n }\n\n /**\n * Get model for a specific work type\n *\n * Resolution order:\n * 1. Per-project/global override (if configured)\n * 2. Smart selection (capability-based)\n */\n getModel(workTypeId: WorkTypeId): ModelResolutionResult {\n validateWorkType(workTypeId);\n\n let model: ModelId;\n let source: 'override' | 'smart';\n let originalModel: ModelId | undefined;\n let selection: { score: number; reason: string };\n\n // Check for override first\n if (this.config.overrides[workTypeId]) {\n model = this.config.overrides[workTypeId]!;\n source = 'override';\n selection = {\n score: 100,\n reason: `Explicit override: ${model}`,\n };\n } else {\n // Use smart (capability-based) selection\n const availableModels = this.getAvailableModels();\n const result = selectModel(workTypeId, availableModels);\n model = result.model;\n source = 'smart';\n selection = {\n score: result.score,\n reason: result.reason,\n };\n }\n\n // Apply fallback if provider is disabled\n originalModel = model;\n model = applyFallback(model, this.config.enabledProviders);\n\n return {\n model,\n workType: workTypeId,\n source,\n usedFallback: model !== originalModel,\n originalModel: model !== originalModel ? originalModel : undefined,\n selection,\n };\n }\n\n /**\n * Get just the model ID for a work type (convenience method)\n */\n getModelId(workTypeId: WorkTypeId): ModelId {\n return this.getModel(workTypeId).model;\n }\n\n /**\n * Check if a work type has an override configured\n */\n hasOverride(workTypeId: WorkTypeId): boolean {\n return workTypeId in this.config.overrides;\n }\n\n /**\n * Get the set of enabled providers\n */\n getEnabledProviders(): Set<ModelProvider> {\n return this.config.enabledProviders;\n }\n\n /**\n * Get all configured overrides\n */\n getOverrides(): Partial<Record<WorkTypeId, ModelId>> {\n return { ...this.config.overrides };\n }\n\n /**\n * Get API keys configuration\n */\n getApiKeys(): { openai?: string; google?: string; zai?: string; kimi?: string } {\n return { ...this.config.apiKeys };\n }\n\n /**\n * Get Gemini thinking level\n */\n getGeminiThinkingLevel(): 1 | 2 | 3 | 4 {\n return this.config.geminiThinkingLevel;\n }\n\n /**\n * Reload configuration from disk\n */\n reloadConfig(): void {\n this.config = loadConfig().config;\n this.availableModels = null; // Clear cache\n }\n\n /**\n * Get debug information about current configuration\n */\n getDebugInfo(): {\n enabledProviders: string[];\n availableModelCount: number;\n overrideCount: number;\n hasApiKeys: {\n openai: boolean;\n google: boolean;\n zai: boolean;\n kimi: boolean;\n };\n } {\n return {\n enabledProviders: Array.from(this.config.enabledProviders),\n availableModelCount: this.getAvailableModels().length,\n overrideCount: Object.keys(this.config.overrides).length,\n hasApiKeys: {\n openai: !!this.config.apiKeys.openai,\n google: !!this.config.apiKeys.google,\n zai: !!this.config.apiKeys.zai,\n kimi: !!this.config.apiKeys.kimi,\n },\n };\n }\n}\n\n/**\n * Global router instance\n */\nlet globalRouter: WorkTypeRouter | null = null;\n\n/**\n * Get the global work type router instance\n */\nexport function getGlobalRouter(): WorkTypeRouter {\n if (!globalRouter) {\n globalRouter = new WorkTypeRouter();\n }\n return globalRouter;\n}\n\n/**\n * Reset the global router (useful for testing)\n */\nexport function resetGlobalRouter(): void {\n globalRouter = null;\n}\n\n/**\n * Reload global router configuration\n */\nexport function reloadGlobalRouter(): void {\n if (globalRouter) {\n globalRouter.reloadConfig();\n }\n}\n\n/**\n * Get model using the global router\n */\nexport function getModel(workTypeId: WorkTypeId): ModelResolutionResult {\n return getGlobalRouter().getModel(workTypeId);\n}\n\n/**\n * Get just the model ID using the global router\n */\nexport function getModelId(workTypeId: WorkTypeId): ModelId {\n return getGlobalRouter().getModelId(workTypeId);\n}\n\n/**\n * Check for override using the global router\n */\nexport function hasOverride(workTypeId: WorkTypeId): boolean {\n return getGlobalRouter().hasOverride(workTypeId);\n}\n\n/**\n * Get debug info using the global router\n */\nexport function getDebugInfo() {\n return getGlobalRouter().getDebugInfo();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"work-type-router-CWVW2Wk_.js","names":[],"sources":["../../src/lib/work-types.ts","../../src/lib/model-fallback.ts","../../src/lib/smart-model-selector.ts","../../src/lib/work-type-router.ts"],"sourcesContent":["/**\n * Work Type Registry\n *\n * Central registry of all work type IDs used for model routing.\n * Each work type represents a specific context where AI agents operate,\n * allowing fine-grained control over which models handle which tasks.\n */\n\n/**\n * Metadata for each work type\n */\nexport interface WorkTypeMetadata {\n /** Broad category this work type belongs to */\n category: 'issue-agent' | 'specialist' | 'subagent' | 'convoy' | 'pre-work' | 'cli';\n /** Optional phase within the category (e.g., for issue-agent phases) */\n phase?: string;\n /** Human-readable description */\n description: string;\n}\n\n/**\n * Complete registry of all 23 work types with metadata\n */\nexport const WORK_TYPES = {\n // Issue agent phases (6)\n 'issue-agent:exploration': {\n phase: 'exploration',\n category: 'issue-agent',\n description: 'Exploring codebase and understanding requirements',\n },\n 'issue-agent:implementation': {\n phase: 'implementation',\n category: 'issue-agent',\n description: 'Writing code to implement features or fixes',\n },\n 'issue-agent:testing': {\n phase: 'testing',\n category: 'issue-agent',\n description: 'Running tests and verifying functionality',\n },\n 'issue-agent:documentation': {\n phase: 'documentation',\n category: 'issue-agent',\n description: 'Writing documentation and updating docs',\n },\n 'issue-agent:review-response': {\n phase: 'review-response',\n category: 'issue-agent',\n description: 'Responding to code review feedback',\n },\n\n // Specialist agents (3)\n 'specialist-review-agent': {\n category: 'specialist',\n description: 'Comprehensive code review specialist',\n },\n 'specialist-test-agent': {\n category: 'specialist',\n description: 'Test generation and verification specialist',\n },\n 'specialist-merge-agent': {\n category: 'specialist',\n description: 'Merge request finalization specialist',\n },\n\n // Subagents (4)\n 'subagent:explore': {\n category: 'subagent',\n description: 'Fast codebase exploration subagent',\n },\n 'subagent:plan': {\n category: 'subagent',\n description: 'Implementation planning subagent',\n },\n 'subagent:bash': {\n category: 'subagent',\n description: 'Command execution specialist subagent',\n },\n 'subagent:general-purpose': {\n category: 'subagent',\n description: 'General-purpose task subagent',\n },\n\n // Convoy members (5)\n 'convoy:security-reviewer': {\n category: 'convoy',\n description: 'Security-focused code reviewer in convoy',\n },\n 'convoy:performance-reviewer': {\n category: 'convoy',\n description: 'Performance-focused code reviewer in convoy',\n },\n 'convoy:correctness-reviewer': {\n category: 'convoy',\n description: 'Correctness-focused code reviewer in convoy',\n },\n 'convoy:requirements-reviewer': {\n category: 'convoy',\n description: 'Verifies code changes satisfy the original issue requirements and vBRIEF acceptance criteria',\n },\n 'convoy:synthesis-agent': {\n category: 'convoy',\n description: 'Synthesizes findings from convoy reviewers',\n },\n\n // Pre-work agents (5)\n 'planning-agent': {\n category: 'pre-work',\n description: 'Interactive planning and discovery agent',\n },\n // CLI contexts (2)\n 'cli:interactive': {\n category: 'cli',\n description: 'Interactive CLI session',\n },\n 'cli:quick-command': {\n category: 'cli',\n description: 'Quick one-off CLI commands',\n },\n} as const;\n\n/**\n * Type-safe work type IDs\n */\nexport type WorkTypeId = keyof typeof WORK_TYPES;\n\n/**\n * Valid work type categories\n */\nexport type WorkTypeCategory = WorkTypeMetadata['category'];\n\n/**\n * Get all work type IDs\n */\nexport function getAllWorkTypes(): WorkTypeId[] {\n return Object.keys(WORK_TYPES) as WorkTypeId[];\n}\n\n/**\n * Get all work types in a specific category\n */\nexport function getWorkTypesByCategory(category: WorkTypeCategory): WorkTypeId[] {\n return getAllWorkTypes().filter((id) => WORK_TYPES[id].category === category);\n}\n\n/**\n * Check if a string is a valid work type ID\n */\nexport function isValidWorkType(id: string): id is WorkTypeId {\n return id in WORK_TYPES;\n}\n\n/**\n * Get metadata for a work type\n */\nexport function getWorkTypeMetadata(id: WorkTypeId): WorkTypeMetadata {\n return WORK_TYPES[id];\n}\n\n/**\n * Get human-readable name for a work type\n */\nexport function getWorkTypeName(id: WorkTypeId): string {\n const metadata = WORK_TYPES[id];\n if ('phase' in metadata && metadata.phase) {\n return `${metadata.category} (${metadata.phase})`;\n }\n return id;\n}\n\n/**\n * Validate work type ID and throw if invalid\n */\nexport function validateWorkType(id: string): asserts id is WorkTypeId {\n if (!isValidWorkType(id)) {\n throw new Error(\n `Invalid work type ID: ${id}. Valid types: ${getAllWorkTypes().join(', ')}`\n );\n }\n}\n","/**\n * Model Fallback Strategy\n *\n * When a non-Anthropic model is selected but its API key is missing,\n * automatically fallback to an equivalent Anthropic model. This ensures\n * Panopticon always works even without configuring external providers.\n */\n\nimport { ModelId, AnthropicModel, OpenAIModel, GoogleModel, ZAIModel } from './settings.js';\n\n/**\n * AI model provider types\n */\nexport type ModelProvider = 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi' | 'minimax' | 'openrouter';\n\n/**\n * Map of model ID to provider\n */\nconst MODEL_PROVIDERS: Record<ModelId, ModelProvider> = {\n // Anthropic models\n 'claude-opus-4-6': 'anthropic',\n 'claude-sonnet-4-6': 'anthropic',\n 'claude-sonnet-4-5': 'anthropic',\n 'claude-haiku-4-5': 'anthropic',\n\n // OpenAI models\n 'gpt-5.2-codex': 'openai',\n 'o3-deep-research': 'openai',\n 'gpt-4o': 'openai',\n 'gpt-4o-mini': 'openai',\n\n // Google models\n 'gemini-3-pro-preview': 'google',\n 'gemini-3-flash-preview': 'google',\n 'gemini-2.5-pro': 'google',\n 'gemini-2.5-flash': 'google',\n\n // Z.AI models\n 'glm-4.7': 'zai',\n 'glm-4.7-flash': 'zai',\n\n // Kimi models\n 'kimi-k2': 'kimi',\n 'kimi-k2.5': 'kimi',\n\n // MiniMax models\n 'minimax-m2.7': 'minimax',\n 'minimax-m2.7-highspeed': 'minimax',\n};\n\n/**\n * Fallback mapping: non-Anthropic model → Anthropic equivalent\n *\n * Mapping strategy:\n * - Premium models (GPT-5.2, O3, Gemini Pro) → Sonnet 4.6 (good balance)\n * - Economy models (GPT-4o-mini, Gemini Flash, GLM Flash) → Haiku 4.5\n * - GPT-4o → Sonnet 4.6 (similar tier)\n * - GLM-4.7 → Haiku 4.5 (economy tier)\n *\n * Note: We intentionally avoid Opus 4.6 as default fallback to keep costs reasonable.\n * Users who want Opus can explicitly set it in their config.\n */\nconst FALLBACK_MAP: Record<string, AnthropicModel> = {\n // OpenAI → Anthropic\n 'gpt-5.2-codex': 'claude-sonnet-4-6', // Premium code model → Sonnet\n 'o3-deep-research': 'claude-sonnet-4-6', // Premium research model → Sonnet\n 'gpt-4o': 'claude-sonnet-4-6', // Flagship model → Sonnet\n 'gpt-4o-mini': 'claude-haiku-4-5', // Economy model → Haiku\n\n // Google → Anthropic\n 'gemini-3-pro-preview': 'claude-sonnet-4-6', // Premium model → Sonnet\n 'gemini-3-flash-preview': 'claude-haiku-4-5', // Fast model → Haiku\n\n // Z.AI → Anthropic\n 'glm-4.7': 'claude-haiku-4-5', // Standard model → Haiku\n 'glm-4.7-flash': 'claude-haiku-4-5', // Fast model → Haiku\n\n // Kimi → Anthropic\n 'kimi-k2': 'claude-sonnet-4-6', // Good balance model → Sonnet\n 'kimi-k2.5': 'claude-sonnet-4-6', // Premium model → Sonnet\n\n // MiniMax → Anthropic\n 'minimax-m2.7': 'claude-sonnet-4-6', // Near-Opus performance → Sonnet\n 'minimax-m2.7-highspeed': 'claude-sonnet-4-6', // Same quality, faster → Sonnet\n};\n\n/**\n * Default fallback when model not in explicit mapping\n */\nconst DEFAULT_FALLBACK: AnthropicModel = 'claude-sonnet-4-6';\n\n/**\n * Check if a model ID is an OpenRouter model\n *\n * OpenRouter model IDs use the format \"organization/model-name\" (e.g., \"qwen/qwen3.6-plus:free\").\n * This is distinct from all other providers which use simple identifiers without slashes.\n */\nexport function isOpenRouterModel(modelId: string): boolean {\n return modelId.includes('/');\n}\n\n/**\n * Get the provider for a model ID\n */\nexport function getModelProvider(modelId: ModelId | string): ModelProvider {\n if (isOpenRouterModel(modelId)) return 'openrouter';\n return (MODEL_PROVIDERS as Record<string, ModelProvider>)[modelId] ?? 'anthropic';\n}\n\n/**\n * Check if a model requires an external API key\n */\nexport function requiresExternalKey(modelId: ModelId | string): boolean {\n return getModelProvider(modelId) !== 'anthropic';\n}\n\n/**\n * Get all models for a specific provider\n */\nexport function getModelsByProvider(provider: ModelProvider): ModelId[] {\n return Object.entries(MODEL_PROVIDERS)\n .filter(([_, p]) => p === provider)\n .map(([modelId]) => modelId as ModelId);\n}\n\n/**\n * Check if a provider is enabled (has API key configured)\n *\n * @param provider Provider to check\n * @param enabledProviders Set of enabled provider names\n * @returns true if provider is enabled or is Anthropic (always enabled)\n */\nexport function isProviderEnabled(\n provider: ModelProvider,\n enabledProviders: Set<ModelProvider>\n): boolean {\n // Anthropic is always enabled (required)\n if (provider === 'anthropic') return true;\n\n return enabledProviders.has(provider);\n}\n\n/**\n * Apply fallback strategy for a model\n *\n * If the model's provider is disabled (no API key), return an Anthropic equivalent.\n * Otherwise, return the original model.\n *\n * @param modelId Requested model\n * @param enabledProviders Set of enabled provider names\n * @returns Original model if provider enabled, otherwise Anthropic fallback\n */\nexport function applyFallback(\n modelId: ModelId,\n enabledProviders: Set<ModelProvider>\n): ModelId {\n const provider = getModelProvider(modelId);\n\n // If provider is enabled, use the requested model\n if (isProviderEnabled(provider, enabledProviders)) {\n return modelId;\n }\n\n // Provider disabled — fall back to the equivalent Anthropic model\n const fallback = getFallbackModel(modelId);\n console.warn(`Model ${modelId} requires ${provider} API key which is not configured, falling back to ${fallback}`);\n return fallback;\n}\n\n/**\n * Get the fallback model for a given model (useful for preview/display)\n *\n * @param modelId Model to get fallback for\n * @returns Anthropic fallback model\n */\nexport function getFallbackModel(modelId: ModelId): AnthropicModel {\n // Anthropic models fallback to themselves\n if (getModelProvider(modelId) === 'anthropic') {\n return modelId as AnthropicModel;\n }\n\n return FALLBACK_MAP[modelId] || DEFAULT_FALLBACK;\n}\n\n/**\n * Detect enabled providers from API keys configuration\n *\n * @param apiKeys API keys object from settings\n * @returns Set of enabled provider names\n */\nexport function detectEnabledProviders(apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n}): Set<ModelProvider> {\n const enabled = new Set<ModelProvider>(['anthropic']); // Always enabled\n\n // Check each optional provider\n if (apiKeys.openai && apiKeys.openai.trim()) {\n enabled.add('openai');\n }\n if (apiKeys.google && apiKeys.google.trim()) {\n enabled.add('google');\n }\n if (apiKeys.zai && apiKeys.zai.trim()) {\n enabled.add('zai');\n }\n if (apiKeys.kimi && apiKeys.kimi.trim()) {\n enabled.add('kimi');\n }\n\n return enabled;\n}\n\n/**\n * Filter a list of models to only those available with enabled providers\n *\n * @param models List of models to filter\n * @param enabledProviders Set of enabled provider names\n * @returns Filtered list of models\n */\nexport function filterAvailableModels(\n models: ModelId[],\n enabledProviders: Set<ModelProvider>\n): ModelId[] {\n return models.filter((modelId) => {\n const provider = getModelProvider(modelId);\n return isProviderEnabled(provider, enabledProviders);\n });\n}\n\n/**\n * Get all available models (across all enabled providers)\n *\n * @param enabledProviders Set of enabled provider names\n * @returns List of available model IDs\n */\nexport function getAvailableModels(enabledProviders: Set<ModelProvider>): ModelId[] {\n return Object.keys(MODEL_PROVIDERS).filter((modelId) => {\n const provider = MODEL_PROVIDERS[modelId as ModelId];\n return isProviderEnabled(provider, enabledProviders);\n }) as ModelId[];\n}\n","/**\n * Smart Model Selector\n *\n * Intelligently selects the best model for each work type based on:\n * 1. What models the user has enabled (API keys configured)\n * 2. Capability scores for the required skills\n *\n * This is an opinionated system - always pick the BEST model for each job.\n * Users control cost by which providers they enable, not a sensitivity slider.\n */\n\nimport { ModelId } from './settings.js';\nimport { WorkTypeId } from './work-types.js';\nimport {\n MODEL_CAPABILITIES,\n SkillDimension,\n ModelCapability,\n getModelCapability,\n} from './model-capabilities.js';\n\n/**\n * Skill requirements for a work type\n * Higher weight = more important for this task\n */\nexport interface SkillRequirement {\n skill: SkillDimension;\n weight: number; // 0-1, how important this skill is\n}\n\n/**\n * Work type to skill mapping\n * Defines what skills each work type needs\n */\nexport const WORK_TYPE_REQUIREMENTS: Record<WorkTypeId, SkillRequirement[]> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ISSUE AGENT PHASES\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'issue-agent:exploration': [\n { skill: 'speed', weight: 0.4 }, // Need fast exploration\n { skill: 'context-length', weight: 0.3 }, // Large codebases\n { skill: 'synthesis', weight: 0.3 }, // Understanding structure\n ],\n\n 'issue-agent:implementation': [\n { skill: 'code-generation', weight: 0.6 }, // Primary skill\n { skill: 'debugging', weight: 0.2 }, // Avoiding bugs\n { skill: 'testing', weight: 0.2 }, // Writing testable code\n ],\n\n 'issue-agent:testing': [\n { skill: 'testing', weight: 0.5 }, // Primary skill\n { skill: 'code-generation', weight: 0.3 }, // Writing test code\n { skill: 'debugging', weight: 0.2 }, // Finding edge cases\n ],\n\n 'issue-agent:documentation': [\n { skill: 'documentation', weight: 0.6 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Summarizing\n { skill: 'speed', weight: 0.1 }, // Fast iteration\n ],\n\n 'issue-agent:review-response': [\n { skill: 'code-review', weight: 0.4 }, // Understanding feedback\n { skill: 'code-generation', weight: 0.3 }, // Making fixes\n { skill: 'debugging', weight: 0.3 }, // Finding issues\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SPECIALIST AGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'specialist-review-agent': [\n { skill: 'code-review', weight: 0.5 }, // Primary skill\n { skill: 'security', weight: 0.25 }, // Security awareness\n { skill: 'performance', weight: 0.25 }, // Performance awareness\n ],\n\n 'specialist-test-agent': [\n { skill: 'testing', weight: 0.5 }, // Primary skill\n { skill: 'code-generation', weight: 0.3 }, // Writing tests\n { skill: 'debugging', weight: 0.2 }, // Finding issues\n ],\n\n 'specialist-merge-agent': [\n { skill: 'code-review', weight: 0.4 }, // Understanding conflicts\n { skill: 'synthesis', weight: 0.3 }, // Merging changes\n { skill: 'debugging', weight: 0.3 }, // Resolving issues\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SUBAGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'subagent:explore': [\n { skill: 'speed', weight: 0.5 }, // Need speed\n { skill: 'context-length', weight: 0.3 }, // Large scope\n { skill: 'synthesis', weight: 0.2 }, // Quick understanding\n ],\n\n 'subagent:plan': [\n { skill: 'planning', weight: 0.5 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Combining info\n { skill: 'speed', weight: 0.2 }, // Quick iteration\n ],\n\n 'subagent:bash': [\n { skill: 'speed', weight: 0.6 }, // Fast execution\n { skill: 'code-generation', weight: 0.3 }, // Command generation\n { skill: 'debugging', weight: 0.1 }, // Error handling\n ],\n\n 'subagent:general-purpose': [\n { skill: 'speed', weight: 0.3 }, // Balanced\n { skill: 'synthesis', weight: 0.3 }, // General understanding\n { skill: 'code-generation', weight: 0.4 }, // General tasks\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // CONVOY MEMBERS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'convoy:security-reviewer': [\n { skill: 'security', weight: 0.7 }, // PRIMARY - never compromise\n { skill: 'code-review', weight: 0.2 }, // Code understanding\n { skill: 'debugging', weight: 0.1 }, // Finding vulnerabilities\n ],\n\n 'convoy:performance-reviewer': [\n { skill: 'performance', weight: 0.6 }, // Primary skill\n { skill: 'code-review', weight: 0.3 }, // Code understanding\n { skill: 'debugging', weight: 0.1 }, // Finding bottlenecks\n ],\n\n 'convoy:correctness-reviewer': [\n { skill: 'code-review', weight: 0.4 }, // Primary skill\n { skill: 'debugging', weight: 0.4 }, // Finding bugs\n { skill: 'testing', weight: 0.2 }, // Test coverage\n ],\n\n 'convoy:requirements-reviewer': [\n { skill: 'planning', weight: 0.4 }, // Mapping requirements to code\n { skill: 'code-review', weight: 0.4 }, // Understanding what code does\n { skill: 'documentation', weight: 0.2 }, // Reading vBRIEF structure\n ],\n\n 'convoy:synthesis-agent': [\n { skill: 'synthesis', weight: 0.6 }, // Primary skill\n { skill: 'documentation', weight: 0.2 }, // Clear writing\n { skill: 'planning', weight: 0.2 }, // Organizing findings\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // PRE-WORK AGENTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'planning-agent': [\n { skill: 'planning', weight: 0.5 }, // Primary skill\n { skill: 'synthesis', weight: 0.3 }, // Combining requirements\n { skill: 'documentation', weight: 0.2 }, // Documenting decisions\n ],\n\n // ═══════════════════════════════════════════════════════════════════════════\n // CLI CONTEXTS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'cli:interactive': [\n { skill: 'speed', weight: 0.4 }, // Responsive\n { skill: 'synthesis', weight: 0.3 }, // Understanding context\n { skill: 'code-generation', weight: 0.3 }, // Quick code\n ],\n\n 'cli:quick-command': [\n { skill: 'speed', weight: 0.7 }, // Must be fast\n { skill: 'code-generation', weight: 0.2 }, // Simple generation\n { skill: 'synthesis', weight: 0.1 }, // Quick understanding\n ],\n};\n\n/**\n * Selection result with explanation\n */\nexport interface ModelSelectionResult {\n /** Selected model */\n model: ModelId;\n /** Score that led to selection (0-100) */\n score: number;\n /** Why this model was selected */\n reason: string;\n /** All candidates that were considered */\n candidates: Array<{\n model: ModelId;\n score: number;\n available: boolean;\n }>;\n}\n\n/**\n * Selection options\n */\nexport interface SelectionOptions {\n /**\n * Minimum capability threshold (0-100)\n * Models below this score are excluded\n * Default: 50\n */\n minCapability?: number;\n\n /**\n * Force a specific model (bypass selection)\n */\n forceModel?: ModelId;\n}\n\n/**\n * Calculate weighted skill score for a model given requirements\n */\nfunction calculateSkillScore(\n model: ModelId,\n requirements: SkillRequirement[]\n): number {\n const cap = getModelCapability(model);\n let totalScore = 0;\n let totalWeight = 0;\n\n for (const req of requirements) {\n totalScore += cap.skills[req.skill] * req.weight;\n totalWeight += req.weight;\n }\n\n return totalWeight > 0 ? totalScore / totalWeight : 0;\n}\n\n/**\n * Calculate final selection score - pure capability based\n *\n * We're opinionated: always pick the BEST model for the job.\n * Users control cost by which providers they enable.\n */\nfunction calculateSelectionScore(\n model: ModelId,\n skillScore: number\n): number {\n // Pure quality - just return the skill score\n // Cost control is done by which providers the user enables\n return skillScore;\n}\n\n/**\n * Select the best model for a work type from available models\n */\nexport function selectModel(\n workType: WorkTypeId,\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): ModelSelectionResult {\n const { minCapability = 50, forceModel } = options;\n\n // Force model if specified and available\n if (forceModel) {\n if (availableModels.includes(forceModel)) {\n return {\n model: forceModel,\n score: 100,\n reason: `Forced selection: ${forceModel}`,\n candidates: [{ model: forceModel, score: 100, available: true }],\n };\n }\n // Fall through to normal selection if forced model not available\n }\n\n const requirements = WORK_TYPE_REQUIREMENTS[workType];\n const allModels = Object.keys(MODEL_CAPABILITIES) as ModelId[];\n\n // Calculate scores for all models - pure capability based\n // Users control cost by which providers they enable\n const candidates = allModels.map((model) => {\n const skillScore = calculateSkillScore(model, requirements);\n const selectionScore = calculateSelectionScore(model, skillScore);\n const available = availableModels.includes(model);\n\n return {\n model,\n skillScore,\n score: selectionScore,\n available,\n };\n });\n\n // Filter to available models with minimum capability\n const eligible = candidates.filter(\n (c) => c.available && c.skillScore >= minCapability\n );\n\n // Sort by selection score (descending)\n eligible.sort((a, b) => b.score - a.score);\n\n // Fallback: if no eligible models, use best available regardless of threshold\n if (eligible.length === 0) {\n const fallback = candidates\n .filter((c) => c.available)\n .sort((a, b) => b.score - a.score)[0];\n\n if (!fallback) {\n // No available models at all - use Anthropic default\n return {\n model: 'claude-sonnet-4-6',\n score: 0,\n reason: 'No models available, falling back to default',\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n }\n\n return {\n model: fallback.model,\n score: fallback.score,\n reason: `Best available (below min threshold): ${fallback.model}`,\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n }\n\n const selected = eligible[0];\n const cap = getModelCapability(selected.model);\n\n // Generate reason\n const topSkills = requirements\n .sort((a, b) => b.weight - a.weight)\n .slice(0, 2)\n .map((r) => r.skill);\n\n const reason = `Best for ${workType}: ${cap.displayName} (${topSkills.join(', ')}: ${Math.round(selected.skillScore)}, cost: $${cap.costPer1MTokens}/1M)`;\n\n return {\n model: selected.model,\n score: selected.score,\n reason,\n candidates: candidates.map((c) => ({\n model: c.model,\n score: c.score,\n available: c.available,\n })),\n };\n}\n\n/**\n * Select models for all work types at once\n */\nexport function selectAllModels(\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): Record<WorkTypeId, ModelSelectionResult> {\n const workTypes = Object.keys(WORK_TYPE_REQUIREMENTS) as WorkTypeId[];\n const results: Record<WorkTypeId, ModelSelectionResult> = {} as Record<\n WorkTypeId,\n ModelSelectionResult\n >;\n\n for (const workType of workTypes) {\n results[workType] = selectModel(workType, availableModels, options);\n }\n\n return results;\n}\n\n/**\n * Get simple model mapping (for backward compatibility with presets)\n */\nexport function getSimpleModelMapping(\n availableModels: ModelId[],\n options: SelectionOptions = {}\n): Record<WorkTypeId, ModelId> {\n const results = selectAllModels(availableModels, options);\n const mapping: Record<WorkTypeId, ModelId> = {} as Record<WorkTypeId, ModelId>;\n\n for (const [workType, result] of Object.entries(results)) {\n mapping[workType as WorkTypeId] = result.model;\n }\n\n return mapping;\n}\n\n/**\n * Pretty print selection results for debugging\n */\nexport function formatSelectionResults(\n results: Record<WorkTypeId, ModelSelectionResult>\n): string {\n const lines: string[] = ['Model Selection Results', '='.repeat(60)];\n\n for (const [workType, result] of Object.entries(results)) {\n lines.push(`${workType}: ${result.model}`);\n lines.push(` Reason: ${result.reason}`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n","/**\n * Work Type Router\n *\n * Routes work types to appropriate models using smart (capability-based) selection.\n * Picks the best model for each job based on:\n * 1. What models the user has enabled (API keys configured)\n * 2. Capability scores for the required skills\n * 3. Cost optimization (configurable)\n */\n\nimport { WorkTypeId, isValidWorkType, validateWorkType, getAllWorkTypes } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { applyFallback, ModelProvider, getModelsByProvider } from './model-fallback.js';\nimport { loadConfig, NormalizedConfig } from './config-yaml.js';\nimport { selectModel, ModelSelectionResult } from './smart-model-selector.js';\n\n// Re-export WorkTypeId for backward compatibility\nexport type { WorkTypeId } from './work-types.js';\n\n/**\n * Model resolution result with debugging info\n */\nexport interface ModelResolutionResult {\n /** Final model to use */\n model: ModelId;\n /** Work type that was resolved */\n workType: WorkTypeId;\n /** How the model was determined */\n source: 'override' | 'smart';\n /** Whether fallback was applied (provider disabled) */\n usedFallback: boolean;\n /** Original model before fallback */\n originalModel?: ModelId;\n /** Smart selection details */\n selection: {\n score: number;\n reason: string;\n };\n}\n\n/**\n * Work Type Router\n *\n * Main router class for resolving work types to models.\n */\nexport class WorkTypeRouter {\n private config: NormalizedConfig;\n private availableModels: ModelId[] | null = null;\n\n constructor(config?: NormalizedConfig) {\n this.config = config || loadConfig().config;\n }\n\n /**\n * Get list of available models based on enabled providers\n */\n private getAvailableModels(): ModelId[] {\n if (this.availableModels) {\n return this.availableModels;\n }\n\n const available: ModelId[] = [];\n for (const provider of this.config.enabledProviders) {\n available.push(...getModelsByProvider(provider));\n }\n this.availableModels = available;\n return available;\n }\n\n /**\n * Get model for a specific work type\n *\n * Resolution order:\n * 1. Per-project/global override (if configured)\n * 2. Smart selection (capability-based)\n */\n getModel(workTypeId: WorkTypeId): ModelResolutionResult {\n validateWorkType(workTypeId);\n\n let model: ModelId;\n let source: 'override' | 'smart';\n let originalModel: ModelId | undefined;\n let selection: { score: number; reason: string };\n\n // Check for override first\n if (this.config.overrides[workTypeId]) {\n model = this.config.overrides[workTypeId]!;\n source = 'override';\n selection = {\n score: 100,\n reason: `Explicit override: ${model}`,\n };\n } else {\n // Use smart (capability-based) selection\n const availableModels = this.getAvailableModels();\n const result = selectModel(workTypeId, availableModels);\n model = result.model;\n source = 'smart';\n selection = {\n score: result.score,\n reason: result.reason,\n };\n }\n\n // Apply fallback if provider is disabled\n originalModel = model;\n model = applyFallback(model, this.config.enabledProviders);\n\n return {\n model,\n workType: workTypeId,\n source,\n usedFallback: model !== originalModel,\n originalModel: model !== originalModel ? originalModel : undefined,\n selection,\n };\n }\n\n /**\n * Get just the model ID for a work type (convenience method)\n */\n getModelId(workTypeId: WorkTypeId): ModelId {\n return this.getModel(workTypeId).model;\n }\n\n /**\n * Check if a work type has an override configured\n */\n hasOverride(workTypeId: WorkTypeId): boolean {\n return workTypeId in this.config.overrides;\n }\n\n /**\n * Get the set of enabled providers\n */\n getEnabledProviders(): Set<ModelProvider> {\n return this.config.enabledProviders;\n }\n\n /**\n * Get all configured overrides\n */\n getOverrides(): Partial<Record<WorkTypeId, ModelId>> {\n return { ...this.config.overrides };\n }\n\n /**\n * Get API keys configuration\n */\n getApiKeys(): { openai?: string; google?: string; zai?: string; kimi?: string } {\n return { ...this.config.apiKeys };\n }\n\n /**\n * Get Gemini thinking level\n */\n getGeminiThinkingLevel(): 1 | 2 | 3 | 4 {\n return this.config.geminiThinkingLevel;\n }\n\n /**\n * Reload configuration from disk\n */\n reloadConfig(): void {\n this.config = loadConfig().config;\n this.availableModels = null; // Clear cache\n }\n\n /**\n * Get debug information about current configuration\n */\n getDebugInfo(): {\n enabledProviders: string[];\n availableModelCount: number;\n overrideCount: number;\n hasApiKeys: {\n openai: boolean;\n google: boolean;\n zai: boolean;\n kimi: boolean;\n };\n } {\n return {\n enabledProviders: Array.from(this.config.enabledProviders),\n availableModelCount: this.getAvailableModels().length,\n overrideCount: Object.keys(this.config.overrides).length,\n hasApiKeys: {\n openai: !!this.config.apiKeys.openai,\n google: !!this.config.apiKeys.google,\n zai: !!this.config.apiKeys.zai,\n kimi: !!this.config.apiKeys.kimi,\n },\n };\n }\n}\n\n/**\n * Global router instance\n */\nlet globalRouter: WorkTypeRouter | null = null;\n\n/**\n * Get the global work type router instance\n */\nexport function getGlobalRouter(): WorkTypeRouter {\n if (!globalRouter) {\n globalRouter = new WorkTypeRouter();\n }\n return globalRouter;\n}\n\n/**\n * Reset the global router (useful for testing)\n */\nexport function resetGlobalRouter(): void {\n globalRouter = null;\n}\n\n/**\n * Reload global router configuration\n */\nexport function reloadGlobalRouter(): void {\n if (globalRouter) {\n globalRouter.reloadConfig();\n }\n}\n\n/**\n * Get model using the global router\n */\nexport function getModel(workTypeId: WorkTypeId): ModelResolutionResult {\n return getGlobalRouter().getModel(workTypeId);\n}\n\n/**\n * Get just the model ID using the global router\n */\nexport function getModelId(workTypeId: WorkTypeId): ModelId {\n return getGlobalRouter().getModelId(workTypeId);\n}\n\n/**\n * Check for override using the global router\n */\nexport function hasOverride(workTypeId: WorkTypeId): boolean {\n return getGlobalRouter().hasOverride(workTypeId);\n}\n\n/**\n * Get debug info using the global router\n */\nexport function getDebugInfo() {\n return getGlobalRouter().getDebugInfo();\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as getModelId, c as reloadGlobalRouter, i as getModel, l as resetGlobalRouter, n as getDebugInfo, o as hasOverride, r as getGlobalRouter, s as init_work_type_router, t as WorkTypeRouter } from "./work-type-router-
|
|
1
|
+
import { a as getModelId, c as reloadGlobalRouter, i as getModel, l as resetGlobalRouter, n as getDebugInfo, o as hasOverride, r as getGlobalRouter, s as init_work_type_router, t as WorkTypeRouter } from "./work-type-router-CWVW2Wk_.js";
|
|
2
2
|
init_work_type_router();
|
|
3
3
|
export { WorkTypeRouter, getDebugInfo, getGlobalRouter, getModel, getModelId, hasOverride, reloadGlobalRouter, resetGlobalRouter };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as
|
|
5
|
-
import { t as
|
|
6
|
-
import { t as
|
|
1
|
+
import { _ as init_paths, s as PANOPTICON_HOME, t as AGENTS_DIR } from "./paths-BDyJ7BiV.js";
|
|
2
|
+
import { n as extractPrefix, r as init_issue_id, t as extractNumber } from "./issue-id-vwYJdsf8.js";
|
|
3
|
+
import { o as init_tmux, u as sessionExists } from "./tmux-LwG0tHhU.js";
|
|
4
|
+
import { i as stepSkipped, n as stepFailed, r as stepOk, t as getLinearApiKey } from "./types-RKZjGE5N.js";
|
|
5
|
+
import { r as findWorkspacePath, t as archivePlanning } from "./archive-planning-BmW9UDTr.js";
|
|
6
|
+
import { t as closeIssue } from "./close-issue-DtKdsSTm.js";
|
|
7
|
+
import { t as compactBeads } from "./compact-beads-D8Vt3qyv.js";
|
|
7
8
|
import { existsSync, readFileSync, rmSync, unlinkSync } from "fs";
|
|
8
9
|
import { basename, join } from "path";
|
|
9
10
|
import { exec } from "child_process";
|
|
@@ -22,6 +23,7 @@ import { promisify } from "util";
|
|
|
22
23
|
*/
|
|
23
24
|
init_paths();
|
|
24
25
|
init_tmux();
|
|
26
|
+
init_issue_id();
|
|
25
27
|
const execAsync$1 = promisify(exec);
|
|
26
28
|
/**
|
|
27
29
|
* Kill tmux sessions associated with an issue.
|
|
@@ -51,7 +53,7 @@ async function stopTldrDaemon(workspacePath) {
|
|
|
51
53
|
const venvPath = join(workspacePath, ".venv");
|
|
52
54
|
if (!existsSync(venvPath)) return stepSkipped(step, ["No .venv found"]);
|
|
53
55
|
try {
|
|
54
|
-
const { getTldrDaemonService } = await import("./tldr-daemon-
|
|
56
|
+
const { getTldrDaemonService } = await import("./tldr-daemon-Cfs0bXTi.js");
|
|
55
57
|
await getTldrDaemonService(workspacePath, venvPath).stop();
|
|
56
58
|
return stepOk(step, ["Stopped TLDR daemon"]);
|
|
57
59
|
} catch {
|
|
@@ -64,7 +66,7 @@ async function stopTldrDaemon(workspacePath) {
|
|
|
64
66
|
async function stopDocker(workspacePath, projectName, issueLower) {
|
|
65
67
|
const step = "teardown:docker";
|
|
66
68
|
try {
|
|
67
|
-
const { stopWorkspaceDocker } = await import("./workspace-manager-
|
|
69
|
+
const { stopWorkspaceDocker } = await import("./workspace-manager-BYfzs_t2.js");
|
|
68
70
|
await stopWorkspaceDocker(workspacePath, projectName, issueLower);
|
|
69
71
|
return stepOk(step, ["Stopped Docker containers"]);
|
|
70
72
|
} catch {
|
|
@@ -249,7 +251,7 @@ async function deleteBranches(projectPath, issueLower) {
|
|
|
249
251
|
async function clearShadowState(issueId) {
|
|
250
252
|
const step = "teardown:shadow-state";
|
|
251
253
|
try {
|
|
252
|
-
const { removeShadowState } = await import("./shadow-state-
|
|
254
|
+
const { removeShadowState } = await import("./shadow-state-BIexcxkv.js");
|
|
253
255
|
if (removeShadowState(issueId).success) return stepOk(step, [`Cleared shadow state for ${issueId}`]);
|
|
254
256
|
return stepSkipped(step, ["No shadow state found"]);
|
|
255
257
|
} catch {
|
|
@@ -309,7 +311,7 @@ function buildPlaceholders(ctx, opts, workspacePath) {
|
|
|
309
311
|
async function removeTunnelConfig(tunnelConfig, placeholders) {
|
|
310
312
|
const step = "teardown:tunnel";
|
|
311
313
|
try {
|
|
312
|
-
const { removeTunnelIngress } = await import("./tunnel-
|
|
314
|
+
const { removeTunnelIngress } = await import("./tunnel-0RzzuXPf.js");
|
|
313
315
|
return stepOk(step, (await removeTunnelIngress(tunnelConfig, placeholders)).steps || ["Removed tunnel ingress"]);
|
|
314
316
|
} catch (err) {
|
|
315
317
|
return stepSkipped(step, [`Tunnel cleanup warning: ${err.message}`]);
|
|
@@ -321,7 +323,7 @@ async function removeTunnelConfig(tunnelConfig, placeholders) {
|
|
|
321
323
|
async function removeHumeEviConfig(humeConfig, placeholders) {
|
|
322
324
|
const step = "teardown:hume";
|
|
323
325
|
try {
|
|
324
|
-
const { deleteHumeConfig } = await import("./hume-
|
|
326
|
+
const { deleteHumeConfig } = await import("./hume-CA2pftu_.js");
|
|
325
327
|
return stepOk(step, (await deleteHumeConfig(humeConfig, placeholders)).steps || ["Removed Hume EVI config"]);
|
|
326
328
|
} catch (err) {
|
|
327
329
|
return stepSkipped(step, [`Hume cleanup warning: ${err.message}`]);
|
|
@@ -345,7 +347,7 @@ async function removeHumeEviConfig(humeConfig, placeholders) {
|
|
|
345
347
|
*/
|
|
346
348
|
async function teardownWorkspace(ctx, opts = {}) {
|
|
347
349
|
const issueLower = ctx.issueId.toLowerCase();
|
|
348
|
-
const projName = opts.projectName || ctx.projectName || ctx.issueId
|
|
350
|
+
const projName = opts.projectName || ctx.projectName || (extractPrefix(ctx.issueId)?.toLowerCase() ?? ctx.issueId);
|
|
349
351
|
const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);
|
|
350
352
|
const shouldDeleteWorkspace = opts.deleteWorkspace !== false;
|
|
351
353
|
const results = [];
|
|
@@ -381,6 +383,7 @@ async function teardownWorkspace(ctx, opts = {}) {
|
|
|
381
383
|
* deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue
|
|
382
384
|
*/
|
|
383
385
|
init_paths();
|
|
386
|
+
init_issue_id();
|
|
384
387
|
const execAsync = promisify(exec);
|
|
385
388
|
/**
|
|
386
389
|
* Build a WorkflowResult from collected steps.
|
|
@@ -553,7 +556,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
553
556
|
const branchName = `feature/${ctx.issueId.toLowerCase()}`;
|
|
554
557
|
try {
|
|
555
558
|
try {
|
|
556
|
-
const { loadReviewStatuses } = await import("./review-status-
|
|
559
|
+
const { loadReviewStatuses } = await import("./review-status-BtXqWBhS.js");
|
|
557
560
|
if (loadReviewStatuses()[ctx.issueId.toUpperCase()]?.mergeStatus === "merged") return stepOk(step, ["Merge specialist confirmed merge completed"]);
|
|
558
561
|
} catch {}
|
|
559
562
|
const { stdout: branchExists } = await execAsync(`git branch --list "${branchName}" 2>/dev/null || true`, {
|
|
@@ -567,6 +570,13 @@ async function verifyBranchMerged(ctx) {
|
|
|
567
570
|
});
|
|
568
571
|
return stepOk(step, ["All commits merged to main"]);
|
|
569
572
|
} catch {
|
|
573
|
+
try {
|
|
574
|
+
const { stdout: codeDiff } = await execAsync(`git diff main...${branchName} -- ':!.planning' ':!docs/prds' ':!.panopticon/prompts' 2>/dev/null || true`, {
|
|
575
|
+
cwd: ctx.projectPath,
|
|
576
|
+
encoding: "utf-8"
|
|
577
|
+
});
|
|
578
|
+
if (!codeDiff.trim()) return stepOk(step, ["Code changes squash-merged to main (only planning artifacts remain on branch)"]);
|
|
579
|
+
} catch {}
|
|
570
580
|
const { stdout: unmerged } = await execAsync(`git log main..${branchName} --oneline 2>/dev/null || true`, {
|
|
571
581
|
cwd: ctx.projectPath,
|
|
572
582
|
encoding: "utf-8"
|
|
@@ -586,6 +596,13 @@ async function verifyBranchMerged(ctx) {
|
|
|
586
596
|
});
|
|
587
597
|
return stepOk(step, ["Remote branch fully merged"]);
|
|
588
598
|
} catch {
|
|
599
|
+
try {
|
|
600
|
+
const { stdout: codeDiff } = await execAsync(`git diff main...origin/${branchName} -- ':!.planning' ':!docs/prds' ':!.panopticon/prompts' 2>/dev/null || true`, {
|
|
601
|
+
cwd: ctx.projectPath,
|
|
602
|
+
encoding: "utf-8"
|
|
603
|
+
});
|
|
604
|
+
if (!codeDiff.trim()) return stepOk(step, ["Remote code changes squash-merged to main (only planning artifacts remain on branch)"]);
|
|
605
|
+
} catch {}
|
|
589
606
|
const { stdout: remoteUnmerged } = await execAsync(`git log main..origin/${branchName} --oneline 2>/dev/null || true`, {
|
|
590
607
|
cwd: ctx.projectPath,
|
|
591
608
|
encoding: "utf-8"
|
|
@@ -622,8 +639,9 @@ async function resetIssueToTodo(ctx) {
|
|
|
622
639
|
if (linearApiKey) {
|
|
623
640
|
const { LinearClient } = await import("@linear/sdk");
|
|
624
641
|
const client = new LinearClient({ apiKey: linearApiKey });
|
|
625
|
-
const issueNum =
|
|
626
|
-
const teamKey = ctx.issueId
|
|
642
|
+
const issueNum = extractNumber(ctx.issueId);
|
|
643
|
+
const teamKey = extractPrefix(ctx.issueId);
|
|
644
|
+
if (issueNum === null || teamKey === null) return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);
|
|
627
645
|
const results = await client.issues({
|
|
628
646
|
filter: {
|
|
629
647
|
number: { eq: issueNum },
|
|
@@ -653,7 +671,7 @@ async function resetIssueToTodo(ctx) {
|
|
|
653
671
|
async function clearReviewStatusStep(issueId) {
|
|
654
672
|
const step = "clear-review-status";
|
|
655
673
|
try {
|
|
656
|
-
const { clearReviewStatus } = await import("./review-status-
|
|
674
|
+
const { clearReviewStatus } = await import("./review-status-BtXqWBhS.js");
|
|
657
675
|
clearReviewStatus(issueId.toUpperCase());
|
|
658
676
|
return stepOk(step, ["Review status cleared"]);
|
|
659
677
|
} catch {
|
|
@@ -677,4 +695,4 @@ async function clearReviewStatusStep(issueId) {
|
|
|
677
695
|
//#endregion
|
|
678
696
|
export { teardownWorkspace as a, deepWipe as i, close as n, closeOut as r, approve as t };
|
|
679
697
|
|
|
680
|
-
//# sourceMappingURL=workflows-
|
|
698
|
+
//# sourceMappingURL=workflows-BSMipN07.js.map
|