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.
Files changed (311) hide show
  1. package/README.md +2 -2
  2. package/dist/{agents-DfYify9s.js → agents-CfFDs52G.js} +14 -14
  3. package/dist/{agents-DfYify9s.js.map → agents-CfFDs52G.js.map} +1 -1
  4. package/dist/{agents-BKsVoIc9.js → agents-D_2oRFVf.js} +1 -1
  5. package/dist/{archive-planning-BJrZ3tmN.js → archive-planning-D97ziGec.js} +3 -3
  6. package/dist/{archive-planning-BJrZ3tmN.js.map → archive-planning-D97ziGec.js.map} +1 -1
  7. package/dist/{archive-planning-C3m3hfa5.js → archive-planning-DK90wn9Q.js} +1 -1
  8. package/dist/{browser-Cvdznzc0.js → browser-CX7jXfXX.js} +1 -1
  9. package/dist/{browser-Cvdznzc0.js.map → browser-CX7jXfXX.js.map} +1 -1
  10. package/dist/{clean-planning-DvhZAUv4.js → clean-planning-D_lz4aQq.js} +2 -2
  11. package/dist/{clean-planning-DvhZAUv4.js.map → clean-planning-D_lz4aQq.js.map} +1 -1
  12. package/dist/clean-planning-x1S-JdmO.js +2 -0
  13. package/dist/cli/index.js +291 -760
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/{close-issue-Dr7yZmrr.js → close-issue-CaFE0stN.js} +11 -7
  16. package/dist/close-issue-CaFE0stN.js.map +1 -0
  17. package/dist/close-issue-CjcfZI9s.js +2 -0
  18. package/dist/compact-beads-B0_qE1w3.js +2 -0
  19. package/dist/{compact-beads-BCOtIIRl.js → compact-beads-CjFkteSU.js} +2 -2
  20. package/dist/{compact-beads-BCOtIIRl.js.map → compact-beads-CjFkteSU.js.map} +1 -1
  21. package/dist/{config-CRzMQRgA.js → config-BQNKsi9G.js} +2 -2
  22. package/dist/{config-CRzMQRgA.js.map → config-BQNKsi9G.js.map} +1 -1
  23. package/dist/{config-BYgUzQ21.js → config-agyKgF5C.js} +1 -1
  24. package/dist/{config-yaml-BgOACZAB.js → config-yaml-DGbLSMCa.js} +1 -1
  25. package/dist/{config-yaml-BgOACZAB.js.map → config-yaml-DGbLSMCa.js.map} +1 -1
  26. package/dist/{config-yaml-fdyvyL0S.js → config-yaml-Dqt4FWQH.js} +1 -1
  27. package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js → acceptance-criteria-Dk9hhiYj.js} +1 -1
  28. package/dist/dashboard/{acceptance-criteria-e5iiHlRx.js.map → acceptance-criteria-Dk9hhiYj.js.map} +1 -1
  29. package/dist/dashboard/{agent-enrichment-C67LJBgD.js → agent-enrichment-DdO7ZqjI.js} +11 -7
  30. package/dist/dashboard/agent-enrichment-DdO7ZqjI.js.map +1 -0
  31. package/dist/dashboard/{agent-enrichment-Cq0P1cNZ.js → agent-enrichment-dLeGE1fX.js} +1 -1
  32. package/dist/dashboard/{agents-YyO6t5Xa.js → agents-DCpQQ_W5.js} +14 -14
  33. package/dist/dashboard/{agents-YyO6t5Xa.js.map → agents-DCpQQ_W5.js.map} +1 -1
  34. package/dist/dashboard/{agents-BVBVCyat.js → agents-Dgh2TjSp.js} +1 -1
  35. package/dist/dashboard/{archive-planning-h-hAjk0P.js → archive-planning-BmW9UDTr.js} +3 -3
  36. package/dist/dashboard/{archive-planning-h-hAjk0P.js.map → archive-planning-BmW9UDTr.js.map} +1 -1
  37. package/dist/dashboard/{archive-planning-CScs1MOC.js → archive-planning-C3Ebf9yC.js} +1 -1
  38. package/dist/dashboard/{beads-qNB0yAHV.js → beads-Bv-AdX7G.js} +3 -3
  39. package/dist/dashboard/{beads-qNB0yAHV.js.map → beads-Bv-AdX7G.js.map} +1 -1
  40. package/dist/dashboard/{beads-D_FRedEJ.js → beads-By6-X07V.js} +1 -1
  41. package/dist/dashboard/clean-planning-D60L8rPY.js +2 -0
  42. package/dist/dashboard/{clean-planning-qafw99vY.js → clean-planning-VEJu5suh.js} +2 -2
  43. package/dist/dashboard/{clean-planning-qafw99vY.js.map → clean-planning-VEJu5suh.js.map} +1 -1
  44. package/dist/dashboard/close-issue-C2KeSKKJ.js +2 -0
  45. package/dist/dashboard/{close-issue-DfIggeZD.js → close-issue-DtKdsSTm.js} +11 -7
  46. package/dist/dashboard/close-issue-DtKdsSTm.js.map +1 -0
  47. package/dist/dashboard/compact-beads-C7BN5N11.js +2 -0
  48. package/dist/dashboard/{compact-beads-Dt0qTqsC.js → compact-beads-D8Vt3qyv.js} +2 -2
  49. package/dist/dashboard/{compact-beads-Dt0qTqsC.js.map → compact-beads-D8Vt3qyv.js.map} +1 -1
  50. package/dist/dashboard/{config-CUREjHP7.js → config-CDkGjnwy.js} +2 -2
  51. package/dist/dashboard/{config-CUREjHP7.js.map → config-CDkGjnwy.js.map} +1 -1
  52. package/dist/dashboard/{config-BeI3uy-8.js → config-CTXkBATQ.js} +1 -1
  53. package/dist/dashboard/{database-CozA13Wy.js → database-DhqASALP.js} +1 -1
  54. package/dist/dashboard/{database-C0y0hXBx.js → database-cxmQryoh.js} +2 -2
  55. package/dist/dashboard/{database-C0y0hXBx.js.map → database-cxmQryoh.js.map} +1 -1
  56. package/dist/dashboard/{dist-src-oG2iHzgI.js → dist-src-DTm11oQr.js} +1 -1
  57. package/dist/dashboard/{dist-src-oG2iHzgI.js.map → dist-src-DTm11oQr.js.map} +1 -1
  58. package/dist/dashboard/{event-store-D7kLBd07.js → event-store-VWWUmOfn.js} +1 -1
  59. package/dist/dashboard/{event-store-O9q0Gweh.js → event-store-vSmAA3Zp.js} +9 -4
  60. package/dist/dashboard/event-store-vSmAA3Zp.js.map +1 -0
  61. package/dist/dashboard/{factory-BnLdiQW-.js → factory-C8nhLGHB.js} +3 -3
  62. package/dist/dashboard/{factory-BnLdiQW-.js.map → factory-C8nhLGHB.js.map} +1 -1
  63. package/dist/dashboard/{feedback-writer-DyovUANg.js → feedback-writer-CudSe1WK.js} +2 -2
  64. package/dist/dashboard/{feedback-writer-DyovUANg.js.map → feedback-writer-CudSe1WK.js.map} +1 -1
  65. package/dist/dashboard/{feedback-writer-gSUv_W0h.js → feedback-writer-Wgv1cd1r.js} +1 -1
  66. package/dist/dashboard/{git-utils-BJRioREj.js → git-utils-C1m4SwAe.js} +1 -1
  67. package/dist/dashboard/{git-utils-BJRioREj.js.map → git-utils-C1m4SwAe.js.map} +1 -1
  68. package/dist/dashboard/{git-utils-BtCRddq3.js → git-utils-DQI8EYoj.js} +1 -1
  69. package/dist/dashboard/{github-app-XO-LBUGk.js → github-app-DClWjjHr.js} +1 -1
  70. package/dist/dashboard/{github-app-XO-LBUGk.js.map → github-app-DClWjjHr.js.map} +1 -1
  71. package/dist/dashboard/{health-events-db-584nYgJB.js → health-events-db-BMXQfInV.js} +1 -1
  72. package/dist/dashboard/{health-events-db-B3ChzN65.js → health-events-db-Do4NrOhC.js} +2 -2
  73. package/dist/dashboard/{health-events-db-B3ChzN65.js.map → health-events-db-Do4NrOhC.js.map} +1 -1
  74. package/dist/dashboard/{hooks-CKhs3N68.js → hooks-CB4T47NC.js} +1 -1
  75. package/dist/dashboard/{hooks-CErbP8Oq.js → hooks-CjqXOlNb.js} +2 -2
  76. package/dist/dashboard/{hooks-CErbP8Oq.js.map → hooks-CjqXOlNb.js.map} +1 -1
  77. package/dist/dashboard/hume-CA2pftu_.js +3 -0
  78. package/dist/dashboard/{hume-CX_U3Qha.js → hume-JsAlMOJC.js} +2 -2
  79. package/dist/dashboard/{hume-CX_U3Qha.js.map → hume-JsAlMOJC.js.map} +1 -1
  80. package/dist/dashboard/{inspect-agent-B57kGDUV.js → inspect-agent-7eour7EA.js} +3 -3
  81. package/dist/dashboard/{inspect-agent-B57kGDUV.js.map → inspect-agent-7eour7EA.js.map} +1 -1
  82. package/dist/dashboard/{io-yGovuG4U.js → io-CWlFW78i.js} +1 -1
  83. package/dist/dashboard/{io-AJg-mzFi.js → io-DKS6359z.js} +1 -1
  84. package/dist/dashboard/{io-AJg-mzFi.js.map → io-DKS6359z.js.map} +1 -1
  85. package/dist/dashboard/issue-id-vwYJdsf8.js +62 -0
  86. package/dist/dashboard/issue-id-vwYJdsf8.js.map +1 -0
  87. package/dist/dashboard/{issue-service-singleton-DQK42EqH.js → issue-service-singleton-Co__-6kL.js} +1 -1
  88. package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js → issue-service-singleton-Wv4xBm3y.js} +7 -7
  89. package/dist/dashboard/{issue-service-singleton-sb2HkB9f.js.map → issue-service-singleton-Wv4xBm3y.js.map} +1 -1
  90. package/dist/dashboard/{label-cleanup-CZEsbtq9.js → label-cleanup-nVKTmIIW.js} +7 -4
  91. package/dist/dashboard/label-cleanup-nVKTmIIW.js.map +1 -0
  92. package/dist/dashboard/lifecycle-BcUmtkR4.js +7 -0
  93. package/dist/dashboard/{merge-agent-GLtMEsTu.js → merge-agent-CGN3TT0a.js} +1 -1
  94. package/dist/dashboard/{merge-agent-twroFuAh.js → merge-agent-yudQOPZc.js} +148 -46
  95. package/dist/dashboard/merge-agent-yudQOPZc.js.map +1 -0
  96. package/dist/dashboard/{paths-COdEvoXR.js → paths-BDyJ7BiV.js} +19 -2
  97. package/dist/dashboard/{paths-COdEvoXR.js.map → paths-BDyJ7BiV.js.map} +1 -1
  98. package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js → pipeline-notifier-CCSN-jar.js} +1 -1
  99. package/dist/dashboard/{pipeline-notifier-DM5AHG5Q.js.map → pipeline-notifier-CCSN-jar.js.map} +1 -1
  100. package/dist/dashboard/{plan-utils-BkCIhn3B.js → plan-utils-Bkcsqr_s.js} +3 -3
  101. package/dist/dashboard/{plan-utils-BkCIhn3B.js.map → plan-utils-Bkcsqr_s.js.map} +1 -1
  102. package/dist/dashboard/{prd-draft-D09Afalc.js → prd-draft-BD8oMkZ1.js} +2 -2
  103. package/dist/dashboard/{prd-draft-D09Afalc.js.map → prd-draft-BD8oMkZ1.js.map} +1 -1
  104. package/dist/dashboard/{projection-cache-DQ9zegkK.js → projection-cache-C0EL8s8h.js} +1 -1
  105. package/dist/dashboard/{projection-cache-DQ9zegkK.js.map → projection-cache-C0EL8s8h.js.map} +1 -1
  106. package/dist/dashboard/{projects-DyT3vSy-.js → projects-C5ozxjwP.js} +1 -1
  107. package/dist/dashboard/{projects-Cq3TWdPS.js → projects-CFVl4oHn.js} +25 -13
  108. package/dist/dashboard/projects-CFVl4oHn.js.map +1 -0
  109. package/dist/dashboard/{providers-Ck2sQd_F.js → providers-B5Y4H2Mg.js} +4 -4
  110. package/dist/dashboard/providers-B5Y4H2Mg.js.map +1 -0
  111. package/dist/dashboard/{providers-DVQnDekG.js → providers-csVZVPkE.js} +1 -1
  112. package/dist/dashboard/public/assets/{dist-CCJbQrSB.js → dist-CXaO6nOE.js} +1 -1
  113. package/dist/dashboard/public/assets/index-CzFZIb87.js +212 -0
  114. package/dist/dashboard/public/assets/index-OEEbThNN.css +1 -0
  115. package/dist/dashboard/public/index.html +2 -2
  116. package/dist/dashboard/rally-6McpKKRa.js +3 -0
  117. package/dist/dashboard/{rally-Cwuae-4C.js → rally-YjFRxIiC.js} +2 -2
  118. package/dist/dashboard/{rally-Cwuae-4C.js.map → rally-YjFRxIiC.js.map} +1 -1
  119. package/dist/dashboard/{rally-api-DSUxm7EO.js → rally-api-C0WqCSkT.js} +1 -1
  120. package/dist/dashboard/{rally-api-DSUxm7EO.js.map → rally-api-C0WqCSkT.js.map} +1 -1
  121. package/dist/dashboard/{rally-api-CEH5KZi4.js → rally-api-DNttdCW4.js} +1 -1
  122. package/dist/dashboard/{remote-BHTTMpJJ.js → remote-Cigqjj3f.js} +2 -2
  123. package/dist/dashboard/{remote-BXo_iIku.js → remote-ObpNZ7hF.js} +2 -2
  124. package/dist/dashboard/{remote-BXo_iIku.js.map → remote-ObpNZ7hF.js.map} +1 -1
  125. package/dist/dashboard/{remote-agents-CTKVhFFY.js → remote-agents-Bf3GuM7t.js} +1 -1
  126. package/dist/dashboard/{remote-agents-C0_0LLNd.js → remote-agents-DFyjT1Le.js} +1 -1
  127. package/dist/dashboard/{remote-agents-C0_0LLNd.js.map → remote-agents-DFyjT1Le.js.map} +1 -1
  128. package/dist/dashboard/{review-status-CK3eBGyb.js → review-status-BtXqWBhS.js} +1 -1
  129. package/dist/dashboard/{review-status-CV55Tl-n.js → review-status-Bymwzh2i.js} +44 -4
  130. package/dist/dashboard/{review-status-CV55Tl-n.js.map → review-status-Bymwzh2i.js.map} +1 -1
  131. package/dist/dashboard/server.js +559 -253
  132. package/dist/dashboard/server.js.map +1 -1
  133. package/dist/dashboard/{settings-CuHV-wcv.js → settings-BHlDG7TK.js} +2 -2
  134. package/dist/dashboard/settings-BHlDG7TK.js.map +1 -0
  135. package/dist/dashboard/settings-XWvDcj-D.js +2 -0
  136. package/dist/dashboard/{shadow-engineering-BUeZunaE.js → shadow-engineering-lIn1W_95.js} +1 -1
  137. package/dist/dashboard/{shadow-engineering-BUeZunaE.js.map → shadow-engineering-lIn1W_95.js.map} +1 -1
  138. package/dist/dashboard/{shadow-state-DHQ-kASN.js → shadow-state-BIexcxkv.js} +1 -1
  139. package/dist/dashboard/{shadow-state-DHQ-kASN.js.map → shadow-state-BIexcxkv.js.map} +1 -1
  140. package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js → spawn-planning-session-33Jf-d5T.js} +6 -6
  141. package/dist/dashboard/{spawn-planning-session-8FFAqLdK.js.map → spawn-planning-session-33Jf-d5T.js.map} +1 -1
  142. package/dist/dashboard/{spawn-planning-session-U0Lqpjen.js → spawn-planning-session-D5hrVdWM.js} +1 -1
  143. package/dist/dashboard/{specialist-context-ColzlmGE.js → specialist-context-DGukHSn8.js} +6 -6
  144. package/dist/dashboard/{specialist-context-ColzlmGE.js.map → specialist-context-DGukHSn8.js.map} +1 -1
  145. package/dist/dashboard/{specialist-logs-BhmDpFIq.js → specialist-logs-CIw4qfTy.js} +1 -1
  146. package/dist/dashboard/{specialists-C6s3U6tX.js → specialists-B_zrayaP.js} +37 -36
  147. package/dist/dashboard/specialists-B_zrayaP.js.map +1 -0
  148. package/dist/dashboard/{specialists-Cny632-T.js → specialists-Cp-PgspS.js} +1 -1
  149. package/dist/dashboard/{test-agent-queue-tqI4VDsu.js → test-agent-queue-ypF_ecHo.js} +4 -4
  150. package/dist/dashboard/{test-agent-queue-tqI4VDsu.js.map → test-agent-queue-ypF_ecHo.js.map} +1 -1
  151. package/dist/dashboard/{tldr-daemon-BNFyS7W_.js → tldr-daemon-B_oLRD8z.js} +2 -2
  152. package/dist/dashboard/{tldr-daemon-BNFyS7W_.js.map → tldr-daemon-B_oLRD8z.js.map} +1 -1
  153. package/dist/dashboard/{tldr-daemon-A6JqC59u.js → tldr-daemon-Cfs0bXTi.js} +1 -1
  154. package/dist/dashboard/{tmux-DYGAVJfb.js → tmux-BzxdKItf.js} +1 -1
  155. package/dist/dashboard/{tmux-IlN1Slv-.js → tmux-LwG0tHhU.js} +2 -2
  156. package/dist/dashboard/{tmux-IlN1Slv-.js.map → tmux-LwG0tHhU.js.map} +1 -1
  157. package/dist/dashboard/{tracker-config-BzNLnmcE.js → tracker-config-BP59uH4V.js} +1 -1
  158. package/dist/dashboard/{tracker-config-CNM_5rEf.js → tracker-config-e7ph1QqT.js} +2 -2
  159. package/dist/dashboard/{tracker-config-CNM_5rEf.js.map → tracker-config-e7ph1QqT.js.map} +1 -1
  160. package/dist/dashboard/{tunnel-D2BkwU7k.js → tunnel-0RzzuXPf.js} +1 -1
  161. package/dist/dashboard/{tunnel-Dub2hiAA.js → tunnel-DldbBPWL.js} +2 -2
  162. package/dist/dashboard/{tunnel-Dub2hiAA.js.map → tunnel-DldbBPWL.js.map} +1 -1
  163. package/dist/dashboard/{types-CWA-o4UN.js → types-RKZjGE5N.js} +1 -1
  164. package/dist/dashboard/{types-CWA-o4UN.js.map → types-RKZjGE5N.js.map} +1 -1
  165. package/dist/dashboard/{vtt-parser-BAXygRf0.js → vtt-parser-99vFekRQ.js} +1 -1
  166. package/dist/dashboard/{vtt-parser-BAXygRf0.js.map → vtt-parser-99vFekRQ.js.map} +1 -1
  167. package/dist/dashboard/{work-agent-prompt-JYq_OugP.js → work-agent-prompt-fCg67nyo.js} +65 -10
  168. package/dist/dashboard/{work-agent-prompt-JYq_OugP.js.map → work-agent-prompt-fCg67nyo.js.map} +1 -1
  169. package/dist/dashboard/{work-type-router-Cxp8_ur2.js → work-type-router-CWVW2Wk_.js} +1 -1
  170. package/dist/dashboard/{work-type-router-Cxp8_ur2.js.map → work-type-router-CWVW2Wk_.js.map} +1 -1
  171. package/dist/dashboard/{work-type-router-Com2amST.js → work-type-router-Di5gCQwh.js} +1 -1
  172. package/dist/dashboard/{workflows-N1UTipYl.js → workflows-BSMipN07.js} +35 -17
  173. package/dist/dashboard/workflows-BSMipN07.js.map +1 -0
  174. package/dist/dashboard/workflows-DaYWQIS2.js +2 -0
  175. package/dist/dashboard/{workspace-config-cmp5_ipD.js → workspace-config-DVDR-Ukh.js} +1 -1
  176. package/dist/dashboard/workspace-config-DVDR-Ukh.js.map +1 -0
  177. package/dist/dashboard/{workspace-manager-CjpWPgzL.js → workspace-manager-BYfzs_t2.js} +1 -1
  178. package/dist/dashboard/{workspace-manager-D_y9ZmW_.js → workspace-manager-C7OfT62A.js} +44 -24
  179. package/dist/dashboard/workspace-manager-C7OfT62A.js.map +1 -0
  180. package/dist/{dns-BKzHm-2q.js → dns-D_aKQJjb.js} +1 -1
  181. package/dist/{dns-DZwOWvVO.js → dns-Yxq4NNS7.js} +1 -1
  182. package/dist/{dns-DZwOWvVO.js.map → dns-Yxq4NNS7.js.map} +1 -1
  183. package/dist/{factory-DFu3IT4r.js → factory-BRBGw6OB.js} +1 -1
  184. package/dist/{factory-DfzczxN1.js → factory-DzsOiZVc.js} +3 -3
  185. package/dist/{factory-DfzczxN1.js.map → factory-DzsOiZVc.js.map} +1 -1
  186. package/dist/{feedback-writer-CwdnOkPO.js → feedback-writer-ygXN5F9N.js} +2 -2
  187. package/dist/{feedback-writer-CwdnOkPO.js.map → feedback-writer-ygXN5F9N.js.map} +1 -1
  188. package/dist/{github-app-CHKwxOeQ.js → github-app-DykduJ0X.js} +1 -1
  189. package/dist/{github-app-CHKwxOeQ.js.map → github-app-DykduJ0X.js.map} +1 -1
  190. package/dist/hume-9nv1VmMV.js +3 -0
  191. package/dist/{hume-DnV-tDsh.js → hume-DoCbph2h.js} +2 -2
  192. package/dist/{hume-DnV-tDsh.js.map → hume-DoCbph2h.js.map} +1 -1
  193. package/dist/index.d.ts +17 -2
  194. package/dist/index.d.ts.map +1 -1
  195. package/dist/index.js +8 -7
  196. package/dist/issue-id-CAcekoIw.js +62 -0
  197. package/dist/issue-id-CAcekoIw.js.map +1 -0
  198. package/dist/{label-cleanup-31ElPqqv.js → label-cleanup-C8R9Rspn.js} +7 -4
  199. package/dist/label-cleanup-C8R9Rspn.js.map +1 -0
  200. package/dist/{manifest-DL0oDbpv.js → manifest-B4ghOD-V.js} +1 -1
  201. package/dist/{manifest-DL0oDbpv.js.map → manifest-B4ghOD-V.js.map} +1 -1
  202. package/dist/{merge-agent-VQH9z9t8.js → merge-agent-DlUiUanN.js} +86 -33
  203. package/dist/merge-agent-DlUiUanN.js.map +1 -0
  204. package/dist/{paths-lMaxrYtT.js → paths-CDJ_HsbN.js} +19 -2
  205. package/dist/{paths-lMaxrYtT.js.map → paths-CDJ_HsbN.js.map} +1 -1
  206. package/dist/{pipeline-notifier-OJ-d3Y60.js → pipeline-notifier-XgDdCdvT.js} +1 -1
  207. package/dist/{pipeline-notifier-OJ-d3Y60.js.map → pipeline-notifier-XgDdCdvT.js.map} +1 -1
  208. package/dist/{projects-CvLepaxC.js → projects-Bk-5QhFQ.js} +25 -13
  209. package/dist/projects-Bk-5QhFQ.js.map +1 -0
  210. package/dist/{projects-DMWmPeIU.js → projects-DhU7rAVN.js} +1 -1
  211. package/dist/{providers-DcCPZ5K4.js → providers-DSU1vfQF.js} +4 -4
  212. package/dist/providers-DSU1vfQF.js.map +1 -0
  213. package/dist/rally-DdPvGa-w.js +3 -0
  214. package/dist/{rally-uUUZXp1h.js → rally-Dy00NElU.js} +1 -1
  215. package/dist/{rally-uUUZXp1h.js.map → rally-Dy00NElU.js.map} +1 -1
  216. package/dist/{remote-CkLBqLJc.js → remote-CYiOJg0q.js} +2 -2
  217. package/dist/{remote-CkLBqLJc.js.map → remote-CYiOJg0q.js.map} +1 -1
  218. package/dist/{remote-agents-C5Bd2fgt.js → remote-agents-CZXrUF4f.js} +1 -1
  219. package/dist/{remote-agents-C5Bd2fgt.js.map → remote-agents-CZXrUF4f.js.map} +1 -1
  220. package/dist/{remote-agents-BTzD-wMQ.js → remote-agents-ycHHVsgf.js} +1 -1
  221. package/dist/{remote-workspace-Dxghqiti.js → remote-workspace-CA33UuVI.js} +4 -4
  222. package/dist/{remote-workspace-Dxghqiti.js.map → remote-workspace-CA33UuVI.js.map} +1 -1
  223. package/dist/{review-status-2TdtHNcs.js → review-status-D6H2WOw8.js} +1 -1
  224. package/dist/{review-status-Bm1bWNEa.js → review-status-DEDvCKMP.js} +44 -4
  225. package/dist/{review-status-Bm1bWNEa.js.map → review-status-DEDvCKMP.js.map} +1 -1
  226. package/dist/{tracker-C_62ukEq.js → settings-BcWPTrua.js} +7 -199
  227. package/dist/settings-BcWPTrua.js.map +1 -0
  228. package/dist/shadow-state-BZzxfEGw.js +2 -0
  229. package/dist/{shadow-state-CFFHf05M.js → shadow-state-CE3dQfll.js} +1 -1
  230. package/dist/{shadow-state-CFFHf05M.js.map → shadow-state-CE3dQfll.js.map} +1 -1
  231. package/dist/{specialist-context-BdNFsfMG.js → specialist-context-BAUWL1Fl.js} +6 -6
  232. package/dist/{specialist-context-BdNFsfMG.js.map → specialist-context-BAUWL1Fl.js.map} +1 -1
  233. package/dist/{specialist-logs-CLztE_bE.js → specialist-logs-DQKKQV9B.js} +1 -1
  234. package/dist/{specialists-aUoUVWsN.js → specialists-Bfb9ATzw.js} +1 -1
  235. package/dist/{specialists-DEKqgkxp.js → specialists-D7Kj5o6s.js} +35 -34
  236. package/dist/specialists-D7Kj5o6s.js.map +1 -0
  237. package/dist/sync-DMfgd389.js +693 -0
  238. package/dist/sync-DMfgd389.js.map +1 -0
  239. package/dist/sync-TL6y-8K6.js +2 -0
  240. package/dist/{tldr-daemon-BCEFPItr.js → tldr-daemon-CFx4LXAl.js} +2 -2
  241. package/dist/{tldr-daemon-BCEFPItr.js.map → tldr-daemon-CFx4LXAl.js.map} +1 -1
  242. package/dist/{tldr-daemon-xBAx4cBE.js → tldr-daemon-D_EooADG.js} +1 -1
  243. package/dist/{tmux-DN6H886Y.js → tmux-CBtui_Cl.js} +1 -1
  244. package/dist/{tmux-CKdNxxJx.js → tmux-D6Ah4I8z.js} +2 -2
  245. package/dist/{tmux-CKdNxxJx.js.map → tmux-D6Ah4I8z.js.map} +1 -1
  246. package/dist/tracker-BhYYvU3p.js +198 -0
  247. package/dist/tracker-BhYYvU3p.js.map +1 -0
  248. package/dist/{tracker-utils-CVU2W1sX.js → tracker-utils-ChQyut8w.js} +34 -12
  249. package/dist/tracker-utils-ChQyut8w.js.map +1 -0
  250. package/dist/{traefik-DHgBoWXX.js → traefik-C80EbDu_.js} +4 -4
  251. package/dist/{traefik-DHgBoWXX.js.map → traefik-C80EbDu_.js.map} +1 -1
  252. package/dist/{traefik-BR-edbZv.js → traefik-CgHl7Bge.js} +1 -1
  253. package/dist/{tunnel-BZO9Q5oe.js → tunnel-DXOJ1wMM.js} +1 -1
  254. package/dist/{tunnel-Bl1qNSyQ.js → tunnel-DzXEPwIc.js} +2 -2
  255. package/dist/{tunnel-Bl1qNSyQ.js.map → tunnel-DzXEPwIc.js.map} +1 -1
  256. package/dist/{types-DewGdaIP.js → types-BhJj1SP1.js} +1 -1
  257. package/dist/{types-DewGdaIP.js.map → types-BhJj1SP1.js.map} +1 -1
  258. package/dist/{work-type-router-CS2BB1vS.js → work-type-router-CHjciPyS.js} +3 -3
  259. package/dist/{work-type-router-CS2BB1vS.js.map → work-type-router-CHjciPyS.js.map} +1 -1
  260. package/dist/{workspace-config-CNXOpKuj.js → workspace-config-fUafvYMp.js} +1 -1
  261. package/dist/workspace-config-fUafvYMp.js.map +1 -0
  262. package/dist/workspace-manager-B9jS4Dsq.js +3 -0
  263. package/dist/{workspace-manager-CncdZkIy.js → workspace-manager-DuLhnzJV.js} +112 -27
  264. package/dist/workspace-manager-DuLhnzJV.js.map +1 -0
  265. package/package.json +2 -1
  266. package/scripts/post-merge-deploy.sh +25 -5
  267. package/scripts/record-cost-event.js +57 -7
  268. package/scripts/record-cost-event.js.map +1 -1
  269. package/skills/pan-help/SKILL.md +1 -1
  270. package/skills/pan-sync/SKILL.md +6 -6
  271. package/skills/workspace-add-repo/skill.md +46 -0
  272. package/templates/claude-md/sections/warnings.md +15 -2
  273. package/dist/clean-planning-sZXvy3Y5.js +0 -2
  274. package/dist/close-issue-Dml437qV.js +0 -2
  275. package/dist/close-issue-Dr7yZmrr.js.map +0 -1
  276. package/dist/compact-beads-iu218JcO.js +0 -2
  277. package/dist/dashboard/agent-enrichment-C67LJBgD.js.map +0 -1
  278. package/dist/dashboard/clean-planning-DCu3cOTu.js +0 -2
  279. package/dist/dashboard/close-issue-DfIggeZD.js.map +0 -1
  280. package/dist/dashboard/close-issue-DwdwYtar.js +0 -2
  281. package/dist/dashboard/compact-beads-DXY2fK2s.js +0 -2
  282. package/dist/dashboard/event-store-O9q0Gweh.js.map +0 -1
  283. package/dist/dashboard/hume-MZndNDVU.js +0 -3
  284. package/dist/dashboard/label-cleanup-CZEsbtq9.js.map +0 -1
  285. package/dist/dashboard/lifecycle-ZTYdrr2O.js +0 -7
  286. package/dist/dashboard/merge-agent-twroFuAh.js.map +0 -1
  287. package/dist/dashboard/projects-Cq3TWdPS.js.map +0 -1
  288. package/dist/dashboard/providers-Ck2sQd_F.js.map +0 -1
  289. package/dist/dashboard/public/assets/index-CpSmB2ts.css +0 -1
  290. package/dist/dashboard/public/assets/index-yarWhi0M.js +0 -214
  291. package/dist/dashboard/rally-CQ1OBJrJ.js +0 -3
  292. package/dist/dashboard/settings-CuHV-wcv.js.map +0 -1
  293. package/dist/dashboard/settings-DMeGBRsk.js +0 -2
  294. package/dist/dashboard/specialists-C6s3U6tX.js.map +0 -1
  295. package/dist/dashboard/workflows-B2ARUpOa.js +0 -2
  296. package/dist/dashboard/workflows-N1UTipYl.js.map +0 -1
  297. package/dist/dashboard/workspace-config-cmp5_ipD.js.map +0 -1
  298. package/dist/dashboard/workspace-manager-D_y9ZmW_.js.map +0 -1
  299. package/dist/hume-BjmwmJ9E.js +0 -3
  300. package/dist/label-cleanup-31ElPqqv.js.map +0 -1
  301. package/dist/merge-agent-VQH9z9t8.js.map +0 -1
  302. package/dist/projects-CvLepaxC.js.map +0 -1
  303. package/dist/providers-DcCPZ5K4.js.map +0 -1
  304. package/dist/rally-DR9x8--6.js +0 -3
  305. package/dist/shadow-state-p3jpGRPJ.js +0 -2
  306. package/dist/specialists-DEKqgkxp.js.map +0 -1
  307. package/dist/tracker-C_62ukEq.js.map +0 -1
  308. package/dist/tracker-utils-CVU2W1sX.js.map +0 -1
  309. package/dist/workspace-config-CNXOpKuj.js.map +0 -1
  310. package/dist/workspace-manager-CncdZkIy.js.map +0 -1
  311. package/dist/workspace-manager-Cx0r2Jnv.js +0 -3
@@ -1,4 +1,5 @@
1
- import { i as stepSkipped, n as stepFailed, r as stepOk, t as getLinearApiKey } from "./types-DewGdaIP.js";
1
+ import { n as extractPrefix, r as init_issue_id, t as extractNumber } from "./issue-id-CAcekoIw.js";
2
+ import { i as stepSkipped, n as stepFailed, r as stepOk, t as getLinearApiKey } from "./types-BhJj1SP1.js";
2
3
  import { exec } from "child_process";
3
4
  import { promisify } from "util";
4
5
  //#region src/lib/lifecycle/close-issue.ts
@@ -15,6 +16,7 @@ import { promisify } from "util";
15
16
  * 3. Remove workflow labels (in-progress, in-review, needs-close-out)
16
17
  * 4. Add completion comment
17
18
  */
19
+ init_issue_id();
18
20
  const execAsync = promisify(exec);
19
21
  const CLOSED_OUT_LABEL = "closed-out";
20
22
  const CLOSED_OUT_COLOR = "1d4ed8";
@@ -122,8 +124,9 @@ async function closeLinearDirect(ctx, apiKey) {
122
124
  try {
123
125
  const { LinearClient } = await import("@linear/sdk");
124
126
  const client = new LinearClient({ apiKey });
125
- const issueNumber = parseInt(ctx.issueId.split("-").pop() || "0", 10);
126
- const issuePrefix = ctx.issueId.split("-")[0].toUpperCase();
127
+ const issueNumber = extractNumber(ctx.issueId);
128
+ const issuePrefix = extractPrefix(ctx.issueId);
129
+ if (issueNumber === null || issuePrefix === null) return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);
127
130
  const results = await client.issues({
128
131
  filter: {
129
132
  number: { eq: issueNumber },
@@ -157,7 +160,7 @@ async function closeRallyDirect(ctx) {
157
160
  const step = "close-issue:transition";
158
161
  if (!ctx.rally) return stepFailed(step, "Rally config not provided");
159
162
  try {
160
- const { RallyTracker } = await import("./rally-DR9x8--6.js");
163
+ const { RallyTracker } = await import("./rally-DdPvGa-w.js");
161
164
  await new RallyTracker({
162
165
  apiKey: ctx.rally.apiKey,
163
166
  server: ctx.rally.server,
@@ -210,8 +213,9 @@ async function applyLabelLinear(ctx, apiKey) {
210
213
  try {
211
214
  const { LinearClient } = await import("@linear/sdk");
212
215
  const client = new LinearClient({ apiKey });
213
- const issueNum = parseInt(ctx.issueId.split("-").pop() || "0", 10);
214
- const teamKey = ctx.issueId.split("-")[0].toUpperCase();
216
+ const issueNum = extractNumber(ctx.issueId);
217
+ const teamKey = extractPrefix(ctx.issueId);
218
+ if (issueNum === null || teamKey === null) return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);
215
219
  const results = await client.issues({
216
220
  filter: {
217
221
  number: { eq: issueNum },
@@ -246,4 +250,4 @@ async function applyLabelLinear(ctx, apiKey) {
246
250
  //#endregion
247
251
  export { closeIssue as t };
248
252
 
249
- //# sourceMappingURL=close-issue-Dr7yZmrr.js.map
253
+ //# sourceMappingURL=close-issue-CaFE0stN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-issue-CaFE0stN.js","names":[],"sources":["../src/lib/lifecycle/close-issue.ts"],"sourcesContent":["/**\n * close-issue — Transition issue to closed/done state + label management.\n *\n * Uses the IssueTracker abstraction when available, with fallback to\n * direct API calls for contexts where the tracker isn't set up (e.g.,\n * standalone CLI).\n *\n * Operations:\n * 1. Transition issue to closed state\n * 2. Add 'closed-out' label\n * 3. Remove workflow labels (in-progress, in-review, needs-close-out)\n * 4. Add completion comment\n */\n\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { IssueTracker } from '../tracker/interface.js';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { extractNumber, extractPrefix } from '../issue-id.js';\n\nconst execAsync = promisify(exec);\n\nconst CLOSED_OUT_LABEL = 'closed-out';\nconst CLOSED_OUT_COLOR = '1d4ed8';\nconst WORKFLOW_LABELS = ['in-progress', 'in-review', 'needs-close-out'];\n\n/** Options for close-issue */\nexport interface CloseIssueOptions {\n /** IssueTracker instance (preferred — uses abstraction layer) */\n tracker?: IssueTracker;\n /** Reason for closing */\n reason?: string;\n /** Comment to add when closing */\n comment?: string;\n /** Apply the closed-out label. Default: true */\n applyLabel?: boolean;\n /** Only apply label (skip state transition). Default: false */\n labelOnly?: boolean;\n}\n\n/**\n * Close an issue and manage labels.\n *\n * If a tracker is provided, uses the abstraction layer.\n * Otherwise, falls back to direct gh CLI (GitHub) or Linear SDK calls.\n */\nexport async function closeIssue(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Promise<StepResult[]> {\n const results: StepResult[] = [];\n const { applyLabel = true, labelOnly = false, comment } = opts;\n\n // Step 1: Transition to closed (unless labelOnly)\n if (!labelOnly) {\n const closeResult = opts.tracker\n ? await closeViaTracker(ctx, opts.tracker, comment)\n : await closeViaDirect(ctx, comment);\n results.push(closeResult);\n\n // If close failed, don't bother with labels\n if (!closeResult.success && !closeResult.skipped) {\n return results;\n }\n }\n\n // Step 2: Close any open PR for the feature branch (GitHub only)\n if (ctx.github) {\n const prResult = await closeGitHubPr(ctx);\n results.push(prResult);\n }\n\n // Step 3: Apply closed-out label + remove workflow labels\n if (applyLabel) {\n const labelResult = await applyClosedOutLabel(ctx, opts.tracker);\n results.push(labelResult);\n }\n\n return results;\n}\n\n/**\n * Close via IssueTracker abstraction.\n */\nasync function closeViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n try {\n await tracker.transitionIssue(ctx.issueId, 'closed');\n if (comment) {\n try {\n await tracker.addComment(ctx.issueId, comment);\n } catch {\n // Non-fatal — comment is best-effort\n }\n }\n return stepOk(step, [`Closed ${ctx.issueId} via ${tracker.name} tracker`]);\n } catch (err) {\n return stepFailed(step, `Failed to close via tracker: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close via direct API calls (fallback when no tracker configured).\n * Determines issue type from context and uses appropriate method.\n */\nasync function closeViaDirect(\n ctx: LifecycleContext,\n comment?: string,\n): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n if (ctx.github) {\n return closeGitHubDirect(ctx, comment);\n }\n\n // Rally issue\n if (ctx.rally) {\n return closeRallyDirect(ctx);\n }\n\n // Try Linear\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return closeLinearDirect(ctx, linearApiKey);\n }\n\n return stepFailed(step, 'No tracker available and cannot determine issue type');\n}\n\n/**\n * Close a GitHub issue via gh CLI.\n */\nasync function closeGitHubDirect(ctx: LifecycleContext, comment?: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.github) {\n return stepFailed(step, 'GitHub config not provided');\n }\n const { owner, repo, number } = ctx.github;\n try {\n const commentArg = comment ? ` --comment \"${comment.replace(/\"/g, '\\\\\"')}\"` : '';\n await execAsync(\n `gh issue close ${number} --repo ${owner}/${repo}${commentArg}`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed GitHub issue #${number} on ${owner}/${repo}`]);\n } catch (err) {\n return stepFailed(step, `gh issue close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Close any open GitHub PR for the feature branch.\n */\nasync function closeGitHubPr(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:close-pr';\n if (!ctx.github) {\n return stepSkipped(step, ['Not a GitHub issue']);\n }\n const { owner, repo } = ctx.github;\n const issueLower = ctx.issueId.toLowerCase();\n const branchName = `feature/${issueLower}`;\n\n try {\n const { stdout: prListRaw } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --head \"${branchName}\" --state open --json number --jq '.[0].number'`,\n { encoding: 'utf-8' },\n );\n const prNumber = prListRaw.trim();\n if (!prNumber) {\n return stepSkipped(step, ['No open PR found for branch']);\n }\n await execAsync(\n `gh pr close ${prNumber} --repo ${owner}/${repo} --comment \"Merged via Panopticon lifecycle\"`,\n { encoding: 'utf-8' },\n );\n return stepOk(step, [`Closed PR #${prNumber} on ${owner}/${repo}`]);\n } catch (err) {\n return stepSkipped(step, [`PR close failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n/**\n * Rate limit circuit breaker for Linear API.\n * After hitting a rate limit, stop all Linear API calls for COOLDOWN_MS.\n * This prevents the 24,626-call storm that exhausted Linear's 5000 req/hr limit (PAN-328).\n */\nlet _linearRateLimitUntil = 0;\nconst LINEAR_RATE_LIMIT_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour (matches Linear's 5000/hr window)\n\n/**\n * Close a Linear issue via SDK (find by identifier, transition to Done).\n */\nasync function closeLinearDirect(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:transition';\n\n // Circuit breaker: if we recently hit a rate limit, fail fast without making API calls\n if (Date.now() < _linearRateLimitUntil) {\n const remainingMin = Math.ceil((_linearRateLimitUntil - Date.now()) / 60000);\n return stepFailed(step, `Linear rate limit cooldown active (${remainingMin}min remaining). Issue will be closed during close-out ceremony.`);\n }\n\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNumber = extractNumber(ctx.issueId);\n const issuePrefix = extractPrefix(ctx.issueId);\n if (issueNumber === null || issuePrefix === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNumber },\n team: { key: { eq: issuePrefix } },\n },\n first: 1,\n });\n\n if (results.nodes.length === 0) {\n return stepFailed(step, `Issue ${ctx.issueId} not found in Linear`);\n }\n\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const doneState = states.nodes.find(s => s.name === 'Done') ||\n states.nodes.find(s => s.type === 'completed');\n if (doneState) {\n await issue.update({ stateId: doneState.id });\n }\n }\n\n return stepOk(step, [`Moved Linear issue ${ctx.issueId} to Done`]);\n } catch (err) {\n const message = (err as Error).message;\n\n // Detect rate limit errors and activate circuit breaker\n if (message.includes('Rate limit') || message.includes('rate limit') || message.includes('429')) {\n _linearRateLimitUntil = Date.now() + LINEAR_RATE_LIMIT_COOLDOWN_MS;\n console.warn(`[close-issue] Linear rate limit hit — circuit breaker activated for 1 hour`);\n return stepFailed(step, `Linear rate limit exceeded. Circuit breaker activated — no Linear API calls for 1 hour. Issue will be closed during close-out ceremony.`);\n }\n\n return stepFailed(step, `Linear close failed: ${message}`);\n }\n}\n\n/**\n * Close a Rally issue via RallyTracker.\n */\nasync function closeRallyDirect(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:transition';\n if (!ctx.rally) {\n return stepFailed(step, 'Rally config not provided');\n }\n try {\n const { RallyTracker } = await import('../tracker/rally.js');\n const tracker = new RallyTracker({\n apiKey: ctx.rally.apiKey,\n server: ctx.rally.server,\n workspace: ctx.rally.workspace,\n project: ctx.rally.project,\n });\n await tracker.transitionIssue(ctx.issueId, 'closed');\n return stepOk(step, [`Closed Rally issue ${ctx.issueId}`]);\n } catch (err) {\n return stepFailed(step, `Rally close failed: ${(err as Error).message}`);\n }\n}\n\n/**\n * Apply 'closed-out' label and remove workflow labels.\n * Uses tracker if available, falls back to direct calls.\n */\nasync function applyClosedOutLabel(\n ctx: LifecycleContext,\n tracker?: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n\n if (tracker) {\n return applyLabelViaTracker(ctx, tracker);\n }\n\n if (ctx.github) {\n return applyLabelGitHub(ctx);\n }\n\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n return applyLabelLinear(ctx, linearApiKey);\n }\n\n return stepSkipped(step, ['No tracker available for label management']);\n}\n\nasync function applyLabelViaTracker(\n ctx: LifecycleContext,\n tracker: IssueTracker,\n): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const issue = await tracker.getIssue(ctx.issueId);\n const newLabels = issue.labels.filter(l => !WORKFLOW_LABELS.includes(l));\n if (!newLabels.includes(CLOSED_OUT_LABEL)) {\n newLabels.push(CLOSED_OUT_LABEL);\n }\n await tracker.updateIssue(ctx.issueId, { labels: newLabels });\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label via ${tracker.name} tracker`]);\n } catch (err) {\n // Label management is non-fatal\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelGitHub(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-issue:label';\n if (!ctx.github) return stepSkipped(step);\n const { owner, repo, number } = ctx.github;\n\n try {\n // Ensure label exists\n await execAsync(\n `gh label create \"${CLOSED_OUT_LABEL}\" --repo ${owner}/${repo} --color \"${CLOSED_OUT_COLOR}\" --description \"Verified and closed out\" --force 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n // Add label\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"${CLOSED_OUT_LABEL}\"`,\n { encoding: 'utf-8' },\n );\n // Remove workflow labels\n for (const label of WORKFLOW_LABELS) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\" 2>/dev/null || true`,\n { encoding: 'utf-8' },\n );\n }\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on GitHub`]);\n } catch (err) {\n return stepSkipped(step, [`Label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\nasync function applyLabelLinear(ctx: LifecycleContext, apiKey: string): Promise<StepResult> {\n const step = 'close-issue:label';\n try {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey });\n\n const issueNum = extractNumber(ctx.issueId);\n const teamKey = extractPrefix(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length === 0) {\n return stepSkipped(step, ['Issue not found for label management']);\n }\n\n const issue = results.nodes[0];\n\n // Find or create closed-out label\n const labels = await client.issueLabels({ filter: { name: { eq: CLOSED_OUT_LABEL } } });\n let labelId: string;\n if (labels.nodes.length > 0) {\n labelId = labels.nodes[0].id;\n } else {\n const created = await client.createIssueLabel({ name: CLOSED_OUT_LABEL, color: `#${CLOSED_OUT_COLOR}` });\n const createdLabel = await created.issueLabel;\n labelId = createdLabel ? createdLabel.id : '';\n }\n\n if (labelId) {\n const existingLabels = await issue.labels();\n const labelIds = existingLabels.nodes.map(l => l.id);\n if (!labelIds.includes(labelId)) {\n labelIds.push(labelId);\n await issue.update({ labelIds });\n }\n }\n\n return stepOk(step, [`Applied '${CLOSED_OUT_LABEL}' label on Linear`]);\n } catch (err) {\n return stepSkipped(step, [`Linear label management failed (non-fatal): ${(err as Error).message}`]);\n }\n}\n\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { t as closeIssue } from "./close-issue-CaFE0stN.js";
2
+ export { closeIssue };
@@ -0,0 +1,2 @@
1
+ import { t as compactBeads } from "./compact-beads-CjFkteSU.js";
2
+ export { compactBeads };
@@ -1,4 +1,4 @@
1
- import { i as stepSkipped, n as stepFailed, r as stepOk } from "./types-DewGdaIP.js";
1
+ import { i as stepSkipped, n as stepFailed, r as stepOk } from "./types-BhJj1SP1.js";
2
2
  import { existsSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { exec } from "child_process";
@@ -43,4 +43,4 @@ async function compactBeads(ctx, opts = {}) {
43
43
  //#endregion
44
44
  export { compactBeads as t };
45
45
 
46
- //# sourceMappingURL=compact-beads-BCOtIIRl.js.map
46
+ //# sourceMappingURL=compact-beads-CjFkteSU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compact-beads-BCOtIIRl.js","names":[],"sources":["../src/lib/lifecycle/compact-beads.ts"],"sourcesContent":["/**\n * compact-beads — Beads compaction + git commit/push.\n *\n * Extracted from merge-agent.ts conditionalBeadsCompaction().\n * Compacts closed beads older than 30 days and commits the result.\n */\n\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\n\nconst execAsync = promisify(exec);\n\n/** Options for beads compaction */\nexport interface CompactBeadsOptions {\n /** Number of days to keep closed beads. Default: 30 */\n days?: number;\n}\n\n/**\n * Compact closed beads older than N days.\n * Idempotent — returns skip if no beads to compact.\n */\nexport async function compactBeads(\n ctx: LifecycleContext,\n opts: CompactBeadsOptions = {},\n): Promise<StepResult> {\n const { days = 30 } = opts;\n const step = 'compact-beads';\n\n // Check if bd CLI is available\n try {\n await execAsync('which bd', { encoding: 'utf-8' });\n } catch {\n return stepSkipped(step, ['bd CLI not available']);\n }\n\n // Check if .beads directory exists\n const beadsDir = join(ctx.projectPath, '.beads');\n if (!existsSync(beadsDir)) {\n return stepSkipped(step, ['No .beads directory in project']);\n }\n\n // Count old closed beads\n try {\n const { stdout: countOutput } = await execAsync(\n `bd list --status closed --json 2>/dev/null | jq '[.[] | select(.closed_at != null) | select((now - (.closed_at | fromdateiso8601)) > (${days} * 24 * 60 * 60))] | length' 2>/dev/null || echo \"0\"`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n const count = parseInt(countOutput.trim(), 10) || 0;\n if (count === 0) {\n return stepSkipped(step, ['No closed beads older than ' + days + ' days']);\n }\n\n // Run compaction\n await execAsync(`bd admin compact --days ${days}`, {\n cwd: ctx.projectPath,\n encoding: 'utf-8',\n });\n\n // Beads are ephemeral (derived from vBRIEF) — no git commit needed\n return stepOk(step, [`Compacted ${count} closed beads`]);\n } catch (err) {\n return stepFailed(step, `Beads compaction failed: ${(err as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAM,YAAY,UAAU,KAAK;;;;;AAYjC,eAAsB,aACpB,KACA,OAA4B,EAAE,EACT;CACrB,MAAM,EAAE,OAAO,OAAO;CACtB,MAAM,OAAO;AAGb,KAAI;AACF,QAAM,UAAU,YAAY,EAAE,UAAU,SAAS,CAAC;SAC5C;AACN,SAAO,YAAY,MAAM,CAAC,uBAAuB,CAAC;;AAKpD,KAAI,CAAC,WADY,KAAK,IAAI,aAAa,SAAS,CACvB,CACvB,QAAO,YAAY,MAAM,CAAC,iCAAiC,CAAC;AAI9D,KAAI;EACF,MAAM,EAAE,QAAQ,gBAAgB,MAAM,UACpC,yIAAyI,KAAK,uDAC9I;GAAE,KAAK,IAAI;GAAa,UAAU;GAAS,CAC5C;EAED,MAAM,QAAQ,SAAS,YAAY,MAAM,EAAE,GAAG,IAAI;AAClD,MAAI,UAAU,EACZ,QAAO,YAAY,MAAM,CAAC,gCAAgC,OAAO,QAAQ,CAAC;AAI5E,QAAM,UAAU,2BAA2B,QAAQ;GACjD,KAAK,IAAI;GACT,UAAU;GACX,CAAC;AAGF,SAAO,OAAO,MAAM,CAAC,aAAa,MAAM,eAAe,CAAC;UACjD,KAAK;AACZ,SAAO,WAAW,MAAM,4BAA6B,IAAc,UAAU"}
1
+ {"version":3,"file":"compact-beads-CjFkteSU.js","names":[],"sources":["../src/lib/lifecycle/compact-beads.ts"],"sourcesContent":["/**\n * compact-beads — Beads compaction + git commit/push.\n *\n * Extracted from merge-agent.ts conditionalBeadsCompaction().\n * Compacts closed beads older than 30 days and commits the result.\n */\n\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport type { LifecycleContext, StepResult } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\n\nconst execAsync = promisify(exec);\n\n/** Options for beads compaction */\nexport interface CompactBeadsOptions {\n /** Number of days to keep closed beads. Default: 30 */\n days?: number;\n}\n\n/**\n * Compact closed beads older than N days.\n * Idempotent — returns skip if no beads to compact.\n */\nexport async function compactBeads(\n ctx: LifecycleContext,\n opts: CompactBeadsOptions = {},\n): Promise<StepResult> {\n const { days = 30 } = opts;\n const step = 'compact-beads';\n\n // Check if bd CLI is available\n try {\n await execAsync('which bd', { encoding: 'utf-8' });\n } catch {\n return stepSkipped(step, ['bd CLI not available']);\n }\n\n // Check if .beads directory exists\n const beadsDir = join(ctx.projectPath, '.beads');\n if (!existsSync(beadsDir)) {\n return stepSkipped(step, ['No .beads directory in project']);\n }\n\n // Count old closed beads\n try {\n const { stdout: countOutput } = await execAsync(\n `bd list --status closed --json 2>/dev/null | jq '[.[] | select(.closed_at != null) | select((now - (.closed_at | fromdateiso8601)) > (${days} * 24 * 60 * 60))] | length' 2>/dev/null || echo \"0\"`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n const count = parseInt(countOutput.trim(), 10) || 0;\n if (count === 0) {\n return stepSkipped(step, ['No closed beads older than ' + days + ' days']);\n }\n\n // Run compaction\n await execAsync(`bd admin compact --days ${days}`, {\n cwd: ctx.projectPath,\n encoding: 'utf-8',\n });\n\n // Beads are ephemeral (derived from vBRIEF) — no git commit needed\n return stepOk(step, [`Compacted ${count} closed beads`]);\n } catch (err) {\n return stepFailed(step, `Beads compaction failed: ${(err as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAM,YAAY,UAAU,KAAK;;;;;AAYjC,eAAsB,aACpB,KACA,OAA4B,EAAE,EACT;CACrB,MAAM,EAAE,OAAO,OAAO;CACtB,MAAM,OAAO;AAGb,KAAI;AACF,QAAM,UAAU,YAAY,EAAE,UAAU,SAAS,CAAC;SAC5C;AACN,SAAO,YAAY,MAAM,CAAC,uBAAuB,CAAC;;AAKpD,KAAI,CAAC,WADY,KAAK,IAAI,aAAa,SAAS,CACvB,CACvB,QAAO,YAAY,MAAM,CAAC,iCAAiC,CAAC;AAI9D,KAAI;EACF,MAAM,EAAE,QAAQ,gBAAgB,MAAM,UACpC,yIAAyI,KAAK,uDAC9I;GAAE,KAAK,IAAI;GAAa,UAAU;GAAS,CAC5C;EAED,MAAM,QAAQ,SAAS,YAAY,MAAM,EAAE,GAAG,IAAI;AAClD,MAAI,UAAU,EACZ,QAAO,YAAY,MAAM,CAAC,gCAAgC,OAAO,QAAQ,CAAC;AAI5E,QAAM,UAAU,2BAA2B,QAAQ;GACjD,KAAK,IAAI;GACT,UAAU;GACX,CAAC;AAGF,SAAO,OAAO,MAAM,CAAC,aAAa,MAAM,eAAe,CAAC;UACjD,KAAK;AACZ,SAAO,WAAW,MAAM,4BAA6B,IAAc,UAAU"}
@@ -1,5 +1,5 @@
1
1
  import { t as __esmMin } from "./chunk-ruWRV7i3.js";
2
- import { W as init_paths, m as CONFIG_FILE } from "./paths-lMaxrYtT.js";
2
+ import { G as init_paths, m as CONFIG_FILE } from "./paths-CDJ_HsbN.js";
3
3
  import { existsSync, readFileSync, writeFileSync } from "fs";
4
4
  import { dirname, join, parse } from "path";
5
5
  import { homedir } from "os";
@@ -116,4 +116,4 @@ var init_config = __esmMin((() => {
116
116
  //#endregion
117
117
  export { init_config as a, getDevrootPath as i, getDashboardApiUrl as n, loadConfig as o, getDefaultConfig as r, saveConfig as s, findDevrootForProject as t };
118
118
 
119
- //# sourceMappingURL=config-CRzMQRgA.js.map
119
+ //# sourceMappingURL=config-BQNKsi9G.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-CRzMQRgA.js","names":["parse","parsePath"],"sources":["../src/lib/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'fs';\nimport { join, dirname, parse as parsePath } from 'path';\nimport { homedir } from 'os';\nimport { parse, stringify } from '@iarna/toml';\nimport { CONFIG_FILE } from './paths.js';\nimport type { TrackerType } from './tracker/interface.js';\n\n// Individual tracker configuration\nexport interface LinearConfig {\n type: 'linear';\n api_key_env?: string; // Env var name for API key (default: LINEAR_API_KEY)\n team?: string; // Default team prefix (e.g., 'MIN')\n}\n\nexport interface GitHubConfig {\n type: 'github';\n token_env?: string; // Env var name for token (default: GITHUB_TOKEN)\n owner: string; // Repository owner\n repo: string; // Repository name\n}\n\nexport interface GitLabConfig {\n type: 'gitlab';\n token_env?: string; // Env var name for token (default: GITLAB_TOKEN)\n project_id: string; // GitLab project ID\n}\n\nexport interface RallyConfig {\n type: 'rally';\n api_key_env?: string; // Env var name for API key (default: RALLY_API_KEY)\n server?: string; // Rally server URL (default: rally1.rallydev.com)\n workspace?: string; // Rally workspace OID (e.g., \"/workspace/12345\")\n project?: string; // Rally project OID (e.g., \"/project/67890\")\n}\n\nexport type TrackerConfigItem = LinearConfig | GitHubConfig | GitLabConfig | RallyConfig;\n\nexport interface TrackersConfig {\n primary: TrackerType;\n secondary?: TrackerType;\n linear?: LinearConfig;\n github?: GitHubConfig;\n gitlab?: GitLabConfig;\n rally?: RallyConfig;\n}\n\nexport interface RemoteFlyConfig {\n /** Fly.io app name for workspace machines */\n app?: string;\n /** Fly.io org slug */\n org?: string;\n /** Default region (e.g. 'iad') */\n region?: string;\n /** Machine size (e.g. 'shared-cpu-2x') */\n vm_size?: string;\n /** Memory in MB */\n vm_memory?: number;\n /** Docker image for workspace machines */\n image?: string;\n /** Stop machine when agent is idle */\n auto_stop?: boolean;\n /** Seconds of inactivity before stop */\n auto_stop_timeout?: number;\n /** Env var name for API token (default: FLY_API_TOKEN) */\n api_token_env?: string;\n}\n\nexport interface RemoteConfig {\n /** Enable remote workspace support */\n enabled: boolean;\n /** Remote provider type */\n provider?: 'fly';\n /** Default location for new workspaces */\n default_location?: 'local' | 'remote';\n /** Auto-hibernate idle workspaces after N minutes (0 = disabled) */\n auto_hibernate_minutes?: number;\n /** Fly.io specific configuration */\n fly?: RemoteFlyConfig;\n}\n\nexport interface ShadowConfig {\n enabled: boolean;\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\nexport interface PanopticonConfig {\n panopticon: {\n version: string;\n };\n sync: {\n backup_before_sync: boolean;\n auto_sync?: boolean;\n strategy?: 'symlink' | 'copy';\n /** Parent directory where all projects live (e.g., ~/Projects).\n * Skills are placed at <devroot>/.claude/skills/ (project level).\n * Set to null or empty string to disable devroot skill placement. */\n devroot?: string | null;\n };\n trackers: TrackersConfig;\n dashboard: {\n port: number;\n api_port: number;\n };\n traefik: {\n enabled: boolean;\n dashboard_port?: number;\n domain?: string;\n dns_sync_method?: 'wsl2hosts' | 'hosts_file' | 'dnsmasq';\n };\n remote?: RemoteConfig;\n shadow: ShadowConfig;\n}\n\nconst DEFAULT_CONFIG: PanopticonConfig = {\n panopticon: {\n version: '1.0.0',\n },\n sync: {\n backup_before_sync: true,\n auto_sync: false,\n strategy: 'symlink',\n devroot: '~/Projects',\n },\n trackers: {\n primary: 'linear',\n linear: {\n type: 'linear',\n api_key_env: 'LINEAR_API_KEY',\n },\n },\n dashboard: {\n port: 3010,\n api_port: 3011,\n },\n traefik: {\n enabled: false,\n dashboard_port: 8080,\n domain: 'pan.localhost',\n },\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Deep merge utility that recursively merges objects.\n * - Recursively merges nested objects\n * - Arrays in overrides replace defaults (not concatenated)\n * - User values take precedence over defaults\n */\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults };\n\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const defaultVal = defaults[key];\n const overrideVal = overrides[key];\n\n // Skip undefined values in overrides\n if (overrideVal === undefined) continue;\n\n // Deep merge if both values are non-array objects\n if (\n typeof defaultVal === 'object' &&\n defaultVal !== null &&\n !Array.isArray(defaultVal) &&\n typeof overrideVal === 'object' &&\n overrideVal !== null &&\n !Array.isArray(overrideVal)\n ) {\n result[key] = deepMerge(defaultVal, overrideVal as any);\n } else {\n // For primitives, arrays, or null - override wins\n result[key] = overrideVal as T[keyof T];\n }\n }\n\n return result;\n}\n\nexport function loadConfig(): PanopticonConfig {\n if (!existsSync(CONFIG_FILE)) {\n return DEFAULT_CONFIG;\n }\n\n try {\n const content = readFileSync(CONFIG_FILE, 'utf8');\n const parsed = parse(content) as unknown as Partial<PanopticonConfig>;\n return deepMerge(DEFAULT_CONFIG, parsed);\n } catch (error) {\n console.error('Warning: Failed to parse config, using defaults');\n return DEFAULT_CONFIG;\n }\n}\n\nexport function saveConfig(config: PanopticonConfig): void {\n const content = stringify(config as any);\n writeFileSync(CONFIG_FILE, content, 'utf8');\n}\n\nexport function getDefaultConfig(): PanopticonConfig {\n return JSON.parse(JSON.stringify(DEFAULT_CONFIG));\n}\n\n/**\n * Get the dashboard API base URL from config.\n * Reads from DASHBOARD_URL env var first, then config file, then defaults.\n */\nexport function getDashboardApiUrl(): string {\n if (process.env.DASHBOARD_URL) return process.env.DASHBOARD_URL;\n const config = loadConfig();\n const port = config.dashboard?.api_port || 3011;\n return `http://localhost:${port}`;\n}\n\n/**\n * Get the resolved devroot path from config.\n * Returns null if devroot is disabled (set to null or empty string).\n * Resolves ~ to home directory and validates the directory exists.\n */\nexport function getDevrootPath(): string | null {\n const config = loadConfig();\n const devroot = config.sync?.devroot;\n\n if (!devroot) return null;\n\n // Resolve ~ to home directory\n const resolved = devroot.startsWith('~/')\n ? join(homedir(), devroot.slice(2))\n : devroot;\n\n if (!existsSync(resolved)) return null;\n\n return resolved;\n}\n\n/**\n * Find the devroot for a given project path.\n * Tries config first, then walks up from projectPath looking for .claude/ directory.\n * Returns the project path itself as last resort.\n */\nexport function findDevrootForProject(projectPath: string): string {\n // 1. Explicit config takes priority\n const configured = getDevrootPath();\n if (configured) return configured;\n\n // 2. Walk up from project path to find nearest .claude/ directory\n let dir = projectPath;\n const root = parsePath(dir).root;\n while (dir !== root && dir !== homedir()) {\n const parent = dirname(dir);\n if (parent === dir) break;\n if (existsSync(join(parent, '.claude'))) {\n return parent;\n }\n dir = parent;\n }\n\n // 3. Fallback to project path itself\n return projectPath;\n}\n\n"],"mappings":";;;;;;;;;;;;;AAiKA,SAAS,UAA4B,UAAa,WAA0B;CAC1E,MAAM,SAAS,EAAE,GAAG,UAAU;AAE9B,MAAK,MAAM,OAAO,OAAO,KAAK,UAAU,EAAiB;EACvD,MAAM,aAAa,SAAS;EAC5B,MAAM,cAAc,UAAU;AAG9B,MAAI,gBAAgB,KAAA,EAAW;AAG/B,MACE,OAAO,eAAe,YACtB,eAAe,QACf,CAAC,MAAM,QAAQ,WAAW,IAC1B,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,CAAC,MAAM,QAAQ,YAAY,CAE3B,QAAO,OAAO,UAAU,YAAY,YAAmB;MAGvD,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAgB,aAA+B;AAC7C,KAAI,CAAC,WAAW,YAAY,CAC1B,QAAO;AAGT,KAAI;AAGF,SAAO,UAAU,gBADFA,QADC,aAAa,aAAa,OAAO,CACpB,CACW;UACjC,OAAO;AACd,UAAQ,MAAM,kDAAkD;AAChE,SAAO;;;AAIX,SAAgB,WAAW,QAAgC;AAEzD,eAAc,aADE,UAAU,OAAc,EACJ,OAAO;;AAG7C,SAAgB,mBAAqC;AACnD,QAAO,KAAK,MAAM,KAAK,UAAU,eAAe,CAAC;;;;;;AAOnD,SAAgB,qBAA6B;AAC3C,KAAI,QAAQ,IAAI,cAAe,QAAO,QAAQ,IAAI;AAGlD,QAAO,oBAFQ,YAAY,CACP,WAAW,YAAY;;;;;;;AAS7C,SAAgB,iBAAgC;CAE9C,MAAM,UADS,YAAY,CACJ,MAAM;AAE7B,KAAI,CAAC,QAAS,QAAO;CAGrB,MAAM,WAAW,QAAQ,WAAW,KAAK,GACrC,KAAK,SAAS,EAAE,QAAQ,MAAM,EAAE,CAAC,GACjC;AAEJ,KAAI,CAAC,WAAW,SAAS,CAAE,QAAO;AAElC,QAAO;;;;;;;AAQT,SAAgB,sBAAsB,aAA6B;CAEjE,MAAM,aAAa,gBAAgB;AACnC,KAAI,WAAY,QAAO;CAGvB,IAAI,MAAM;CACV,MAAM,OAAOC,MAAU,IAAI,CAAC;AAC5B,QAAO,QAAQ,QAAQ,QAAQ,SAAS,EAAE;EACxC,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,MAAI,WAAW,KAAK,QAAQ,UAAU,CAAC,CACrC,QAAO;AAET,QAAM;;AAIR,QAAO;;;;aAzQgC;AAkHnC,kBAAmC;EACvC,YAAY,EACV,SAAS,SACV;EACD,MAAM;GACJ,oBAAoB;GACpB,WAAW;GACX,UAAU;GACV,SAAS;GACV;EACD,UAAU;GACR,SAAS;GACT,QAAQ;IACN,MAAM;IACN,aAAa;IACd;GACF;EACD,WAAW;GACT,MAAM;GACN,UAAU;GACX;EACD,SAAS;GACP,SAAS;GACT,gBAAgB;GAChB,QAAQ;GACT;EACD,QAAQ;GACN,SAAS;GACT,UAAU;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACR;GACF;EACF"}
1
+ {"version":3,"file":"config-BQNKsi9G.js","names":["parse","parsePath"],"sources":["../src/lib/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'fs';\nimport { join, dirname, parse as parsePath } from 'path';\nimport { homedir } from 'os';\nimport { parse, stringify } from '@iarna/toml';\nimport { CONFIG_FILE } from './paths.js';\nimport type { TrackerType } from './tracker/interface.js';\n\n// Individual tracker configuration\nexport interface LinearConfig {\n type: 'linear';\n api_key_env?: string; // Env var name for API key (default: LINEAR_API_KEY)\n team?: string; // Default team prefix (e.g., 'MIN')\n}\n\nexport interface GitHubConfig {\n type: 'github';\n token_env?: string; // Env var name for token (default: GITHUB_TOKEN)\n owner: string; // Repository owner\n repo: string; // Repository name\n}\n\nexport interface GitLabConfig {\n type: 'gitlab';\n token_env?: string; // Env var name for token (default: GITLAB_TOKEN)\n project_id: string; // GitLab project ID\n}\n\nexport interface RallyConfig {\n type: 'rally';\n api_key_env?: string; // Env var name for API key (default: RALLY_API_KEY)\n server?: string; // Rally server URL (default: rally1.rallydev.com)\n workspace?: string; // Rally workspace OID (e.g., \"/workspace/12345\")\n project?: string; // Rally project OID (e.g., \"/project/67890\")\n}\n\nexport type TrackerConfigItem = LinearConfig | GitHubConfig | GitLabConfig | RallyConfig;\n\nexport interface TrackersConfig {\n primary: TrackerType;\n secondary?: TrackerType;\n linear?: LinearConfig;\n github?: GitHubConfig;\n gitlab?: GitLabConfig;\n rally?: RallyConfig;\n}\n\nexport interface RemoteFlyConfig {\n /** Fly.io app name for workspace machines */\n app?: string;\n /** Fly.io org slug */\n org?: string;\n /** Default region (e.g. 'iad') */\n region?: string;\n /** Machine size (e.g. 'shared-cpu-2x') */\n vm_size?: string;\n /** Memory in MB */\n vm_memory?: number;\n /** Docker image for workspace machines */\n image?: string;\n /** Stop machine when agent is idle */\n auto_stop?: boolean;\n /** Seconds of inactivity before stop */\n auto_stop_timeout?: number;\n /** Env var name for API token (default: FLY_API_TOKEN) */\n api_token_env?: string;\n}\n\nexport interface RemoteConfig {\n /** Enable remote workspace support */\n enabled: boolean;\n /** Remote provider type */\n provider?: 'fly';\n /** Default location for new workspaces */\n default_location?: 'local' | 'remote';\n /** Auto-hibernate idle workspaces after N minutes (0 = disabled) */\n auto_hibernate_minutes?: number;\n /** Fly.io specific configuration */\n fly?: RemoteFlyConfig;\n}\n\nexport interface ShadowConfig {\n enabled: boolean;\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\nexport interface PanopticonConfig {\n panopticon: {\n version: string;\n };\n sync: {\n backup_before_sync: boolean;\n auto_sync?: boolean;\n strategy?: 'symlink' | 'copy';\n /** Parent directory where all projects live (e.g., ~/Projects).\n * Skills are placed at <devroot>/.claude/skills/ (project level).\n * Set to null or empty string to disable devroot skill placement. */\n devroot?: string | null;\n };\n trackers: TrackersConfig;\n dashboard: {\n port: number;\n api_port: number;\n };\n traefik: {\n enabled: boolean;\n dashboard_port?: number;\n domain?: string;\n dns_sync_method?: 'wsl2hosts' | 'hosts_file' | 'dnsmasq';\n };\n remote?: RemoteConfig;\n shadow: ShadowConfig;\n}\n\nconst DEFAULT_CONFIG: PanopticonConfig = {\n panopticon: {\n version: '1.0.0',\n },\n sync: {\n backup_before_sync: true,\n auto_sync: false,\n strategy: 'symlink',\n devroot: '~/Projects',\n },\n trackers: {\n primary: 'linear',\n linear: {\n type: 'linear',\n api_key_env: 'LINEAR_API_KEY',\n },\n },\n dashboard: {\n port: 3010,\n api_port: 3011,\n },\n traefik: {\n enabled: false,\n dashboard_port: 8080,\n domain: 'pan.localhost',\n },\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Deep merge utility that recursively merges objects.\n * - Recursively merges nested objects\n * - Arrays in overrides replace defaults (not concatenated)\n * - User values take precedence over defaults\n */\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults };\n\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const defaultVal = defaults[key];\n const overrideVal = overrides[key];\n\n // Skip undefined values in overrides\n if (overrideVal === undefined) continue;\n\n // Deep merge if both values are non-array objects\n if (\n typeof defaultVal === 'object' &&\n defaultVal !== null &&\n !Array.isArray(defaultVal) &&\n typeof overrideVal === 'object' &&\n overrideVal !== null &&\n !Array.isArray(overrideVal)\n ) {\n result[key] = deepMerge(defaultVal, overrideVal as any);\n } else {\n // For primitives, arrays, or null - override wins\n result[key] = overrideVal as T[keyof T];\n }\n }\n\n return result;\n}\n\nexport function loadConfig(): PanopticonConfig {\n if (!existsSync(CONFIG_FILE)) {\n return DEFAULT_CONFIG;\n }\n\n try {\n const content = readFileSync(CONFIG_FILE, 'utf8');\n const parsed = parse(content) as unknown as Partial<PanopticonConfig>;\n return deepMerge(DEFAULT_CONFIG, parsed);\n } catch (error) {\n console.error('Warning: Failed to parse config, using defaults');\n return DEFAULT_CONFIG;\n }\n}\n\nexport function saveConfig(config: PanopticonConfig): void {\n const content = stringify(config as any);\n writeFileSync(CONFIG_FILE, content, 'utf8');\n}\n\nexport function getDefaultConfig(): PanopticonConfig {\n return JSON.parse(JSON.stringify(DEFAULT_CONFIG));\n}\n\n/**\n * Get the dashboard API base URL from config.\n * Reads from DASHBOARD_URL env var first, then config file, then defaults.\n */\nexport function getDashboardApiUrl(): string {\n if (process.env.DASHBOARD_URL) return process.env.DASHBOARD_URL;\n const config = loadConfig();\n const port = config.dashboard?.api_port || 3011;\n return `http://localhost:${port}`;\n}\n\n/**\n * Get the resolved devroot path from config.\n * Returns null if devroot is disabled (set to null or empty string).\n * Resolves ~ to home directory and validates the directory exists.\n */\nexport function getDevrootPath(): string | null {\n const config = loadConfig();\n const devroot = config.sync?.devroot;\n\n if (!devroot) return null;\n\n // Resolve ~ to home directory\n const resolved = devroot.startsWith('~/')\n ? join(homedir(), devroot.slice(2))\n : devroot;\n\n if (!existsSync(resolved)) return null;\n\n return resolved;\n}\n\n/**\n * Find the devroot for a given project path.\n * Tries config first, then walks up from projectPath looking for .claude/ directory.\n * Returns the project path itself as last resort.\n */\nexport function findDevrootForProject(projectPath: string): string {\n // 1. Explicit config takes priority\n const configured = getDevrootPath();\n if (configured) return configured;\n\n // 2. Walk up from project path to find nearest .claude/ directory\n let dir = projectPath;\n const root = parsePath(dir).root;\n while (dir !== root && dir !== homedir()) {\n const parent = dirname(dir);\n if (parent === dir) break;\n if (existsSync(join(parent, '.claude'))) {\n return parent;\n }\n dir = parent;\n }\n\n // 3. Fallback to project path itself\n return projectPath;\n}\n\n"],"mappings":";;;;;;;;;;;;;AAiKA,SAAS,UAA4B,UAAa,WAA0B;CAC1E,MAAM,SAAS,EAAE,GAAG,UAAU;AAE9B,MAAK,MAAM,OAAO,OAAO,KAAK,UAAU,EAAiB;EACvD,MAAM,aAAa,SAAS;EAC5B,MAAM,cAAc,UAAU;AAG9B,MAAI,gBAAgB,KAAA,EAAW;AAG/B,MACE,OAAO,eAAe,YACtB,eAAe,QACf,CAAC,MAAM,QAAQ,WAAW,IAC1B,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,CAAC,MAAM,QAAQ,YAAY,CAE3B,QAAO,OAAO,UAAU,YAAY,YAAmB;MAGvD,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAgB,aAA+B;AAC7C,KAAI,CAAC,WAAW,YAAY,CAC1B,QAAO;AAGT,KAAI;AAGF,SAAO,UAAU,gBADFA,QADC,aAAa,aAAa,OAAO,CACpB,CACW;UACjC,OAAO;AACd,UAAQ,MAAM,kDAAkD;AAChE,SAAO;;;AAIX,SAAgB,WAAW,QAAgC;AAEzD,eAAc,aADE,UAAU,OAAc,EACJ,OAAO;;AAG7C,SAAgB,mBAAqC;AACnD,QAAO,KAAK,MAAM,KAAK,UAAU,eAAe,CAAC;;;;;;AAOnD,SAAgB,qBAA6B;AAC3C,KAAI,QAAQ,IAAI,cAAe,QAAO,QAAQ,IAAI;AAGlD,QAAO,oBAFQ,YAAY,CACP,WAAW,YAAY;;;;;;;AAS7C,SAAgB,iBAAgC;CAE9C,MAAM,UADS,YAAY,CACJ,MAAM;AAE7B,KAAI,CAAC,QAAS,QAAO;CAGrB,MAAM,WAAW,QAAQ,WAAW,KAAK,GACrC,KAAK,SAAS,EAAE,QAAQ,MAAM,EAAE,CAAC,GACjC;AAEJ,KAAI,CAAC,WAAW,SAAS,CAAE,QAAO;AAElC,QAAO;;;;;;;AAQT,SAAgB,sBAAsB,aAA6B;CAEjE,MAAM,aAAa,gBAAgB;AACnC,KAAI,WAAY,QAAO;CAGvB,IAAI,MAAM;CACV,MAAM,OAAOC,MAAU,IAAI,CAAC;AAC5B,QAAO,QAAQ,QAAQ,QAAQ,SAAS,EAAE;EACxC,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,MAAI,WAAW,KAAK,QAAQ,UAAU,CAAC,CACrC,QAAO;AAET,QAAM;;AAIR,QAAO;;;;aAzQgC;AAkHnC,kBAAmC;EACvC,YAAY,EACV,SAAS,SACV;EACD,MAAM;GACJ,oBAAoB;GACpB,WAAW;GACX,UAAU;GACV,SAAS;GACV;EACD,UAAU;GACR,SAAS;GACT,QAAQ;IACN,MAAM;IACN,aAAa;IACd;GACF;EACD,WAAW;GACT,MAAM;GACN,UAAU;GACX;EACD,SAAS;GACP,SAAS;GACT,gBAAgB;GAChB,QAAQ;GACT;EACD,QAAQ;GACN,SAAS;GACT,UAAU;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACR;GACF;EACF"}
@@ -1,3 +1,3 @@
1
- import { a as init_config, n as getDashboardApiUrl } from "./config-CRzMQRgA.js";
1
+ import { a as init_config, n as getDashboardApiUrl } from "./config-BQNKsi9G.js";
2
2
  init_config();
3
3
  export { getDashboardApiUrl };
@@ -725,4 +725,4 @@ var init_config_yaml = __esmMin((() => {
725
725
  //#endregion
726
726
  export { getModelCapability as a, MODEL_CAPABILITIES as i, init_config_yaml as n, init_model_capabilities as o, loadConfig as r, hasGlobalConfig as t };
727
727
 
728
- //# sourceMappingURL=config-yaml-BgOACZAB.js.map
728
+ //# sourceMappingURL=config-yaml-DGbLSMCa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-yaml-BgOACZAB.js","names":[],"sources":["../src/lib/model-capabilities.ts","../src/lib/config-yaml.ts"],"sourcesContent":["/**\n * Model Capability Matrix\n *\n * Defines capability scores for each model across different skill dimensions.\n * This enables intelligent model selection based on what the user has enabled\n * rather than static presets.\n *\n * Scores: 0-100 where 100 = best in class\n * Cost: $/1M tokens (input + output average)\n *\n * Last updated: 2026-01-29\n * Sources:\n * - SWE-bench Verified leaderboard (vals.ai)\n * - LiveCodeBench v6\n * - LMSYS Chatbot Arena\n * - Artificial Analysis\n * - Official provider pricing pages\n */\n\nimport { ModelId } from './settings.js';\n\n/**\n * Model ID deprecation mapping\n *\n * Maps deprecated model IDs to their current replacements.\n * When a model ID changes (e.g., claude-opus-4-5 → claude-opus-4-6),\n * add the mapping here to enable automatic migration.\n *\n * Strategy: Single-hop only. When a newer version arrives (e.g., 4-7),\n * add both old→new mappings (4-5→4-7 and 4-6→4-7).\n */\nexport const MODEL_DEPRECATIONS: Record<string, ModelId> = {\n 'claude-opus-4-5': 'claude-opus-4-6',\n 'claude-sonnet-4-5': 'claude-sonnet-4-6',\n};\n\n/**\n * Resolve a model ID to its current version\n *\n * If the model ID is deprecated, returns the replacement.\n * Otherwise, returns the model ID unchanged.\n *\n * @param modelId - Model ID to resolve (may be deprecated)\n * @returns Current model ID\n */\nexport function resolveModelId(modelId: string): ModelId {\n return (MODEL_DEPRECATIONS[modelId] as ModelId) || (modelId as ModelId);\n}\n\n/**\n * Skill dimensions that models are evaluated on\n */\nexport type SkillDimension =\n | 'code-generation' // Writing new code\n | 'code-review' // Finding issues in code\n | 'debugging' // Root cause analysis\n | 'planning' // Architecture and strategy\n | 'documentation' // Writing docs, PRDs\n | 'testing' // Test generation and analysis\n | 'security' // Security analysis\n | 'performance' // Performance optimization\n | 'synthesis' // Combining information\n | 'speed' // Response latency\n | 'context-length'; // Max context window\n\n/**\n * Capability profile for a single model\n */\nexport interface ModelCapability {\n /** Model identifier */\n model: ModelId;\n /** Provider for this model */\n provider: 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi' | 'minimax' | 'openrouter';\n /** Display name */\n displayName: string;\n /** Cost per 1M tokens (average of input/output) in USD */\n costPer1MTokens: number;\n /** Capability scores (0-100) for each skill dimension */\n skills: Record<SkillDimension, number>;\n /** Context window size in tokens */\n contextWindow: number;\n /** Additional notes about this model's strengths */\n notes?: string;\n}\n\n/**\n * Master capability database\n *\n * Scores are based on:\n * - Public benchmarks (HumanEval, SWE-bench, MBPP)\n * - Community consensus\n * - Practical experience\n *\n * These are baseline scores - run Kimi 2.5 research to refine.\n */\nexport const MODEL_CAPABILITIES: Record<ModelId, ModelCapability> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ANTHROPIC MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'claude-opus-4-6': {\n model: 'claude-opus-4-6',\n provider: 'anthropic',\n displayName: 'Claude Opus 4.6',\n costPer1MTokens: 45.0, // $5 in / $25 out → same pricing as 4.5\n contextWindow: 200000, // 1M available via opt-in beta, but we use 200K\n skills: {\n 'code-generation': 96, // 80.9% SWE-bench (first >80%), 89.4% Aider Polyglot\n 'code-review': 98,\n debugging: 97,\n planning: 99, // User confirms: \"Opus 4.6 planning for sure\"\n documentation: 95,\n testing: 92,\n security: 98, // Best for security review\n performance: 90,\n synthesis: 98, // Best for combining info across domains\n speed: 40, // Slower but 76% more token efficient\n 'context-length': 95,\n },\n notes: 'Successor to Opus 4.5. Same pricing, 1M context available (opt-in beta). Best for planning, security, complex reasoning.',\n },\n\n 'claude-sonnet-4-6': {\n model: 'claude-sonnet-4-6',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.6',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 94,\n 'code-review': 94,\n debugging: 92,\n planning: 90,\n documentation: 92,\n testing: 92,\n security: 88,\n performance: 88,\n synthesis: 90,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Successor to Sonnet 4.5. Same pricing tier. Improved coding and reasoning.',\n },\n\n 'claude-sonnet-4-5': {\n model: 'claude-sonnet-4-5',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.5',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 92, // 77.2% SWE-bench (82% parallel), beats GPT-5 Codex (74.5%)\n 'code-review': 92,\n debugging: 90,\n planning: 88,\n documentation: 90, // 100% AIME with Python\n testing: 90, // 50% Terminal-Bench, 61.4% OSWorld\n security: 85,\n performance: 85,\n synthesis: 88,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Best value: 77.2% SWE-bench at 1/5th Opus cost. Beats GPT-5 Codex.',\n },\n\n 'claude-haiku-4-5': {\n model: 'claude-haiku-4-5',\n provider: 'anthropic',\n displayName: 'Claude Haiku 4.5',\n costPer1MTokens: 4.0, // $0.80 in / $4 out → avg ~$2.4\n contextWindow: 200000,\n skills: {\n 'code-generation': 75,\n 'code-review': 72,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 60,\n performance: 65,\n synthesis: 68,\n speed: 95, // Fastest Anthropic\n 'context-length': 95,\n },\n notes: 'Fast and cheap, good for simple tasks and exploration',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // OPENAI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gpt-5.2-codex': {\n model: 'gpt-5.2-codex',\n provider: 'openai',\n displayName: 'GPT-5.2 Codex',\n costPer1MTokens: 75.0, // Premium tier ~$75/M\n contextWindow: 128000,\n skills: {\n 'code-generation': 95, // 80% SWE-bench Verified, 55.6% SWE-bench Pro\n 'code-review': 90,\n debugging: 92, // 92.4% GPQA Diamond\n planning: 88,\n documentation: 85,\n testing: 90,\n security: 85,\n performance: 88, // 52.9% ARC-AGI-2 (best reasoning)\n synthesis: 88, // 100% AIME 2025 without tools\n speed: 55,\n 'context-length': 75,\n },\n notes: 'Premium coding: 80% SWE-bench. Best raw reasoning (52.9% ARC-AGI-2). Expensive.',\n },\n\n 'o3-deep-research': {\n model: 'o3-deep-research',\n provider: 'openai',\n displayName: 'O3 Deep Research',\n costPer1MTokens: 100.0, // Expensive reasoning model\n contextWindow: 200000,\n skills: {\n 'code-generation': 85,\n 'code-review': 95,\n debugging: 98, // Best for debugging\n planning: 95,\n documentation: 88,\n testing: 85,\n security: 92,\n performance: 92,\n synthesis: 95,\n speed: 20, // Very slow (reasoning chains)\n 'context-length': 95,\n },\n notes: 'Deep reasoning model, excellent for complex debugging and analysis',\n },\n\n 'gpt-4o': {\n model: 'gpt-4o',\n provider: 'openai',\n displayName: 'GPT-4o',\n costPer1MTokens: 15.0, // $5 in / $15 out\n contextWindow: 128000,\n skills: {\n 'code-generation': 88,\n 'code-review': 85,\n debugging: 85,\n planning: 82,\n documentation: 88,\n testing: 82,\n security: 78,\n performance: 80,\n synthesis: 85,\n speed: 75,\n 'context-length': 75,\n },\n notes: 'Good all-rounder, competitive with Sonnet',\n },\n\n 'gpt-4o-mini': {\n model: 'gpt-4o-mini',\n provider: 'openai',\n displayName: 'GPT-4o Mini',\n costPer1MTokens: 1.0, // Very cheap\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 60,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 60,\n synthesis: 62,\n speed: 92,\n 'context-length': 75,\n },\n notes: 'Budget option, good for simple tasks',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // GOOGLE MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gemini-3-pro-preview': {\n model: 'gemini-3-pro-preview',\n provider: 'google',\n displayName: 'Gemini 3 Pro',\n costPer1MTokens: 12.0, // $4.2 in / $18.9 out\n contextWindow: 1000000, // 1M context!\n skills: {\n 'code-generation': 90, // 2439 Elo LiveCodeBench Pro (first >1500 on LMArena)\n 'code-review': 88,\n debugging: 85,\n planning: 85,\n documentation: 88,\n testing: 85, // ~95% AIME 2025\n security: 78,\n performance: 85, // Strong multimodal\n synthesis: 90, // Best for combining large codebases\n speed: 80,\n 'context-length': 100, // Best context - 1M tokens\n },\n notes: 'First to exceed 1500 Elo on LMArena. Best for large codebase analysis with 1M context.',\n },\n\n 'gemini-3-flash-preview': {\n model: 'gemini-3-flash-preview',\n provider: 'google',\n displayName: 'Gemini 3 Flash',\n costPer1MTokens: 0.5, // Very cheap\n contextWindow: 1000000,\n skills: {\n 'code-generation': 75,\n 'code-review': 70,\n debugging: 68,\n planning: 62,\n documentation: 72,\n testing: 68,\n security: 55,\n performance: 65,\n synthesis: 70,\n speed: 98, // Fastest overall\n 'context-length': 100,\n },\n notes: 'Extremely fast and cheap, huge context, great for exploration',\n },\n\n 'gemini-2.5-pro': {\n model: 'gemini-2.5-pro',\n provider: 'google',\n displayName: 'Gemini 2.5 Pro',\n costPer1MTokens: 12.0,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 92,\n 'code-review': 90,\n debugging: 88,\n planning: 88,\n documentation: 90,\n testing: 87,\n security: 82,\n performance: 88,\n synthesis: 92,\n speed: 75,\n 'context-length': 100,\n },\n notes: 'Advanced reasoning and code capabilities with 1M context',\n },\n\n 'gemini-2.5-flash': {\n model: 'gemini-2.5-flash',\n provider: 'google',\n displayName: 'Gemini 2.5 Flash',\n costPer1MTokens: 0.6,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 78,\n 'code-review': 73,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 58,\n performance: 68,\n synthesis: 73,\n speed: 95,\n 'context-length': 100,\n },\n notes: 'Fast and efficient with large context support',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Z.AI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'glm-4.7': {\n model: 'glm-4.7',\n provider: 'zai',\n displayName: 'GLM 4.7',\n costPer1MTokens: 5.0,\n contextWindow: 200000, // 200K context, 128K output\n skills: {\n 'code-generation': 88, // 73.8% SWE-bench, 84.9 LiveCodeBench v6 (open-source SOTA)\n 'code-review': 85,\n debugging: 85, // Strong debugging with Interleaved Thinking\n planning: 82, // 95.7% AIME 2025 (beats Gemini 3 & GPT-5.1)\n documentation: 80,\n testing: 82, // 87.4 τ²-Bench (SOTA for tool use)\n security: 72,\n performance: 78,\n synthesis: 85, // Preserved Thinking retains context across turns\n speed: 80,\n 'context-length': 95, // 200K context\n },\n notes: 'Top open-source for agentic coding. 73.8% SWE-bench, best tool use. 400B params with Interleaved Thinking.',\n },\n\n 'glm-4.7-flash': {\n model: 'glm-4.7-flash',\n provider: 'zai',\n displayName: 'GLM 4.7 Flash',\n costPer1MTokens: 1.5,\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 62,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 62,\n synthesis: 65,\n speed: 92, // Fast inference\n 'context-length': 75,\n },\n notes: 'Fast and affordable. Good for quick iterations and exploration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // KIMI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'kimi-k2': {\n model: 'kimi-k2',\n provider: 'kimi',\n displayName: 'Kimi K2',\n costPer1MTokens: 1.4, // $0.16 in / $2.63 out → very cheap\n contextWindow: 131000,\n skills: {\n 'code-generation': 82, // 65.8% SWE-bench (beats GPT-4.1 at 54.6%)\n 'code-review': 80,\n debugging: 78,\n planning: 75,\n documentation: 80,\n testing: 75,\n security: 70,\n performance: 72,\n synthesis: 78,\n speed: 80,\n 'context-length': 75,\n },\n notes: 'Strong value: 65.8% SWE-bench at very low cost. Good for routine tasks.',\n },\n\n 'kimi-k2.5': {\n model: 'kimi-k2.5',\n provider: 'kimi',\n displayName: 'Kimi K2.5',\n costPer1MTokens: 8.0, // ~5.1x cheaper than GPT-5.2\n contextWindow: 256000,\n skills: {\n 'code-generation': 92, // 76.8% SWE-bench, 85 LiveCodeBench v6\n 'code-review': 90,\n debugging: 90, // Strong analytical capabilities\n planning: 88, // User confirms \"highly capable\"\n documentation: 88,\n testing: 88, // 92% coding accuracy\n security: 82,\n performance: 85,\n synthesis: 92, // Can coordinate 100 sub-agents, 1500 tool calls\n speed: 75, // MoE: 1T total params, 32B active\n 'context-length': 98, // 256K context\n },\n notes: 'Best open-source coding model. 5x cheaper than GPT-5.2. Excellent for frontend dev and multi-agent orchestration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // MINIMAX MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'minimax-m2.7': {\n model: 'minimax-m2.7',\n provider: 'minimax',\n displayName: 'MiniMax M2.7',\n costPer1MTokens: 1.5, // $0.30/M in + $1.20/M out, blended ~$0.06/M with auto-cache\n contextWindow: 204800,\n skills: {\n 'code-generation': 90, // 56.22% SWE-Pro (Opus ~57-58%), 55.6% VIBE-Pro\n 'code-review': 88,\n debugging: 88, // 57.0% Terminal Bench 2\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90, // Self-evolving agent, 97% skill adherence on complex tasks\n speed: 80, // 10B active params (MoE)\n 'context-length': 92, // 204K context\n },\n notes: '10B active params, 56.22% SWE-Pro, 1495 ELO GDPval-AA. $0.06/M blended with auto-cache.',\n },\n\n 'minimax-m2.7-highspeed': {\n model: 'minimax-m2.7-highspeed',\n provider: 'minimax',\n displayName: 'MiniMax M2.7 Highspeed',\n costPer1MTokens: 1.5, // Same pricing as M2.7\n contextWindow: 204800,\n skills: {\n 'code-generation': 90,\n 'code-review': 88,\n debugging: 88,\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90,\n speed: 92, // 100 tps, 3x faster than Opus\n 'context-length': 92,\n },\n notes: 'Identical quality to M2.7, 100 tps (3x Opus speed). Best for high-throughput agent work.',\n },\n};\n\n/**\n * Get capability profile for a model\n */\nexport function getModelCapability(model: ModelId): ModelCapability {\n return MODEL_CAPABILITIES[model];\n}\n\n/**\n * Get all models sorted by a specific skill (descending)\n */\nexport function getModelsBySkill(skill: SkillDimension): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[b].skills[skill] - MODEL_CAPABILITIES[a].skills[skill]\n );\n}\n\n/**\n * Get all models for a provider\n */\nexport function getModelsForProvider(\n provider: ModelCapability['provider']\n): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).filter(\n (model) => MODEL_CAPABILITIES[model].provider === provider\n );\n}\n\n/**\n * Get cheapest models (sorted by cost ascending)\n */\nexport function getCheapestModels(): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[a].costPer1MTokens - MODEL_CAPABILITIES[b].costPer1MTokens\n );\n}\n\n/**\n * Calculate cost efficiency score for a skill\n * Higher = better value (skill score / cost)\n */\nexport function getValueScore(model: ModelId, skill: SkillDimension): number {\n const cap = MODEL_CAPABILITIES[model];\n return cap.skills[skill] / Math.log10(cap.costPer1MTokens + 1);\n}\n\n/**\n * Get all skill dimensions\n */\nexport function getAllSkillDimensions(): SkillDimension[] {\n return [\n 'code-generation',\n 'code-review',\n 'debugging',\n 'planning',\n 'documentation',\n 'testing',\n 'security',\n 'performance',\n 'synthesis',\n 'speed',\n 'context-length',\n ];\n}\n","/**\n * YAML Configuration Loader\n *\n * Loads and merges configuration from:\n * 1. Global config: ~/.panopticon/config.yaml\n * 2. Per-project config: .pan.yaml (project root, falls back to .panopticon.yaml with deprecation warning)\n *\n * Uses smart (capability-based) model selection - no legacy presets.\n */\n\nimport { readFileSync, existsSync, writeFileSync, copyFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { WorkTypeId } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { ModelProvider } from './model-fallback.js';\nimport { MODEL_DEPRECATIONS, resolveModelId } from './model-capabilities.js';\n\n/**\n * Provider configuration (enable/disable + API keys)\n */\nexport interface ProviderConfig {\n /** Whether this provider is enabled */\n enabled: boolean;\n /** API key (optional, can use env var) */\n api_key?: string;\n}\n\n/**\n * Shadow mode configuration\n */\nexport interface ShadowConfig {\n /** Global shadow mode default */\n enabled?: boolean;\n\n /** Per-tracker overrides */\n trackers?: {\n linear?: boolean;\n github?: boolean;\n gitlab?: boolean;\n rally?: boolean;\n };\n}\n\n/**\n * Complete configuration structure (YAML schema)\n */\nexport interface YamlConfig {\n /** Model configuration */\n models?: {\n /** Provider enable/disable and API keys */\n providers?: {\n anthropic?: ProviderConfig | boolean;\n openai?: ProviderConfig | boolean;\n google?: ProviderConfig | boolean;\n zai?: ProviderConfig | boolean;\n kimi?: ProviderConfig | boolean;\n minimax?: ProviderConfig | boolean;\n openrouter?: ProviderConfig | boolean;\n };\n\n /** Per-work-type overrides (explicit model for specific tasks) */\n overrides?: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level (1-4) */\n gemini_thinking_level?: 1 | 2 | 3 | 4;\n };\n\n /** OpenRouter-specific configuration */\n openrouter?: {\n /** Favorite model IDs to show in ModelPicker */\n favorites?: string[];\n };\n\n /** Legacy API keys (for backward compatibility) */\n api_keys?: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** Tracker API keys (override environment variables) */\n tracker_keys?: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow?: ShadowConfig;\n\n /** Multi-tool sync configuration */\n tools?: {\n /**\n * Additional AI tools to sync skills to.\n * Supported: 'cursor' | 'codex' | 'windsurf' | 'cline' | 'copilot' | 'aider'\n * Per-project .pan.yaml values merge additively with global config.\n */\n also_sync?: string[];\n };\n}\n\n/**\n * Normalized shadow configuration\n */\nexport interface NormalizedShadowConfig {\n /** Global shadow mode enabled */\n enabled: boolean;\n\n /** Per-tracker overrides */\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\n/**\n * Normalized configuration (after loading and merging)\n */\nexport interface NormalizedConfig {\n /** Enabled providers */\n enabledProviders: Set<ModelProvider>;\n\n /** API keys by provider */\n apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** OpenRouter favorite model IDs (shown in ModelPicker) */\n openrouterFavorites: string[];\n\n /** Per-work-type overrides */\n overrides: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level */\n geminiThinkingLevel: 1 | 2 | 3 | 4;\n\n /** Tracker API keys */\n trackerKeys: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow: NormalizedShadowConfig;\n}\n\n/**\n * Model ID migration result\n *\n * Returned when deprecated model IDs are automatically migrated\n * during config load.\n */\nexport interface MigrationResult {\n /** List of migrated model IDs */\n migrated: Array<{\n /** Work type that was migrated */\n workType: WorkTypeId;\n /** Old (deprecated) model ID */\n from: string;\n /** New (current) model ID */\n to: string;\n }>;\n /** Whether config.yaml was backed up before migration */\n backedUp: boolean;\n}\n\n/**\n * Config load result (config + optional migration info)\n */\nexport interface ConfigLoadResult {\n /** Normalized configuration */\n config: NormalizedConfig;\n /** Migration result (if any deprecated models were migrated) */\n migration?: MigrationResult;\n}\n\n/**\n * Default configuration (used when no config files exist)\n */\nconst DEFAULT_CONFIG: NormalizedConfig = {\n enabledProviders: new Set(['anthropic']), // Only Anthropic by default\n apiKeys: {},\n openrouterFavorites: [],\n overrides: {},\n geminiThinkingLevel: 3,\n trackerKeys: {},\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Path to global config file\n */\nconst GLOBAL_CONFIG_PATH = join(homedir(), '.panopticon', 'config.yaml');\n\n/**\n * Normalize a provider config (handle both boolean and object forms)\n */\nfunction normalizeProviderConfig(\n providerConfig: ProviderConfig | boolean | undefined,\n fallbackKey?: string\n): { enabled: boolean; api_key?: string } {\n if (providerConfig === undefined) {\n return { enabled: false };\n }\n\n if (typeof providerConfig === 'boolean') {\n return { enabled: providerConfig, api_key: fallbackKey };\n }\n\n return {\n enabled: providerConfig.enabled,\n api_key: providerConfig.api_key || fallbackKey,\n };\n}\n\n/**\n * Resolve environment variables in config values.\n * If the env var is not set, returns the original reference (e.g., \"$OPENAI_API_KEY\")\n * so the UI can show that it's configured via env var but not resolved.\n */\nfunction resolveEnvVar(value: string | undefined): string | undefined {\n if (!value) return undefined;\n\n // Replace $VAR_NAME or ${VAR_NAME} with environment variable\n // If env var is not set, keep the original reference\n return value.replace(/\\$\\{?([A-Z_][A-Z0-9_]*)\\}?/g, (match, varName) => {\n const envValue = process.env[varName];\n return envValue !== undefined ? envValue : match; // Keep $VAR_NAME if not set\n });\n}\n\n/**\n * Load and parse a YAML config file\n */\nfunction loadYamlFile(filePath: string): YamlConfig | null {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed = yaml.load(content) as YamlConfig;\n return parsed || {};\n } catch (error) {\n console.error(`Error loading YAML config from ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Find project root by looking for .git directory\n */\nfunction findProjectRoot(startDir: string = process.cwd()): string | null {\n let currentDir = startDir;\n\n while (currentDir !== '/') {\n if (existsSync(join(currentDir, '.git'))) {\n return currentDir;\n }\n currentDir = join(currentDir, '..');\n }\n\n return null;\n}\n\n/**\n * Load per-project config (.pan.yaml in project root, with fallback to .panopticon.yaml)\n */\nfunction loadProjectConfig(): YamlConfig | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) {\n return null;\n }\n\n const newConfigPath = join(projectRoot, '.pan.yaml');\n if (existsSync(newConfigPath)) {\n return loadYamlFile(newConfigPath);\n }\n\n const legacyConfigPath = join(projectRoot, '.panopticon.yaml');\n if (existsSync(legacyConfigPath)) {\n process.stderr.write(\n `[panopticon] Deprecation warning: .panopticon.yaml is deprecated. Rename it to .pan.yaml.\\n`\n );\n return loadYamlFile(legacyConfigPath);\n }\n\n return null;\n}\n\n/**\n * Load global config (~/.panopticon/config.yaml)\n */\nfunction loadGlobalConfig(): YamlConfig | null {\n return loadYamlFile(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Merge shadow configuration from multiple sources\n */\nfunction mergeShadowConfig(\n result: NormalizedShadowConfig,\n config: YamlConfig | null\n): void {\n if (!config?.shadow) return;\n\n // Merge global enabled flag\n if (config.shadow.enabled !== undefined) {\n result.enabled = config.shadow.enabled;\n }\n\n // Merge per-tracker overrides\n if (config.shadow.trackers) {\n if (config.shadow.trackers.linear !== undefined) {\n result.trackers.linear = config.shadow.trackers.linear;\n }\n if (config.shadow.trackers.github !== undefined) {\n result.trackers.github = config.shadow.trackers.github;\n }\n if (config.shadow.trackers.gitlab !== undefined) {\n result.trackers.gitlab = config.shadow.trackers.gitlab;\n }\n if (config.shadow.trackers.rally !== undefined) {\n result.trackers.rally = config.shadow.trackers.rally;\n }\n }\n}\n\n/**\n * Merge multiple configs with precedence: project > global > defaults\n */\nfunction mergeConfigs(...configs: (YamlConfig | null)[]): NormalizedConfig {\n const result: NormalizedConfig = {\n ...DEFAULT_CONFIG,\n enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),\n shadow: {\n enabled: DEFAULT_CONFIG.shadow.enabled,\n trackers: { ...DEFAULT_CONFIG.shadow.trackers },\n },\n };\n\n // Filter out null configs\n const validConfigs = configs.filter((c): c is YamlConfig => c !== null);\n\n // Merge in reverse order (lowest precedence first)\n for (const config of validConfigs.reverse()) {\n // Merge providers\n if (config.models?.providers) {\n const providers = config.models.providers;\n const legacyKeys = config.api_keys || {};\n\n // Anthropic\n const anthropic = normalizeProviderConfig(providers.anthropic);\n if (anthropic.enabled) {\n result.enabledProviders.add('anthropic');\n }\n\n // OpenAI\n const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);\n if (openai.enabled) {\n result.enabledProviders.add('openai');\n if (openai.api_key) {\n result.apiKeys.openai = resolveEnvVar(openai.api_key);\n }\n }\n\n // Google\n const google = normalizeProviderConfig(providers.google, legacyKeys.google);\n if (google.enabled) {\n result.enabledProviders.add('google');\n if (google.api_key) {\n result.apiKeys.google = resolveEnvVar(google.api_key);\n }\n }\n\n // Z.AI\n const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);\n if (zai.enabled) {\n result.enabledProviders.add('zai');\n if (zai.api_key) {\n result.apiKeys.zai = resolveEnvVar(zai.api_key);\n }\n }\n\n // Kimi\n const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);\n if (kimi.enabled) {\n result.enabledProviders.add('kimi');\n if (kimi.api_key) {\n result.apiKeys.kimi = resolveEnvVar(kimi.api_key);\n }\n }\n\n // MiniMax\n const minimax = normalizeProviderConfig(providers.minimax, legacyKeys.minimax);\n if (minimax.enabled) {\n result.enabledProviders.add('minimax');\n if (minimax.api_key) {\n result.apiKeys.minimax = resolveEnvVar(minimax.api_key);\n }\n }\n\n // OpenRouter\n const openrouter = normalizeProviderConfig(providers.openrouter);\n if (openrouter.enabled) {\n result.enabledProviders.add('openrouter');\n if (openrouter.api_key) {\n result.apiKeys.openrouter = resolveEnvVar(openrouter.api_key);\n }\n }\n }\n\n // Merge OpenRouter favorites\n if (config.openrouter?.favorites) {\n result.openrouterFavorites = config.openrouter.favorites;\n }\n\n // Merge legacy API keys (for backward compatibility)\n // If a `models.providers` section exists, the enabled state is already set above — don't\n // override it here. Only auto-enable from API keys when there's no explicit providers config.\n if (config.api_keys) {\n const hasProvidersConfig = !!config.models?.providers;\n if (config.api_keys.openai) {\n result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);\n if (!hasProvidersConfig) result.enabledProviders.add('openai');\n }\n if (config.api_keys.google) {\n result.apiKeys.google = resolveEnvVar(config.api_keys.google);\n if (!hasProvidersConfig) result.enabledProviders.add('google');\n }\n if (config.api_keys.zai) {\n result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);\n if (!hasProvidersConfig) result.enabledProviders.add('zai');\n }\n if (config.api_keys.kimi) {\n result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);\n if (!hasProvidersConfig) result.enabledProviders.add('kimi');\n }\n if (config.api_keys.minimax) {\n result.apiKeys.minimax = resolveEnvVar(config.api_keys.minimax);\n if (!hasProvidersConfig) result.enabledProviders.add('minimax');\n }\n if (config.api_keys.openrouter) {\n result.apiKeys.openrouter = resolveEnvVar(config.api_keys.openrouter);\n if (!hasProvidersConfig) result.enabledProviders.add('openrouter');\n }\n }\n\n // Merge overrides\n if (config.models?.overrides) {\n result.overrides = {\n ...result.overrides,\n ...config.models.overrides,\n };\n }\n\n // Merge Gemini thinking level\n if (config.models?.gemini_thinking_level) {\n result.geminiThinkingLevel = config.models.gemini_thinking_level;\n }\n\n // Merge tracker keys\n if (config.tracker_keys) {\n if (config.tracker_keys.linear) {\n result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);\n }\n if (config.tracker_keys.github) {\n result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);\n }\n if (config.tracker_keys.gitlab) {\n result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);\n }\n if (config.tracker_keys.rally) {\n result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);\n }\n }\n\n // Merge shadow configuration\n mergeShadowConfig(result.shadow, config);\n }\n\n return result;\n}\n\n/**\n * Detect deprecated model IDs in config overrides\n *\n * Returns array of migrations to perform, or empty array if none found.\n */\nfunction detectDeprecatedModels(config: YamlConfig | null): Array<{\n workType: WorkTypeId;\n from: string;\n to: string;\n}> {\n if (!config?.models?.overrides) {\n return [];\n }\n\n const migrations: Array<{ workType: WorkTypeId; from: string; to: string }> = [];\n\n for (const [workType, modelId] of Object.entries(config.models.overrides)) {\n if (modelId && MODEL_DEPRECATIONS[modelId]) {\n migrations.push({\n workType: workType as WorkTypeId,\n from: modelId,\n to: MODEL_DEPRECATIONS[modelId],\n });\n }\n }\n\n return migrations;\n}\n\n/**\n * Apply deprecation migrations to a YamlConfig (in-place)\n */\nfunction applyMigrations(\n config: YamlConfig,\n migrations: Array<{ workType: WorkTypeId; from: string; to: string }>\n): void {\n if (!config.models) {\n config.models = {};\n }\n if (!config.models.overrides) {\n config.models.overrides = {};\n }\n\n for (const { workType, to } of migrations) {\n config.models.overrides[workType] = to as ModelId;\n }\n}\n\n/**\n * Create backup of global config file\n */\nfunction backupGlobalConfig(): boolean {\n try {\n const backupPath = `${GLOBAL_CONFIG_PATH}.bak`;\n copyFileSync(GLOBAL_CONFIG_PATH, backupPath);\n console.log(`✓ Backed up config.yaml → config.yaml.bak`);\n return true;\n } catch (error) {\n console.error(`Failed to create config backup:`, error);\n return false;\n }\n}\n\n/**\n * Write YamlConfig back to global config file\n */\nfunction writeGlobalConfig(config: YamlConfig): void {\n const yamlContent = yaml.dump(config, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n });\n\n writeFileSync(GLOBAL_CONFIG_PATH, yamlContent, 'utf-8');\n}\n\n/**\n * Load complete configuration (global + project + defaults)\n * Also loads API keys from environment variables as fallback\n *\n * IMPORTANT: This function may modify config.yaml if deprecated model IDs\n * are detected. A backup is created before any modifications.\n */\nexport function loadConfig(): ConfigLoadResult {\n let globalConfig = loadGlobalConfig();\n const projectConfig = loadProjectConfig();\n\n // Check for deprecated models in global config\n let migrationResult: MigrationResult | undefined;\n if (globalConfig && hasGlobalConfig()) {\n const migrations = detectDeprecatedModels(globalConfig);\n\n if (migrations.length > 0) {\n // Create backup\n const backedUp = backupGlobalConfig();\n\n // Apply migrations to global config\n applyMigrations(globalConfig, migrations);\n\n // Write migrated config back to disk\n writeGlobalConfig(globalConfig);\n\n // Log migrations\n console.log('\\n🔄 Model ID Migration:');\n for (const { workType, from, to } of migrations) {\n console.log(` ${workType}: ${from} → ${to}`);\n }\n console.log('');\n\n migrationResult = { migrated: migrations, backedUp };\n }\n }\n\n const config = mergeConfigs(projectConfig, globalConfig);\n\n // Load API keys from environment variables as fallback\n // This allows using ~/.panopticon.env for API keys\n if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {\n config.apiKeys.openai = process.env.OPENAI_API_KEY;\n config.enabledProviders.add('openai');\n }\n if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {\n config.apiKeys.google = process.env.GOOGLE_API_KEY;\n config.enabledProviders.add('google');\n }\n if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {\n config.apiKeys.zai = process.env.ZAI_API_KEY;\n config.enabledProviders.add('zai');\n }\n if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {\n config.apiKeys.kimi = process.env.KIMI_API_KEY;\n config.enabledProviders.add('kimi');\n }\n if (process.env.OPENROUTER_API_KEY && !config.apiKeys.openrouter) {\n config.apiKeys.openrouter = process.env.OPENROUTER_API_KEY;\n config.enabledProviders.add('openrouter');\n }\n\n // Load tracker API keys from environment variables as fallback\n if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {\n config.trackerKeys.linear = process.env.LINEAR_API_KEY;\n }\n if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {\n config.trackerKeys.github = process.env.GITHUB_TOKEN;\n }\n if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {\n config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;\n }\n if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {\n config.trackerKeys.rally = process.env.RALLY_API_KEY;\n }\n\n // Load shadow mode from environment as fallback\n // Environment variable takes precedence over config file\n if (process.env.SHADOW_MODE !== undefined) {\n const envShadowMode = ['true', '1', 'yes'].includes(process.env.SHADOW_MODE.toLowerCase());\n config.shadow.enabled = envShadowMode;\n }\n\n return { config, migration: migrationResult };\n}\n\n/**\n * Check if a project-level config exists (.pan.yaml or .panopticon.yaml)\n */\nexport function hasProjectConfig(): boolean {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n return existsSync(join(projectRoot, '.pan.yaml')) || existsSync(join(projectRoot, '.panopticon.yaml'));\n}\n\n/**\n * Check if global config exists\n */\nexport function hasGlobalConfig(): boolean {\n return existsSync(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Get path to global config file\n */\nexport function getGlobalConfigPath(): string {\n return GLOBAL_CONFIG_PATH;\n}\n\n/**\n * Get path to project config file (null if not in a project).\n * Returns .pan.yaml if it exists, falls back to .panopticon.yaml, otherwise returns .pan.yaml as default.\n */\nexport function getProjectConfigPath(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n if (existsSync(join(projectRoot, '.pan.yaml'))) {\n return join(projectRoot, '.pan.yaml');\n }\n if (existsSync(join(projectRoot, '.panopticon.yaml'))) {\n return join(projectRoot, '.panopticon.yaml');\n }\n return join(projectRoot, '.pan.yaml');\n}\n"],"mappings":";;;;;;;;;AAwgBA,SAAgB,mBAAmB,OAAiC;AAClE,QAAO,mBAAmB;;;;AA1ef,sBAA8C;EACzD,mBAAmB;EACnB,qBAAqB;EACtB;AA6DY,sBAAuD;EAKlE,mBAAmB;GACjB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,UAAU;GACR,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,eAAe;GACb,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,wBAAwB;GACtB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,0BAA0B;GACxB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,kBAAkB;GAChB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,aAAa;GACX,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,gBAAgB;GACd,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IA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"}
1
+ {"version":3,"file":"config-yaml-DGbLSMCa.js","names":[],"sources":["../src/lib/model-capabilities.ts","../src/lib/config-yaml.ts"],"sourcesContent":["/**\n * Model Capability Matrix\n *\n * Defines capability scores for each model across different skill dimensions.\n * This enables intelligent model selection based on what the user has enabled\n * rather than static presets.\n *\n * Scores: 0-100 where 100 = best in class\n * Cost: $/1M tokens (input + output average)\n *\n * Last updated: 2026-01-29\n * Sources:\n * - SWE-bench Verified leaderboard (vals.ai)\n * - LiveCodeBench v6\n * - LMSYS Chatbot Arena\n * - Artificial Analysis\n * - Official provider pricing pages\n */\n\nimport { ModelId } from './settings.js';\n\n/**\n * Model ID deprecation mapping\n *\n * Maps deprecated model IDs to their current replacements.\n * When a model ID changes (e.g., claude-opus-4-5 → claude-opus-4-6),\n * add the mapping here to enable automatic migration.\n *\n * Strategy: Single-hop only. When a newer version arrives (e.g., 4-7),\n * add both old→new mappings (4-5→4-7 and 4-6→4-7).\n */\nexport const MODEL_DEPRECATIONS: Record<string, ModelId> = {\n 'claude-opus-4-5': 'claude-opus-4-6',\n 'claude-sonnet-4-5': 'claude-sonnet-4-6',\n};\n\n/**\n * Resolve a model ID to its current version\n *\n * If the model ID is deprecated, returns the replacement.\n * Otherwise, returns the model ID unchanged.\n *\n * @param modelId - Model ID to resolve (may be deprecated)\n * @returns Current model ID\n */\nexport function resolveModelId(modelId: string): ModelId {\n return (MODEL_DEPRECATIONS[modelId] as ModelId) || (modelId as ModelId);\n}\n\n/**\n * Skill dimensions that models are evaluated on\n */\nexport type SkillDimension =\n | 'code-generation' // Writing new code\n | 'code-review' // Finding issues in code\n | 'debugging' // Root cause analysis\n | 'planning' // Architecture and strategy\n | 'documentation' // Writing docs, PRDs\n | 'testing' // Test generation and analysis\n | 'security' // Security analysis\n | 'performance' // Performance optimization\n | 'synthesis' // Combining information\n | 'speed' // Response latency\n | 'context-length'; // Max context window\n\n/**\n * Capability profile for a single model\n */\nexport interface ModelCapability {\n /** Model identifier */\n model: ModelId;\n /** Provider for this model */\n provider: 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi' | 'minimax' | 'openrouter';\n /** Display name */\n displayName: string;\n /** Cost per 1M tokens (average of input/output) in USD */\n costPer1MTokens: number;\n /** Capability scores (0-100) for each skill dimension */\n skills: Record<SkillDimension, number>;\n /** Context window size in tokens */\n contextWindow: number;\n /** Additional notes about this model's strengths */\n notes?: string;\n}\n\n/**\n * Master capability database\n *\n * Scores are based on:\n * - Public benchmarks (HumanEval, SWE-bench, MBPP)\n * - Community consensus\n * - Practical experience\n *\n * These are baseline scores - run Kimi 2.5 research to refine.\n */\nexport const MODEL_CAPABILITIES: Record<ModelId, ModelCapability> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ANTHROPIC MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'claude-opus-4-6': {\n model: 'claude-opus-4-6',\n provider: 'anthropic',\n displayName: 'Claude Opus 4.6',\n costPer1MTokens: 45.0, // $5 in / $25 out → same pricing as 4.5\n contextWindow: 200000, // 1M available via opt-in beta, but we use 200K\n skills: {\n 'code-generation': 96, // 80.9% SWE-bench (first >80%), 89.4% Aider Polyglot\n 'code-review': 98,\n debugging: 97,\n planning: 99, // User confirms: \"Opus 4.6 planning for sure\"\n documentation: 95,\n testing: 92,\n security: 98, // Best for security review\n performance: 90,\n synthesis: 98, // Best for combining info across domains\n speed: 40, // Slower but 76% more token efficient\n 'context-length': 95,\n },\n notes: 'Successor to Opus 4.5. Same pricing, 1M context available (opt-in beta). Best for planning, security, complex reasoning.',\n },\n\n 'claude-sonnet-4-6': {\n model: 'claude-sonnet-4-6',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.6',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 94,\n 'code-review': 94,\n debugging: 92,\n planning: 90,\n documentation: 92,\n testing: 92,\n security: 88,\n performance: 88,\n synthesis: 90,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Successor to Sonnet 4.5. Same pricing tier. Improved coding and reasoning.',\n },\n\n 'claude-sonnet-4-5': {\n model: 'claude-sonnet-4-5',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.5',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 92, // 77.2% SWE-bench (82% parallel), beats GPT-5 Codex (74.5%)\n 'code-review': 92,\n debugging: 90,\n planning: 88,\n documentation: 90, // 100% AIME with Python\n testing: 90, // 50% Terminal-Bench, 61.4% OSWorld\n security: 85,\n performance: 85,\n synthesis: 88,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Best value: 77.2% SWE-bench at 1/5th Opus cost. Beats GPT-5 Codex.',\n },\n\n 'claude-haiku-4-5': {\n model: 'claude-haiku-4-5',\n provider: 'anthropic',\n displayName: 'Claude Haiku 4.5',\n costPer1MTokens: 4.0, // $0.80 in / $4 out → avg ~$2.4\n contextWindow: 200000,\n skills: {\n 'code-generation': 75,\n 'code-review': 72,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 60,\n performance: 65,\n synthesis: 68,\n speed: 95, // Fastest Anthropic\n 'context-length': 95,\n },\n notes: 'Fast and cheap, good for simple tasks and exploration',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // OPENAI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gpt-5.2-codex': {\n model: 'gpt-5.2-codex',\n provider: 'openai',\n displayName: 'GPT-5.2 Codex',\n costPer1MTokens: 75.0, // Premium tier ~$75/M\n contextWindow: 128000,\n skills: {\n 'code-generation': 95, // 80% SWE-bench Verified, 55.6% SWE-bench Pro\n 'code-review': 90,\n debugging: 92, // 92.4% GPQA Diamond\n planning: 88,\n documentation: 85,\n testing: 90,\n security: 85,\n performance: 88, // 52.9% ARC-AGI-2 (best reasoning)\n synthesis: 88, // 100% AIME 2025 without tools\n speed: 55,\n 'context-length': 75,\n },\n notes: 'Premium coding: 80% SWE-bench. Best raw reasoning (52.9% ARC-AGI-2). Expensive.',\n },\n\n 'o3-deep-research': {\n model: 'o3-deep-research',\n provider: 'openai',\n displayName: 'O3 Deep Research',\n costPer1MTokens: 100.0, // Expensive reasoning model\n contextWindow: 200000,\n skills: {\n 'code-generation': 85,\n 'code-review': 95,\n debugging: 98, // Best for debugging\n planning: 95,\n documentation: 88,\n testing: 85,\n security: 92,\n performance: 92,\n synthesis: 95,\n speed: 20, // Very slow (reasoning chains)\n 'context-length': 95,\n },\n notes: 'Deep reasoning model, excellent for complex debugging and analysis',\n },\n\n 'gpt-4o': {\n model: 'gpt-4o',\n provider: 'openai',\n displayName: 'GPT-4o',\n costPer1MTokens: 15.0, // $5 in / $15 out\n contextWindow: 128000,\n skills: {\n 'code-generation': 88,\n 'code-review': 85,\n debugging: 85,\n planning: 82,\n documentation: 88,\n testing: 82,\n security: 78,\n performance: 80,\n synthesis: 85,\n speed: 75,\n 'context-length': 75,\n },\n notes: 'Good all-rounder, competitive with Sonnet',\n },\n\n 'gpt-4o-mini': {\n model: 'gpt-4o-mini',\n provider: 'openai',\n displayName: 'GPT-4o Mini',\n costPer1MTokens: 1.0, // Very cheap\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 60,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 60,\n synthesis: 62,\n speed: 92,\n 'context-length': 75,\n },\n notes: 'Budget option, good for simple tasks',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // GOOGLE MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gemini-3-pro-preview': {\n model: 'gemini-3-pro-preview',\n provider: 'google',\n displayName: 'Gemini 3 Pro',\n costPer1MTokens: 12.0, // $4.2 in / $18.9 out\n contextWindow: 1000000, // 1M context!\n skills: {\n 'code-generation': 90, // 2439 Elo LiveCodeBench Pro (first >1500 on LMArena)\n 'code-review': 88,\n debugging: 85,\n planning: 85,\n documentation: 88,\n testing: 85, // ~95% AIME 2025\n security: 78,\n performance: 85, // Strong multimodal\n synthesis: 90, // Best for combining large codebases\n speed: 80,\n 'context-length': 100, // Best context - 1M tokens\n },\n notes: 'First to exceed 1500 Elo on LMArena. Best for large codebase analysis with 1M context.',\n },\n\n 'gemini-3-flash-preview': {\n model: 'gemini-3-flash-preview',\n provider: 'google',\n displayName: 'Gemini 3 Flash',\n costPer1MTokens: 0.5, // Very cheap\n contextWindow: 1000000,\n skills: {\n 'code-generation': 75,\n 'code-review': 70,\n debugging: 68,\n planning: 62,\n documentation: 72,\n testing: 68,\n security: 55,\n performance: 65,\n synthesis: 70,\n speed: 98, // Fastest overall\n 'context-length': 100,\n },\n notes: 'Extremely fast and cheap, huge context, great for exploration',\n },\n\n 'gemini-2.5-pro': {\n model: 'gemini-2.5-pro',\n provider: 'google',\n displayName: 'Gemini 2.5 Pro',\n costPer1MTokens: 12.0,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 92,\n 'code-review': 90,\n debugging: 88,\n planning: 88,\n documentation: 90,\n testing: 87,\n security: 82,\n performance: 88,\n synthesis: 92,\n speed: 75,\n 'context-length': 100,\n },\n notes: 'Advanced reasoning and code capabilities with 1M context',\n },\n\n 'gemini-2.5-flash': {\n model: 'gemini-2.5-flash',\n provider: 'google',\n displayName: 'Gemini 2.5 Flash',\n costPer1MTokens: 0.6,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 78,\n 'code-review': 73,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 58,\n performance: 68,\n synthesis: 73,\n speed: 95,\n 'context-length': 100,\n },\n notes: 'Fast and efficient with large context support',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Z.AI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'glm-4.7': {\n model: 'glm-4.7',\n provider: 'zai',\n displayName: 'GLM 4.7',\n costPer1MTokens: 5.0,\n contextWindow: 200000, // 200K context, 128K output\n skills: {\n 'code-generation': 88, // 73.8% SWE-bench, 84.9 LiveCodeBench v6 (open-source SOTA)\n 'code-review': 85,\n debugging: 85, // Strong debugging with Interleaved Thinking\n planning: 82, // 95.7% AIME 2025 (beats Gemini 3 & GPT-5.1)\n documentation: 80,\n testing: 82, // 87.4 τ²-Bench (SOTA for tool use)\n security: 72,\n performance: 78,\n synthesis: 85, // Preserved Thinking retains context across turns\n speed: 80,\n 'context-length': 95, // 200K context\n },\n notes: 'Top open-source for agentic coding. 73.8% SWE-bench, best tool use. 400B params with Interleaved Thinking.',\n },\n\n 'glm-4.7-flash': {\n model: 'glm-4.7-flash',\n provider: 'zai',\n displayName: 'GLM 4.7 Flash',\n costPer1MTokens: 1.5,\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 62,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 62,\n synthesis: 65,\n speed: 92, // Fast inference\n 'context-length': 75,\n },\n notes: 'Fast and affordable. Good for quick iterations and exploration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // KIMI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'kimi-k2': {\n model: 'kimi-k2',\n provider: 'kimi',\n displayName: 'Kimi K2',\n costPer1MTokens: 1.4, // $0.16 in / $2.63 out → very cheap\n contextWindow: 131000,\n skills: {\n 'code-generation': 82, // 65.8% SWE-bench (beats GPT-4.1 at 54.6%)\n 'code-review': 80,\n debugging: 78,\n planning: 75,\n documentation: 80,\n testing: 75,\n security: 70,\n performance: 72,\n synthesis: 78,\n speed: 80,\n 'context-length': 75,\n },\n notes: 'Strong value: 65.8% SWE-bench at very low cost. Good for routine tasks.',\n },\n\n 'kimi-k2.5': {\n model: 'kimi-k2.5',\n provider: 'kimi',\n displayName: 'Kimi K2.5',\n costPer1MTokens: 8.0, // ~5.1x cheaper than GPT-5.2\n contextWindow: 256000,\n skills: {\n 'code-generation': 92, // 76.8% SWE-bench, 85 LiveCodeBench v6\n 'code-review': 90,\n debugging: 90, // Strong analytical capabilities\n planning: 88, // User confirms \"highly capable\"\n documentation: 88,\n testing: 88, // 92% coding accuracy\n security: 82,\n performance: 85,\n synthesis: 92, // Can coordinate 100 sub-agents, 1500 tool calls\n speed: 75, // MoE: 1T total params, 32B active\n 'context-length': 98, // 256K context\n },\n notes: 'Best open-source coding model. 5x cheaper than GPT-5.2. Excellent for frontend dev and multi-agent orchestration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // MINIMAX MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'minimax-m2.7': {\n model: 'minimax-m2.7',\n provider: 'minimax',\n displayName: 'MiniMax M2.7',\n costPer1MTokens: 1.5, // $0.30/M in + $1.20/M out, blended ~$0.06/M with auto-cache\n contextWindow: 204800,\n skills: {\n 'code-generation': 90, // 56.22% SWE-Pro (Opus ~57-58%), 55.6% VIBE-Pro\n 'code-review': 88,\n debugging: 88, // 57.0% Terminal Bench 2\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90, // Self-evolving agent, 97% skill adherence on complex tasks\n speed: 80, // 10B active params (MoE)\n 'context-length': 92, // 204K context\n },\n notes: '10B active params, 56.22% SWE-Pro, 1495 ELO GDPval-AA. $0.06/M blended with auto-cache.',\n },\n\n 'minimax-m2.7-highspeed': {\n model: 'minimax-m2.7-highspeed',\n provider: 'minimax',\n displayName: 'MiniMax M2.7 Highspeed',\n costPer1MTokens: 1.5, // Same pricing as M2.7\n contextWindow: 204800,\n skills: {\n 'code-generation': 90,\n 'code-review': 88,\n debugging: 88,\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90,\n speed: 92, // 100 tps, 3x faster than Opus\n 'context-length': 92,\n },\n notes: 'Identical quality to M2.7, 100 tps (3x Opus speed). Best for high-throughput agent work.',\n },\n};\n\n/**\n * Get capability profile for a model\n */\nexport function getModelCapability(model: ModelId): ModelCapability {\n return MODEL_CAPABILITIES[model];\n}\n\n/**\n * Get all models sorted by a specific skill (descending)\n */\nexport function getModelsBySkill(skill: SkillDimension): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[b].skills[skill] - MODEL_CAPABILITIES[a].skills[skill]\n );\n}\n\n/**\n * Get all models for a provider\n */\nexport function getModelsForProvider(\n provider: ModelCapability['provider']\n): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).filter(\n (model) => MODEL_CAPABILITIES[model].provider === provider\n );\n}\n\n/**\n * Get cheapest models (sorted by cost ascending)\n */\nexport function getCheapestModels(): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[a].costPer1MTokens - MODEL_CAPABILITIES[b].costPer1MTokens\n );\n}\n\n/**\n * Calculate cost efficiency score for a skill\n * Higher = better value (skill score / cost)\n */\nexport function getValueScore(model: ModelId, skill: SkillDimension): number {\n const cap = MODEL_CAPABILITIES[model];\n return cap.skills[skill] / Math.log10(cap.costPer1MTokens + 1);\n}\n\n/**\n * Get all skill dimensions\n */\nexport function getAllSkillDimensions(): SkillDimension[] {\n return [\n 'code-generation',\n 'code-review',\n 'debugging',\n 'planning',\n 'documentation',\n 'testing',\n 'security',\n 'performance',\n 'synthesis',\n 'speed',\n 'context-length',\n ];\n}\n","/**\n * YAML Configuration Loader\n *\n * Loads and merges configuration from:\n * 1. Global config: ~/.panopticon/config.yaml\n * 2. Per-project config: .pan.yaml (project root, falls back to .panopticon.yaml with deprecation warning)\n *\n * Uses smart (capability-based) model selection - no legacy presets.\n */\n\nimport { readFileSync, existsSync, writeFileSync, copyFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { WorkTypeId } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { ModelProvider } from './model-fallback.js';\nimport { MODEL_DEPRECATIONS, resolveModelId } from './model-capabilities.js';\n\n/**\n * Provider configuration (enable/disable + API keys)\n */\nexport interface ProviderConfig {\n /** Whether this provider is enabled */\n enabled: boolean;\n /** API key (optional, can use env var) */\n api_key?: string;\n}\n\n/**\n * Shadow mode configuration\n */\nexport interface ShadowConfig {\n /** Global shadow mode default */\n enabled?: boolean;\n\n /** Per-tracker overrides */\n trackers?: {\n linear?: boolean;\n github?: boolean;\n gitlab?: boolean;\n rally?: boolean;\n };\n}\n\n/**\n * Complete configuration structure (YAML schema)\n */\nexport interface YamlConfig {\n /** Model configuration */\n models?: {\n /** Provider enable/disable and API keys */\n providers?: {\n anthropic?: ProviderConfig | boolean;\n openai?: ProviderConfig | boolean;\n google?: ProviderConfig | boolean;\n zai?: ProviderConfig | boolean;\n kimi?: ProviderConfig | boolean;\n minimax?: ProviderConfig | boolean;\n openrouter?: ProviderConfig | boolean;\n };\n\n /** Per-work-type overrides (explicit model for specific tasks) */\n overrides?: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level (1-4) */\n gemini_thinking_level?: 1 | 2 | 3 | 4;\n };\n\n /** OpenRouter-specific configuration */\n openrouter?: {\n /** Favorite model IDs to show in ModelPicker */\n favorites?: string[];\n };\n\n /** Legacy API keys (for backward compatibility) */\n api_keys?: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** Tracker API keys (override environment variables) */\n tracker_keys?: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow?: ShadowConfig;\n\n /** Multi-tool sync configuration */\n tools?: {\n /**\n * Additional AI tools to sync skills to.\n * Supported: 'cursor' | 'codex' | 'windsurf' | 'cline' | 'copilot' | 'aider'\n * Per-project .pan.yaml values merge additively with global config.\n */\n also_sync?: string[];\n };\n}\n\n/**\n * Normalized shadow configuration\n */\nexport interface NormalizedShadowConfig {\n /** Global shadow mode enabled */\n enabled: boolean;\n\n /** Per-tracker overrides */\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\n/**\n * Normalized configuration (after loading and merging)\n */\nexport interface NormalizedConfig {\n /** Enabled providers */\n enabledProviders: Set<ModelProvider>;\n\n /** API keys by provider */\n apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** OpenRouter favorite model IDs (shown in ModelPicker) */\n openrouterFavorites: string[];\n\n /** Per-work-type overrides */\n overrides: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level */\n geminiThinkingLevel: 1 | 2 | 3 | 4;\n\n /** Tracker API keys */\n trackerKeys: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow: NormalizedShadowConfig;\n}\n\n/**\n * Model ID migration result\n *\n * Returned when deprecated model IDs are automatically migrated\n * during config load.\n */\nexport interface MigrationResult {\n /** List of migrated model IDs */\n migrated: Array<{\n /** Work type that was migrated */\n workType: WorkTypeId;\n /** Old (deprecated) model ID */\n from: string;\n /** New (current) model ID */\n to: string;\n }>;\n /** Whether config.yaml was backed up before migration */\n backedUp: boolean;\n}\n\n/**\n * Config load result (config + optional migration info)\n */\nexport interface ConfigLoadResult {\n /** Normalized configuration */\n config: NormalizedConfig;\n /** Migration result (if any deprecated models were migrated) */\n migration?: MigrationResult;\n}\n\n/**\n * Default configuration (used when no config files exist)\n */\nconst DEFAULT_CONFIG: NormalizedConfig = {\n enabledProviders: new Set(['anthropic']), // Only Anthropic by default\n apiKeys: {},\n openrouterFavorites: [],\n overrides: {},\n geminiThinkingLevel: 3,\n trackerKeys: {},\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Path to global config file\n */\nconst GLOBAL_CONFIG_PATH = join(homedir(), '.panopticon', 'config.yaml');\n\n/**\n * Normalize a provider config (handle both boolean and object forms)\n */\nfunction normalizeProviderConfig(\n providerConfig: ProviderConfig | boolean | undefined,\n fallbackKey?: string\n): { enabled: boolean; api_key?: string } {\n if (providerConfig === undefined) {\n return { enabled: false };\n }\n\n if (typeof providerConfig === 'boolean') {\n return { enabled: providerConfig, api_key: fallbackKey };\n }\n\n return {\n enabled: providerConfig.enabled,\n api_key: providerConfig.api_key || fallbackKey,\n };\n}\n\n/**\n * Resolve environment variables in config values.\n * If the env var is not set, returns the original reference (e.g., \"$OPENAI_API_KEY\")\n * so the UI can show that it's configured via env var but not resolved.\n */\nfunction resolveEnvVar(value: string | undefined): string | undefined {\n if (!value) return undefined;\n\n // Replace $VAR_NAME or ${VAR_NAME} with environment variable\n // If env var is not set, keep the original reference\n return value.replace(/\\$\\{?([A-Z_][A-Z0-9_]*)\\}?/g, (match, varName) => {\n const envValue = process.env[varName];\n return envValue !== undefined ? envValue : match; // Keep $VAR_NAME if not set\n });\n}\n\n/**\n * Load and parse a YAML config file\n */\nfunction loadYamlFile(filePath: string): YamlConfig | null {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed = yaml.load(content) as YamlConfig;\n return parsed || {};\n } catch (error) {\n console.error(`Error loading YAML config from ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Find project root by looking for .git directory\n */\nfunction findProjectRoot(startDir: string = process.cwd()): string | null {\n let currentDir = startDir;\n\n while (currentDir !== '/') {\n if (existsSync(join(currentDir, '.git'))) {\n return currentDir;\n }\n currentDir = join(currentDir, '..');\n }\n\n return null;\n}\n\n/**\n * Load per-project config (.pan.yaml in project root, with fallback to .panopticon.yaml)\n */\nfunction loadProjectConfig(): YamlConfig | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) {\n return null;\n }\n\n const newConfigPath = join(projectRoot, '.pan.yaml');\n if (existsSync(newConfigPath)) {\n return loadYamlFile(newConfigPath);\n }\n\n const legacyConfigPath = join(projectRoot, '.panopticon.yaml');\n if (existsSync(legacyConfigPath)) {\n process.stderr.write(\n `[panopticon] Deprecation warning: .panopticon.yaml is deprecated. Rename it to .pan.yaml.\\n`\n );\n return loadYamlFile(legacyConfigPath);\n }\n\n return null;\n}\n\n/**\n * Load global config (~/.panopticon/config.yaml)\n */\nfunction loadGlobalConfig(): YamlConfig | null {\n return loadYamlFile(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Merge shadow configuration from multiple sources\n */\nfunction mergeShadowConfig(\n result: NormalizedShadowConfig,\n config: YamlConfig | null\n): void {\n if (!config?.shadow) return;\n\n // Merge global enabled flag\n if (config.shadow.enabled !== undefined) {\n result.enabled = config.shadow.enabled;\n }\n\n // Merge per-tracker overrides\n if (config.shadow.trackers) {\n if (config.shadow.trackers.linear !== undefined) {\n result.trackers.linear = config.shadow.trackers.linear;\n }\n if (config.shadow.trackers.github !== undefined) {\n result.trackers.github = config.shadow.trackers.github;\n }\n if (config.shadow.trackers.gitlab !== undefined) {\n result.trackers.gitlab = config.shadow.trackers.gitlab;\n }\n if (config.shadow.trackers.rally !== undefined) {\n result.trackers.rally = config.shadow.trackers.rally;\n }\n }\n}\n\n/**\n * Merge multiple configs with precedence: project > global > defaults\n */\nfunction mergeConfigs(...configs: (YamlConfig | null)[]): NormalizedConfig {\n const result: NormalizedConfig = {\n ...DEFAULT_CONFIG,\n enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),\n shadow: {\n enabled: DEFAULT_CONFIG.shadow.enabled,\n trackers: { ...DEFAULT_CONFIG.shadow.trackers },\n },\n };\n\n // Filter out null configs\n const validConfigs = configs.filter((c): c is YamlConfig => c !== null);\n\n // Merge in reverse order (lowest precedence first)\n for (const config of validConfigs.reverse()) {\n // Merge providers\n if (config.models?.providers) {\n const providers = config.models.providers;\n const legacyKeys = config.api_keys || {};\n\n // Anthropic\n const anthropic = normalizeProviderConfig(providers.anthropic);\n if (anthropic.enabled) {\n result.enabledProviders.add('anthropic');\n }\n\n // OpenAI\n const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);\n if (openai.enabled) {\n result.enabledProviders.add('openai');\n if (openai.api_key) {\n result.apiKeys.openai = resolveEnvVar(openai.api_key);\n }\n }\n\n // Google\n const google = normalizeProviderConfig(providers.google, legacyKeys.google);\n if (google.enabled) {\n result.enabledProviders.add('google');\n if (google.api_key) {\n result.apiKeys.google = resolveEnvVar(google.api_key);\n }\n }\n\n // Z.AI\n const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);\n if (zai.enabled) {\n result.enabledProviders.add('zai');\n if (zai.api_key) {\n result.apiKeys.zai = resolveEnvVar(zai.api_key);\n }\n }\n\n // Kimi\n const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);\n if (kimi.enabled) {\n result.enabledProviders.add('kimi');\n if (kimi.api_key) {\n result.apiKeys.kimi = resolveEnvVar(kimi.api_key);\n }\n }\n\n // MiniMax\n const minimax = normalizeProviderConfig(providers.minimax, legacyKeys.minimax);\n if (minimax.enabled) {\n result.enabledProviders.add('minimax');\n if (minimax.api_key) {\n result.apiKeys.minimax = resolveEnvVar(minimax.api_key);\n }\n }\n\n // OpenRouter\n const openrouter = normalizeProviderConfig(providers.openrouter);\n if (openrouter.enabled) {\n result.enabledProviders.add('openrouter');\n if (openrouter.api_key) {\n result.apiKeys.openrouter = resolveEnvVar(openrouter.api_key);\n }\n }\n }\n\n // Merge OpenRouter favorites\n if (config.openrouter?.favorites) {\n result.openrouterFavorites = config.openrouter.favorites;\n }\n\n // Merge legacy API keys (for backward compatibility)\n // If a `models.providers` section exists, the enabled state is already set above — don't\n // override it here. Only auto-enable from API keys when there's no explicit providers config.\n if (config.api_keys) {\n const hasProvidersConfig = !!config.models?.providers;\n if (config.api_keys.openai) {\n result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);\n if (!hasProvidersConfig) result.enabledProviders.add('openai');\n }\n if (config.api_keys.google) {\n result.apiKeys.google = resolveEnvVar(config.api_keys.google);\n if (!hasProvidersConfig) result.enabledProviders.add('google');\n }\n if (config.api_keys.zai) {\n result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);\n if (!hasProvidersConfig) result.enabledProviders.add('zai');\n }\n if (config.api_keys.kimi) {\n result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);\n if (!hasProvidersConfig) result.enabledProviders.add('kimi');\n }\n if (config.api_keys.minimax) {\n result.apiKeys.minimax = resolveEnvVar(config.api_keys.minimax);\n if (!hasProvidersConfig) result.enabledProviders.add('minimax');\n }\n if (config.api_keys.openrouter) {\n result.apiKeys.openrouter = resolveEnvVar(config.api_keys.openrouter);\n if (!hasProvidersConfig) result.enabledProviders.add('openrouter');\n }\n }\n\n // Merge overrides\n if (config.models?.overrides) {\n result.overrides = {\n ...result.overrides,\n ...config.models.overrides,\n };\n }\n\n // Merge Gemini thinking level\n if (config.models?.gemini_thinking_level) {\n result.geminiThinkingLevel = config.models.gemini_thinking_level;\n }\n\n // Merge tracker keys\n if (config.tracker_keys) {\n if (config.tracker_keys.linear) {\n result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);\n }\n if (config.tracker_keys.github) {\n result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);\n }\n if (config.tracker_keys.gitlab) {\n result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);\n }\n if (config.tracker_keys.rally) {\n result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);\n }\n }\n\n // Merge shadow configuration\n mergeShadowConfig(result.shadow, config);\n }\n\n return result;\n}\n\n/**\n * Detect deprecated model IDs in config overrides\n *\n * Returns array of migrations to perform, or empty array if none found.\n */\nfunction detectDeprecatedModels(config: YamlConfig | null): Array<{\n workType: WorkTypeId;\n from: string;\n to: string;\n}> {\n if (!config?.models?.overrides) {\n return [];\n }\n\n const migrations: Array<{ workType: WorkTypeId; from: string; to: string }> = [];\n\n for (const [workType, modelId] of Object.entries(config.models.overrides)) {\n if (modelId && MODEL_DEPRECATIONS[modelId]) {\n migrations.push({\n workType: workType as WorkTypeId,\n from: modelId,\n to: MODEL_DEPRECATIONS[modelId],\n });\n }\n }\n\n return migrations;\n}\n\n/**\n * Apply deprecation migrations to a YamlConfig (in-place)\n */\nfunction applyMigrations(\n config: YamlConfig,\n migrations: Array<{ workType: WorkTypeId; from: string; to: string }>\n): void {\n if (!config.models) {\n config.models = {};\n }\n if (!config.models.overrides) {\n config.models.overrides = {};\n }\n\n for (const { workType, to } of migrations) {\n config.models.overrides[workType] = to as ModelId;\n }\n}\n\n/**\n * Create backup of global config file\n */\nfunction backupGlobalConfig(): boolean {\n try {\n const backupPath = `${GLOBAL_CONFIG_PATH}.bak`;\n copyFileSync(GLOBAL_CONFIG_PATH, backupPath);\n console.log(`✓ Backed up config.yaml → config.yaml.bak`);\n return true;\n } catch (error) {\n console.error(`Failed to create config backup:`, error);\n return false;\n }\n}\n\n/**\n * Write YamlConfig back to global config file\n */\nfunction writeGlobalConfig(config: YamlConfig): void {\n const yamlContent = yaml.dump(config, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n });\n\n writeFileSync(GLOBAL_CONFIG_PATH, yamlContent, 'utf-8');\n}\n\n/**\n * Load complete configuration (global + project + defaults)\n * Also loads API keys from environment variables as fallback\n *\n * IMPORTANT: This function may modify config.yaml if deprecated model IDs\n * are detected. A backup is created before any modifications.\n */\nexport function loadConfig(): ConfigLoadResult {\n let globalConfig = loadGlobalConfig();\n const projectConfig = loadProjectConfig();\n\n // Check for deprecated models in global config\n let migrationResult: MigrationResult | undefined;\n if (globalConfig && hasGlobalConfig()) {\n const migrations = detectDeprecatedModels(globalConfig);\n\n if (migrations.length > 0) {\n // Create backup\n const backedUp = backupGlobalConfig();\n\n // Apply migrations to global config\n applyMigrations(globalConfig, migrations);\n\n // Write migrated config back to disk\n writeGlobalConfig(globalConfig);\n\n // Log migrations\n console.log('\\n🔄 Model ID Migration:');\n for (const { workType, from, to } of migrations) {\n console.log(` ${workType}: ${from} → ${to}`);\n }\n console.log('');\n\n migrationResult = { migrated: migrations, backedUp };\n }\n }\n\n const config = mergeConfigs(projectConfig, globalConfig);\n\n // Load API keys from environment variables as fallback\n // This allows using ~/.panopticon.env for API keys\n if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {\n config.apiKeys.openai = process.env.OPENAI_API_KEY;\n config.enabledProviders.add('openai');\n }\n if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {\n config.apiKeys.google = process.env.GOOGLE_API_KEY;\n config.enabledProviders.add('google');\n }\n if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {\n config.apiKeys.zai = process.env.ZAI_API_KEY;\n config.enabledProviders.add('zai');\n }\n if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {\n config.apiKeys.kimi = process.env.KIMI_API_KEY;\n config.enabledProviders.add('kimi');\n }\n if (process.env.OPENROUTER_API_KEY && !config.apiKeys.openrouter) {\n config.apiKeys.openrouter = process.env.OPENROUTER_API_KEY;\n config.enabledProviders.add('openrouter');\n }\n\n // Load tracker API keys from environment variables as fallback\n if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {\n config.trackerKeys.linear = process.env.LINEAR_API_KEY;\n }\n if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {\n config.trackerKeys.github = process.env.GITHUB_TOKEN;\n }\n if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {\n config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;\n }\n if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {\n config.trackerKeys.rally = process.env.RALLY_API_KEY;\n }\n\n // Load shadow mode from environment as fallback\n // Environment variable takes precedence over config file\n if (process.env.SHADOW_MODE !== undefined) {\n const envShadowMode = ['true', '1', 'yes'].includes(process.env.SHADOW_MODE.toLowerCase());\n config.shadow.enabled = envShadowMode;\n }\n\n return { config, migration: migrationResult };\n}\n\n/**\n * Check if a project-level config exists (.pan.yaml or .panopticon.yaml)\n */\nexport function hasProjectConfig(): boolean {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n return existsSync(join(projectRoot, '.pan.yaml')) || existsSync(join(projectRoot, '.panopticon.yaml'));\n}\n\n/**\n * Check if global config exists\n */\nexport function hasGlobalConfig(): boolean {\n return existsSync(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Get path to global config file\n */\nexport function getGlobalConfigPath(): string {\n return GLOBAL_CONFIG_PATH;\n}\n\n/**\n * Get path to project config file (null if not in a project).\n * Returns .pan.yaml if it exists, falls back to .panopticon.yaml, otherwise returns .pan.yaml as default.\n */\nexport function getProjectConfigPath(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n if (existsSync(join(projectRoot, '.pan.yaml'))) {\n return join(projectRoot, '.pan.yaml');\n }\n if (existsSync(join(projectRoot, '.panopticon.yaml'))) {\n return join(projectRoot, '.panopticon.yaml');\n }\n return join(projectRoot, '.pan.yaml');\n}\n"],"mappings":";;;;;;;;;AAwgBA,SAAgB,mBAAmB,OAAiC;AAClE,QAAO,mBAAmB;;;;AA1ef,sBAA8C;EACzD,mBAAmB;EACnB,qBAAqB;EACtB;AA6DY,sBAAuD;EAKlE,mBAAmB;GACjB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,UAAU;GACR,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,eAAe;GACb,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,wBAAwB;GACtB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,0BAA0B;GACxB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,kBAAkB;GAChB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,aAAa;GACX,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,gBAAgB;GACd,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,0BAA0B;GACxB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EACF;;;;;;;;;;;;;;;;ACvSD,SAAS,wBACP,gBACA,aACwC;AACxC,KAAI,mBAAmB,KAAA,EACrB,QAAO,EAAE,SAAS,OAAO;AAG3B,KAAI,OAAO,mBAAmB,UAC5B,QAAO;EAAE,SAAS;EAAgB,SAAS;EAAa;AAG1D,QAAO;EACL,SAAS,eAAe;EACxB,SAAS,eAAe,WAAW;EACpC;;;;;;;AAQH,SAAS,cAAc,OAA+C;AACpE,KAAI,CAAC,MAAO,QAAO,KAAA;AAInB,QAAO,MAAM,QAAQ,gCAAgC,OAAO,YAAY;EACtE,MAAM,WAAW,QAAQ,IAAI;AAC7B,SAAO,aAAa,KAAA,IAAY,WAAW;GAC3C;;;;;AAMJ,SAAS,aAAa,UAAqC;AACzD,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;AAGT,KAAI;EACF,MAAM,UAAU,aAAa,UAAU,QAAQ;AAE/C,SADe,KAAK,KAAK,QAAQ,IAChB,EAAE;UACZ,OAAO;AACd,UAAQ,MAAM,kCAAkC,SAAS,IAAI,MAAM;AACnE,SAAO;;;;;;AAOX,SAAS,gBAAgB,WAAmB,QAAQ,KAAK,EAAiB;CACxE,IAAI,aAAa;AAEjB,QAAO,eAAe,KAAK;AACzB,MAAI,WAAW,KAAK,YAAY,OAAO,CAAC,CACtC,QAAO;AAET,eAAa,KAAK,YAAY,KAAK;;AAGrC,QAAO;;;;;AAMT,SAAS,oBAAuC;CAC9C,MAAM,cAAc,iBAAiB;AACrC,KAAI,CAAC,YACH,QAAO;CAGT,MAAM,gBAAgB,KAAK,aAAa,YAAY;AACpD,KAAI,WAAW,cAAc,CAC3B,QAAO,aAAa,cAAc;CAGpC,MAAM,mBAAmB,KAAK,aAAa,mBAAmB;AAC9D,KAAI,WAAW,iBAAiB,EAAE;AAChC,UAAQ,OAAO,MACb,8FACD;AACD,SAAO,aAAa,iBAAiB;;AAGvC,QAAO;;;;;AAMT,SAAS,mBAAsC;AAC7C,QAAO,aAAa,mBAAmB;;;;;AAMzC,SAAS,kBACP,QACA,QACM;AACN,KAAI,CAAC,QAAQ,OAAQ;AAGrB,KAAI,OAAO,OAAO,YAAY,KAAA,EAC5B,QAAO,UAAU,OAAO,OAAO;AAIjC,KAAI,OAAO,OAAO,UAAU;AAC1B,MAAI,OAAO,OAAO,SAAS,WAAW,KAAA,EACpC,QAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAElD,MAAI,OAAO,OAAO,SAAS,WAAW,KAAA,EACpC,QAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAElD,MAAI,OAAO,OAAO,SAAS,WAAW,KAAA,EACpC,QAAO,SAAS,SAAS,OAAO,OAAO,SAAS;AAElD,MAAI,OAAO,OAAO,SAAS,UAAU,KAAA,EACnC,QAAO,SAAS,QAAQ,OAAO,OAAO,SAAS;;;;;;AAQrD,SAAS,aAAa,GAAG,SAAkD;CACzE,MAAM,SAA2B;EAC/B,GAAG;EACH,kBAAkB,IAAI,IAAI,eAAe,iBAAiB;EAC1D,QAAQ;GACN,SAAS,eAAe,OAAO;GAC/B,UAAU,EAAE,GAAG,eAAe,OAAO,UAAU;GAChD;EACF;CAGD,MAAM,eAAe,QAAQ,QAAQ,MAAuB,MAAM,KAAK;AAGvE,MAAK,MAAM,UAAU,aAAa,SAAS,EAAE;AAE3C,MAAI,OAAO,QAAQ,WAAW;GAC5B,MAAM,YAAY,OAAO,OAAO;GAChC,MAAM,aAAa,OAAO,YAAY,EAAE;AAIxC,OADkB,wBAAwB,UAAU,UAAU,CAChD,QACZ,QAAO,iBAAiB,IAAI,YAAY;GAI1C,MAAM,SAAS,wBAAwB,UAAU,QAAQ,WAAW,OAAO;AAC3E,OAAI,OAAO,SAAS;AAClB,WAAO,iBAAiB,IAAI,SAAS;AACrC,QAAI,OAAO,QACT,QAAO,QAAQ,SAAS,cAAc,OAAO,QAAQ;;GAKzD,MAAM,SAAS,wBAAwB,UAAU,QAAQ,WAAW,OAAO;AAC3E,OAAI,OAAO,SAAS;AAClB,WAAO,iBAAiB,IAAI,SAAS;AACrC,QAAI,OAAO,QACT,QAAO,QAAQ,SAAS,cAAc,OAAO,QAAQ;;GAKzD,MAAM,MAAM,wBAAwB,UAAU,KAAK,WAAW,IAAI;AAClE,OAAI,IAAI,SAAS;AACf,WAAO,iBAAiB,IAAI,MAAM;AAClC,QAAI,IAAI,QACN,QAAO,QAAQ,MAAM,cAAc,IAAI,QAAQ;;GAKnD,MAAM,OAAO,wBAAwB,UAAU,MAAM,WAAW,KAAK;AACrE,OAAI,KAAK,SAAS;AAChB,WAAO,iBAAiB,IAAI,OAAO;AACnC,QAAI,KAAK,QACP,QAAO,QAAQ,OAAO,cAAc,KAAK,QAAQ;;GAKrD,MAAM,UAAU,wBAAwB,UAAU,SAAS,WAAW,QAAQ;AAC9E,OAAI,QAAQ,SAAS;AACnB,WAAO,iBAAiB,IAAI,UAAU;AACtC,QAAI,QAAQ,QACV,QAAO,QAAQ,UAAU,cAAc,QAAQ,QAAQ;;GAK3D,MAAM,aAAa,wBAAwB,UAAU,WAAW;AAChE,OAAI,WAAW,SAAS;AACtB,WAAO,iBAAiB,IAAI,aAAa;AACzC,QAAI,WAAW,QACb,QAAO,QAAQ,aAAa,cAAc,WAAW,QAAQ;;;AAMnE,MAAI,OAAO,YAAY,UACrB,QAAO,sBAAsB,OAAO,WAAW;AAMjD,MAAI,OAAO,UAAU;GACnB,MAAM,qBAAqB,CAAC,CAAC,OAAO,QAAQ;AAC5C,OAAI,OAAO,SAAS,QAAQ;AAC1B,WAAO,QAAQ,SAAS,cAAc,OAAO,SAAS,OAAO;AAC7D,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,SAAS;;AAEhE,OAAI,OAAO,SAAS,QAAQ;AAC1B,WAAO,QAAQ,SAAS,cAAc,OAAO,SAAS,OAAO;AAC7D,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,SAAS;;AAEhE,OAAI,OAAO,SAAS,KAAK;AACvB,WAAO,QAAQ,MAAM,cAAc,OAAO,SAAS,IAAI;AACvD,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,MAAM;;AAE7D,OAAI,OAAO,SAAS,MAAM;AACxB,WAAO,QAAQ,OAAO,cAAc,OAAO,SAAS,KAAK;AACzD,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,OAAO;;AAE9D,OAAI,OAAO,SAAS,SAAS;AAC3B,WAAO,QAAQ,UAAU,cAAc,OAAO,SAAS,QAAQ;AAC/D,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,UAAU;;AAEjE,OAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,QAAQ,aAAa,cAAc,OAAO,SAAS,WAAW;AACrE,QAAI,CAAC,mBAAoB,QAAO,iBAAiB,IAAI,aAAa;;;AAKtE,MAAI,OAAO,QAAQ,UACjB,QAAO,YAAY;GACjB,GAAG,OAAO;GACV,GAAG,OAAO,OAAO;GAClB;AAIH,MAAI,OAAO,QAAQ,sBACjB,QAAO,sBAAsB,OAAO,OAAO;AAI7C,MAAI,OAAO,cAAc;AACvB,OAAI,OAAO,aAAa,OACtB,QAAO,YAAY,SAAS,cAAc,OAAO,aAAa,OAAO;AAEvE,OAAI,OAAO,aAAa,OACtB,QAAO,YAAY,SAAS,cAAc,OAAO,aAAa,OAAO;AAEvE,OAAI,OAAO,aAAa,OACtB,QAAO,YAAY,SAAS,cAAc,OAAO,aAAa,OAAO;AAEvE,OAAI,OAAO,aAAa,MACtB,QAAO,YAAY,QAAQ,cAAc,OAAO,aAAa,MAAM;;AAKvE,oBAAkB,OAAO,QAAQ,OAAO;;AAG1C,QAAO;;;;;;;AAQT,SAAS,uBAAuB,QAI7B;AACD,KAAI,CAAC,QAAQ,QAAQ,UACnB,QAAO,EAAE;CAGX,MAAM,aAAwE,EAAE;AAEhF,MAAK,MAAM,CAAC,UAAU,YAAY,OAAO,QAAQ,OAAO,OAAO,UAAU,CACvE,KAAI,WAAW,mBAAmB,SAChC,YAAW,KAAK;EACJ;EACV,MAAM;EACN,IAAI,mBAAmB;EACxB,CAAC;AAIN,QAAO;;;;;AAMT,SAAS,gBACP,QACA,YACM;AACN,KAAI,CAAC,OAAO,OACV,QAAO,SAAS,EAAE;AAEpB,KAAI,CAAC,OAAO,OAAO,UACjB,QAAO,OAAO,YAAY,EAAE;AAG9B,MAAK,MAAM,EAAE,UAAU,QAAQ,WAC7B,QAAO,OAAO,UAAU,YAAY;;;;;AAOxC,SAAS,qBAA8B;AACrC,KAAI;AAEF,eAAa,oBADM,GAAG,mBAAmB,MACG;AAC5C,UAAQ,IAAI,4CAA4C;AACxD,SAAO;UACA,OAAO;AACd,UAAQ,MAAM,mCAAmC,MAAM;AACvD,SAAO;;;;;;AAOX,SAAS,kBAAkB,QAA0B;AAOnD,eAAc,oBANM,KAAK,KAAK,QAAQ;EACpC,QAAQ;EACR,WAAW;EACX,QAAQ;EACT,CAAC,EAE6C,QAAQ;;;;;;;;;AAUzD,SAAgB,aAA+B;CAC7C,IAAI,eAAe,kBAAkB;CACrC,MAAM,gBAAgB,mBAAmB;CAGzC,IAAI;AACJ,KAAI,gBAAgB,iBAAiB,EAAE;EACrC,MAAM,aAAa,uBAAuB,aAAa;AAEvD,MAAI,WAAW,SAAS,GAAG;GAEzB,MAAM,WAAW,oBAAoB;AAGrC,mBAAgB,cAAc,WAAW;AAGzC,qBAAkB,aAAa;AAG/B,WAAQ,IAAI,2BAA2B;AACvC,QAAK,MAAM,EAAE,UAAU,MAAM,QAAQ,WACnC,SAAQ,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;AAE/C,WAAQ,IAAI,GAAG;AAEf,qBAAkB;IAAE,UAAU;IAAY;IAAU;;;CAIxD,MAAM,SAAS,aAAa,eAAe,aAAa;AAIxD,KAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,QAAQ,QAAQ;AACxD,SAAO,QAAQ,SAAS,QAAQ,IAAI;AACpC,SAAO,iBAAiB,IAAI,SAAS;;AAEvC,KAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,QAAQ,QAAQ;AACxD,SAAO,QAAQ,SAAS,QAAQ,IAAI;AACpC,SAAO,iBAAiB,IAAI,SAAS;;AAEvC,KAAI,QAAQ,IAAI,eAAe,CAAC,OAAO,QAAQ,KAAK;AAClD,SAAO,QAAQ,MAAM,QAAQ,IAAI;AACjC,SAAO,iBAAiB,IAAI,MAAM;;AAEpC,KAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,QAAQ,MAAM;AACpD,SAAO,QAAQ,OAAO,QAAQ,IAAI;AAClC,SAAO,iBAAiB,IAAI,OAAO;;AAErC,KAAI,QAAQ,IAAI,sBAAsB,CAAC,OAAO,QAAQ,YAAY;AAChE,SAAO,QAAQ,aAAa,QAAQ,IAAI;AACxC,SAAO,iBAAiB,IAAI,aAAa;;AAI3C,KAAI,QAAQ,IAAI,kBAAkB,CAAC,OAAO,YAAY,OACpD,QAAO,YAAY,SAAS,QAAQ,IAAI;AAE1C,KAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,YAAY,OAClD,QAAO,YAAY,SAAS,QAAQ,IAAI;AAE1C,KAAI,QAAQ,IAAI,gBAAgB,CAAC,OAAO,YAAY,OAClD,QAAO,YAAY,SAAS,QAAQ,IAAI;AAE1C,KAAI,QAAQ,IAAI,iBAAiB,CAAC,OAAO,YAAY,MACnD,QAAO,YAAY,QAAQ,QAAQ,IAAI;AAKzC,KAAI,QAAQ,IAAI,gBAAgB,KAAA,GAAW;EACzC,MAAM,gBAAgB;GAAC;GAAQ;GAAK;GAAM,CAAC,SAAS,QAAQ,IAAI,YAAY,aAAa,CAAC;AAC1F,SAAO,OAAO,UAAU;;AAG1B,QAAO;EAAE;EAAQ,WAAW;EAAiB;;;;;AAe/C,SAAgB,kBAA2B;AACzC,QAAO,WAAW,mBAAmB;;;;0BAzpBsC;AAiLvE,kBAAmC;EACvC,kBAAkB,IAAI,IAAI,CAAC,YAAY,CAAC;EACxC,SAAS,EAAE;EACX,qBAAqB,EAAE;EACvB,WAAW,EAAE;EACb,qBAAqB;EACrB,aAAa,EAAE;EACf,QAAQ;GACN,SAAS;GACT,UAAU;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACR;GACF;EACF;AAKK,sBAAqB,KAAK,SAAS,EAAE,eAAe,cAAc"}
@@ -1,3 +1,3 @@
1
- import { n as init_config_yaml, r as loadConfig } from "./config-yaml-BgOACZAB.js";
1
+ import { n as init_config_yaml, r as loadConfig } from "./config-yaml-DGbLSMCa.js";
2
2
  init_config_yaml();
3
3
  export { loadConfig };
@@ -19,4 +19,4 @@ function extractACFromDocument(doc) {
19
19
  //#endregion
20
20
  export { extractACFromDocument as t };
21
21
 
22
- //# sourceMappingURL=acceptance-criteria-e5iiHlRx.js.map
22
+ //# sourceMappingURL=acceptance-criteria-Dk9hhiYj.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"acceptance-criteria-e5iiHlRx.js","names":[],"sources":["../../src/lib/vbrief/acceptance-criteria.ts"],"sourcesContent":["/**\n * vBRIEF Acceptance Criteria Extraction & Validation\n *\n * Shared utilities for extracting, formatting, and validating acceptance\n * criteria from vBRIEF plan documents. Used by specialist prompts,\n * verification gates, and completion checks.\n */\n\nimport { readWorkspacePlan } from './io.js';\nimport type { VBriefDocument, VBriefItem, VBriefItemStatus, VBriefSubItem } from './types.js';\n\n/** A single acceptance criterion with its parent task context. */\nexport interface AcceptanceCriterion {\n /** Parent item ID (e.g., \"create-ac-module\") */\n itemId: string;\n /** Parent item title */\n itemTitle: string;\n /** Sub-item ID (e.g., \"create-ac-module.extract-fn\") */\n subItemId: string;\n /** AC description */\n title: string;\n /** Current status */\n status: VBriefItemStatus;\n}\n\n/** Result of checking whether all AC are completed. */\nexport interface ACCompletionResult {\n allCompleted: boolean;\n incomplete: AcceptanceCriterion[];\n}\n\n/**\n * Extract all acceptance criteria from a vBRIEF plan.\n *\n * Reads plan.vbrief.json from the workspace and returns all subItems\n * where metadata.kind === 'acceptance_criterion', enriched with parent\n * task context.\n *\n * @returns Array of acceptance criteria, or empty array if no plan exists\n * or no AC are found (legacy workspace compatibility).\n */\nexport function extractAcceptanceCriteria(workspacePath: string): AcceptanceCriterion[] {\n const doc = readWorkspacePlan(workspacePath);\n if (!doc) return [];\n return extractACFromDocument(doc);\n}\n\n/**\n * Extract AC from an already-loaded document (avoids re-reading the file).\n */\nexport function extractACFromDocument(doc: VBriefDocument): AcceptanceCriterion[] {\n const criteria: AcceptanceCriterion[] = [];\n\n for (const item of doc.plan.items) {\n if (!item.subItems) continue;\n for (const sub of item.subItems) {\n if (sub.metadata?.kind === 'acceptance_criterion') {\n criteria.push({\n itemId: item.id,\n itemTitle: item.title,\n subItemId: sub.id,\n title: sub.title,\n status: sub.status,\n });\n }\n }\n }\n\n return criteria;\n}\n\n/**\n * Format acceptance criteria as a markdown checklist grouped by parent task.\n *\n * Output example:\n * ```\n * ### Create vBRIEF acceptance criteria extraction module\n * - [x] extractAcceptanceCriteria reads plan.vbrief.json and returns AC subItems\n * - [ ] formatAcceptanceCriteria produces markdown checklist grouped by parent task\n * ```\n *\n * @returns Formatted markdown string, or empty string if no criteria.\n */\nexport function formatAcceptanceCriteria(criteria: AcceptanceCriterion[]): string {\n if (criteria.length === 0) return '';\n\n // Group by parent item\n const groups = new Map<string, { title: string; items: AcceptanceCriterion[] }>();\n for (const ac of criteria) {\n let group = groups.get(ac.itemId);\n if (!group) {\n group = { title: ac.itemTitle, items: [] };\n groups.set(ac.itemId, group);\n }\n group.items.push(ac);\n }\n\n const lines: string[] = [];\n for (const group of Array.from(groups.values())) {\n lines.push(`### ${group.title}`);\n for (const ac of group.items) {\n const check = ac.status === 'completed' ? 'x' : ' ';\n lines.push(`- [${check}] ${ac.title}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n').trimEnd();\n}\n\n/**\n * Check whether all acceptance criteria in a workspace plan are completed.\n *\n * @returns { allCompleted: true, incomplete: [] } when all AC are done or\n * no plan/AC exist (legacy workspace compatibility).\n */\nexport function checkAllCriteriaCompleted(workspacePath: string): ACCompletionResult {\n const criteria = extractAcceptanceCriteria(workspacePath);\n if (criteria.length === 0) return { allCompleted: true, incomplete: [] };\n\n const incomplete = criteria.filter(\n ac => ac.status !== 'completed' && ac.status !== 'cancelled'\n );\n\n return { allCompleted: incomplete.length === 0, incomplete };\n}\n"],"mappings":";;;;AAkDA,SAAgB,sBAAsB,KAA4C;CAChF,MAAM,WAAkC,EAAE;AAE1C,MAAK,MAAM,QAAQ,IAAI,KAAK,OAAO;AACjC,MAAI,CAAC,KAAK,SAAU;AACpB,OAAK,MAAM,OAAO,KAAK,SACrB,KAAI,IAAI,UAAU,SAAS,uBACzB,UAAS,KAAK;GACZ,QAAQ,KAAK;GACb,WAAW,KAAK;GAChB,WAAW,IAAI;GACf,OAAO,IAAI;GACX,QAAQ,IAAI;GACb,CAAC;;AAKR,QAAO"}
1
+ {"version":3,"file":"acceptance-criteria-Dk9hhiYj.js","names":[],"sources":["../../src/lib/vbrief/acceptance-criteria.ts"],"sourcesContent":["/**\n * vBRIEF Acceptance Criteria Extraction & Validation\n *\n * Shared utilities for extracting, formatting, and validating acceptance\n * criteria from vBRIEF plan documents. Used by specialist prompts,\n * verification gates, and completion checks.\n */\n\nimport { readWorkspacePlan } from './io.js';\nimport type { VBriefDocument, VBriefItem, VBriefItemStatus, VBriefSubItem } from './types.js';\n\n/** A single acceptance criterion with its parent task context. */\nexport interface AcceptanceCriterion {\n /** Parent item ID (e.g., \"create-ac-module\") */\n itemId: string;\n /** Parent item title */\n itemTitle: string;\n /** Sub-item ID (e.g., \"create-ac-module.extract-fn\") */\n subItemId: string;\n /** AC description */\n title: string;\n /** Current status */\n status: VBriefItemStatus;\n}\n\n/** Result of checking whether all AC are completed. */\nexport interface ACCompletionResult {\n allCompleted: boolean;\n incomplete: AcceptanceCriterion[];\n}\n\n/**\n * Extract all acceptance criteria from a vBRIEF plan.\n *\n * Reads plan.vbrief.json from the workspace and returns all subItems\n * where metadata.kind === 'acceptance_criterion', enriched with parent\n * task context.\n *\n * @returns Array of acceptance criteria, or empty array if no plan exists\n * or no AC are found (legacy workspace compatibility).\n */\nexport function extractAcceptanceCriteria(workspacePath: string): AcceptanceCriterion[] {\n const doc = readWorkspacePlan(workspacePath);\n if (!doc) return [];\n return extractACFromDocument(doc);\n}\n\n/**\n * Extract AC from an already-loaded document (avoids re-reading the file).\n */\nexport function extractACFromDocument(doc: VBriefDocument): AcceptanceCriterion[] {\n const criteria: AcceptanceCriterion[] = [];\n\n for (const item of doc.plan.items) {\n if (!item.subItems) continue;\n for (const sub of item.subItems) {\n if (sub.metadata?.kind === 'acceptance_criterion') {\n criteria.push({\n itemId: item.id,\n itemTitle: item.title,\n subItemId: sub.id,\n title: sub.title,\n status: sub.status,\n });\n }\n }\n }\n\n return criteria;\n}\n\n/**\n * Format acceptance criteria as a markdown checklist grouped by parent task.\n *\n * Output example:\n * ```\n * ### Create vBRIEF acceptance criteria extraction module\n * - [x] extractAcceptanceCriteria reads plan.vbrief.json and returns AC subItems\n * - [ ] formatAcceptanceCriteria produces markdown checklist grouped by parent task\n * ```\n *\n * @returns Formatted markdown string, or empty string if no criteria.\n */\nexport function formatAcceptanceCriteria(criteria: AcceptanceCriterion[]): string {\n if (criteria.length === 0) return '';\n\n // Group by parent item\n const groups = new Map<string, { title: string; items: AcceptanceCriterion[] }>();\n for (const ac of criteria) {\n let group = groups.get(ac.itemId);\n if (!group) {\n group = { title: ac.itemTitle, items: [] };\n groups.set(ac.itemId, group);\n }\n group.items.push(ac);\n }\n\n const lines: string[] = [];\n for (const group of Array.from(groups.values())) {\n lines.push(`### ${group.title}`);\n for (const ac of group.items) {\n const check = ac.status === 'completed' ? 'x' : ' ';\n lines.push(`- [${check}] ${ac.title}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n').trimEnd();\n}\n\n/**\n * Check whether all acceptance criteria in a workspace plan are completed.\n *\n * @returns { allCompleted: true, incomplete: [] } when all AC are done or\n * no plan/AC exist (legacy workspace compatibility).\n */\nexport function checkAllCriteriaCompleted(workspacePath: string): ACCompletionResult {\n const criteria = extractAcceptanceCriteria(workspacePath);\n if (criteria.length === 0) return { allCompleted: true, incomplete: [] };\n\n const incomplete = criteria.filter(\n ac => ac.status !== 'completed' && ac.status !== 'cancelled'\n );\n\n return { allCompleted: incomplete.length === 0, incomplete };\n}\n"],"mappings":";;;;AAkDA,SAAgB,sBAAsB,KAA4C;CAChF,MAAM,WAAkC,EAAE;AAE1C,MAAK,MAAM,QAAQ,IAAI,KAAK,OAAO;AACjC,MAAI,CAAC,KAAK,SAAU;AACpB,OAAK,MAAM,OAAO,KAAK,SACrB,KAAI,IAAI,UAAU,SAAS,uBACzB,UAAS,KAAK;GACZ,QAAQ,KAAK;GACb,WAAW,KAAK;GAChB,WAAW,IAAI;GACf,OAAO,IAAI;GACX,QAAQ,IAAI;GACb,CAAC;;AAKR,QAAO"}
@@ -1,6 +1,8 @@
1
- import { p as init_projects, y as resolveProjectFromIssue } from "./projects-Cq3TWdPS.js";
2
- import { i as init_tracker_config, t as getGitHubConfig } from "./tracker-config-CNM_5rEf.js";
3
- import { a as getAgentDir, m as init_agents, s as getAgentRuntimeState } from "./agents-YyO6t5Xa.js";
1
+ import { _ as init_paths, h as encodeClaudeProjectDir } from "./paths-BDyJ7BiV.js";
2
+ import { n as extractPrefix, r as init_issue_id } from "./issue-id-vwYJdsf8.js";
3
+ import { p as init_projects, y as resolveProjectFromIssue } from "./projects-CFVl4oHn.js";
4
+ import { i as init_tracker_config, t as getGitHubConfig } from "./tracker-config-e7ph1QqT.js";
5
+ import { a as getAgentDir, m as init_agents, s as getAgentRuntimeState } from "./agents-DCpQQ_W5.js";
4
6
  import { existsSync, readFileSync, readdirSync, statSync } from "fs";
5
7
  import { join } from "path";
6
8
  import { homedir } from "os";
@@ -17,13 +19,14 @@ import { readFile } from "fs/promises";
17
19
  * Used by both the legacy REST /api/agents endpoint and the new
18
20
  * AgentEnrichmentService background poller.
19
21
  */
22
+ init_paths();
20
23
  init_agents();
21
24
  init_projects();
22
25
  init_tracker_config();
26
+ init_issue_id();
23
27
  const execAsync = promisify(exec);
24
28
  function getClaudeProjectDir(workspacePath) {
25
- const dirName = workspacePath.replace(/^\//, "").replace(/\//g, "-");
26
- return join(homedir(), ".claude", "projects", `-${dirName}`);
29
+ return join(homedir(), ".claude", "projects", encodeClaudeProjectDir(workspacePath));
27
30
  }
28
31
  function getActiveSessionPath(projectDir) {
29
32
  if (!existsSync(projectDir)) return null;
@@ -66,7 +69,8 @@ async function getAgentWorkspace(agentId) {
66
69
  if (trimmed && existsSync(trimmed)) return trimmed;
67
70
  } catch {}
68
71
  const issueId = agentId.replace(/^(agent-|planning-)/, "").toUpperCase();
69
- const prefix = issueId.split("-")[0];
72
+ const prefix = extractPrefix(issueId);
73
+ if (!prefix) return null;
70
74
  try {
71
75
  const projectPath = getProjectPathByPrefix(prefix);
72
76
  const workspacePath = join(projectPath, "workspaces", `feature-${issueId.toLowerCase()}`);
@@ -164,4 +168,4 @@ async function computeAgentEnrichment(agentId, startedAt, hasActiveSpecialist, s
164
168
  //#endregion
165
169
  export { getAgentPendingQuestions as a, getPendingQuestions as c, getAgentJsonlPath as i, getActiveSessionPath as n, getAgentWorkspace as o, getAgentJsonlMtime as r, getClaudeProjectDir as s, computeAgentEnrichment as t };
166
170
 
167
- //# sourceMappingURL=agent-enrichment-C67LJBgD.js.map
171
+ //# sourceMappingURL=agent-enrichment-DdO7ZqjI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-enrichment-DdO7ZqjI.js","names":[],"sources":["../../src/lib/agent-enrichment.ts"],"sourcesContent":["/**\n * Agent enrichment utilities (PAN-440)\n *\n * Shared functions for computing enrichment fields:\n * agentPhase, hasPendingQuestion, pendingQuestionCount, resolution, resolutionCount\n *\n * Used by both the legacy REST /api/agents endpoint and the new\n * AgentEnrichmentService background poller.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport { homedir } from 'os'\nimport { join } from 'path'\nimport { encodeClaudeProjectDir } from './paths.js'\nimport { promisify } from 'util'\nimport { exec } from 'child_process'\nimport { getAgentRuntimeState, getAgentDir } from './agents.js'\nimport { resolveProjectFromIssue } from './projects.js'\nimport { getGitHubConfig } from '../dashboard/server/services/tracker-config.js'\nimport { extractPrefix } from './issue-id.js'\n\nconst execAsync = promisify(exec)\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface QuestionOption { label: string; description: string }\nexport interface Question { question: string; header: string; options: QuestionOption[]; multiSelect: boolean }\nexport interface PendingQuestion { toolId: string; timestamp: string; questions: Question[] }\n\nexport interface AgentEnrichment {\n agentPhase: 'planning' | 'exploration' | 'implementation' | 'testing' | 'documentation' | 'pre_push' | 'post_push'\n hasPendingQuestion: boolean\n pendingQuestionCount: number\n resolution: string\n resolutionCount: number\n}\n\n// ─── JSONL path helpers ───────────────────────────────────────────────────────\n\nexport function getClaudeProjectDir(workspacePath: string): string {\n return join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspacePath))\n}\n\nexport function getActiveSessionPath(projectDir: string): string | null {\n if (!existsSync(projectDir)) return null\n try {\n const files = readdirSync(projectDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({\n name: f,\n path: join(projectDir, f),\n mtime: statSync(join(projectDir, f)).mtime.getTime(),\n }))\n .sort((a, b) => b.mtime - a.mtime)\n return files.length > 0 ? files[0].path : null\n } catch {\n return null\n }\n}\n\nfunction getProjectPathByPrefix(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`\n const resolved = resolveProjectFromIssue(issueId)\n if (resolved) return resolved.projectPath\n const config = getGitHubConfig()\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '')\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ]\n for (const path of possiblePaths) {\n if (existsSync(path)) return path\n }\n }\n }\n }\n return join(homedir(), 'Projects')\n}\n\nexport async function getAgentWorkspace(agentId: string): Promise<string | null> {\n const stateFile = join(getAgentDir(agentId), 'state.json')\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n if (state.workspace) return state.workspace\n } catch {}\n }\n try {\n const { stdout: paneCwd } = await execAsync(\n `tmux display-message -t ${agentId} -p '#{pane_current_path}' 2>/dev/null`,\n { encoding: 'utf-8' }\n )\n const trimmed = paneCwd.trim()\n if (trimmed && existsSync(trimmed)) return trimmed\n } catch {}\n const issueId = agentId.replace(/^(agent-|planning-)/, '').toUpperCase()\n const prefix = extractPrefix(issueId)\n if (!prefix) return null\n try {\n const projectPath = getProjectPathByPrefix(prefix)\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`)\n if (existsSync(workspacePath)) return workspacePath\n return projectPath\n } catch {\n return null\n }\n}\n\nexport async function getAgentJsonlPath(agentId: string): Promise<string | null> {\n const workspace = await getAgentWorkspace(agentId)\n if (!workspace) return null\n const projectDir = getClaudeProjectDir(workspace)\n return getActiveSessionPath(projectDir)\n}\n\n// ─── JSONL scanning ───────────────────────────────────────────────────────────\n\nexport async function getPendingQuestions(jsonlPath: string): Promise<PendingQuestion[]> {\n if (!existsSync(jsonlPath)) return []\n try {\n const content = await readFile(jsonlPath, 'utf-8')\n const lines = content.split('\\n').filter(line => line.trim())\n const toolCalls = new Map<string, PendingQuestion>()\n const answeredIds = new Set<string>()\n for (const line of lines) {\n try {\n const entry = JSON.parse(line)\n const messageContent = entry.message?.content\n if (!Array.isArray(messageContent)) continue\n for (const item of messageContent) {\n if (item.type === 'tool_use' && item.name === 'AskUserQuestion') {\n toolCalls.set(item.id, {\n toolId: item.id,\n timestamp: entry.timestamp || new Date().toISOString(),\n questions: item.input?.questions || [],\n })\n }\n if (item.type === 'tool_result' && item.tool_use_id) {\n answeredIds.add(item.tool_use_id)\n }\n }\n } catch {}\n }\n return Array.from(toolCalls.entries())\n .filter(([id]) => !answeredIds.has(id))\n .map(([, question]) => question)\n } catch {\n return []\n }\n}\n\nexport async function getAgentPendingQuestions(agentId: string): Promise<PendingQuestion[]> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath) return []\n return getPendingQuestions(jsonlPath)\n}\n\n/**\n * Get the mtime (ms since epoch) of the agent's active JSONL session file.\n * Returns null if the file doesn't exist or the path can't be resolved.\n * Used by AgentEnrichmentService to skip JSONL scans when the file is unchanged.\n */\nexport async function getAgentJsonlMtime(agentId: string): Promise<number | null> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath || !existsSync(jsonlPath)) return null\n try {\n return statSync(jsonlPath).mtime.getTime()\n } catch {\n return null\n }\n}\n\n// ─── Enrichment computation ───────────────────────────────────────────────────\n\n/**\n * Compute the full enrichment snapshot for a single agent.\n *\n * @param agentId - Agent session name (e.g. 'agent-pan-440', 'planning-pan-440')\n * @param startedAt - ISO timestamp when the agent started (filters stale questions)\n * @param hasActiveSpecialist - Whether the agent's issue has an active specialist running\n * @param skipJsonlScan - Skip JSONL file scan (use when mtime is unchanged); still reads runtime state\n */\nexport async function computeAgentEnrichment(\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n skipJsonlScan?: boolean,\n): Promise<AgentEnrichment> {\n const isPlanning = agentId.startsWith('planning-')\n\n // Read state.json for current phase\n const stateFile = join(getAgentDir(agentId), 'state.json')\n let statePhase: string | undefined\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n statePhase = state.phase\n } catch {}\n }\n\n const agentPhase = (isPlanning ? 'planning' : (statePhase || 'implementation')) as AgentEnrichment['agentPhase']\n\n // Get runtime state for resolution + idle detection\n const runtimeState = getAgentRuntimeState(agentId)\n const isIdle = runtimeState?.state === 'idle' ||\n (runtimeState?.currentTool === 'AskUserQuestion')\n\n // Get pending questions, filtered by agent start time\n // Skip JSONL scan when mtime is unchanged (optimization for static TUI sessions)\n let pendingQuestions: PendingQuestion[] = []\n if (!skipJsonlScan) {\n pendingQuestions = await getAgentPendingQuestions(agentId)\n if (pendingQuestions.length > 0 && startedAt) {\n const agentStartTime = new Date(startedAt).getTime()\n pendingQuestions = pendingQuestions.filter(q => {\n const qTime = new Date(q.timestamp).getTime()\n return !isNaN(qTime) && qTime >= agentStartTime\n })\n }\n }\n\n const hasPendingQuestion =\n !hasActiveSpecialist &&\n (pendingQuestions.length > 0 || isIdle || runtimeState?.resolution === 'needs_input')\n\n return {\n agentPhase,\n hasPendingQuestion,\n pendingQuestionCount: pendingQuestions.length,\n resolution: runtimeState?.resolution || 'working',\n resolutionCount: runtimeState?.resolutionCount || 0,\n }\n}\n"],"mappings":"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"}