@overdeck/core 0.45.10 → 0.45.11

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 (219) hide show
  1. package/dist/{agent-state-KVbJEG5f.js → agent-state-RLhS7ckO.js} +1 -1
  2. package/dist/{agents-pp8FO7Uj.js → agents-By3u_fQ8.js} +1 -1
  3. package/dist/{agents-CQHK0CSA.js → agents-CgVr77Qq.js} +12 -12
  4. package/dist/{agents-CQHK0CSA.js.map → agents-CgVr77Qq.js.map} +1 -1
  5. package/dist/{backlog-auto-trigger-CyuoS0nl.js → backlog-auto-trigger-B_-1tk4e.js} +5 -5
  6. package/dist/{backlog-auto-trigger-CyuoS0nl.js.map → backlog-auto-trigger-B_-1tk4e.js.map} +1 -1
  7. package/dist/cli/index.js +45 -45
  8. package/dist/{cli-Cewlu3yU.js → cli-DGeSW85B.js} +2 -2
  9. package/dist/{cli-Cewlu3yU.js.map → cli-DGeSW85B.js.map} +1 -1
  10. package/dist/cli-MTB9IOoR.js +3 -0
  11. package/dist/{close-issue-BJGX9_z7.js → close-issue-Djajb6Sv.js} +2 -2
  12. package/dist/{close-issue-BJGX9_z7.js.map → close-issue-Djajb6Sv.js.map} +1 -1
  13. package/dist/{concurrency-Ca-fC7tA.js → concurrency-4rzjHAQA.js} +1 -1
  14. package/dist/{conversation-compaction-B_6s9EvM.js → conversation-compaction-BQvZh09I.js} +4 -4
  15. package/dist/{conversation-compaction-B_6s9EvM.js.map → conversation-compaction-BQvZh09I.js.map} +1 -1
  16. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js → advancing-selfheal-BezNfM3P.js} +3 -3
  17. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js.map → advancing-selfheal-BezNfM3P.js.map} +1 -1
  18. package/dist/dashboard/{agent-state-xk_G8t90.js → agent-state-BUANcaA7.js} +1 -1
  19. package/dist/dashboard/{agent-state-service-C1iUF9Ft.js → agent-state-service-DmFpIMbP.js} +1 -1
  20. package/dist/dashboard/{agent-state-service-5ge9xxhx.js → agent-state-service-tXUdp6UF.js} +2 -2
  21. package/dist/dashboard/{agent-state-service-5ge9xxhx.js.map → agent-state-service-tXUdp6UF.js.map} +1 -1
  22. package/dist/dashboard/{agents-BIggLvi_.js → agents-C-vqmKDi.js} +1 -1
  23. package/dist/dashboard/{agents-DHvsjsuP.js → agents-C7V4odQ6.js} +11 -11
  24. package/dist/dashboard/{agents-DHvsjsuP.js.map → agents-C7V4odQ6.js.map} +1 -1
  25. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js → backlog-auto-trigger-Cb8PR-5w.js} +2 -2
  26. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js.map → backlog-auto-trigger-Cb8PR-5w.js.map} +1 -1
  27. package/dist/dashboard/{cli-CvK9xuSS.js → cli-BgzbB6h3.js} +2 -2
  28. package/dist/dashboard/{cli-CvK9xuSS.js.map → cli-BgzbB6h3.js.map} +1 -1
  29. package/dist/dashboard/cli-DCqy_wFa.js +3 -0
  30. package/dist/dashboard/{close-issue-BnpErnD1.js → close-issue-BWf3pxn4.js} +2 -2
  31. package/dist/dashboard/{close-issue-BnpErnD1.js.map → close-issue-BWf3pxn4.js.map} +1 -1
  32. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js → closed-issue-reaper-D7XPq5pY.js} +4 -4
  33. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js.map → closed-issue-reaper-D7XPq5pY.js.map} +1 -1
  34. package/dist/dashboard/{closed-issue-reaper-BLCQwTpF.js → closed-issue-reaper-VSTSTROK.js} +1 -1
  35. package/dist/dashboard/{conflict-gate-JdFuhKuF.js → conflict-gate-DwOslTij.js} +1 -1
  36. package/dist/dashboard/{conflict-gate-BhKvrPxn.js → conflict-gate-FFEvTDVA.js} +3 -3
  37. package/dist/dashboard/{conflict-gate-BhKvrPxn.js.map → conflict-gate-FFEvTDVA.js.map} +1 -1
  38. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js → conversation-compaction-CCqN4hWk.js} +4 -4
  39. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js.map → conversation-compaction-CCqN4hWk.js.map} +1 -1
  40. package/dist/dashboard/{conversation-compaction-Ba5aZdrb.js → conversation-compaction-CL5Ti4j4.js} +1 -1
  41. package/dist/dashboard/{deacon-j3qSZ7--.js → deacon-CGHl90eC.js} +107 -50
  42. package/dist/dashboard/{deacon-j3qSZ7--.js.map → deacon-CGHl90eC.js.map} +1 -1
  43. package/dist/dashboard/{deacon-Cv2RMfHs.js → deacon-DMPh40Im.js} +2 -2
  44. package/dist/dashboard/deacon-swarm-CVTiNL0N.js +2 -0
  45. package/dist/dashboard/{deacon-swarm-DadWNi5J.js → deacon-swarm-aMMJTq6t.js} +4 -4
  46. package/dist/dashboard/{deacon-swarm-DadWNi5J.js.map → deacon-swarm-aMMJTq6t.js.map} +1 -1
  47. package/dist/dashboard/deacon.js +4 -4
  48. package/dist/dashboard/{feedback-target-oE4-Esxl.js → feedback-target-dJjFfITP.js} +4 -4
  49. package/dist/dashboard/{feedback-target-oE4-Esxl.js.map → feedback-target-dJjFfITP.js.map} +1 -1
  50. package/dist/dashboard/{feedback-target-C4DaDSZ8.js → feedback-target-nQSM6ygL.js} +1 -1
  51. package/dist/dashboard/{flywheel-ClMjGW_u.js → flywheel-BLoVNtJV.js} +6 -6
  52. package/dist/dashboard/{flywheel-ClMjGW_u.js.map → flywheel-BLoVNtJV.js.map} +1 -1
  53. package/dist/dashboard/{flywheel-DefHRzY4.js → flywheel-Cqf8vu7N.js} +6 -6
  54. package/dist/dashboard/{flywheel-DefHRzY4.js.map → flywheel-Cqf8vu7N.js.map} +1 -1
  55. package/dist/dashboard/{flywheel-BqDdGvrQ.js → flywheel-PJsbrXNY.js} +1 -1
  56. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js → flywheel-merge-order-B7BH8mZ3.js} +2 -2
  57. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js.map → flywheel-merge-order-B7BH8mZ3.js.map} +1 -1
  58. package/dist/dashboard/{flywheel-merge-order-B6YDSElh.js → flywheel-merge-order-CCwF60b-.js} +1 -1
  59. package/dist/dashboard/{harness-resolve-DKeSVsJL.js → harness-resolve-B3f9G1Kp.js} +2 -2
  60. package/dist/dashboard/{harness-resolve-DKeSVsJL.js.map → harness-resolve-B3f9G1Kp.js.map} +1 -1
  61. package/dist/dashboard/{inspect-agent-BKKc0XnB.js → inspect-agent-CKF5-SJr.js} +3 -3
  62. package/dist/dashboard/{inspect-agent-BKKc0XnB.js.map → inspect-agent-CKF5-SJr.js.map} +1 -1
  63. package/dist/dashboard/inspect-agent-CqpEWGWy.js +2 -0
  64. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js → issue-lifecycle-5LbOcATP.js} +7 -7
  65. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js.map → issue-lifecycle-5LbOcATP.js.map} +1 -1
  66. package/dist/dashboard/{issue-lifecycle-nY4esZn8.js → issue-lifecycle-CjYqQ669.js} +1 -1
  67. package/dist/dashboard/{issue-service-singleton-Bnqv1uaa.js → issue-service-singleton-B8iu_TKV.js} +1 -1
  68. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js → issue-service-singleton-CDGt__oQ.js} +2 -2
  69. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js.map → issue-service-singleton-CDGt__oQ.js.map} +1 -1
  70. package/dist/dashboard/{lifecycle-CGKm7doE.js → lifecycle-CEg4sHZH.js} +4 -4
  71. package/dist/dashboard/{lifecycle-CGKm7doE.js.map → lifecycle-CEg4sHZH.js.map} +1 -1
  72. package/dist/dashboard/{merge-agent-Dh8Y9PDO.js → merge-agent-7nBiCytp.js} +1 -1
  73. package/dist/dashboard/{merge-agent-BcgAYStl.js → merge-agent-C81xPIGd.js} +6 -6
  74. package/dist/dashboard/{merge-agent-BcgAYStl.js.map → merge-agent-C81xPIGd.js.map} +1 -1
  75. package/dist/dashboard/merge-ops-DE_6CnmZ.js +2 -0
  76. package/dist/dashboard/{merge-train-DmxlYbEo.js → merge-train-D3qzRccL.js} +2 -2
  77. package/dist/dashboard/{merge-train-DmxlYbEo.js.map → merge-train-D3qzRccL.js.map} +1 -1
  78. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js → merge-train-deps-Be5ElC_P.js} +2 -2
  79. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js.map → merge-train-deps-Be5ElC_P.js.map} +1 -1
  80. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js → orphan-proposed-reconciler-Boq_XaRe.js} +3 -3
  81. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js.map → orphan-proposed-reconciler-Boq_XaRe.js.map} +1 -1
  82. package/dist/dashboard/{orphan-proposed-reconciler-mXV--tcB.js → orphan-proposed-reconciler-C0sPnDq_.js} +1 -1
  83. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js → reconstruct-cache-C2pCltOq.js} +3 -3
  84. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js.map → reconstruct-cache-C2pCltOq.js.map} +1 -1
  85. package/dist/dashboard/{remote-completion-Bklh6loJ.js → remote-completion-BmxD5UMQ.js} +3 -3
  86. package/dist/dashboard/{remote-completion-Bklh6loJ.js.map → remote-completion-BmxD5UMQ.js.map} +1 -1
  87. package/dist/dashboard/resume-C-nxu9Y4.js +3 -0
  88. package/dist/dashboard/{review-agent-phzqNqIs.js → review-agent-D6P6d3pz.js} +26 -17
  89. package/dist/dashboard/review-agent-D6P6d3pz.js.map +1 -0
  90. package/dist/dashboard/review-agent-DTINEffB.js +3 -0
  91. package/dist/dashboard/{review-context-CCq56O_Y.js → review-context-Dyo0tsBa.js} +2 -2
  92. package/dist/dashboard/{review-context-CCq56O_Y.js.map → review-context-Dyo0tsBa.js.map} +1 -1
  93. package/dist/dashboard/{review-status-BQjqQXZG.js → review-status-B5ZASOo4.js} +18 -5
  94. package/dist/dashboard/review-status-B5ZASOo4.js.map +1 -0
  95. package/dist/dashboard/{review-status-bBkqUj5i.js → review-status-h7BXIA4z.js} +1 -1
  96. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js → review-verdict-feedback-B70VbfRL.js} +4 -4
  97. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js.map → review-verdict-feedback-B70VbfRL.js.map} +1 -1
  98. package/dist/dashboard/{review-verdict-feedback-cZh9Wi9G.js → review-verdict-feedback-CorgH49f.js} +1 -1
  99. package/dist/dashboard/{sequence-io-B2GmilSI.js → sequence-io-6YP8fVpH.js} +2 -2
  100. package/dist/dashboard/{sequence-io-B2GmilSI.js.map → sequence-io-6YP8fVpH.js.map} +1 -1
  101. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js → sequencer-agent-ja-nkEc1.js} +5 -5
  102. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js.map → sequencer-agent-ja-nkEc1.js.map} +1 -1
  103. package/dist/dashboard/server.js +66 -55
  104. package/dist/dashboard/server.js.map +1 -1
  105. package/dist/dashboard/{service-CILd-uOt.js → service-HKDrIy0h.js} +24 -24
  106. package/dist/dashboard/{service-CILd-uOt.js.map → service-HKDrIy0h.js.map} +1 -1
  107. package/dist/dashboard/{smart-compaction-Bgp_gwcA.js → smart-compaction-L0LSnF0_.js} +1 -1
  108. package/dist/dashboard/{smart-compaction-CrP90OFE.js → smart-compaction-uke1npUz.js} +2 -2
  109. package/dist/dashboard/{smart-compaction-CrP90OFE.js.map → smart-compaction-uke1npUz.js.map} +1 -1
  110. package/dist/dashboard/{spawn-planning-session-D15KbPOy.js → spawn-planning-session-CSMWco8B.js} +1 -1
  111. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js → spawn-planning-session-CXc0NkwV.js} +3 -3
  112. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js.map → spawn-planning-session-CXc0NkwV.js.map} +1 -1
  113. package/dist/dashboard/{specialist-context-CwKkcXL9.js → specialist-context-BGCJ24LH.js} +3 -3
  114. package/dist/dashboard/{specialist-context-CwKkcXL9.js.map → specialist-context-BGCJ24LH.js.map} +1 -1
  115. package/dist/dashboard/{specialist-logs-CvDxquV_.js → specialist-logs--Uz-r8ia.js} +2 -2
  116. package/dist/dashboard/{specialist-logs-CvDxquV_.js.map → specialist-logs--Uz-r8ia.js.map} +1 -1
  117. package/dist/dashboard/{specialists-BCjcG4uE.js → specialists-Ca9XSBCS.js} +1 -1
  118. package/dist/dashboard/{specialists-D07ybnam.js → specialists-DEfrAbWs.js} +36 -36
  119. package/dist/dashboard/{specialists-D07ybnam.js.map → specialists-DEfrAbWs.js.map} +1 -1
  120. package/dist/dashboard/{specialists-F2uMO4QS.js → specialists-DeZx9xom.js} +1 -1
  121. package/dist/dashboard/{specialists-BYi0k8CZ.js → specialists-nZ0mRwpZ.js} +12 -12
  122. package/dist/dashboard/{specialists-BYi0k8CZ.js.map → specialists-nZ0mRwpZ.js.map} +1 -1
  123. package/dist/dashboard/{summary-fork-DedqguZb.js → summary-fork-2a1L2DIU.js} +2 -2
  124. package/dist/dashboard/{summary-fork-EjvOkEPl.js → summary-fork-Xn0QUNmb.js} +3 -3
  125. package/dist/dashboard/{summary-fork-EjvOkEPl.js.map → summary-fork-Xn0QUNmb.js.map} +1 -1
  126. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js → teardown-workspace-DhA6oAIK.js} +2 -2
  127. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js.map → teardown-workspace-DhA6oAIK.js.map} +1 -1
  128. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js → test-agent-queue-B_ukPyIK.js} +3 -3
  129. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js.map → test-agent-queue-B_ukPyIK.js.map} +1 -1
  130. package/dist/dashboard/{uat-train-CtNGgXxj.js → uat-train-BLBAbJ8Y.js} +10 -10
  131. package/dist/dashboard/{uat-train-CtNGgXxj.js.map → uat-train-BLBAbJ8Y.js.map} +1 -1
  132. package/dist/dashboard/{uat-train-C3hWXtip.js → uat-train-DPfGR9_I.js} +1 -1
  133. package/dist/dashboard/{verification-runner-DuZRHjPU.js → verification-runner-KTGTNugM.js} +1 -1
  134. package/dist/dashboard/{verification-runner-BPAhPWR7.js → verification-runner-R2-4XgMO.js} +4 -4
  135. package/dist/dashboard/{verification-runner-BPAhPWR7.js.map → verification-runner-R2-4XgMO.js.map} +1 -1
  136. package/dist/dashboard/{workflows-H-COJ84X.js → workflows-CSKX5MhC.js} +7 -7
  137. package/dist/dashboard/{workflows-H-COJ84X.js.map → workflows-CSKX5MhC.js.map} +1 -1
  138. package/dist/dashboard/workflows-DVwXauef.js +2 -0
  139. package/dist/dashboard/{workspaces-CRU9_vRn.js → workspaces-DXLDgXo2.js} +32 -32
  140. package/dist/dashboard/{workspaces-CRU9_vRn.js.map → workspaces-DXLDgXo2.js.map} +1 -1
  141. package/dist/docs-index.sqlite +0 -0
  142. package/dist/{feedback-target-DTGAhFkM.js → feedback-target-BsKN4Tr_.js} +4 -4
  143. package/dist/{feedback-target-DTGAhFkM.js.map → feedback-target-BsKN4Tr_.js.map} +1 -1
  144. package/dist/{feedback-target-DutF088V.js → feedback-target-DM1WzOKy.js} +1 -1
  145. package/dist/{harness-resolve-Bz1prk6B.js → harness-resolve-B-YDNwNW.js} +2 -2
  146. package/dist/{harness-resolve-Bz1prk6B.js.map → harness-resolve-B-YDNwNW.js.map} +1 -1
  147. package/dist/{issue-lifecycle-fVqvCdKM.js → issue-lifecycle-Bc1l3U9f.js} +7 -7
  148. package/dist/{issue-lifecycle-fVqvCdKM.js.map → issue-lifecycle-Bc1l3U9f.js.map} +1 -1
  149. package/dist/{issue-service-singleton-CZm8p6Rj.js → issue-service-singleton-D-8Q6tdT.js} +1 -1
  150. package/dist/{issue-service-singleton-Cr73JAaR.js → issue-service-singleton-D1433I7n.js} +2 -2
  151. package/dist/{issue-service-singleton-Cr73JAaR.js.map → issue-service-singleton-D1433I7n.js.map} +1 -1
  152. package/dist/{lifecycle-CR6tZfl1.js → lifecycle-SmrqysDf.js} +7 -7
  153. package/dist/{lifecycle-CR6tZfl1.js.map → lifecycle-SmrqysDf.js.map} +1 -1
  154. package/dist/{lifecycle-D_EWiUjk.js → lifecycle-mRMyiIXs.js} +2 -2
  155. package/dist/{merge-agent-LM4avOtA.js → merge-agent-B9hIZ6Z0.js} +6 -6
  156. package/dist/{merge-agent-LM4avOtA.js.map → merge-agent-B9hIZ6Z0.js.map} +1 -1
  157. package/dist/{merge-agent-Bj046HE1.js → merge-agent-DUj22cXL.js} +1 -1
  158. package/dist/{merge-fulfillment-DrnC1Kmg.js → merge-fulfillment-__P4WE-T.js} +3 -3
  159. package/dist/{merge-fulfillment-DrnC1Kmg.js.map → merge-fulfillment-__P4WE-T.js.map} +1 -1
  160. package/dist/{reconstruct-cache-NPqoAUg-.js → reconstruct-cache-9SWp82SQ.js} +3 -3
  161. package/dist/{reconstruct-cache-NPqoAUg-.js.map → reconstruct-cache-9SWp82SQ.js.map} +1 -1
  162. package/dist/{records-BIkN1LQ4.js → records-CTmvuRzz.js} +14 -1
  163. package/dist/{records-BIkN1LQ4.js.map → records-CTmvuRzz.js.map} +1 -1
  164. package/dist/records-backfill-BQcmTmwF.js +2 -0
  165. package/dist/{records-backfill-HM0Pln6N.js → records-backfill-BrNsv_zT.js} +2 -2
  166. package/dist/{records-backfill-HM0Pln6N.js.map → records-backfill-BrNsv_zT.js.map} +1 -1
  167. package/dist/resume-Bn4kDk9x.js +3 -0
  168. package/dist/{review-agent-DFosQzCG.js → review-agent-PhqOhpx3.js} +24 -16
  169. package/dist/{review-agent-DFosQzCG.js.map → review-agent-PhqOhpx3.js.map} +1 -1
  170. package/dist/review-agent-hLbRRxZF.js +3 -0
  171. package/dist/{review-status-iJwfEoNX.js → review-status-B_XXomV0.js} +6 -6
  172. package/dist/{review-status-iJwfEoNX.js.map → review-status-B_XXomV0.js.map} +1 -1
  173. package/dist/{review-status-BbNVlXh1.js → review-status-Bo-EFVKh.js} +1 -1
  174. package/dist/{review-verdict-feedback-BGPw3z1X.js → review-verdict-feedback-CXHkoiKZ.js} +1 -1
  175. package/dist/{review-verdict-feedback-CCZ05atV.js → review-verdict-feedback-DGV13LDb.js} +4 -4
  176. package/dist/{review-verdict-feedback-CCZ05atV.js.map → review-verdict-feedback-DGV13LDb.js.map} +1 -1
  177. package/dist/{sequence-io-CcxZNHcD.js → sequence-io-Cg4NHAco.js} +1 -1
  178. package/dist/{sequence-io-Cx1usxG3.js → sequence-io-CyxvNHfA.js} +2 -2
  179. package/dist/{sequence-io-Cx1usxG3.js.map → sequence-io-CyxvNHfA.js.map} +1 -1
  180. package/dist/{smart-compaction-ByUEgQkF.js → smart-compaction-ByKXTQNy.js} +2 -2
  181. package/dist/{smart-compaction-ByUEgQkF.js.map → smart-compaction-ByKXTQNy.js.map} +1 -1
  182. package/dist/{smart-compaction-DfLe_CtH.js → smart-compaction-U6e_uB1E.js} +1 -1
  183. package/dist/{specialist-context-McuGkaeR.js → specialist-context-BUTadO-U.js} +3 -3
  184. package/dist/{specialist-context-McuGkaeR.js.map → specialist-context-BUTadO-U.js.map} +1 -1
  185. package/dist/{specialist-logs-CUUJ4eiB.js → specialist-logs-pEMCTtGZ.js} +2 -2
  186. package/dist/{specialist-logs-CUUJ4eiB.js.map → specialist-logs-pEMCTtGZ.js.map} +1 -1
  187. package/dist/{specialists-BSrylD1S.js → specialists-BGFnrQ8n.js} +1 -1
  188. package/dist/{specialists-B0elgInc.js → specialists-DeTkkp8b.js} +12 -12
  189. package/dist/{specialists-B0elgInc.js.map → specialists-DeTkkp8b.js.map} +1 -1
  190. package/dist/{summary-fork-DskMRnyb.js → summary-fork-CgzThE2-.js} +2 -2
  191. package/dist/{summary-fork-CPUTKcAT.js → summary-fork-Dx1RMuvU.js} +3 -3
  192. package/dist/{summary-fork-CPUTKcAT.js.map → summary-fork-Dx1RMuvU.js.map} +1 -1
  193. package/dist/{sync-BiNVQHBf.js → sync-Bsww9tuj.js} +2 -2
  194. package/dist/{sync-BiNVQHBf.js.map → sync-Bsww9tuj.js.map} +1 -1
  195. package/dist/sync-CylE_SjB.js +2 -0
  196. package/dist/{verdict-restore-DQiV7_fe.js → verdict-restore-DGpX0ZOJ.js} +5 -5
  197. package/dist/{verdict-restore-DQiV7_fe.js.map → verdict-restore-DGpX0ZOJ.js.map} +1 -1
  198. package/dist/{webhook-handlers-DHBSEDEm.js → webhook-handlers-BjsfIL7u.js} +3 -3
  199. package/dist/{webhook-handlers-DHBSEDEm.js.map → webhook-handlers-BjsfIL7u.js.map} +1 -1
  200. package/dist/{workspace-migrate-duUsWNpd.js → workspace-migrate-BBQ8nAnP.js} +2 -2
  201. package/dist/{workspace-migrate-duUsWNpd.js.map → workspace-migrate-BBQ8nAnP.js.map} +1 -1
  202. package/dist/{workspace-reap-DLCIoJmn.js → workspace-reap-12O9354k.js} +2 -2
  203. package/dist/{workspace-reap-DLCIoJmn.js.map → workspace-reap-12O9354k.js.map} +1 -1
  204. package/package.json +1 -1
  205. package/scripts/file-size-baseline.txt +1 -1
  206. package/dist/cli-DBWG12XK.js +0 -3
  207. package/dist/dashboard/cli-CJDWXE_Y.js +0 -3
  208. package/dist/dashboard/deacon-swarm-D1iAUX-P.js +0 -2
  209. package/dist/dashboard/inspect-agent-vm88mLYr.js +0 -2
  210. package/dist/dashboard/merge-ops-QdBiQ8XB.js +0 -2
  211. package/dist/dashboard/resume-DnIsmIgC.js +0 -3
  212. package/dist/dashboard/review-agent-DgySDb-G.js +0 -3
  213. package/dist/dashboard/review-agent-phzqNqIs.js.map +0 -1
  214. package/dist/dashboard/review-status-BQjqQXZG.js.map +0 -1
  215. package/dist/dashboard/workflows-Cz8OtiB4.js +0 -2
  216. package/dist/records-backfill-DEgtRyKL.js +0 -2
  217. package/dist/resume-DGymuXZM.js +0 -3
  218. package/dist/review-agent-DjVadURa.js +0 -3
  219. package/dist/sync-4jqukkxz.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"sync-BiNVQHBf.js","names":[],"sources":["../src/lib/config-migration.ts","../src/lib/agent-directory-cleanup.ts","../src/lib/multi-tool-sync.ts","../src/cli/commands/sync.ts"],"sourcesContent":["/**\n * Configuration Migration\n *\n * Migrates from legacy settings.json format to new config.yaml format.\n * Legacy presets are no longer supported - all selection is now smart/capability-based.\n */\n\nimport { Effect } from 'effect';\nimport { readFileSync, writeFileSync, existsSync, renameSync, readdirSync, lstatSync, readlinkSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { loadSettingsSync, type SettingsConfig } from './settings.js';\nimport { type YamlConfig } from './config-yaml.js';\nimport { type ModelId } from './settings.js';\nimport { FsError } from './errors.js';\n\n/** Path to legacy settings file */\nconst LEGACY_SETTINGS_PATH = join(homedir(), '.overdeck', 'settings.json');\n\n/** Path to new config file */\nconst NEW_CONFIG_PATH = join(homedir(), '.overdeck', 'config.yaml');\n\n/** Path to backup of legacy settings */\nconst BACKUP_SETTINGS_PATH = join(homedir(), '.overdeck', 'settings.json.backup');\n\n/**\n * Check if migration is needed\n * Returns true if settings.json exists and config.yaml doesn't\n */\nexport function needsMigrationSync(): boolean {\n return existsSync(LEGACY_SETTINGS_PATH) && !existsSync(NEW_CONFIG_PATH);\n}\n\n/**\n * Check if legacy settings exist (even if already migrated)\n */\nexport function hasLegacySettingsSync(): boolean {\n return existsSync(LEGACY_SETTINGS_PATH);\n}\n\n/**\n * Determine which providers are enabled based on API keys\n */\nfunction detectEnabledProviders(settings: SettingsConfig): {\n anthropic: boolean;\n openai: boolean;\n google: boolean;\n kimi: boolean;\n} {\n return {\n anthropic: true, // Always enabled\n openai: !!settings.api_keys.openai,\n google: !!settings.api_keys.google,\n kimi: false, // Legacy settings don't have Kimi\n };\n}\n\n/**\n * Convert legacy settings.json to new config.yaml format\n */\nexport function convertToYamlConfigSync(settings: SettingsConfig): YamlConfig {\n const providers = detectEnabledProviders(settings);\n\n const config: YamlConfig = {\n models: {\n providers,\n overrides: {}, // No overrides from legacy\n gemini_thinking_level: 3,\n },\n api_keys: settings.api_keys,\n };\n\n return config;\n}\n\n/**\n * Migration options\n */\nexport interface MigrationOptions {\n /** Create backup of legacy settings (default: true) */\n backup?: boolean;\n /** Delete legacy settings after migration (default: false) */\n deleteLegacy?: boolean;\n /** Dry run - don't actually write files (default: false) */\n dryRun?: boolean;\n}\n\n/**\n * Migration result\n */\nexport interface MigrationResult {\n success: boolean;\n overridesCount: number;\n providersEnabled: string[];\n message: string;\n error?: string;\n}\n\nexport function migrateConfigSync(options: MigrationOptions = {}): MigrationResult {\n const { backup = true, deleteLegacy = false, dryRun = false } = options;\n\n try {\n // Check if migration is needed\n if (!needsMigrationSync()) {\n if (existsSync(NEW_CONFIG_PATH)) {\n return {\n success: true,\n overridesCount: 0,\n providersEnabled: ['anthropic'],\n message: 'Config already migrated (config.yaml exists)',\n };\n }\n return {\n success: false,\n overridesCount: 0,\n providersEnabled: [],\n message: 'No legacy settings.json found to migrate',\n };\n }\n\n // Load legacy settings\n const settings = loadSettingsSync();\n\n // Convert to YAML config\n const yamlConfig = convertToYamlConfigSync(settings);\n\n // Generate YAML content\n const yamlContent = yaml.dump(yamlConfig, {\n indent: 2,\n lineWidth: 120,\n noRefs: true,\n });\n\n // Dry run - just return what would happen\n if (dryRun) {\n const providersEnabled = Object.entries(yamlConfig.models?.providers || {})\n .filter(([_, enabled]) => enabled)\n .map(([name]) => name);\n\n return {\n success: true,\n overridesCount: Object.keys(yamlConfig.models?.overrides || {}).length,\n providersEnabled,\n message: `Would migrate to smart selection with ${providersEnabled.length} providers enabled`,\n };\n }\n\n // Write new config.yaml\n writeFileSync(NEW_CONFIG_PATH, yamlContent, 'utf-8');\n\n // Back up legacy settings if requested\n if (backup) {\n const legacyContent = readFileSync(LEGACY_SETTINGS_PATH, 'utf-8');\n writeFileSync(BACKUP_SETTINGS_PATH, legacyContent, 'utf-8');\n }\n\n // Delete legacy settings if requested\n if (deleteLegacy) {\n renameSync(LEGACY_SETTINGS_PATH, `${LEGACY_SETTINGS_PATH}.migrated`);\n }\n\n const providersEnabled = Object.entries(yamlConfig.models?.providers || {})\n .filter(([_, enabled]) => enabled)\n .map(([name]) => name);\n\n return {\n success: true,\n overridesCount: Object.keys(yamlConfig.models?.overrides || {}).length,\n providersEnabled,\n message: `Successfully migrated to smart selection with ${providersEnabled.length} providers`,\n };\n } catch (error: any) {\n return {\n success: false,\n overridesCount: 0,\n providersEnabled: [],\n message: 'Migration failed',\n error: error.message,\n };\n }\n}\n\n/**\n * Get migration status\n */\nexport function getMigrationStatusSync(): {\n needsMigration: boolean;\n hasLegacySettings: boolean;\n hasNewConfig: boolean;\n} {\n return {\n needsMigration: needsMigrationSync(),\n hasLegacySettings: existsSync(LEGACY_SETTINGS_PATH),\n hasNewConfig: existsSync(NEW_CONFIG_PATH),\n };\n}\n\n/**\n * Clean up legacy runtime symlinks from removed runtimes.\n *\n * PAN-142: Overdeck consolidated to Claude Code as the sole runtime.\n * This removes any Overdeck-managed symlinks from legacy runtime directories\n * (codex, cursor, gemini, opencode).\n */\nexport interface LegacyCleanupResult {\n cleaned: string[];\n total: number;\n errors: string[];\n}\n\nexport function cleanupLegacyRuntimeSymlinksSync(): LegacyCleanupResult {\n const legacyDirs = [\n { name: 'codex', base: join(homedir(), '.codex') },\n { name: 'cursor', base: join(homedir(), '.cursor') },\n { name: 'gemini', base: join(homedir(), '.gemini') },\n { name: 'opencode', base: join(homedir(), '.opencode') },\n ];\n\n const cleaned: string[] = [];\n const errors: string[] = [];\n\n for (const { name, base } of legacyDirs) {\n for (const subdir of ['skills', 'commands', 'agents']) {\n const dir = join(base, subdir);\n if (!existsSync(dir)) continue;\n\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const entryPath = join(dir, entry);\n try {\n const stats = lstatSync(entryPath);\n if (!stats.isSymbolicLink()) continue;\n\n const linkTarget = readlinkSync(entryPath);\n // Only remove symlinks pointing to Overdeck directories\n if (linkTarget.includes('.overdeck')) {\n unlinkSync(entryPath);\n cleaned.push(`${name}/${subdir}/${entry}`);\n }\n } catch (err: any) {\n errors.push(`${name}/${subdir}/${entry}: ${err.message}`);\n }\n }\n } catch (err: any) {\n // Directory may not be readable, that's fine\n errors.push(`${name}/${subdir}: ${err.message}`);\n }\n }\n }\n\n return { cleaned, total: cleaned.length, errors };\n}\n\n/**\n * Migrate legacy sync config by stripping the 'targets' field from config.toml.\n * This handles users who had `targets = [\"claude\", \"codex\"]` in their config.\n */\nexport function migrateSyncTargetsSync(): { migrated: boolean; hadNonClaudeTargets: boolean } {\n const configPath = join(homedir(), '.overdeck', 'config.toml');\n\n if (!existsSync(configPath)) {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n\n try {\n const content = readFileSync(configPath, 'utf-8');\n\n // Check if targets field exists\n const targetsMatch = content.match(/^targets\\s*=\\s*\\[([^\\]]*)\\]/m);\n if (!targetsMatch) {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n\n // Check if non-claude targets were configured\n const targetsStr = targetsMatch[1];\n const hadNonClaudeTargets = /codex|cursor|gemini|opencode/i.test(targetsStr);\n\n // Remove the targets line\n const newContent = content.replace(/^targets\\s*=\\s*\\[[^\\]]*\\]\\s*\\n?/m, '');\n writeFileSync(configPath, newContent, 'utf-8');\n\n return { migrated: true, hadNonClaudeTargets };\n } catch {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n// One-shot config-migration helpers. Each is run at most once per install at\n// CLI startup. Errors are caught internally (return false / empty result), so\n// Effect.sync is appropriate here.\n\n/** True when legacy settings.json exists and config.yaml doesn't. Pure. */\nexport const needsMigration = (): Effect.Effect<boolean> =>\n Effect.sync(() => needsMigrationSync());\n\n/** True if any legacy settings.json is present (with or without yaml). Pure. */\nexport const hasLegacySettings = (): Effect.Effect<boolean> =>\n Effect.sync(() => hasLegacySettingsSync());\n\n/** Convert a SettingsConfig into a YamlConfig. Pure. */\nexport const convertToYamlConfig = (\n settings: SettingsConfig,\n): Effect.Effect<YamlConfig> => Effect.sync(() => convertToYamlConfigSync(settings));\n\n/** Run the settings.json → config.yaml migration. Surfaces FsError on IO failure. */\nexport const migrateConfig = (\n options: MigrationOptions = {},\n): Effect.Effect<MigrationResult, FsError> =>\n Effect.try({\n try: () => migrateConfigSync(options),\n catch: (cause) =>\n new FsError({ path: NEW_CONFIG_PATH, operation: 'migrate-config', cause }),\n });\n\n/** Inspect migration state without modifying anything. Pure. */\nexport const getMigrationStatus = (): Effect.Effect<\n ReturnType<typeof getMigrationStatusSync>\n> => Effect.sync(() => getMigrationStatusSync());\n\n/** Sweep legacy runtime symlinks under ~/.overdeck (idempotent). */\nexport const cleanupLegacyRuntimeSymlinks =\n (): Effect.Effect<LegacyCleanupResult> =>\n Effect.sync(() => cleanupLegacyRuntimeSymlinksSync());\n\n/** Migrate `targets = [...]` lines out of config.toml. Pure-ish (catches). */\nexport const migrateSyncTargets = (): Effect.Effect<{\n migrated: boolean;\n hadNonClaudeTargets: boolean;\n}> => Effect.sync(() => migrateSyncTargetsSync());\n","/**\n * Agent directory cleanup — identifies and removes orphaned legacy agent directories.\n *\n * Valid directories (preserved):\n * - agent-<issueId> — work agents (always preserved)\n * - planning-<issueId> — planning agents (only preserved while tmux session is running)\n * - conv-* — conversation directories (ALWAYS preserved: ohmypi\n * conversations write their only transcript to\n * conv-<name>/sessions/*.jsonl via --session-dir, and\n * codex conversations keep rollout JSONLs under\n * conv-<name>/codex-home/sessions/. Deleting these\n * destroys conversation history permanently — 2026-07-05\n * incident: a boot-time `pan sync` wiped every ended\n * pi/codex conversation transcript.)\n *\n * Legacy directories (eligible for cleanup when no tmux session is running):\n * - work-<issueId>, review-<issueId>, test-<issueId>, merge-<issueId>\n * - agent-<number>, agent-agent-*, agent-* with uppercase prefix\n * - specialist-*\n * - Any other non-standard name\n */\n\nimport { existsSync, readdirSync, rmSync } from 'fs';\nimport { access, readdir, rm } from 'node:fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from './paths.js';\nimport { listSessionNames } from './tmux.js';\nimport { parseIssueIdSync } from './issue-id.js';\nimport { FsError } from './errors.js';\nimport { getAgentStateSync } from './agents.js';\n\nexport const CLOSED_ISSUE_AGENT_DIR_GRACE_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Valid agent directory naming patterns.\n *\n * Standard issue IDs: PREFIX-NUMBER → normalized to lowercase with dash\n * Rally issue IDs: PREFIX_NUMBER → normalized to lowercase, no dash\n *\n * Examples of valid names:\n * agent-pan-801, agent-min-215, agent-f29698\n * planning-pan-801, planning-min-215\n *\n * Examples of legacy names (no longer created):\n * agent-108, agent-MIN-791, agent-agent-pan-699, agent-pan-test-1\n * work-pan-208, review-pan-646, test-pan-646, merge-pan-646\n * conv-20260411-1125, specialist-overdeck-test-agent\n */\n\n/**\n * Check whether a directory name matches a valid work-agent directory pattern.\n *\n * Work-agent directories (agent-<issueId>) are always preserved.\n * Planning-agent directories are handled separately — they are only valid\n * while their tmux session is running.\n */\nexport function isValidAgentDirectoryName(name: string): boolean {\n const match = name.match(/^agent-(.+)$/);\n if (!match) return false;\n\n const suffix = match[1]!;\n // Current code always lowercases issue IDs before creating directories\n if (suffix !== suffix.toLowerCase()) return false;\n\n // Direct agent-<issueId> directories (work agents, role orchestrators without suffix)\n if (parseIssueIdSync(suffix) !== null) return true;\n\n // Specialist directories: agent-<issueId>-<role> or agent-<issueId>-<role>-<subRole>\n // e.g. agent-pan-457-review-correctness, agent-pan-457-test, agent-pan-457-ship\n // Also work agents with slots: agent-pan-457-1\n const parts = suffix.split('-');\n for (let i = 2; i <= parts.length; i++) {\n const candidate = parts.slice(0, i).join('-');\n if (parseIssueIdSync(candidate) !== null) {\n const remainder = parts.slice(i).join('-');\n if (remainder && /^[a-z0-9-]+$/.test(remainder)) return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check whether a directory name is a conversation (conv-*) directory.\n *\n * Conversation launcher/state files live in ~/.overdeck/conversations/, but the\n * conv-* directory under ~/.overdeck/agents/ is the canonical transcript home\n * for non-claude harnesses: ohmypi writes sessions/*.jsonl there (--session-dir)\n * and codex keeps rollout JSONLs under codex-home/sessions/. These directories\n * must NEVER be auto-removed — doing so permanently destroys conversation\n * history.\n */\nexport function isConversationDirectory(name: string): boolean {\n return name.startsWith('conv-');\n}\n\n/**\n * Extract the issue ID from a planning-* directory name.\n * Returns null if the name is not a planning directory or the issue ID is invalid.\n */\nexport function getPlanningIssueId(name: string): string | null {\n const match = name.match(/^planning-(.+)$/);\n if (!match) return null;\n\n const issueId = match[1]!;\n if (issueId !== issueId.toLowerCase()) return null;\n if (parseIssueIdSync(issueId) === null) return null;\n\n return issueId;\n}\n\nexport function getAgentDirectoryIssueId(name: string): string | null {\n const match = name.match(/^(?:agent|planning)-(.+)$/);\n if (!match) return null;\n\n const suffix = match[1]!;\n if (suffix !== suffix.toLowerCase()) return null;\n\n const direct = parseIssueIdSync(suffix);\n if (direct) return direct.raw.toUpperCase();\n\n const parts = suffix.split('-');\n for (let i = 1; i <= parts.length; i++) {\n const candidate = parts.slice(0, i).join('-');\n const parsed = parseIssueIdSync(candidate);\n if (parsed) return parsed.raw.toUpperCase();\n }\n\n return null;\n}\n\nexport interface OrphanedAgentDir {\n name: string;\n path: string;\n hasRunningSession: boolean;\n}\n\nasync function findOrphanedAgentDirsPromise(\n agentsDir: string = AGENTS_DIR,\n): Promise<OrphanedAgentDir[]> {\n if (!existsSync(agentsDir)) {\n return [];\n }\n\n const entries = readdirSync(agentsDir, { withFileTypes: true });\n const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n\n const sessionNames = await Effect.runPromise(listSessionNames());\n const sessionSet = new Set(sessionNames);\n\n const orphaned: OrphanedAgentDir[] = [];\n for (const name of dirs) {\n // Work-agent directories are always valid\n if (isValidAgentDirectoryName(name)) continue;\n\n // Conversation directories hold the only transcript for ohmypi/codex\n // conversations (sessions/*.jsonl, codex-home/sessions/). Never touch them.\n if (isConversationDirectory(name)) continue;\n\n // Planning directories are valid only while their tmux session is running\n const planningIssueId = getPlanningIssueId(name);\n if (planningIssueId !== null) {\n if (sessionSet.has(name)) continue; // running session — preserve\n // No running session — orphaned\n }\n\n const dirPath = join(agentsDir, name);\n orphaned.push({\n name,\n path: dirPath,\n hasRunningSession: sessionSet.has(name),\n });\n }\n\n return orphaned;\n}\n\nexport interface CleanupResult {\n /** Directories that were removed */\n removed: string[];\n /** Directories skipped because a tmux session is still running */\n protected: string[];\n /** Directories that would be removed (dry run) */\n wouldRemove: string[];\n /** Total orphaned directories found */\n totalOrphaned: number;\n}\n\nasync function cleanupAgentDirectoriesPromise(options: {\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n} = {}): Promise<CleanupResult> {\n const { dryRun = false, force = false, agentsDir = AGENTS_DIR } = options;\n\n const orphaned = await Effect.runPromise(findOrphanedAgentDirs(agentsDir));\n const protectedDirs = orphaned.filter((d) => d.hasRunningSession);\n const removable = orphaned.filter((d) => !d.hasRunningSession);\n\n const result: CleanupResult = {\n removed: [],\n protected: protectedDirs.map((d) => d.name),\n wouldRemove: [],\n totalOrphaned: orphaned.length,\n };\n\n if (removable.length === 0) {\n return result;\n }\n\n if (dryRun) {\n result.wouldRemove = removable.map((d) => d.name);\n return result;\n }\n\n if (!force) {\n // Prompt for confirmation is handled by the CLI caller; this function\n // focuses on the actual cleanup logic.\n }\n\n for (const dir of removable) {\n try {\n rmSync(dir.path, { recursive: true, force: true });\n result.removed.push(dir.name);\n } catch {\n // Non-fatal — directory may have already been removed or permissions changed.\n }\n }\n\n return result;\n}\n\ntype IssueReadSourceState = {\n identifier?: unknown;\n id?: unknown;\n status?: unknown;\n state?: unknown;\n canonicalStatus?: unknown;\n rawTrackerState?: unknown;\n completedAt?: unknown;\n closedAt?: unknown;\n};\n\nexport interface ClosedIssueAgentDir {\n name: string;\n path: string;\n issueId: string;\n closedAt: string;\n ageMs: number;\n hasRunningSession: boolean;\n hasStateFile: boolean;\n containsJsonl: boolean;\n}\n\nexport interface ClosedIssueAgentCleanupResult {\n removed: string[];\n protected: string[];\n wouldRemove: string[];\n totalCandidates: number;\n}\n\nfunction normalizeIssueId(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (!trimmed) return null;\n const parsed = parseIssueIdSync(trimmed);\n return (parsed?.raw ?? trimmed).toUpperCase();\n}\n\nfunction isClosedIssueState(issue: IssueReadSourceState): boolean {\n const state = String(issue.state ?? '').toLowerCase();\n const status = String(issue.status ?? '').toLowerCase();\n const canonicalStatus = String(issue.canonicalStatus ?? '').toLowerCase();\n const rawTrackerState = String(issue.rawTrackerState ?? '').toLowerCase();\n return Boolean(\n issue.completedAt ||\n issue.closedAt ||\n state === 'closed' ||\n status === 'done' ||\n status === 'closed' ||\n status === 'cancelled' ||\n status === 'canceled' ||\n status === 'completed' ||\n canonicalStatus === 'done' ||\n canonicalStatus === 'closed' ||\n canonicalStatus === 'cancelled' ||\n canonicalStatus === 'canceled' ||\n canonicalStatus === 'completed' ||\n rawTrackerState === 'closed' ||\n rawTrackerState === 'done' ||\n rawTrackerState === 'completed',\n );\n}\n\nfunction getClosedIssueTimes(issues: unknown[]): Map<string, number> {\n const closed = new Map<string, number>();\n for (const issue of issues) {\n if (!issue || typeof issue !== 'object') continue;\n const item = issue as IssueReadSourceState;\n const issueId = normalizeIssueId(item.identifier) ?? normalizeIssueId(item.id);\n if (!issueId || !isClosedIssueState(item)) continue;\n\n const closedTimestamp = item.completedAt ?? item.closedAt;\n if (typeof closedTimestamp !== 'string') continue;\n\n const closedAtMs = Date.parse(closedTimestamp);\n if (!Number.isFinite(closedAtMs)) continue;\n closed.set(issueId, closedAtMs);\n }\n return closed;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readAgentStateIssueId(dirPath: string): Promise<string | null> {\n try {\n const agentId = dirPath.split('/').pop() ?? '';\n const issueId = getAgentStateSync(agentId)?.issueId;\n return issueId ? normalizeIssueId(issueId) : null;\n } catch {\n return null;\n }\n}\n\nasync function directoryContainsJsonl(dirPath: string): Promise<boolean> {\n let entries;\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name);\n if (entry.isFile() && entry.name.endsWith('.jsonl')) return true;\n if (entry.isDirectory() && await directoryContainsJsonl(entryPath)) return true;\n }\n\n return false;\n}\n\nasync function findClosedIssueAgentDirsPromise(options: {\n issues: unknown[];\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Promise<ClosedIssueAgentDir[]> {\n const agentsDir = options.agentsDir ?? AGENTS_DIR;\n const nowMs = options.nowMs ?? Date.now();\n const graceMs = options.graceMs ?? CLOSED_ISSUE_AGENT_DIR_GRACE_MS;\n const closedIssueTimes = getClosedIssueTimes(options.issues);\n if (closedIssueTimes.size === 0 || !await pathExists(agentsDir)) return [];\n\n const sessionNames = await Effect.runPromise(listSessionNames());\n const sessionSet = new Set(sessionNames);\n const entries = await readdir(agentsDir, { withFileTypes: true });\n const candidates: ClosedIssueAgentDir[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const dirPath = join(agentsDir, entry.name);\n const stateIssueId = await readAgentStateIssueId(dirPath);\n const issueId = stateIssueId ?? getAgentDirectoryIssueId(entry.name);\n if (!issueId) continue;\n\n const closedAtMs = closedIssueTimes.get(issueId);\n if (closedAtMs === undefined) continue;\n\n const ageMs = nowMs - closedAtMs;\n if (ageMs <= graceMs) continue;\n\n candidates.push({\n name: entry.name,\n path: dirPath,\n issueId,\n closedAt: new Date(closedAtMs).toISOString(),\n ageMs,\n hasRunningSession: sessionSet.has(entry.name),\n hasStateFile: getAgentStateSync(entry.name) !== null,\n containsJsonl: await directoryContainsJsonl(dirPath),\n });\n }\n\n return candidates;\n}\n\nasync function cleanupClosedIssueAgentDirectoriesPromise(options: {\n issues: unknown[];\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Promise<ClosedIssueAgentCleanupResult> {\n const candidates = await findClosedIssueAgentDirsPromise(options);\n const protectedDirs = candidates.filter((dir) => dir.hasRunningSession || dir.containsJsonl);\n const removable = candidates.filter((dir) => !dir.hasRunningSession && !dir.containsJsonl);\n const result: ClosedIssueAgentCleanupResult = {\n removed: [],\n protected: protectedDirs.map((dir) => dir.name),\n wouldRemove: [],\n totalCandidates: candidates.length,\n };\n\n if (options.dryRun) {\n result.wouldRemove = removable.map((dir) => dir.name);\n return result;\n }\n\n if (!options.force) {\n // Startup owns the non-interactive cleanup path; CLI callers should use dry-run.\n }\n\n for (const dir of removable) {\n try {\n await rm(dir.path, { recursive: true, force: true });\n result.removed.push(dir.name);\n } catch {\n // Non-fatal — directory may have already been removed or permissions changed.\n }\n }\n\n return result;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native variant of findOrphanedAgentDirs. Fails with FsError if the\n * agents directory listing fails. The tmux listing is wrapped so listing\n * failures bubble up as FsError too (treats tmux as part of the filesystem\n * for purposes of this check).\n */\nexport const findOrphanedAgentDirs = (\n agentsDir: string = AGENTS_DIR,\n): Effect.Effect<readonly OrphanedAgentDir[], FsError> =>\n Effect.tryPromise({\n try: () => findOrphanedAgentDirsPromise(agentsDir),\n catch: (cause) =>\n new FsError({ path: agentsDir, operation: 'findOrphanedAgentDirs', cause }),\n });\n\n/**\n * Effect-native variant of cleanupAgentDirectories. Fails with FsError if the\n * orphan scan fails. Individual rm failures are still swallowed internally so\n * a partial cleanup is the worst case (matches the Promise contract).\n */\nexport const cleanupAgentDirectories = (options: {\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n} = {}): Effect.Effect<CleanupResult, FsError> =>\n Effect.tryPromise({\n try: () => cleanupAgentDirectoriesPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'cleanupAgentDirectories',\n cause,\n }),\n });\n\nexport const findClosedIssueAgentDirs = (options: {\n issues: unknown[];\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Effect.Effect<readonly ClosedIssueAgentDir[], FsError> =>\n Effect.tryPromise({\n try: () => findClosedIssueAgentDirsPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'findClosedIssueAgentDirs',\n cause,\n }),\n });\n\nexport const cleanupClosedIssueAgentDirectories = (options: {\n issues: unknown[];\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Effect.Effect<ClosedIssueAgentCleanupResult, FsError> =>\n Effect.tryPromise({\n try: () => cleanupClosedIssueAgentDirectoriesPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'cleanupClosedIssueAgentDirectories',\n cause,\n }),\n });\n","/**\n * Multi-Tool Skill Sync\n *\n * Writes Overdeck skills to other AI tool formats so skills authored once\n * in .pan/skills/ are available across all configured tools.\n *\n * Configured via `tools.also_sync` in ~/.overdeck/config.yaml and .pan.yaml.\n * Per-project .pan.yaml values are merged additively with global config.\n *\n * Supported targets:\n * cursor → .cursor/rules/<skill-name>.mdc\n * codex → AGENTS.md (named blocks)\n * windsurf → .windsurf/rules/<skill-name>.md\n * cline → .clinerules/<skill-name>.md\n * copilot → .github/instructions/<skill-name>.instructions.md\n * aider → CONVENTIONS.md (named blocks)\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { Effect } from 'effect';\nimport { FsError } from './errors.js';\nimport { OVERDECK_HOME } from './paths.js';\n\nexport type AlsoSyncTool = 'cursor' | 'codex' | 'windsurf' | 'cline' | 'copilot' | 'aider';\n\nexport interface MultiToolSyncResult {\n tool: AlsoSyncTool;\n written: string[];\n skipped: string[];\n errors: string[];\n}\n\n/** Strip YAML frontmatter from a skill markdown file */\nfunction stripFrontmatter(content: string): string {\n if (!content.startsWith('---')) return content;\n const end = content.indexOf('\\n---', 4);\n if (end === -1) return content;\n return content.slice(end + 4).trimStart();\n}\n\n/** Extract the skill name from frontmatter, or fall back to dir name */\nfunction extractSkillName(content: string, fallback: string): string {\n if (!content.startsWith('---')) return fallback;\n const end = content.indexOf('\\n---', 4);\n if (end === -1) return fallback;\n const frontmatter = content.slice(4, end);\n const match = frontmatter.match(/^name:\\s*(.+)$/m);\n return match ? match[1].trim() : fallback;\n}\n\n/** Read main SKILL.md content for a skill directory */\nfunction readSkillContent(skillDir: string): string | null {\n const skillMd = join(skillDir, 'SKILL.md');\n if (!existsSync(skillMd)) {\n // Fallback: any .md file in root\n const files = existsSync(skillDir) ? readdirSync(skillDir).filter(f => f.endsWith('.md')) : [];\n if (files.length === 0) return null;\n return readFileSync(join(skillDir, files[0]), 'utf-8');\n }\n return readFileSync(skillMd, 'utf-8');\n}\n\n/** Collect all skill directories from the given skills root */\nfunction collectSkillDirs(skillsDir: string): Array<{ name: string; dir: string }> {\n if (!existsSync(skillsDir)) return [];\n return readdirSync(skillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => ({ name: e.name, dir: join(skillsDir, e.name) }));\n}\n\n/**\n * Update or insert a named block in a file.\n * Blocks are delimited by: <!-- overdeck:<skill-name> start --> ... <!-- overdeck:<skill-name> end -->\n */\nfunction upsertNamedBlock(filePath: string, blockName: string, content: string): void {\n const startTag = `<!-- overdeck:${blockName} start -->`;\n const endTag = `<!-- overdeck:${blockName} end -->`;\n const block = `${startTag}\\n${content}\\n${endTag}`;\n\n let existing = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';\n\n const startIdx = existing.indexOf(startTag);\n const endIdx = existing.indexOf(endTag);\n\n if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {\n // Replace existing block\n existing = existing.slice(0, startIdx) + block + existing.slice(endIdx + endTag.length);\n } else {\n // Append new block\n if (existing.length > 0 && !existing.endsWith('\\n')) existing += '\\n';\n existing += '\\n' + block + '\\n';\n }\n\n writeFileSync(filePath, existing, 'utf-8');\n}\n\n/** Sync a single skill to the cursor target */\nfunction syncToCursor(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.cursor', 'rules');\n mkdirSync(rulesDir, { recursive: true });\n const body = stripFrontmatter(rawContent);\n // .mdc files: standard markdown, cursor accepts them as context rules\n writeFileSync(join(rulesDir, `${skillName}.mdc`), body, 'utf-8');\n}\n\n/** Sync a single skill to the windsurf target */\nfunction syncToWindsurf(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.windsurf', 'rules');\n mkdirSync(rulesDir, { recursive: true });\n writeFileSync(join(rulesDir, `${skillName}.md`), stripFrontmatter(rawContent), 'utf-8');\n}\n\n/** Sync a single skill to the cline target */\nfunction syncToCline(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.clinerules');\n mkdirSync(rulesDir, { recursive: true });\n writeFileSync(join(rulesDir, `${skillName}.md`), stripFrontmatter(rawContent), 'utf-8');\n}\n\n/** Sync a single skill to the copilot target */\nfunction syncToCopilot(projectPath: string, skillName: string, rawContent: string): void {\n const instructionsDir = join(projectPath, '.github', 'instructions');\n mkdirSync(instructionsDir, { recursive: true });\n writeFileSync(\n join(instructionsDir, `${skillName}.instructions.md`),\n stripFrontmatter(rawContent),\n 'utf-8',\n );\n}\n\n/** Sync a single skill to AGENTS.md (codex) as a named block */\nfunction syncToCodex(projectPath: string, skillName: string, rawContent: string): void {\n const agentsMd = join(projectPath, 'AGENTS.md');\n upsertNamedBlock(agentsMd, skillName, `## ${skillName}\\n\\n${stripFrontmatter(rawContent)}`);\n}\n\n/** Sync a single skill to CONVENTIONS.md (aider) as a named block */\nfunction syncToAider(projectPath: string, skillName: string, rawContent: string): void {\n const conventionsMd = join(projectPath, 'CONVENTIONS.md');\n upsertNamedBlock(conventionsMd, skillName, `## ${skillName}\\n\\n${stripFrontmatter(rawContent)}`);\n}\n\nconst TOOL_WRITERS: Record<AlsoSyncTool, (projectPath: string, name: string, content: string) => void> = {\n cursor: syncToCursor,\n windsurf: syncToWindsurf,\n cline: syncToCline,\n copilot: syncToCopilot,\n codex: syncToCodex,\n aider: syncToAider,\n};\n\n/**\n * Resolve the merged list of tools to sync.\n * Global config is the base; per-project .pan.yaml adds more (never removes).\n */\nexport function resolveAlsoSyncToolsSync(projectPath?: string): AlsoSyncTool[] {\n const tools = new Set<AlsoSyncTool>();\n\n // Read from global config\n const globalConfig = join(OVERDECK_HOME, 'config.yaml');\n if (existsSync(globalConfig)) {\n try {\n const parsed = yaml.load(readFileSync(globalConfig, 'utf-8')) as any;\n const globalTools: string[] = parsed?.tools?.also_sync || [];\n for (const t of globalTools) {\n if (t in TOOL_WRITERS) tools.add(t as AlsoSyncTool);\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Merge per-project .pan.yaml (additive)\n if (projectPath) {\n const panYaml = join(projectPath, '.pan.yaml');\n const legacyYaml = join(projectPath, '.overdeck.yaml');\n const configPath = existsSync(panYaml) ? panYaml : existsSync(legacyYaml) ? legacyYaml : null;\n if (configPath) {\n try {\n const parsed = yaml.load(readFileSync(configPath, 'utf-8')) as any;\n const projectTools: string[] = parsed?.tools?.also_sync || [];\n for (const t of projectTools) {\n if (t in TOOL_WRITERS) tools.add(t as AlsoSyncTool);\n }\n } catch { /* ignore parse errors */ }\n }\n }\n\n return Array.from(tools);\n}\n\n/**\n * Sync skills from a skills directory to all configured tools.\n *\n * @param skillsDir Directory containing skill subdirectories\n * @param projectPath Project root where tool targets live\n * @param tools Tools to sync to (from resolveAlsoSyncTools)\n */\nexport function syncSkillsToToolsSync(\n skillsDir: string,\n projectPath: string,\n tools: AlsoSyncTool[],\n): MultiToolSyncResult[] {\n if (tools.length === 0 || !existsSync(skillsDir)) return [];\n\n const skills = collectSkillDirs(skillsDir);\n const results: MultiToolSyncResult[] = [];\n\n for (const tool of tools) {\n const writer = TOOL_WRITERS[tool];\n const result: MultiToolSyncResult = { tool, written: [], skipped: [], errors: [] };\n\n for (const { name, dir } of skills) {\n try {\n const rawContent = readSkillContent(dir);\n if (!rawContent) {\n result.skipped.push(name);\n continue;\n }\n const displayName = extractSkillName(rawContent, name);\n writer(projectPath, displayName, rawContent);\n result.written.push(name);\n } catch (err: any) {\n result.errors.push(`${name}: ${err.message}`);\n }\n }\n\n results.push(result);\n }\n\n return results;\n}\n\n/**\n * Run the full multi-tool sync for a project.\n * Sources: .pan/skills/ (project-local) and/or ~/.overdeck/skills/ (global).\n */\nexport function runMultiToolSyncSync(projectPath: string): MultiToolSyncResult[] {\n const tools = resolveAlsoSyncToolsSync(projectPath);\n if (tools.length === 0) return [];\n\n const allResults: MultiToolSyncResult[] = [];\n\n // 1. Global skills (from ~/.overdeck/skills/)\n const globalSkillsDir = join(OVERDECK_HOME, 'skills');\n const globalResults = syncSkillsToToolsSync(globalSkillsDir, projectPath, tools);\n allResults.push(...globalResults);\n\n // 2. Project-local skills (from .pan/skills/) — may overwrite global skill entries\n const projectSkillsDir = join(projectPath, '.pan', 'skills');\n if (existsSync(projectSkillsDir)) {\n const projectResults = syncSkillsToToolsSync(projectSkillsDir, projectPath, tools);\n // Merge into existing results (project results override counts, don't duplicate tools)\n for (const pr of projectResults) {\n const existing = allResults.find(r => r.tool === pr.tool);\n if (existing) {\n existing.written.push(...pr.written);\n existing.errors.push(...pr.errors);\n } else {\n allResults.push(pr);\n }\n }\n }\n\n return allResults;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/** Effect variant of {@link resolveAlsoSyncToolsSync}. Pure config read; cannot fail. */\nexport const resolveAlsoSyncTools = (projectPath?: string): Effect.Effect<AlsoSyncTool[], never> =>\n Effect.sync(() => resolveAlsoSyncToolsSync(projectPath));\n\n/** Effect variant of {@link syncSkillsToToolsSync}. */\nexport const syncSkillsToTools = (\n skillsDir: string,\n projectPath: string,\n tools: AlsoSyncTool[],\n): Effect.Effect<MultiToolSyncResult[], FsError> =>\n Effect.try({\n try: () => syncSkillsToToolsSync(skillsDir, projectPath, tools),\n catch: (cause) => new FsError({ path: skillsDir, operation: 'syncSkillsToTools', cause }),\n });\n\n/** Effect variant of {@link runMultiToolSyncSync}. */\nexport const runMultiToolSync = (projectPath: string): Effect.Effect<MultiToolSyncResult[], FsError> =>\n Effect.try({\n try: () => runMultiToolSyncSync(projectPath),\n catch: (cause) => new FsError({ path: projectPath, operation: 'runMultiToolSync', cause }),\n });\n\n","import { Effect } from 'effect';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { execSync } from 'child_process';\nimport { existsSync, readdirSync, readFileSync, writeFileSync, statSync, symlinkSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { loadConfigSync } from '../../lib/config.js';\nimport { parseVBriefFilename } from '../../lib/vbrief/lifecycle.js';\nimport { resolveGitHubIssueSync } from '../../lib/tracker-utils.js';\nimport { createBackupSync } from '../../lib/backup.js';\nimport {\n planSyncSync,\n executeSyncSync,\n refreshCacheSync,\n migrateStalePersonalContentSync,\n removeLegacySkills070Sync,\n planHooksSyncSync,\n syncHooksSync,\n syncStatuslineSync,\n mirrorProjectSkillsSync,\n syncPiSettingsSync,\n syncContextLayersSync,\n isStartupSyncNeededSync,\n writeSyncManifestSync,\n} from '../../lib/sync.js';\nimport { SYNC_TARGET, SYNC_SOURCES, isDevMode } from '../../lib/paths.js';\nimport { checkDevrootDeprecation } from '../../lib/config.js';\nimport { listProjectsSync } from '../../lib/projects.js';\nimport { cleanupLegacyRuntimeSymlinksSync, migrateSyncTargetsSync } from '../../lib/config-migration.js';\nimport { cleanupAgentDirectories } from '../../lib/agent-directory-cleanup.js';\nimport { migrateOverdeckToPanSync } from '../../lib/workspace-manager.js';\nimport { runMultiToolSyncSync, resolveAlsoSyncToolsSync } from '../../lib/multi-tool-sync.js';\nimport { ensurePlaywrightIsolationSync, ensureExcalidrawMcpSync } from '../../lib/claude-mcp.js';\nimport { resolveProjectContextFile } from '../../lib/context-layers/layers.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\n// Bundled git hooks distributed to registered projects (PAN-1201: sync-sources/).\nconst BUNDLED_GIT_HOOKS_DIR = SYNC_SOURCES.gitHooks;\n\n// Helper to check if a command exists\nfunction checkCommand(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\ninterface SyncOptions {\n dryRun?: boolean;\n force?: boolean;\n diff?: boolean;\n backupOnly?: boolean;\n ifChanged?: boolean;\n}\n\nexport async function syncCommand(options: SyncOptions): Promise<void> {\n const timings: Array<{ phase: string; ms: number }> = [];\n function time<T>(phase: string, fn: () => T): T {\n const start = performance.now();\n try {\n return fn();\n } finally {\n timings.push({ phase, ms: Math.round(performance.now() - start) });\n }\n }\n async function timeAsync<T>(phase: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n try {\n return await fn();\n } finally {\n timings.push({ phase, ms: Math.round(performance.now() - start) });\n }\n }\n function printTimings(): void {\n if (timings.length === 0) return;\n const summary = timings.map((t) => `${t.phase}=${t.ms}ms`).join(', ');\n console.log(chalk.dim(`[sync-timing] ${summary}`));\n }\n\n // PAN-1201: warn once if the deprecated sync.devroot is still configured.\n const devrootWarning = checkDevrootDeprecation();\n if (devrootWarning) {\n console.log(chalk.yellow(devrootWarning));\n console.log('');\n }\n\n // Startup-only shortcut: skip the expensive full sync when inputs are unchanged.\n if (options.ifChanged && !options.force) {\n const gate = isStartupSyncNeededSync();\n if (!gate.needed) {\n console.log(chalk.dim('[sync] skipped — inputs unchanged'));\n return;\n }\n }\n\n // Dry run mode\n if (options.dryRun) {\n console.log(chalk.bold('Sync Plan (dry run):\\n'));\n\n // Show dev mode status\n if (isDevMode()) {\n console.log(chalk.magenta('Developer mode detected - dev-skills will be synced\\n'));\n }\n\n // Show hooks plan\n const hooksPlan = planHooksSyncSync();\n if (hooksPlan.length > 0) {\n console.log(chalk.cyan('hooks (bin scripts):'));\n for (const hook of hooksPlan) {\n const icon = hook.status === 'new' ? chalk.green('+') : chalk.blue('↻');\n const status = hook.status === 'new' ? '' : chalk.dim('[update]');\n console.log(` ${icon} ${hook.name} ${status}`);\n }\n console.log('');\n }\n\n // Bundled skills + agents → ~/.claude/\n console.log(chalk.cyan('~/.claude/ (skills + agents):'));\n const plan = planSyncSync();\n const allItems = [...plan.skills, ...plan.agents];\n if (allItems.length === 0) {\n console.log(chalk.dim(' (nothing to sync — check sync-sources/ and run `pan install`)'));\n } else {\n const count = (s: string) => allItems.filter((i) => i.status === s).length;\n console.log(\n ` ${chalk.green(`${count('new')} new`)}, ` +\n `${chalk.blue(`${count('symlink')} update`)}, ` +\n `${chalk.cyan(`${count('adopted')} adopted (legacy pre-manifest installs)`)}, ` +\n `${chalk.dim(`${count('exists')} unchanged`)}, ` +\n `${chalk.yellow(`${count('conflict')} user-modified (skipped)`)}`,\n );\n }\n console.log('');\n\n // Context layers → CLAUDE.md managed regions\n console.log(chalk.cyan('context layers → CLAUDE.md:'));\n console.log(` ${chalk.blue('↻')} global → ~/.claude/CLAUDE.md ${chalk.dim('(managed region)')}`);\n for (const { config } of listProjectsSync()) {\n if (existsSync(resolveProjectContextFile(config.path))) {\n console.log(\n ` ${chalk.blue('↻')} ${config.name} → ${join(config.path, 'CLAUDE.md')} ${chalk.dim('(managed region)')}`,\n );\n }\n }\n\n // Show .pan/skills/ source files for each registered project\n const dryRunProjects = listProjectsSync();\n for (const { config } of dryRunProjects) {\n if (!existsSync(config.path)) continue;\n const panSkillsDir = join(config.path, '.pan', 'skills');\n if (existsSync(panSkillsDir)) {\n const skills = readdirSync(panSkillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n if (skills.length > 0) {\n console.log(chalk.cyan(`\\n.pan/skills/ (${config.name}):`));\n for (const skillName of skills) {\n console.log(` ${chalk.green('+')} ${skillName} ${chalk.green('[project-local]')}`);\n }\n }\n }\n\n // Show multi-tool sync targets\n const tools = resolveAlsoSyncToolsSync(config.path);\n if (tools.length > 0) {\n console.log(chalk.cyan(`\\nmulti-tool sync (${config.name}): ${tools.join(', ')}`));\n const panSkillsDirExists = existsSync(join(config.path, '.pan', 'skills'));\n if (panSkillsDirExists) {\n const skills = readdirSync(join(config.path, '.pan', 'skills'), { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n for (const tool of tools) {\n for (const skillName of skills) {\n console.log(` ${chalk.green('+')} ${skillName} → ${tool}`);\n }\n }\n }\n }\n }\n\n // Agent directory cleanup preview\n const agentCleanupPreview = await Effect.runPromise(cleanupAgentDirectories({ dryRun: true }));\n if (agentCleanupPreview.totalOrphaned > 0) {\n console.log(chalk.cyan(`\\nagent cleanup (~/.overdeck/agents/):`));\n console.log(chalk.dim(` Found ${agentCleanupPreview.totalOrphaned} orphaned directories`));\n for (const name of agentCleanupPreview.wouldRemove) {\n console.log(` ${chalk.red('✗')} ${name}`);\n }\n for (const name of agentCleanupPreview.protected) {\n console.log(` ${chalk.yellow('◆')} ${name} ${chalk.dim('(running session)')}`);\n }\n }\n\n console.log('');\n console.log(chalk.dim('Run without --dry-run to apply changes.'));\n printTimings();\n return;\n }\n\n // Run one-time migration: strip legacy sync targets from config.toml\n const syncMigration = time('migrate-sync-targets', () => migrateSyncTargetsSync());\n if (syncMigration.migrated) {\n if (syncMigration.hadNonClaudeTargets) {\n console.log(chalk.yellow('Config updated: removed non-Claude sync targets (Overdeck now syncs to Claude Code only).'));\n }\n }\n\n // Run one-time migration: remove Overdeck-managed symlinks from legacy runtime dirs\n const cleanupResult = time('cleanup-legacy-runtimes', () => cleanupLegacyRuntimeSymlinksSync());\n if (cleanupResult.cleaned.length > 0) {\n console.log(chalk.dim(`Removed ${cleanupResult.total} legacy runtime symlink(s): ${cleanupResult.cleaned.join(', ')}`));\n }\n\n // One-time migration: remove Overdeck symlinks from ~/.claude/ (devroot replaces this)\n const migration = time('migrate-stale-personal', () => migrateStalePersonalContentSync());\n if (migration.removedSymlinks.length > 0) {\n console.log(chalk.cyan(`Migrated: removed ${migration.removedSymlinks.length} Overdeck symlink(s) from ~/.claude/`));\n if (migration.preservedUserContent.length > 0) {\n console.log(chalk.dim(` Preserved ${migration.preservedUserContent.length} user-created item(s)`));\n }\n }\n\n // 0.7.0 upgrade: remove renamed/deleted legacy skills from ~/.claude/skills/\n const removedLegacy = time('remove-legacy-skills', () => removeLegacySkills070Sync());\n if (removedLegacy.length > 0) {\n console.log(chalk.dim(`Removed ${removedLegacy.length} legacy skill(s) from upgrade to 0.7.0: ${removedLegacy.join(', ')}`));\n }\n\n const config = loadConfigSync();\n\n // Create backup if enabled\n if (config.sync.backup_before_sync) {\n const backupSpinner = ora('Creating backup...').start();\n\n const backupDirs = [\n SYNC_TARGET.skills,\n SYNC_TARGET.commands,\n SYNC_TARGET.agents,\n ];\n\n const backup = time('backup', () => createBackupSync(backupDirs));\n\n if (backup.targets.length > 0) {\n backupSpinner.succeed(`Backup created: ${backup.timestamp}`);\n } else {\n backupSpinner.info('No existing content to backup');\n }\n\n if (options.backupOnly) {\n printTimings();\n return;\n }\n }\n\n // Refresh cache from repo source\n const cacheSpinner = ora('Refreshing cache from repo...').start();\n const cacheResult = time('refresh-cache', () => refreshCacheSync());\n const cacheParts = [];\n if (cacheResult.skills.copied > 0) cacheParts.push(`${cacheResult.skills.copied} skills`);\n if (cacheResult.agents.copied > 0) cacheParts.push(`${cacheResult.agents.copied} agents`);\n if (cacheResult.rules.copied > 0) cacheParts.push(`${cacheResult.rules.copied} rules`);\n cacheSpinner.succeed(`Cache refreshed: ${cacheParts.length > 0 ? cacheParts.join(', ') : 'up to date'}`);\n\n // Distribute bundled skills + agents into the user's Claude Code home.\n const spinner = ora('Distributing skills and agents to ~/.claude/...').start();\n const result = time('execute-sync', () => executeSyncSync({ force: options.force, diff: options.diff }));\n const totalSynced = result.created.length + result.updated.length + result.adopted.length;\n const adoptionSummary = result.adopted.length > 0\n ? `, ${result.adopted.length} adopted (legacy pre-manifest installs)`\n : '';\n\n // Show diffs if requested\n if (result.diffs.length > 0) {\n spinner.info(`Showing diffs for ${result.diffs.length} modified file(s):\\n`);\n for (const d of result.diffs) {\n console.log(chalk.cyan(`--- ${d.path} (installed)`));\n console.log(chalk.cyan(`+++ ${d.path} (current on disk)`));\n // Simple line-by-line diff\n const sourceLines = d.sourceContent.split('\\n');\n const targetLines = d.targetContent.split('\\n');\n const maxLines = Math.max(sourceLines.length, targetLines.length);\n for (let i = 0; i < maxLines; i++) {\n if (sourceLines[i] !== targetLines[i]) {\n if (targetLines[i] !== undefined) console.log(chalk.red(`- ${targetLines[i]}`));\n if (sourceLines[i] !== undefined) console.log(chalk.green(`+ ${sourceLines[i]}`));\n }\n }\n console.log('');\n }\n }\n\n if (result.conflicts.length > 0 && !options.force) {\n spinner.warn(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary}, ${result.conflicts.length} user-modified (skipped)`);\n console.log('');\n console.log(chalk.yellow('Modified since Overdeck installed:'));\n for (const name of result.conflicts) {\n console.log(chalk.dim(` - ${name}`));\n }\n console.log('');\n console.log(chalk.dim('Use --force to overwrite, --diff to see changes.'));\n } else if (result.skipped.length > 0) {\n spinner.succeed(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary} (${result.skipped.length} unchanged or user-owned)`);\n } else {\n spinner.succeed(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary}`);\n }\n\n // Render the layered context into harness CLAUDE.md files (PAN-1201).\n const ctxSpinner = ora('Rendering context layers...').start();\n const ctx = time('context-layers', () => syncContextLayersSync());\n const ctxParts: string[] = [];\n if (ctx.globalStubCreated) ctxParts.push('seeded global.md');\n if (ctx.globalWritten) ctxParts.push('~/.claude/CLAUDE.md');\n if (ctx.projectsWritten.length > 0) {\n ctxParts.push(`${ctx.projectsWritten.length} project file(s)`);\n }\n if (ctx.errors.length > 0) {\n ctxSpinner.warn(`Context layers rendered with ${ctx.errors.length} error(s)`);\n for (const e of ctx.errors) console.log(chalk.red(` ✗ ${e}`));\n } else if (ctxParts.length > 0) {\n ctxSpinner.succeed(`Context layers rendered: ${ctxParts.join(', ')}`);\n } else {\n ctxSpinner.info('Context layers already up to date');\n }\n\n // One-time notice: a managed region was added to a file that already had\n // hand-authored content. Reassure the user their content is preserved and\n // point at the backup taken before the first injection.\n if (ctx.firstInjections.length > 0) {\n console.log(\n chalk.cyan('\\n ℹ Overdeck added a managed region to existing context file(s):'),\n );\n for (const fi of ctx.firstInjections) {\n console.log(` • ${fi.file}`);\n console.log(\n chalk.dim(\n ` Your content outside the markers is untouched. Backup: ${fi.backupPath}`,\n ),\n );\n }\n console.log(\n chalk.dim(\n ' Edit the layer source (pan context edit), never the region between the markers.',\n ),\n );\n }\n\n // Sync hooks (bin scripts)\n const hooksSpinner = ora('Syncing hooks...').start();\n const hooksResult = time('sync-hooks', () => syncHooksSync());\n\n if (hooksResult.errors.length > 0) {\n hooksSpinner.warn(`Synced ${hooksResult.synced.length} hooks, ${hooksResult.errors.length} errors`);\n for (const error of hooksResult.errors) {\n console.log(chalk.red(` ✗ ${error}`));\n }\n } else if (hooksResult.synced.length > 0) {\n hooksSpinner.succeed(`Synced ${hooksResult.synced.length} hooks to ~/.overdeck/bin/`);\n } else {\n hooksSpinner.info('No hooks to sync');\n }\n\n // Ensure beads database exists for each registered project (first-time setup guard).\n // bd install puts the binary in PATH, but bd init must be run once per project to\n // create the Dolt database. Without it, workspace beads creation silently fails.\n const projects = listProjectsSync();\n if (projects.length > 0 && checkCommand('bd')) {\n for (const { key, config } of projects) {\n if (!existsSync(config.path)) continue;\n const mainBeadsDir = join(config.path, '.beads');\n if (!existsSync(mainBeadsDir)) continue; // Project hasn't used beads yet — skip\n const bdRoot = resolveStateReadHomeSync(config).root;\n // Test connectivity. If the database is missing, auto-init.\n try {\n execSync(`bd -C ${JSON.stringify(bdRoot)} list --json --limit 0 2>&1`, { cwd: config.path, stdio: 'pipe', timeout: 8000 });\n } catch (e: any) {\n const msg = String(e?.stdout ?? e?.stderr ?? e?.message ?? '');\n if (msg.includes('database') && (msg.includes('not found') || msg.includes('not exist') || msg.includes('defaulting'))) {\n const beadsSpinner = ora(`Initializing beads database for ${config.name}...`).start();\n try {\n const prefix = (key || config.name).toLowerCase().replace(/[^a-z0-9-]/g, '-');\n execSync(`bd -C ${JSON.stringify(bdRoot)} init --prefix ${prefix}`, { cwd: config.path, stdio: 'pipe', timeout: 20000 });\n try { execSync('git config beads.role contributor', { cwd: config.path, stdio: 'pipe' }); } catch { /* non-fatal */ }\n beadsSpinner.succeed(`Beads database initialized for ${config.name} (prefix: ${prefix})`);\n } catch {\n beadsSpinner.warn(`Could not auto-initialize beads for ${config.name} — run: cd ${config.path} && bd init`);\n }\n }\n }\n }\n }\n\n\n // Check jq availability (required by statusline, beads, specialists)\n if (!checkCommand('jq')) {\n console.log(chalk.yellow('\\n ⚠ jq not found — statusline and other features need it'));\n console.log(chalk.dim(' Install: apt install jq / brew install jq\\n'));\n }\n\n // Sync statusline to all runtimes\n const statuslineSpinner = ora('Syncing statusline...').start();\n const statuslineResult = time('sync-statusline', () => syncStatuslineSync());\n\n if (statuslineResult.errors.length > 0) {\n statuslineSpinner.warn(`Synced statusline to ${statuslineResult.synced.length} runtime(s), ${statuslineResult.errors.length} error(s)`);\n for (const error of statuslineResult.errors) {\n console.log(chalk.red(` ✗ ${error}`));\n }\n } else if (statuslineResult.synced.length > 0) {\n statuslineSpinner.succeed(`Synced statusline to ${statuslineResult.synced.join(', ')}`);\n } else {\n statuslineSpinner.info('No statusline script found (scripts/statusline.sh)');\n }\n\n // Check and install mkcert if missing\n if (!checkCommand('mkcert')) {\n const mkcertSpinner = ora('Installing mkcert...').start();\n try {\n const binDir = join(homedir(), '.local', 'bin');\n mkdirSync(binDir, { recursive: true });\n const mkcertPath = join(binDir, 'mkcert');\n const arch = process.arch === 'x64' ? 'amd64' : process.arch;\n execSync(`curl -sL \"https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-${arch}\" -o \"${mkcertPath}\" && chmod +x \"${mkcertPath}\"`, {\n stdio: 'pipe',\n timeout: 60000,\n });\n mkcertSpinner.succeed('mkcert installed');\n } catch {\n mkcertSpinner.warn('Failed to install mkcert - run: https://github.com/FiloSottile/mkcert/releases');\n }\n }\n\n // Enforce Overdeck-managed MCP server defaults: Playwright --isolated\n // flag (prevents stale zoom/profile state) and the off-the-shelf Excalidraw\n // MCP server (backs the /excalidraw skill). Both helpers are idempotent and\n // mutate the parsed config in place; we only write back if anything changed.\n const mcpPath = join(homedir(), '.claude', 'mcp.json');\n try {\n if (existsSync(mcpPath)) {\n const mcpConfig = JSON.parse(readFileSync(mcpPath, 'utf-8'));\n const playwrightChanged = ensurePlaywrightIsolationSync(mcpConfig);\n const excalidrawChanged = ensureExcalidrawMcpSync(mcpConfig);\n if (playwrightChanged || excalidrawChanged) {\n writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\\n');\n }\n if (playwrightChanged) {\n console.log(chalk.green('✓ Added --isolated to Playwright MCP (prevents stale zoom/profile state)'));\n }\n if (excalidrawChanged) {\n console.log(chalk.green('✓ Registered Excalidraw MCP server (backs the /excalidraw skill)'));\n }\n }\n } catch {\n // Non-fatal — skip if mcp.json can't be read/written\n }\n\n // Migrate .overdeck/ → .pan/ and run multi-tool sync in all registered projects\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n // Migrate .overdeck/ subdirs → .pan/\n const migResult = migrateOverdeckToPanSync(config.path);\n if (migResult.migrated.length > 0) {\n console.log(chalk.cyan(`Migrated .overdeck/ → .pan/ in ${config.name}: ${migResult.migrated.join(', ')}`));\n }\n if (migResult.skipped.length > 0) {\n console.log(chalk.yellow(`Migration skipped (both exist) in ${config.name}: ${migResult.skipped.join(', ')}`));\n }\n for (const err of migResult.errors) {\n console.log(chalk.red(`Migration error in ${config.name}: ${err}`));\n }\n\n // Multi-tool skill sync (cursor, codex, windsurf, cline, copilot, aider)\n const toolSyncResults = runMultiToolSyncSync(config.path);\n for (const r of toolSyncResults) {\n if (r.written.length > 0) {\n console.log(chalk.cyan(`Synced ${r.written.length} skill(s) to ${r.tool} in ${config.name}`));\n }\n for (const err of r.errors) {\n console.log(chalk.red(`Multi-tool sync error (${r.tool}) in ${config.name}: ${err}`));\n }\n }\n }\n\n // Sync git hooks to all registered projects (branch protection)\n if (projects.length > 0 && existsSync(BUNDLED_GIT_HOOKS_DIR)) {\n const gitHooksSpinner = ora('Installing git hooks in registered projects...').start();\n let totalInstalled = 0;\n let projectsUpdated = 0;\n\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n // Find all .git directories (handles polyrepos)\n const gitDirs: string[] = [];\n\n // Check root\n if (existsSync(join(config.path, '.git')) && statSync(join(config.path, '.git')).isDirectory()) {\n gitDirs.push(join(config.path, '.git'));\n } else {\n // Scan for polyrepo\n try {\n const entries = readdirSync(config.path);\n for (const entry of entries) {\n const entryPath = join(config.path, entry);\n const gitPath = join(entryPath, '.git');\n if (existsSync(gitPath) && statSync(gitPath).isDirectory()) {\n gitDirs.push(gitPath);\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n // Install hooks in each git dir\n for (const gitDir of gitDirs) {\n const hooksTarget = join(gitDir, 'hooks');\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n try {\n const hooks = readdirSync(BUNDLED_GIT_HOOKS_DIR).filter(f =>\n statSync(join(BUNDLED_GIT_HOOKS_DIR, f)).isFile()\n );\n\n for (const hook of hooks) {\n const source = join(BUNDLED_GIT_HOOKS_DIR, hook);\n const target = join(hooksTarget, hook);\n\n // Skip if already a symlink to our hook\n if (existsSync(target)) {\n try {\n const { readlinkSync } = await import('fs');\n if (readlinkSync(target) === source) continue;\n } catch {\n // Not a symlink\n }\n // Backup existing\n const { renameSync } = await import('fs');\n try { renameSync(target, `${target}.backup`); } catch {}\n }\n\n try {\n symlinkSync(source, target);\n totalInstalled++;\n } catch {}\n }\n projectsUpdated++;\n } catch {}\n }\n }\n\n if (totalInstalled > 0) {\n gitHooksSpinner.succeed(`Installed git hooks in ${projectsUpdated} project(s)`);\n } else {\n gitHooksSpinner.info('Git hooks already up to date');\n }\n }\n\n // Ensure beads database exists for each registered project (first-time setup guard).\n // bd install puts the binary in PATH, but bd init must be run once per project to\n // create the Dolt database. Without it, workspace beads creation silently fails.\n if (projects.length > 0 && checkCommand('bd')) {\n for (const { key, config } of projects) {\n if (!existsSync(config.path)) continue;\n const mainBeadsDir = join(config.path, '.beads');\n if (!existsSync(mainBeadsDir)) continue; // Project hasn't used beads yet — skip\n const bdRoot = resolveStateReadHomeSync(config).root;\n // Test connectivity. If the database is missing, auto-init.\n try {\n execSync(`bd -C ${JSON.stringify(bdRoot)} list --json --limit 0 2>&1`, { cwd: config.path, stdio: 'pipe', timeout: 8000 });\n } catch (e: any) {\n const msg = String(e?.stdout ?? e?.stderr ?? e?.message ?? '');\n if (msg.includes('database') && (msg.includes('not found') || msg.includes('not exist') || msg.includes('defaulting'))) {\n const beadsSpinner = ora(`Initializing beads database for ${config.name}...`).start();\n try {\n const prefix = (key || config.name).toLowerCase().replace(/[^a-z0-9-]/g, '-');\n execSync(`bd -C ${JSON.stringify(bdRoot)} init --prefix ${prefix}`, { cwd: config.path, stdio: 'pipe', timeout: 20000 });\n beadsSpinner.succeed(`Beads database initialized for ${config.name} (prefix: ${prefix})`);\n } catch {\n beadsSpinner.warn(`Could not auto-initialize beads for ${config.name} — run: cd ${config.path} && bd init`);\n }\n }\n }\n }\n }\n\n // Pi harness — point Pi's settings file at ~/.claude/skills so it sees the\n // same skills tree we just synced. No-op when Pi is not on PATH (PAN-636).\n const piResult = syncPiSettingsSync();\n if (piResult.status === 'created') {\n console.log(chalk.cyan(`Pi settings: created ${piResult.path.replace(homedir(), '~')}`));\n } else if (piResult.status === 'updated') {\n console.log(chalk.cyan(`Pi settings: merged skills entry into ${piResult.path.replace(homedir(), '~')}`));\n } else if (piResult.status === 'skipped' && piResult.reason === 'existing settings.json is not valid JSON') {\n console.log(chalk.yellow(`Pi settings: ${piResult.path.replace(homedir(), '~')} is not valid JSON — left untouched`));\n }\n\n // Mirror project-level skills/ → .claude/skills/ for a project that keeps a\n // top-level skills/ tree, so pan sync works from inside such a project.\n const skillsMirror = mirrorProjectSkillsSync(process.cwd());\n const skillsParts: string[] = [];\n if (skillsMirror.added.length > 0) skillsParts.push(`${skillsMirror.added.length} added`);\n if (skillsMirror.updated.length > 0) skillsParts.push(`${skillsMirror.updated.length} updated`);\n if (skillsMirror.removed.length > 0) skillsParts.push(`${skillsMirror.removed.length} removed`);\n if (skillsParts.length > 0) {\n console.log(chalk.cyan(`Skills mirror: ${skillsParts.join(', ')}`));\n }\n\n // Agent directory cleanup\n const cleanupSpinner = ora('Checking for orphaned agent directories...').start();\n const agentCleanupResult = await Effect.runPromise(cleanupAgentDirectories({ dryRun: false, force: options.force }));\n\n if (agentCleanupResult.totalOrphaned === 0) {\n cleanupSpinner.succeed('No orphaned agent directories found');\n } else {\n const removedCount = agentCleanupResult.removed.length;\n const protectedCount = agentCleanupResult.protected.length;\n\n if (removedCount > 0) {\n cleanupSpinner.succeed(`Removed ${removedCount} orphaned director${removedCount === 1 ? 'y' : 'ies'}`);\n } else if (protectedCount > 0) {\n cleanupSpinner.info(`Found ${protectedCount} orphaned director${protectedCount === 1 ? 'y' : 'ies'} with running sessions (skipped)`);\n }\n\n if (agentCleanupResult.protected.length > 0) {\n console.log(chalk.dim(` Protected (running sessions): ${agentCleanupResult.protected.join(', ')}`));\n }\n }\n\n // vBRIEF state disagreement audit (PAN-946: workspace-9ny)\n try {\n const auditSpinner = ora('Running vBRIEF state audit...').start();\n const disagreements: Array<{ issueId: string; problem: string; fix: string }> = [];\n const hasGh = checkCommand('gh');\n\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n const activeDir = join(config.path, 'vbrief', 'active');\n const completedDir = join(config.path, 'vbrief', 'completed');\n\n // (1) vBRIEF in active/ but tracker says closed\n if (existsSync(activeDir)) {\n const activeFiles = readdirSync(activeDir).filter(\n f => f.endsWith('.vbrief.json') && !f.startsWith('continue-')\n );\n for (const file of activeFiles) {\n const parsed = parseVBriefFilename(file);\n if (!parsed) continue;\n const issueId = parsed.issueId.toUpperCase();\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (ghInfo.isGitHub && hasGh) {\n try {\n const state = execSync(\n `gh issue view ${ghInfo.number} --repo ${ghInfo.owner}/${ghInfo.repo} --json state --jq '.state'`,\n { encoding: 'utf-8', timeout: 10000, stdio: 'pipe' }\n ).trim();\n if (state.toLowerCase() === 'closed') {\n disagreements.push({\n issueId,\n problem: 'vBRIEF in active/ but GitHub issue is closed',\n fix: `pan scope complete ${issueId}`,\n });\n }\n } catch { /* skip if gh fails */ }\n }\n }\n }\n\n // (2) vBRIEF in completed/ but workspace still exists\n if (existsSync(completedDir)) {\n const completedFiles = readdirSync(completedDir).filter(\n f => f.endsWith('.vbrief.json') && !f.startsWith('continue-')\n );\n for (const file of completedFiles) {\n const parsed = parseVBriefFilename(file);\n if (!parsed) continue;\n const issueId = parsed.issueId.toUpperCase();\n const workspacePath = join(config.path, 'workspaces', `feature-${issueId.toLowerCase()}`);\n if (existsSync(workspacePath)) {\n disagreements.push({\n issueId,\n problem: 'vBRIEF in completed/ but workspace worktree still exists',\n fix: `pan close ${issueId}`,\n });\n }\n }\n }\n\n // (3) tracker shows in-progress but no vBRIEF in active/ (scanning worktrees)\n const workspacesDir = join(config.path, 'workspaces');\n if (existsSync(workspacesDir)) {\n let activeIssueIds = new Set<string>();\n if (existsSync(activeDir)) {\n activeIssueIds = new Set(\n readdirSync(activeDir)\n .filter(f => f.endsWith('.vbrief.json') && !f.startsWith('continue-'))\n .map(f => {\n const parsed = parseVBriefFilename(f);\n return parsed ? parsed.issueId.toUpperCase() : '';\n })\n .filter(Boolean)\n );\n }\n\n const worktreeEntries = readdirSync(workspacesDir, { withFileTypes: true })\n .filter(e => e.isDirectory() && e.name.startsWith('feature-'))\n .map(e => e.name.replace('feature-', '').toUpperCase());\n\n for (const worktreeIssueId of worktreeEntries) {\n if (activeIssueIds.has(worktreeIssueId)) continue;\n\n let trackerOpen = false;\n const ghInfo = resolveGitHubIssueSync(worktreeIssueId);\n if (ghInfo.isGitHub && hasGh) {\n try {\n const state = execSync(\n `gh issue view ${ghInfo.number} --repo ${ghInfo.owner}/${ghInfo.repo} --json state --jq '.state'`,\n { encoding: 'utf-8', timeout: 10000, stdio: 'pipe' }\n ).trim();\n trackerOpen = state.toLowerCase() === 'open';\n } catch { /* skip if gh fails — fall through to heuristic */ }\n }\n\n // Flag if tracker is open OR if we couldn't check tracker (workspace without active vBRIEF is always suspicious)\n if (trackerOpen || !ghInfo.isGitHub || !hasGh) {\n disagreements.push({\n issueId: worktreeIssueId,\n problem: trackerOpen\n ? 'Tracker shows open but no vBRIEF in active/'\n : 'Workspace exists but no vBRIEF in active/',\n fix: `pan scope approve ${worktreeIssueId}`,\n });\n }\n }\n }\n }\n\n if (disagreements.length === 0) {\n auditSpinner.succeed('vBRIEF state audit passed — no disagreements');\n } else {\n auditSpinner.warn(`Found ${disagreements.length} vBRIEF state disagreement(s)`);\n for (const d of disagreements) {\n console.log(chalk.yellow(` ⚠ ${d.issueId}: ${d.problem}`));\n console.log(chalk.dim(` Fix: ${d.fix}`));\n }\n }\n } catch (auditErr: any) {\n console.warn(`[pan sync] vBRIEF audit failed (non-fatal): ${auditErr?.message ?? auditErr}`);\n }\n\n // Record the input hash so a future startup sync can skip when nothing changed.\n time('write-sync-manifest', () => writeSyncManifestSync());\n\n printTimings();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sync-Bsww9tuj.js","names":[],"sources":["../src/lib/config-migration.ts","../src/lib/agent-directory-cleanup.ts","../src/lib/multi-tool-sync.ts","../src/cli/commands/sync.ts"],"sourcesContent":["/**\n * Configuration Migration\n *\n * Migrates from legacy settings.json format to new config.yaml format.\n * Legacy presets are no longer supported - all selection is now smart/capability-based.\n */\n\nimport { Effect } from 'effect';\nimport { readFileSync, writeFileSync, existsSync, renameSync, readdirSync, lstatSync, readlinkSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { loadSettingsSync, type SettingsConfig } from './settings.js';\nimport { type YamlConfig } from './config-yaml.js';\nimport { type ModelId } from './settings.js';\nimport { FsError } from './errors.js';\n\n/** Path to legacy settings file */\nconst LEGACY_SETTINGS_PATH = join(homedir(), '.overdeck', 'settings.json');\n\n/** Path to new config file */\nconst NEW_CONFIG_PATH = join(homedir(), '.overdeck', 'config.yaml');\n\n/** Path to backup of legacy settings */\nconst BACKUP_SETTINGS_PATH = join(homedir(), '.overdeck', 'settings.json.backup');\n\n/**\n * Check if migration is needed\n * Returns true if settings.json exists and config.yaml doesn't\n */\nexport function needsMigrationSync(): boolean {\n return existsSync(LEGACY_SETTINGS_PATH) && !existsSync(NEW_CONFIG_PATH);\n}\n\n/**\n * Check if legacy settings exist (even if already migrated)\n */\nexport function hasLegacySettingsSync(): boolean {\n return existsSync(LEGACY_SETTINGS_PATH);\n}\n\n/**\n * Determine which providers are enabled based on API keys\n */\nfunction detectEnabledProviders(settings: SettingsConfig): {\n anthropic: boolean;\n openai: boolean;\n google: boolean;\n kimi: boolean;\n} {\n return {\n anthropic: true, // Always enabled\n openai: !!settings.api_keys.openai,\n google: !!settings.api_keys.google,\n kimi: false, // Legacy settings don't have Kimi\n };\n}\n\n/**\n * Convert legacy settings.json to new config.yaml format\n */\nexport function convertToYamlConfigSync(settings: SettingsConfig): YamlConfig {\n const providers = detectEnabledProviders(settings);\n\n const config: YamlConfig = {\n models: {\n providers,\n overrides: {}, // No overrides from legacy\n gemini_thinking_level: 3,\n },\n api_keys: settings.api_keys,\n };\n\n return config;\n}\n\n/**\n * Migration options\n */\nexport interface MigrationOptions {\n /** Create backup of legacy settings (default: true) */\n backup?: boolean;\n /** Delete legacy settings after migration (default: false) */\n deleteLegacy?: boolean;\n /** Dry run - don't actually write files (default: false) */\n dryRun?: boolean;\n}\n\n/**\n * Migration result\n */\nexport interface MigrationResult {\n success: boolean;\n overridesCount: number;\n providersEnabled: string[];\n message: string;\n error?: string;\n}\n\nexport function migrateConfigSync(options: MigrationOptions = {}): MigrationResult {\n const { backup = true, deleteLegacy = false, dryRun = false } = options;\n\n try {\n // Check if migration is needed\n if (!needsMigrationSync()) {\n if (existsSync(NEW_CONFIG_PATH)) {\n return {\n success: true,\n overridesCount: 0,\n providersEnabled: ['anthropic'],\n message: 'Config already migrated (config.yaml exists)',\n };\n }\n return {\n success: false,\n overridesCount: 0,\n providersEnabled: [],\n message: 'No legacy settings.json found to migrate',\n };\n }\n\n // Load legacy settings\n const settings = loadSettingsSync();\n\n // Convert to YAML config\n const yamlConfig = convertToYamlConfigSync(settings);\n\n // Generate YAML content\n const yamlContent = yaml.dump(yamlConfig, {\n indent: 2,\n lineWidth: 120,\n noRefs: true,\n });\n\n // Dry run - just return what would happen\n if (dryRun) {\n const providersEnabled = Object.entries(yamlConfig.models?.providers || {})\n .filter(([_, enabled]) => enabled)\n .map(([name]) => name);\n\n return {\n success: true,\n overridesCount: Object.keys(yamlConfig.models?.overrides || {}).length,\n providersEnabled,\n message: `Would migrate to smart selection with ${providersEnabled.length} providers enabled`,\n };\n }\n\n // Write new config.yaml\n writeFileSync(NEW_CONFIG_PATH, yamlContent, 'utf-8');\n\n // Back up legacy settings if requested\n if (backup) {\n const legacyContent = readFileSync(LEGACY_SETTINGS_PATH, 'utf-8');\n writeFileSync(BACKUP_SETTINGS_PATH, legacyContent, 'utf-8');\n }\n\n // Delete legacy settings if requested\n if (deleteLegacy) {\n renameSync(LEGACY_SETTINGS_PATH, `${LEGACY_SETTINGS_PATH}.migrated`);\n }\n\n const providersEnabled = Object.entries(yamlConfig.models?.providers || {})\n .filter(([_, enabled]) => enabled)\n .map(([name]) => name);\n\n return {\n success: true,\n overridesCount: Object.keys(yamlConfig.models?.overrides || {}).length,\n providersEnabled,\n message: `Successfully migrated to smart selection with ${providersEnabled.length} providers`,\n };\n } catch (error: any) {\n return {\n success: false,\n overridesCount: 0,\n providersEnabled: [],\n message: 'Migration failed',\n error: error.message,\n };\n }\n}\n\n/**\n * Get migration status\n */\nexport function getMigrationStatusSync(): {\n needsMigration: boolean;\n hasLegacySettings: boolean;\n hasNewConfig: boolean;\n} {\n return {\n needsMigration: needsMigrationSync(),\n hasLegacySettings: existsSync(LEGACY_SETTINGS_PATH),\n hasNewConfig: existsSync(NEW_CONFIG_PATH),\n };\n}\n\n/**\n * Clean up legacy runtime symlinks from removed runtimes.\n *\n * PAN-142: Overdeck consolidated to Claude Code as the sole runtime.\n * This removes any Overdeck-managed symlinks from legacy runtime directories\n * (codex, cursor, gemini, opencode).\n */\nexport interface LegacyCleanupResult {\n cleaned: string[];\n total: number;\n errors: string[];\n}\n\nexport function cleanupLegacyRuntimeSymlinksSync(): LegacyCleanupResult {\n const legacyDirs = [\n { name: 'codex', base: join(homedir(), '.codex') },\n { name: 'cursor', base: join(homedir(), '.cursor') },\n { name: 'gemini', base: join(homedir(), '.gemini') },\n { name: 'opencode', base: join(homedir(), '.opencode') },\n ];\n\n const cleaned: string[] = [];\n const errors: string[] = [];\n\n for (const { name, base } of legacyDirs) {\n for (const subdir of ['skills', 'commands', 'agents']) {\n const dir = join(base, subdir);\n if (!existsSync(dir)) continue;\n\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const entryPath = join(dir, entry);\n try {\n const stats = lstatSync(entryPath);\n if (!stats.isSymbolicLink()) continue;\n\n const linkTarget = readlinkSync(entryPath);\n // Only remove symlinks pointing to Overdeck directories\n if (linkTarget.includes('.overdeck')) {\n unlinkSync(entryPath);\n cleaned.push(`${name}/${subdir}/${entry}`);\n }\n } catch (err: any) {\n errors.push(`${name}/${subdir}/${entry}: ${err.message}`);\n }\n }\n } catch (err: any) {\n // Directory may not be readable, that's fine\n errors.push(`${name}/${subdir}: ${err.message}`);\n }\n }\n }\n\n return { cleaned, total: cleaned.length, errors };\n}\n\n/**\n * Migrate legacy sync config by stripping the 'targets' field from config.toml.\n * This handles users who had `targets = [\"claude\", \"codex\"]` in their config.\n */\nexport function migrateSyncTargetsSync(): { migrated: boolean; hadNonClaudeTargets: boolean } {\n const configPath = join(homedir(), '.overdeck', 'config.toml');\n\n if (!existsSync(configPath)) {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n\n try {\n const content = readFileSync(configPath, 'utf-8');\n\n // Check if targets field exists\n const targetsMatch = content.match(/^targets\\s*=\\s*\\[([^\\]]*)\\]/m);\n if (!targetsMatch) {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n\n // Check if non-claude targets were configured\n const targetsStr = targetsMatch[1];\n const hadNonClaudeTargets = /codex|cursor|gemini|opencode/i.test(targetsStr);\n\n // Remove the targets line\n const newContent = content.replace(/^targets\\s*=\\s*\\[[^\\]]*\\]\\s*\\n?/m, '');\n writeFileSync(configPath, newContent, 'utf-8');\n\n return { migrated: true, hadNonClaudeTargets };\n } catch {\n return { migrated: false, hadNonClaudeTargets: false };\n }\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n// One-shot config-migration helpers. Each is run at most once per install at\n// CLI startup. Errors are caught internally (return false / empty result), so\n// Effect.sync is appropriate here.\n\n/** True when legacy settings.json exists and config.yaml doesn't. Pure. */\nexport const needsMigration = (): Effect.Effect<boolean> =>\n Effect.sync(() => needsMigrationSync());\n\n/** True if any legacy settings.json is present (with or without yaml). Pure. */\nexport const hasLegacySettings = (): Effect.Effect<boolean> =>\n Effect.sync(() => hasLegacySettingsSync());\n\n/** Convert a SettingsConfig into a YamlConfig. Pure. */\nexport const convertToYamlConfig = (\n settings: SettingsConfig,\n): Effect.Effect<YamlConfig> => Effect.sync(() => convertToYamlConfigSync(settings));\n\n/** Run the settings.json → config.yaml migration. Surfaces FsError on IO failure. */\nexport const migrateConfig = (\n options: MigrationOptions = {},\n): Effect.Effect<MigrationResult, FsError> =>\n Effect.try({\n try: () => migrateConfigSync(options),\n catch: (cause) =>\n new FsError({ path: NEW_CONFIG_PATH, operation: 'migrate-config', cause }),\n });\n\n/** Inspect migration state without modifying anything. Pure. */\nexport const getMigrationStatus = (): Effect.Effect<\n ReturnType<typeof getMigrationStatusSync>\n> => Effect.sync(() => getMigrationStatusSync());\n\n/** Sweep legacy runtime symlinks under ~/.overdeck (idempotent). */\nexport const cleanupLegacyRuntimeSymlinks =\n (): Effect.Effect<LegacyCleanupResult> =>\n Effect.sync(() => cleanupLegacyRuntimeSymlinksSync());\n\n/** Migrate `targets = [...]` lines out of config.toml. Pure-ish (catches). */\nexport const migrateSyncTargets = (): Effect.Effect<{\n migrated: boolean;\n hadNonClaudeTargets: boolean;\n}> => Effect.sync(() => migrateSyncTargetsSync());\n","/**\n * Agent directory cleanup — identifies and removes orphaned legacy agent directories.\n *\n * Valid directories (preserved):\n * - agent-<issueId> — work agents (always preserved)\n * - planning-<issueId> — planning agents (only preserved while tmux session is running)\n * - conv-* — conversation directories (ALWAYS preserved: ohmypi\n * conversations write their only transcript to\n * conv-<name>/sessions/*.jsonl via --session-dir, and\n * codex conversations keep rollout JSONLs under\n * conv-<name>/codex-home/sessions/. Deleting these\n * destroys conversation history permanently — 2026-07-05\n * incident: a boot-time `pan sync` wiped every ended\n * pi/codex conversation transcript.)\n *\n * Legacy directories (eligible for cleanup when no tmux session is running):\n * - work-<issueId>, review-<issueId>, test-<issueId>, merge-<issueId>\n * - agent-<number>, agent-agent-*, agent-* with uppercase prefix\n * - specialist-*\n * - Any other non-standard name\n */\n\nimport { existsSync, readdirSync, rmSync } from 'fs';\nimport { access, readdir, rm } from 'node:fs/promises';\nimport { join } from 'path';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from './paths.js';\nimport { listSessionNames } from './tmux.js';\nimport { parseIssueIdSync } from './issue-id.js';\nimport { FsError } from './errors.js';\nimport { getAgentStateSync } from './agents.js';\n\nexport const CLOSED_ISSUE_AGENT_DIR_GRACE_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Valid agent directory naming patterns.\n *\n * Standard issue IDs: PREFIX-NUMBER → normalized to lowercase with dash\n * Rally issue IDs: PREFIX_NUMBER → normalized to lowercase, no dash\n *\n * Examples of valid names:\n * agent-pan-801, agent-min-215, agent-f29698\n * planning-pan-801, planning-min-215\n *\n * Examples of legacy names (no longer created):\n * agent-108, agent-MIN-791, agent-agent-pan-699, agent-pan-test-1\n * work-pan-208, review-pan-646, test-pan-646, merge-pan-646\n * conv-20260411-1125, specialist-overdeck-test-agent\n */\n\n/**\n * Check whether a directory name matches a valid work-agent directory pattern.\n *\n * Work-agent directories (agent-<issueId>) are always preserved.\n * Planning-agent directories are handled separately — they are only valid\n * while their tmux session is running.\n */\nexport function isValidAgentDirectoryName(name: string): boolean {\n const match = name.match(/^agent-(.+)$/);\n if (!match) return false;\n\n const suffix = match[1]!;\n // Current code always lowercases issue IDs before creating directories\n if (suffix !== suffix.toLowerCase()) return false;\n\n // Direct agent-<issueId> directories (work agents, role orchestrators without suffix)\n if (parseIssueIdSync(suffix) !== null) return true;\n\n // Specialist directories: agent-<issueId>-<role> or agent-<issueId>-<role>-<subRole>\n // e.g. agent-pan-457-review-correctness, agent-pan-457-test, agent-pan-457-ship\n // Also work agents with slots: agent-pan-457-1\n const parts = suffix.split('-');\n for (let i = 2; i <= parts.length; i++) {\n const candidate = parts.slice(0, i).join('-');\n if (parseIssueIdSync(candidate) !== null) {\n const remainder = parts.slice(i).join('-');\n if (remainder && /^[a-z0-9-]+$/.test(remainder)) return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check whether a directory name is a conversation (conv-*) directory.\n *\n * Conversation launcher/state files live in ~/.overdeck/conversations/, but the\n * conv-* directory under ~/.overdeck/agents/ is the canonical transcript home\n * for non-claude harnesses: ohmypi writes sessions/*.jsonl there (--session-dir)\n * and codex keeps rollout JSONLs under codex-home/sessions/. These directories\n * must NEVER be auto-removed — doing so permanently destroys conversation\n * history.\n */\nexport function isConversationDirectory(name: string): boolean {\n return name.startsWith('conv-');\n}\n\n/**\n * Extract the issue ID from a planning-* directory name.\n * Returns null if the name is not a planning directory or the issue ID is invalid.\n */\nexport function getPlanningIssueId(name: string): string | null {\n const match = name.match(/^planning-(.+)$/);\n if (!match) return null;\n\n const issueId = match[1]!;\n if (issueId !== issueId.toLowerCase()) return null;\n if (parseIssueIdSync(issueId) === null) return null;\n\n return issueId;\n}\n\nexport function getAgentDirectoryIssueId(name: string): string | null {\n const match = name.match(/^(?:agent|planning)-(.+)$/);\n if (!match) return null;\n\n const suffix = match[1]!;\n if (suffix !== suffix.toLowerCase()) return null;\n\n const direct = parseIssueIdSync(suffix);\n if (direct) return direct.raw.toUpperCase();\n\n const parts = suffix.split('-');\n for (let i = 1; i <= parts.length; i++) {\n const candidate = parts.slice(0, i).join('-');\n const parsed = parseIssueIdSync(candidate);\n if (parsed) return parsed.raw.toUpperCase();\n }\n\n return null;\n}\n\nexport interface OrphanedAgentDir {\n name: string;\n path: string;\n hasRunningSession: boolean;\n}\n\nasync function findOrphanedAgentDirsPromise(\n agentsDir: string = AGENTS_DIR,\n): Promise<OrphanedAgentDir[]> {\n if (!existsSync(agentsDir)) {\n return [];\n }\n\n const entries = readdirSync(agentsDir, { withFileTypes: true });\n const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n\n const sessionNames = await Effect.runPromise(listSessionNames());\n const sessionSet = new Set(sessionNames);\n\n const orphaned: OrphanedAgentDir[] = [];\n for (const name of dirs) {\n // Work-agent directories are always valid\n if (isValidAgentDirectoryName(name)) continue;\n\n // Conversation directories hold the only transcript for ohmypi/codex\n // conversations (sessions/*.jsonl, codex-home/sessions/). Never touch them.\n if (isConversationDirectory(name)) continue;\n\n // Planning directories are valid only while their tmux session is running\n const planningIssueId = getPlanningIssueId(name);\n if (planningIssueId !== null) {\n if (sessionSet.has(name)) continue; // running session — preserve\n // No running session — orphaned\n }\n\n const dirPath = join(agentsDir, name);\n orphaned.push({\n name,\n path: dirPath,\n hasRunningSession: sessionSet.has(name),\n });\n }\n\n return orphaned;\n}\n\nexport interface CleanupResult {\n /** Directories that were removed */\n removed: string[];\n /** Directories skipped because a tmux session is still running */\n protected: string[];\n /** Directories that would be removed (dry run) */\n wouldRemove: string[];\n /** Total orphaned directories found */\n totalOrphaned: number;\n}\n\nasync function cleanupAgentDirectoriesPromise(options: {\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n} = {}): Promise<CleanupResult> {\n const { dryRun = false, force = false, agentsDir = AGENTS_DIR } = options;\n\n const orphaned = await Effect.runPromise(findOrphanedAgentDirs(agentsDir));\n const protectedDirs = orphaned.filter((d) => d.hasRunningSession);\n const removable = orphaned.filter((d) => !d.hasRunningSession);\n\n const result: CleanupResult = {\n removed: [],\n protected: protectedDirs.map((d) => d.name),\n wouldRemove: [],\n totalOrphaned: orphaned.length,\n };\n\n if (removable.length === 0) {\n return result;\n }\n\n if (dryRun) {\n result.wouldRemove = removable.map((d) => d.name);\n return result;\n }\n\n if (!force) {\n // Prompt for confirmation is handled by the CLI caller; this function\n // focuses on the actual cleanup logic.\n }\n\n for (const dir of removable) {\n try {\n rmSync(dir.path, { recursive: true, force: true });\n result.removed.push(dir.name);\n } catch {\n // Non-fatal — directory may have already been removed or permissions changed.\n }\n }\n\n return result;\n}\n\ntype IssueReadSourceState = {\n identifier?: unknown;\n id?: unknown;\n status?: unknown;\n state?: unknown;\n canonicalStatus?: unknown;\n rawTrackerState?: unknown;\n completedAt?: unknown;\n closedAt?: unknown;\n};\n\nexport interface ClosedIssueAgentDir {\n name: string;\n path: string;\n issueId: string;\n closedAt: string;\n ageMs: number;\n hasRunningSession: boolean;\n hasStateFile: boolean;\n containsJsonl: boolean;\n}\n\nexport interface ClosedIssueAgentCleanupResult {\n removed: string[];\n protected: string[];\n wouldRemove: string[];\n totalCandidates: number;\n}\n\nfunction normalizeIssueId(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (!trimmed) return null;\n const parsed = parseIssueIdSync(trimmed);\n return (parsed?.raw ?? trimmed).toUpperCase();\n}\n\nfunction isClosedIssueState(issue: IssueReadSourceState): boolean {\n const state = String(issue.state ?? '').toLowerCase();\n const status = String(issue.status ?? '').toLowerCase();\n const canonicalStatus = String(issue.canonicalStatus ?? '').toLowerCase();\n const rawTrackerState = String(issue.rawTrackerState ?? '').toLowerCase();\n return Boolean(\n issue.completedAt ||\n issue.closedAt ||\n state === 'closed' ||\n status === 'done' ||\n status === 'closed' ||\n status === 'cancelled' ||\n status === 'canceled' ||\n status === 'completed' ||\n canonicalStatus === 'done' ||\n canonicalStatus === 'closed' ||\n canonicalStatus === 'cancelled' ||\n canonicalStatus === 'canceled' ||\n canonicalStatus === 'completed' ||\n rawTrackerState === 'closed' ||\n rawTrackerState === 'done' ||\n rawTrackerState === 'completed',\n );\n}\n\nfunction getClosedIssueTimes(issues: unknown[]): Map<string, number> {\n const closed = new Map<string, number>();\n for (const issue of issues) {\n if (!issue || typeof issue !== 'object') continue;\n const item = issue as IssueReadSourceState;\n const issueId = normalizeIssueId(item.identifier) ?? normalizeIssueId(item.id);\n if (!issueId || !isClosedIssueState(item)) continue;\n\n const closedTimestamp = item.completedAt ?? item.closedAt;\n if (typeof closedTimestamp !== 'string') continue;\n\n const closedAtMs = Date.parse(closedTimestamp);\n if (!Number.isFinite(closedAtMs)) continue;\n closed.set(issueId, closedAtMs);\n }\n return closed;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readAgentStateIssueId(dirPath: string): Promise<string | null> {\n try {\n const agentId = dirPath.split('/').pop() ?? '';\n const issueId = getAgentStateSync(agentId)?.issueId;\n return issueId ? normalizeIssueId(issueId) : null;\n } catch {\n return null;\n }\n}\n\nasync function directoryContainsJsonl(dirPath: string): Promise<boolean> {\n let entries;\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name);\n if (entry.isFile() && entry.name.endsWith('.jsonl')) return true;\n if (entry.isDirectory() && await directoryContainsJsonl(entryPath)) return true;\n }\n\n return false;\n}\n\nasync function findClosedIssueAgentDirsPromise(options: {\n issues: unknown[];\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Promise<ClosedIssueAgentDir[]> {\n const agentsDir = options.agentsDir ?? AGENTS_DIR;\n const nowMs = options.nowMs ?? Date.now();\n const graceMs = options.graceMs ?? CLOSED_ISSUE_AGENT_DIR_GRACE_MS;\n const closedIssueTimes = getClosedIssueTimes(options.issues);\n if (closedIssueTimes.size === 0 || !await pathExists(agentsDir)) return [];\n\n const sessionNames = await Effect.runPromise(listSessionNames());\n const sessionSet = new Set(sessionNames);\n const entries = await readdir(agentsDir, { withFileTypes: true });\n const candidates: ClosedIssueAgentDir[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const dirPath = join(agentsDir, entry.name);\n const stateIssueId = await readAgentStateIssueId(dirPath);\n const issueId = stateIssueId ?? getAgentDirectoryIssueId(entry.name);\n if (!issueId) continue;\n\n const closedAtMs = closedIssueTimes.get(issueId);\n if (closedAtMs === undefined) continue;\n\n const ageMs = nowMs - closedAtMs;\n if (ageMs <= graceMs) continue;\n\n candidates.push({\n name: entry.name,\n path: dirPath,\n issueId,\n closedAt: new Date(closedAtMs).toISOString(),\n ageMs,\n hasRunningSession: sessionSet.has(entry.name),\n hasStateFile: getAgentStateSync(entry.name) !== null,\n containsJsonl: await directoryContainsJsonl(dirPath),\n });\n }\n\n return candidates;\n}\n\nasync function cleanupClosedIssueAgentDirectoriesPromise(options: {\n issues: unknown[];\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Promise<ClosedIssueAgentCleanupResult> {\n const candidates = await findClosedIssueAgentDirsPromise(options);\n const protectedDirs = candidates.filter((dir) => dir.hasRunningSession || dir.containsJsonl);\n const removable = candidates.filter((dir) => !dir.hasRunningSession && !dir.containsJsonl);\n const result: ClosedIssueAgentCleanupResult = {\n removed: [],\n protected: protectedDirs.map((dir) => dir.name),\n wouldRemove: [],\n totalCandidates: candidates.length,\n };\n\n if (options.dryRun) {\n result.wouldRemove = removable.map((dir) => dir.name);\n return result;\n }\n\n if (!options.force) {\n // Startup owns the non-interactive cleanup path; CLI callers should use dry-run.\n }\n\n for (const dir of removable) {\n try {\n await rm(dir.path, { recursive: true, force: true });\n result.removed.push(dir.name);\n } catch {\n // Non-fatal — directory may have already been removed or permissions changed.\n }\n }\n\n return result;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native variant of findOrphanedAgentDirs. Fails with FsError if the\n * agents directory listing fails. The tmux listing is wrapped so listing\n * failures bubble up as FsError too (treats tmux as part of the filesystem\n * for purposes of this check).\n */\nexport const findOrphanedAgentDirs = (\n agentsDir: string = AGENTS_DIR,\n): Effect.Effect<readonly OrphanedAgentDir[], FsError> =>\n Effect.tryPromise({\n try: () => findOrphanedAgentDirsPromise(agentsDir),\n catch: (cause) =>\n new FsError({ path: agentsDir, operation: 'findOrphanedAgentDirs', cause }),\n });\n\n/**\n * Effect-native variant of cleanupAgentDirectories. Fails with FsError if the\n * orphan scan fails. Individual rm failures are still swallowed internally so\n * a partial cleanup is the worst case (matches the Promise contract).\n */\nexport const cleanupAgentDirectories = (options: {\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n} = {}): Effect.Effect<CleanupResult, FsError> =>\n Effect.tryPromise({\n try: () => cleanupAgentDirectoriesPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'cleanupAgentDirectories',\n cause,\n }),\n });\n\nexport const findClosedIssueAgentDirs = (options: {\n issues: unknown[];\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Effect.Effect<readonly ClosedIssueAgentDir[], FsError> =>\n Effect.tryPromise({\n try: () => findClosedIssueAgentDirsPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'findClosedIssueAgentDirs',\n cause,\n }),\n });\n\nexport const cleanupClosedIssueAgentDirectories = (options: {\n issues: unknown[];\n dryRun?: boolean;\n force?: boolean;\n agentsDir?: string;\n nowMs?: number;\n graceMs?: number;\n}): Effect.Effect<ClosedIssueAgentCleanupResult, FsError> =>\n Effect.tryPromise({\n try: () => cleanupClosedIssueAgentDirectoriesPromise(options),\n catch: (cause) =>\n new FsError({\n path: options.agentsDir ?? AGENTS_DIR,\n operation: 'cleanupClosedIssueAgentDirectories',\n cause,\n }),\n });\n","/**\n * Multi-Tool Skill Sync\n *\n * Writes Overdeck skills to other AI tool formats so skills authored once\n * in .pan/skills/ are available across all configured tools.\n *\n * Configured via `tools.also_sync` in ~/.overdeck/config.yaml and .pan.yaml.\n * Per-project .pan.yaml values are merged additively with global config.\n *\n * Supported targets:\n * cursor → .cursor/rules/<skill-name>.mdc\n * codex → AGENTS.md (named blocks)\n * windsurf → .windsurf/rules/<skill-name>.md\n * cline → .clinerules/<skill-name>.md\n * copilot → .github/instructions/<skill-name>.instructions.md\n * aider → CONVENTIONS.md (named blocks)\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { Effect } from 'effect';\nimport { FsError } from './errors.js';\nimport { OVERDECK_HOME } from './paths.js';\n\nexport type AlsoSyncTool = 'cursor' | 'codex' | 'windsurf' | 'cline' | 'copilot' | 'aider';\n\nexport interface MultiToolSyncResult {\n tool: AlsoSyncTool;\n written: string[];\n skipped: string[];\n errors: string[];\n}\n\n/** Strip YAML frontmatter from a skill markdown file */\nfunction stripFrontmatter(content: string): string {\n if (!content.startsWith('---')) return content;\n const end = content.indexOf('\\n---', 4);\n if (end === -1) return content;\n return content.slice(end + 4).trimStart();\n}\n\n/** Extract the skill name from frontmatter, or fall back to dir name */\nfunction extractSkillName(content: string, fallback: string): string {\n if (!content.startsWith('---')) return fallback;\n const end = content.indexOf('\\n---', 4);\n if (end === -1) return fallback;\n const frontmatter = content.slice(4, end);\n const match = frontmatter.match(/^name:\\s*(.+)$/m);\n return match ? match[1].trim() : fallback;\n}\n\n/** Read main SKILL.md content for a skill directory */\nfunction readSkillContent(skillDir: string): string | null {\n const skillMd = join(skillDir, 'SKILL.md');\n if (!existsSync(skillMd)) {\n // Fallback: any .md file in root\n const files = existsSync(skillDir) ? readdirSync(skillDir).filter(f => f.endsWith('.md')) : [];\n if (files.length === 0) return null;\n return readFileSync(join(skillDir, files[0]), 'utf-8');\n }\n return readFileSync(skillMd, 'utf-8');\n}\n\n/** Collect all skill directories from the given skills root */\nfunction collectSkillDirs(skillsDir: string): Array<{ name: string; dir: string }> {\n if (!existsSync(skillsDir)) return [];\n return readdirSync(skillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => ({ name: e.name, dir: join(skillsDir, e.name) }));\n}\n\n/**\n * Update or insert a named block in a file.\n * Blocks are delimited by: <!-- overdeck:<skill-name> start --> ... <!-- overdeck:<skill-name> end -->\n */\nfunction upsertNamedBlock(filePath: string, blockName: string, content: string): void {\n const startTag = `<!-- overdeck:${blockName} start -->`;\n const endTag = `<!-- overdeck:${blockName} end -->`;\n const block = `${startTag}\\n${content}\\n${endTag}`;\n\n let existing = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';\n\n const startIdx = existing.indexOf(startTag);\n const endIdx = existing.indexOf(endTag);\n\n if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {\n // Replace existing block\n existing = existing.slice(0, startIdx) + block + existing.slice(endIdx + endTag.length);\n } else {\n // Append new block\n if (existing.length > 0 && !existing.endsWith('\\n')) existing += '\\n';\n existing += '\\n' + block + '\\n';\n }\n\n writeFileSync(filePath, existing, 'utf-8');\n}\n\n/** Sync a single skill to the cursor target */\nfunction syncToCursor(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.cursor', 'rules');\n mkdirSync(rulesDir, { recursive: true });\n const body = stripFrontmatter(rawContent);\n // .mdc files: standard markdown, cursor accepts them as context rules\n writeFileSync(join(rulesDir, `${skillName}.mdc`), body, 'utf-8');\n}\n\n/** Sync a single skill to the windsurf target */\nfunction syncToWindsurf(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.windsurf', 'rules');\n mkdirSync(rulesDir, { recursive: true });\n writeFileSync(join(rulesDir, `${skillName}.md`), stripFrontmatter(rawContent), 'utf-8');\n}\n\n/** Sync a single skill to the cline target */\nfunction syncToCline(projectPath: string, skillName: string, rawContent: string): void {\n const rulesDir = join(projectPath, '.clinerules');\n mkdirSync(rulesDir, { recursive: true });\n writeFileSync(join(rulesDir, `${skillName}.md`), stripFrontmatter(rawContent), 'utf-8');\n}\n\n/** Sync a single skill to the copilot target */\nfunction syncToCopilot(projectPath: string, skillName: string, rawContent: string): void {\n const instructionsDir = join(projectPath, '.github', 'instructions');\n mkdirSync(instructionsDir, { recursive: true });\n writeFileSync(\n join(instructionsDir, `${skillName}.instructions.md`),\n stripFrontmatter(rawContent),\n 'utf-8',\n );\n}\n\n/** Sync a single skill to AGENTS.md (codex) as a named block */\nfunction syncToCodex(projectPath: string, skillName: string, rawContent: string): void {\n const agentsMd = join(projectPath, 'AGENTS.md');\n upsertNamedBlock(agentsMd, skillName, `## ${skillName}\\n\\n${stripFrontmatter(rawContent)}`);\n}\n\n/** Sync a single skill to CONVENTIONS.md (aider) as a named block */\nfunction syncToAider(projectPath: string, skillName: string, rawContent: string): void {\n const conventionsMd = join(projectPath, 'CONVENTIONS.md');\n upsertNamedBlock(conventionsMd, skillName, `## ${skillName}\\n\\n${stripFrontmatter(rawContent)}`);\n}\n\nconst TOOL_WRITERS: Record<AlsoSyncTool, (projectPath: string, name: string, content: string) => void> = {\n cursor: syncToCursor,\n windsurf: syncToWindsurf,\n cline: syncToCline,\n copilot: syncToCopilot,\n codex: syncToCodex,\n aider: syncToAider,\n};\n\n/**\n * Resolve the merged list of tools to sync.\n * Global config is the base; per-project .pan.yaml adds more (never removes).\n */\nexport function resolveAlsoSyncToolsSync(projectPath?: string): AlsoSyncTool[] {\n const tools = new Set<AlsoSyncTool>();\n\n // Read from global config\n const globalConfig = join(OVERDECK_HOME, 'config.yaml');\n if (existsSync(globalConfig)) {\n try {\n const parsed = yaml.load(readFileSync(globalConfig, 'utf-8')) as any;\n const globalTools: string[] = parsed?.tools?.also_sync || [];\n for (const t of globalTools) {\n if (t in TOOL_WRITERS) tools.add(t as AlsoSyncTool);\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Merge per-project .pan.yaml (additive)\n if (projectPath) {\n const panYaml = join(projectPath, '.pan.yaml');\n const legacyYaml = join(projectPath, '.overdeck.yaml');\n const configPath = existsSync(panYaml) ? panYaml : existsSync(legacyYaml) ? legacyYaml : null;\n if (configPath) {\n try {\n const parsed = yaml.load(readFileSync(configPath, 'utf-8')) as any;\n const projectTools: string[] = parsed?.tools?.also_sync || [];\n for (const t of projectTools) {\n if (t in TOOL_WRITERS) tools.add(t as AlsoSyncTool);\n }\n } catch { /* ignore parse errors */ }\n }\n }\n\n return Array.from(tools);\n}\n\n/**\n * Sync skills from a skills directory to all configured tools.\n *\n * @param skillsDir Directory containing skill subdirectories\n * @param projectPath Project root where tool targets live\n * @param tools Tools to sync to (from resolveAlsoSyncTools)\n */\nexport function syncSkillsToToolsSync(\n skillsDir: string,\n projectPath: string,\n tools: AlsoSyncTool[],\n): MultiToolSyncResult[] {\n if (tools.length === 0 || !existsSync(skillsDir)) return [];\n\n const skills = collectSkillDirs(skillsDir);\n const results: MultiToolSyncResult[] = [];\n\n for (const tool of tools) {\n const writer = TOOL_WRITERS[tool];\n const result: MultiToolSyncResult = { tool, written: [], skipped: [], errors: [] };\n\n for (const { name, dir } of skills) {\n try {\n const rawContent = readSkillContent(dir);\n if (!rawContent) {\n result.skipped.push(name);\n continue;\n }\n const displayName = extractSkillName(rawContent, name);\n writer(projectPath, displayName, rawContent);\n result.written.push(name);\n } catch (err: any) {\n result.errors.push(`${name}: ${err.message}`);\n }\n }\n\n results.push(result);\n }\n\n return results;\n}\n\n/**\n * Run the full multi-tool sync for a project.\n * Sources: .pan/skills/ (project-local) and/or ~/.overdeck/skills/ (global).\n */\nexport function runMultiToolSyncSync(projectPath: string): MultiToolSyncResult[] {\n const tools = resolveAlsoSyncToolsSync(projectPath);\n if (tools.length === 0) return [];\n\n const allResults: MultiToolSyncResult[] = [];\n\n // 1. Global skills (from ~/.overdeck/skills/)\n const globalSkillsDir = join(OVERDECK_HOME, 'skills');\n const globalResults = syncSkillsToToolsSync(globalSkillsDir, projectPath, tools);\n allResults.push(...globalResults);\n\n // 2. Project-local skills (from .pan/skills/) — may overwrite global skill entries\n const projectSkillsDir = join(projectPath, '.pan', 'skills');\n if (existsSync(projectSkillsDir)) {\n const projectResults = syncSkillsToToolsSync(projectSkillsDir, projectPath, tools);\n // Merge into existing results (project results override counts, don't duplicate tools)\n for (const pr of projectResults) {\n const existing = allResults.find(r => r.tool === pr.tool);\n if (existing) {\n existing.written.push(...pr.written);\n existing.errors.push(...pr.errors);\n } else {\n allResults.push(pr);\n }\n }\n }\n\n return allResults;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/** Effect variant of {@link resolveAlsoSyncToolsSync}. Pure config read; cannot fail. */\nexport const resolveAlsoSyncTools = (projectPath?: string): Effect.Effect<AlsoSyncTool[], never> =>\n Effect.sync(() => resolveAlsoSyncToolsSync(projectPath));\n\n/** Effect variant of {@link syncSkillsToToolsSync}. */\nexport const syncSkillsToTools = (\n skillsDir: string,\n projectPath: string,\n tools: AlsoSyncTool[],\n): Effect.Effect<MultiToolSyncResult[], FsError> =>\n Effect.try({\n try: () => syncSkillsToToolsSync(skillsDir, projectPath, tools),\n catch: (cause) => new FsError({ path: skillsDir, operation: 'syncSkillsToTools', cause }),\n });\n\n/** Effect variant of {@link runMultiToolSyncSync}. */\nexport const runMultiToolSync = (projectPath: string): Effect.Effect<MultiToolSyncResult[], FsError> =>\n Effect.try({\n try: () => runMultiToolSyncSync(projectPath),\n catch: (cause) => new FsError({ path: projectPath, operation: 'runMultiToolSync', cause }),\n });\n\n","import { Effect } from 'effect';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { execSync } from 'child_process';\nimport { existsSync, readdirSync, readFileSync, writeFileSync, statSync, symlinkSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { loadConfigSync } from '../../lib/config.js';\nimport { parseVBriefFilename } from '../../lib/vbrief/lifecycle.js';\nimport { resolveGitHubIssueSync } from '../../lib/tracker-utils.js';\nimport { createBackupSync } from '../../lib/backup.js';\nimport {\n planSyncSync,\n executeSyncSync,\n refreshCacheSync,\n migrateStalePersonalContentSync,\n removeLegacySkills070Sync,\n planHooksSyncSync,\n syncHooksSync,\n syncStatuslineSync,\n mirrorProjectSkillsSync,\n syncPiSettingsSync,\n syncContextLayersSync,\n isStartupSyncNeededSync,\n writeSyncManifestSync,\n} from '../../lib/sync.js';\nimport { SYNC_TARGET, SYNC_SOURCES, isDevMode } from '../../lib/paths.js';\nimport { checkDevrootDeprecation } from '../../lib/config.js';\nimport { listProjectsSync } from '../../lib/projects.js';\nimport { cleanupLegacyRuntimeSymlinksSync, migrateSyncTargetsSync } from '../../lib/config-migration.js';\nimport { cleanupAgentDirectories } from '../../lib/agent-directory-cleanup.js';\nimport { migrateOverdeckToPanSync } from '../../lib/workspace-manager.js';\nimport { runMultiToolSyncSync, resolveAlsoSyncToolsSync } from '../../lib/multi-tool-sync.js';\nimport { ensurePlaywrightIsolationSync, ensureExcalidrawMcpSync } from '../../lib/claude-mcp.js';\nimport { resolveProjectContextFile } from '../../lib/context-layers/layers.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\n// Bundled git hooks distributed to registered projects (PAN-1201: sync-sources/).\nconst BUNDLED_GIT_HOOKS_DIR = SYNC_SOURCES.gitHooks;\n\n// Helper to check if a command exists\nfunction checkCommand(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\ninterface SyncOptions {\n dryRun?: boolean;\n force?: boolean;\n diff?: boolean;\n backupOnly?: boolean;\n ifChanged?: boolean;\n}\n\nexport async function syncCommand(options: SyncOptions): Promise<void> {\n const timings: Array<{ phase: string; ms: number }> = [];\n function time<T>(phase: string, fn: () => T): T {\n const start = performance.now();\n try {\n return fn();\n } finally {\n timings.push({ phase, ms: Math.round(performance.now() - start) });\n }\n }\n async function timeAsync<T>(phase: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n try {\n return await fn();\n } finally {\n timings.push({ phase, ms: Math.round(performance.now() - start) });\n }\n }\n function printTimings(): void {\n if (timings.length === 0) return;\n const summary = timings.map((t) => `${t.phase}=${t.ms}ms`).join(', ');\n console.log(chalk.dim(`[sync-timing] ${summary}`));\n }\n\n // PAN-1201: warn once if the deprecated sync.devroot is still configured.\n const devrootWarning = checkDevrootDeprecation();\n if (devrootWarning) {\n console.log(chalk.yellow(devrootWarning));\n console.log('');\n }\n\n // Startup-only shortcut: skip the expensive full sync when inputs are unchanged.\n if (options.ifChanged && !options.force) {\n const gate = isStartupSyncNeededSync();\n if (!gate.needed) {\n console.log(chalk.dim('[sync] skipped — inputs unchanged'));\n return;\n }\n }\n\n // Dry run mode\n if (options.dryRun) {\n console.log(chalk.bold('Sync Plan (dry run):\\n'));\n\n // Show dev mode status\n if (isDevMode()) {\n console.log(chalk.magenta('Developer mode detected - dev-skills will be synced\\n'));\n }\n\n // Show hooks plan\n const hooksPlan = planHooksSyncSync();\n if (hooksPlan.length > 0) {\n console.log(chalk.cyan('hooks (bin scripts):'));\n for (const hook of hooksPlan) {\n const icon = hook.status === 'new' ? chalk.green('+') : chalk.blue('↻');\n const status = hook.status === 'new' ? '' : chalk.dim('[update]');\n console.log(` ${icon} ${hook.name} ${status}`);\n }\n console.log('');\n }\n\n // Bundled skills + agents → ~/.claude/\n console.log(chalk.cyan('~/.claude/ (skills + agents):'));\n const plan = planSyncSync();\n const allItems = [...plan.skills, ...plan.agents];\n if (allItems.length === 0) {\n console.log(chalk.dim(' (nothing to sync — check sync-sources/ and run `pan install`)'));\n } else {\n const count = (s: string) => allItems.filter((i) => i.status === s).length;\n console.log(\n ` ${chalk.green(`${count('new')} new`)}, ` +\n `${chalk.blue(`${count('symlink')} update`)}, ` +\n `${chalk.cyan(`${count('adopted')} adopted (legacy pre-manifest installs)`)}, ` +\n `${chalk.dim(`${count('exists')} unchanged`)}, ` +\n `${chalk.yellow(`${count('conflict')} user-modified (skipped)`)}`,\n );\n }\n console.log('');\n\n // Context layers → CLAUDE.md managed regions\n console.log(chalk.cyan('context layers → CLAUDE.md:'));\n console.log(` ${chalk.blue('↻')} global → ~/.claude/CLAUDE.md ${chalk.dim('(managed region)')}`);\n for (const { config } of listProjectsSync()) {\n if (existsSync(resolveProjectContextFile(config.path))) {\n console.log(\n ` ${chalk.blue('↻')} ${config.name} → ${join(config.path, 'CLAUDE.md')} ${chalk.dim('(managed region)')}`,\n );\n }\n }\n\n // Show .pan/skills/ source files for each registered project\n const dryRunProjects = listProjectsSync();\n for (const { config } of dryRunProjects) {\n if (!existsSync(config.path)) continue;\n const panSkillsDir = join(config.path, '.pan', 'skills');\n if (existsSync(panSkillsDir)) {\n const skills = readdirSync(panSkillsDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n if (skills.length > 0) {\n console.log(chalk.cyan(`\\n.pan/skills/ (${config.name}):`));\n for (const skillName of skills) {\n console.log(` ${chalk.green('+')} ${skillName} ${chalk.green('[project-local]')}`);\n }\n }\n }\n\n // Show multi-tool sync targets\n const tools = resolveAlsoSyncToolsSync(config.path);\n if (tools.length > 0) {\n console.log(chalk.cyan(`\\nmulti-tool sync (${config.name}): ${tools.join(', ')}`));\n const panSkillsDirExists = existsSync(join(config.path, '.pan', 'skills'));\n if (panSkillsDirExists) {\n const skills = readdirSync(join(config.path, '.pan', 'skills'), { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n for (const tool of tools) {\n for (const skillName of skills) {\n console.log(` ${chalk.green('+')} ${skillName} → ${tool}`);\n }\n }\n }\n }\n }\n\n // Agent directory cleanup preview\n const agentCleanupPreview = await Effect.runPromise(cleanupAgentDirectories({ dryRun: true }));\n if (agentCleanupPreview.totalOrphaned > 0) {\n console.log(chalk.cyan(`\\nagent cleanup (~/.overdeck/agents/):`));\n console.log(chalk.dim(` Found ${agentCleanupPreview.totalOrphaned} orphaned directories`));\n for (const name of agentCleanupPreview.wouldRemove) {\n console.log(` ${chalk.red('✗')} ${name}`);\n }\n for (const name of agentCleanupPreview.protected) {\n console.log(` ${chalk.yellow('◆')} ${name} ${chalk.dim('(running session)')}`);\n }\n }\n\n console.log('');\n console.log(chalk.dim('Run without --dry-run to apply changes.'));\n printTimings();\n return;\n }\n\n // Run one-time migration: strip legacy sync targets from config.toml\n const syncMigration = time('migrate-sync-targets', () => migrateSyncTargetsSync());\n if (syncMigration.migrated) {\n if (syncMigration.hadNonClaudeTargets) {\n console.log(chalk.yellow('Config updated: removed non-Claude sync targets (Overdeck now syncs to Claude Code only).'));\n }\n }\n\n // Run one-time migration: remove Overdeck-managed symlinks from legacy runtime dirs\n const cleanupResult = time('cleanup-legacy-runtimes', () => cleanupLegacyRuntimeSymlinksSync());\n if (cleanupResult.cleaned.length > 0) {\n console.log(chalk.dim(`Removed ${cleanupResult.total} legacy runtime symlink(s): ${cleanupResult.cleaned.join(', ')}`));\n }\n\n // One-time migration: remove Overdeck symlinks from ~/.claude/ (devroot replaces this)\n const migration = time('migrate-stale-personal', () => migrateStalePersonalContentSync());\n if (migration.removedSymlinks.length > 0) {\n console.log(chalk.cyan(`Migrated: removed ${migration.removedSymlinks.length} Overdeck symlink(s) from ~/.claude/`));\n if (migration.preservedUserContent.length > 0) {\n console.log(chalk.dim(` Preserved ${migration.preservedUserContent.length} user-created item(s)`));\n }\n }\n\n // 0.7.0 upgrade: remove renamed/deleted legacy skills from ~/.claude/skills/\n const removedLegacy = time('remove-legacy-skills', () => removeLegacySkills070Sync());\n if (removedLegacy.length > 0) {\n console.log(chalk.dim(`Removed ${removedLegacy.length} legacy skill(s) from upgrade to 0.7.0: ${removedLegacy.join(', ')}`));\n }\n\n const config = loadConfigSync();\n\n // Create backup if enabled\n if (config.sync.backup_before_sync) {\n const backupSpinner = ora('Creating backup...').start();\n\n const backupDirs = [\n SYNC_TARGET.skills,\n SYNC_TARGET.commands,\n SYNC_TARGET.agents,\n ];\n\n const backup = time('backup', () => createBackupSync(backupDirs));\n\n if (backup.targets.length > 0) {\n backupSpinner.succeed(`Backup created: ${backup.timestamp}`);\n } else {\n backupSpinner.info('No existing content to backup');\n }\n\n if (options.backupOnly) {\n printTimings();\n return;\n }\n }\n\n // Refresh cache from repo source\n const cacheSpinner = ora('Refreshing cache from repo...').start();\n const cacheResult = time('refresh-cache', () => refreshCacheSync());\n const cacheParts = [];\n if (cacheResult.skills.copied > 0) cacheParts.push(`${cacheResult.skills.copied} skills`);\n if (cacheResult.agents.copied > 0) cacheParts.push(`${cacheResult.agents.copied} agents`);\n if (cacheResult.rules.copied > 0) cacheParts.push(`${cacheResult.rules.copied} rules`);\n cacheSpinner.succeed(`Cache refreshed: ${cacheParts.length > 0 ? cacheParts.join(', ') : 'up to date'}`);\n\n // Distribute bundled skills + agents into the user's Claude Code home.\n const spinner = ora('Distributing skills and agents to ~/.claude/...').start();\n const result = time('execute-sync', () => executeSyncSync({ force: options.force, diff: options.diff }));\n const totalSynced = result.created.length + result.updated.length + result.adopted.length;\n const adoptionSummary = result.adopted.length > 0\n ? `, ${result.adopted.length} adopted (legacy pre-manifest installs)`\n : '';\n\n // Show diffs if requested\n if (result.diffs.length > 0) {\n spinner.info(`Showing diffs for ${result.diffs.length} modified file(s):\\n`);\n for (const d of result.diffs) {\n console.log(chalk.cyan(`--- ${d.path} (installed)`));\n console.log(chalk.cyan(`+++ ${d.path} (current on disk)`));\n // Simple line-by-line diff\n const sourceLines = d.sourceContent.split('\\n');\n const targetLines = d.targetContent.split('\\n');\n const maxLines = Math.max(sourceLines.length, targetLines.length);\n for (let i = 0; i < maxLines; i++) {\n if (sourceLines[i] !== targetLines[i]) {\n if (targetLines[i] !== undefined) console.log(chalk.red(`- ${targetLines[i]}`));\n if (sourceLines[i] !== undefined) console.log(chalk.green(`+ ${sourceLines[i]}`));\n }\n }\n console.log('');\n }\n }\n\n if (result.conflicts.length > 0 && !options.force) {\n spinner.warn(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary}, ${result.conflicts.length} user-modified (skipped)`);\n console.log('');\n console.log(chalk.yellow('Modified since Overdeck installed:'));\n for (const name of result.conflicts) {\n console.log(chalk.dim(` - ${name}`));\n }\n console.log('');\n console.log(chalk.dim('Use --force to overwrite, --diff to see changes.'));\n } else if (result.skipped.length > 0) {\n spinner.succeed(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary} (${result.skipped.length} unchanged or user-owned)`);\n } else {\n spinner.succeed(`Synced ${totalSynced} items to ~/.claude/${adoptionSummary}`);\n }\n\n // Render the layered context into harness CLAUDE.md files (PAN-1201).\n const ctxSpinner = ora('Rendering context layers...').start();\n const ctx = time('context-layers', () => syncContextLayersSync());\n const ctxParts: string[] = [];\n if (ctx.globalStubCreated) ctxParts.push('seeded global.md');\n if (ctx.globalWritten) ctxParts.push('~/.claude/CLAUDE.md');\n if (ctx.projectsWritten.length > 0) {\n ctxParts.push(`${ctx.projectsWritten.length} project file(s)`);\n }\n if (ctx.errors.length > 0) {\n ctxSpinner.warn(`Context layers rendered with ${ctx.errors.length} error(s)`);\n for (const e of ctx.errors) console.log(chalk.red(` ✗ ${e}`));\n } else if (ctxParts.length > 0) {\n ctxSpinner.succeed(`Context layers rendered: ${ctxParts.join(', ')}`);\n } else {\n ctxSpinner.info('Context layers already up to date');\n }\n\n // One-time notice: a managed region was added to a file that already had\n // hand-authored content. Reassure the user their content is preserved and\n // point at the backup taken before the first injection.\n if (ctx.firstInjections.length > 0) {\n console.log(\n chalk.cyan('\\n ℹ Overdeck added a managed region to existing context file(s):'),\n );\n for (const fi of ctx.firstInjections) {\n console.log(` • ${fi.file}`);\n console.log(\n chalk.dim(\n ` Your content outside the markers is untouched. Backup: ${fi.backupPath}`,\n ),\n );\n }\n console.log(\n chalk.dim(\n ' Edit the layer source (pan context edit), never the region between the markers.',\n ),\n );\n }\n\n // Sync hooks (bin scripts)\n const hooksSpinner = ora('Syncing hooks...').start();\n const hooksResult = time('sync-hooks', () => syncHooksSync());\n\n if (hooksResult.errors.length > 0) {\n hooksSpinner.warn(`Synced ${hooksResult.synced.length} hooks, ${hooksResult.errors.length} errors`);\n for (const error of hooksResult.errors) {\n console.log(chalk.red(` ✗ ${error}`));\n }\n } else if (hooksResult.synced.length > 0) {\n hooksSpinner.succeed(`Synced ${hooksResult.synced.length} hooks to ~/.overdeck/bin/`);\n } else {\n hooksSpinner.info('No hooks to sync');\n }\n\n // Ensure beads database exists for each registered project (first-time setup guard).\n // bd install puts the binary in PATH, but bd init must be run once per project to\n // create the Dolt database. Without it, workspace beads creation silently fails.\n const projects = listProjectsSync();\n if (projects.length > 0 && checkCommand('bd')) {\n for (const { key, config } of projects) {\n if (!existsSync(config.path)) continue;\n const mainBeadsDir = join(config.path, '.beads');\n if (!existsSync(mainBeadsDir)) continue; // Project hasn't used beads yet — skip\n const bdRoot = resolveStateReadHomeSync(config).root;\n // Test connectivity. If the database is missing, auto-init.\n try {\n execSync(`bd -C ${JSON.stringify(bdRoot)} list --json --limit 0 2>&1`, { cwd: config.path, stdio: 'pipe', timeout: 8000 });\n } catch (e: any) {\n const msg = String(e?.stdout ?? e?.stderr ?? e?.message ?? '');\n if (msg.includes('database') && (msg.includes('not found') || msg.includes('not exist') || msg.includes('defaulting'))) {\n const beadsSpinner = ora(`Initializing beads database for ${config.name}...`).start();\n try {\n const prefix = (key || config.name).toLowerCase().replace(/[^a-z0-9-]/g, '-');\n execSync(`bd -C ${JSON.stringify(bdRoot)} init --prefix ${prefix}`, { cwd: config.path, stdio: 'pipe', timeout: 20000 });\n try { execSync('git config beads.role contributor', { cwd: config.path, stdio: 'pipe' }); } catch { /* non-fatal */ }\n beadsSpinner.succeed(`Beads database initialized for ${config.name} (prefix: ${prefix})`);\n } catch {\n beadsSpinner.warn(`Could not auto-initialize beads for ${config.name} — run: cd ${config.path} && bd init`);\n }\n }\n }\n }\n }\n\n\n // Check jq availability (required by statusline, beads, specialists)\n if (!checkCommand('jq')) {\n console.log(chalk.yellow('\\n ⚠ jq not found — statusline and other features need it'));\n console.log(chalk.dim(' Install: apt install jq / brew install jq\\n'));\n }\n\n // Sync statusline to all runtimes\n const statuslineSpinner = ora('Syncing statusline...').start();\n const statuslineResult = time('sync-statusline', () => syncStatuslineSync());\n\n if (statuslineResult.errors.length > 0) {\n statuslineSpinner.warn(`Synced statusline to ${statuslineResult.synced.length} runtime(s), ${statuslineResult.errors.length} error(s)`);\n for (const error of statuslineResult.errors) {\n console.log(chalk.red(` ✗ ${error}`));\n }\n } else if (statuslineResult.synced.length > 0) {\n statuslineSpinner.succeed(`Synced statusline to ${statuslineResult.synced.join(', ')}`);\n } else {\n statuslineSpinner.info('No statusline script found (scripts/statusline.sh)');\n }\n\n // Check and install mkcert if missing\n if (!checkCommand('mkcert')) {\n const mkcertSpinner = ora('Installing mkcert...').start();\n try {\n const binDir = join(homedir(), '.local', 'bin');\n mkdirSync(binDir, { recursive: true });\n const mkcertPath = join(binDir, 'mkcert');\n const arch = process.arch === 'x64' ? 'amd64' : process.arch;\n execSync(`curl -sL \"https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-${arch}\" -o \"${mkcertPath}\" && chmod +x \"${mkcertPath}\"`, {\n stdio: 'pipe',\n timeout: 60000,\n });\n mkcertSpinner.succeed('mkcert installed');\n } catch {\n mkcertSpinner.warn('Failed to install mkcert - run: https://github.com/FiloSottile/mkcert/releases');\n }\n }\n\n // Enforce Overdeck-managed MCP server defaults: Playwright --isolated\n // flag (prevents stale zoom/profile state) and the off-the-shelf Excalidraw\n // MCP server (backs the /excalidraw skill). Both helpers are idempotent and\n // mutate the parsed config in place; we only write back if anything changed.\n const mcpPath = join(homedir(), '.claude', 'mcp.json');\n try {\n if (existsSync(mcpPath)) {\n const mcpConfig = JSON.parse(readFileSync(mcpPath, 'utf-8'));\n const playwrightChanged = ensurePlaywrightIsolationSync(mcpConfig);\n const excalidrawChanged = ensureExcalidrawMcpSync(mcpConfig);\n if (playwrightChanged || excalidrawChanged) {\n writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\\n');\n }\n if (playwrightChanged) {\n console.log(chalk.green('✓ Added --isolated to Playwright MCP (prevents stale zoom/profile state)'));\n }\n if (excalidrawChanged) {\n console.log(chalk.green('✓ Registered Excalidraw MCP server (backs the /excalidraw skill)'));\n }\n }\n } catch {\n // Non-fatal — skip if mcp.json can't be read/written\n }\n\n // Migrate .overdeck/ → .pan/ and run multi-tool sync in all registered projects\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n // Migrate .overdeck/ subdirs → .pan/\n const migResult = migrateOverdeckToPanSync(config.path);\n if (migResult.migrated.length > 0) {\n console.log(chalk.cyan(`Migrated .overdeck/ → .pan/ in ${config.name}: ${migResult.migrated.join(', ')}`));\n }\n if (migResult.skipped.length > 0) {\n console.log(chalk.yellow(`Migration skipped (both exist) in ${config.name}: ${migResult.skipped.join(', ')}`));\n }\n for (const err of migResult.errors) {\n console.log(chalk.red(`Migration error in ${config.name}: ${err}`));\n }\n\n // Multi-tool skill sync (cursor, codex, windsurf, cline, copilot, aider)\n const toolSyncResults = runMultiToolSyncSync(config.path);\n for (const r of toolSyncResults) {\n if (r.written.length > 0) {\n console.log(chalk.cyan(`Synced ${r.written.length} skill(s) to ${r.tool} in ${config.name}`));\n }\n for (const err of r.errors) {\n console.log(chalk.red(`Multi-tool sync error (${r.tool}) in ${config.name}: ${err}`));\n }\n }\n }\n\n // Sync git hooks to all registered projects (branch protection)\n if (projects.length > 0 && existsSync(BUNDLED_GIT_HOOKS_DIR)) {\n const gitHooksSpinner = ora('Installing git hooks in registered projects...').start();\n let totalInstalled = 0;\n let projectsUpdated = 0;\n\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n // Find all .git directories (handles polyrepos)\n const gitDirs: string[] = [];\n\n // Check root\n if (existsSync(join(config.path, '.git')) && statSync(join(config.path, '.git')).isDirectory()) {\n gitDirs.push(join(config.path, '.git'));\n } else {\n // Scan for polyrepo\n try {\n const entries = readdirSync(config.path);\n for (const entry of entries) {\n const entryPath = join(config.path, entry);\n const gitPath = join(entryPath, '.git');\n if (existsSync(gitPath) && statSync(gitPath).isDirectory()) {\n gitDirs.push(gitPath);\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n // Install hooks in each git dir\n for (const gitDir of gitDirs) {\n const hooksTarget = join(gitDir, 'hooks');\n if (!existsSync(hooksTarget)) {\n mkdirSync(hooksTarget, { recursive: true });\n }\n\n try {\n const hooks = readdirSync(BUNDLED_GIT_HOOKS_DIR).filter(f =>\n statSync(join(BUNDLED_GIT_HOOKS_DIR, f)).isFile()\n );\n\n for (const hook of hooks) {\n const source = join(BUNDLED_GIT_HOOKS_DIR, hook);\n const target = join(hooksTarget, hook);\n\n // Skip if already a symlink to our hook\n if (existsSync(target)) {\n try {\n const { readlinkSync } = await import('fs');\n if (readlinkSync(target) === source) continue;\n } catch {\n // Not a symlink\n }\n // Backup existing\n const { renameSync } = await import('fs');\n try { renameSync(target, `${target}.backup`); } catch {}\n }\n\n try {\n symlinkSync(source, target);\n totalInstalled++;\n } catch {}\n }\n projectsUpdated++;\n } catch {}\n }\n }\n\n if (totalInstalled > 0) {\n gitHooksSpinner.succeed(`Installed git hooks in ${projectsUpdated} project(s)`);\n } else {\n gitHooksSpinner.info('Git hooks already up to date');\n }\n }\n\n // Ensure beads database exists for each registered project (first-time setup guard).\n // bd install puts the binary in PATH, but bd init must be run once per project to\n // create the Dolt database. Without it, workspace beads creation silently fails.\n if (projects.length > 0 && checkCommand('bd')) {\n for (const { key, config } of projects) {\n if (!existsSync(config.path)) continue;\n const mainBeadsDir = join(config.path, '.beads');\n if (!existsSync(mainBeadsDir)) continue; // Project hasn't used beads yet — skip\n const bdRoot = resolveStateReadHomeSync(config).root;\n // Test connectivity. If the database is missing, auto-init.\n try {\n execSync(`bd -C ${JSON.stringify(bdRoot)} list --json --limit 0 2>&1`, { cwd: config.path, stdio: 'pipe', timeout: 8000 });\n } catch (e: any) {\n const msg = String(e?.stdout ?? e?.stderr ?? e?.message ?? '');\n if (msg.includes('database') && (msg.includes('not found') || msg.includes('not exist') || msg.includes('defaulting'))) {\n const beadsSpinner = ora(`Initializing beads database for ${config.name}...`).start();\n try {\n const prefix = (key || config.name).toLowerCase().replace(/[^a-z0-9-]/g, '-');\n execSync(`bd -C ${JSON.stringify(bdRoot)} init --prefix ${prefix}`, { cwd: config.path, stdio: 'pipe', timeout: 20000 });\n beadsSpinner.succeed(`Beads database initialized for ${config.name} (prefix: ${prefix})`);\n } catch {\n beadsSpinner.warn(`Could not auto-initialize beads for ${config.name} — run: cd ${config.path} && bd init`);\n }\n }\n }\n }\n }\n\n // Pi harness — point Pi's settings file at ~/.claude/skills so it sees the\n // same skills tree we just synced. No-op when Pi is not on PATH (PAN-636).\n const piResult = syncPiSettingsSync();\n if (piResult.status === 'created') {\n console.log(chalk.cyan(`Pi settings: created ${piResult.path.replace(homedir(), '~')}`));\n } else if (piResult.status === 'updated') {\n console.log(chalk.cyan(`Pi settings: merged skills entry into ${piResult.path.replace(homedir(), '~')}`));\n } else if (piResult.status === 'skipped' && piResult.reason === 'existing settings.json is not valid JSON') {\n console.log(chalk.yellow(`Pi settings: ${piResult.path.replace(homedir(), '~')} is not valid JSON — left untouched`));\n }\n\n // Mirror project-level skills/ → .claude/skills/ for a project that keeps a\n // top-level skills/ tree, so pan sync works from inside such a project.\n const skillsMirror = mirrorProjectSkillsSync(process.cwd());\n const skillsParts: string[] = [];\n if (skillsMirror.added.length > 0) skillsParts.push(`${skillsMirror.added.length} added`);\n if (skillsMirror.updated.length > 0) skillsParts.push(`${skillsMirror.updated.length} updated`);\n if (skillsMirror.removed.length > 0) skillsParts.push(`${skillsMirror.removed.length} removed`);\n if (skillsParts.length > 0) {\n console.log(chalk.cyan(`Skills mirror: ${skillsParts.join(', ')}`));\n }\n\n // Agent directory cleanup\n const cleanupSpinner = ora('Checking for orphaned agent directories...').start();\n const agentCleanupResult = await Effect.runPromise(cleanupAgentDirectories({ dryRun: false, force: options.force }));\n\n if (agentCleanupResult.totalOrphaned === 0) {\n cleanupSpinner.succeed('No orphaned agent directories found');\n } else {\n const removedCount = agentCleanupResult.removed.length;\n const protectedCount = agentCleanupResult.protected.length;\n\n if (removedCount > 0) {\n cleanupSpinner.succeed(`Removed ${removedCount} orphaned director${removedCount === 1 ? 'y' : 'ies'}`);\n } else if (protectedCount > 0) {\n cleanupSpinner.info(`Found ${protectedCount} orphaned director${protectedCount === 1 ? 'y' : 'ies'} with running sessions (skipped)`);\n }\n\n if (agentCleanupResult.protected.length > 0) {\n console.log(chalk.dim(` Protected (running sessions): ${agentCleanupResult.protected.join(', ')}`));\n }\n }\n\n // vBRIEF state disagreement audit (PAN-946: workspace-9ny)\n try {\n const auditSpinner = ora('Running vBRIEF state audit...').start();\n const disagreements: Array<{ issueId: string; problem: string; fix: string }> = [];\n const hasGh = checkCommand('gh');\n\n for (const { config } of projects) {\n if (!existsSync(config.path)) continue;\n\n const activeDir = join(config.path, 'vbrief', 'active');\n const completedDir = join(config.path, 'vbrief', 'completed');\n\n // (1) vBRIEF in active/ but tracker says closed\n if (existsSync(activeDir)) {\n const activeFiles = readdirSync(activeDir).filter(\n f => f.endsWith('.vbrief.json') && !f.startsWith('continue-')\n );\n for (const file of activeFiles) {\n const parsed = parseVBriefFilename(file);\n if (!parsed) continue;\n const issueId = parsed.issueId.toUpperCase();\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (ghInfo.isGitHub && hasGh) {\n try {\n const state = execSync(\n `gh issue view ${ghInfo.number} --repo ${ghInfo.owner}/${ghInfo.repo} --json state --jq '.state'`,\n { encoding: 'utf-8', timeout: 10000, stdio: 'pipe' }\n ).trim();\n if (state.toLowerCase() === 'closed') {\n disagreements.push({\n issueId,\n problem: 'vBRIEF in active/ but GitHub issue is closed',\n fix: `pan scope complete ${issueId}`,\n });\n }\n } catch { /* skip if gh fails */ }\n }\n }\n }\n\n // (2) vBRIEF in completed/ but workspace still exists\n if (existsSync(completedDir)) {\n const completedFiles = readdirSync(completedDir).filter(\n f => f.endsWith('.vbrief.json') && !f.startsWith('continue-')\n );\n for (const file of completedFiles) {\n const parsed = parseVBriefFilename(file);\n if (!parsed) continue;\n const issueId = parsed.issueId.toUpperCase();\n const workspacePath = join(config.path, 'workspaces', `feature-${issueId.toLowerCase()}`);\n if (existsSync(workspacePath)) {\n disagreements.push({\n issueId,\n problem: 'vBRIEF in completed/ but workspace worktree still exists',\n fix: `pan close ${issueId}`,\n });\n }\n }\n }\n\n // (3) tracker shows in-progress but no vBRIEF in active/ (scanning worktrees)\n const workspacesDir = join(config.path, 'workspaces');\n if (existsSync(workspacesDir)) {\n let activeIssueIds = new Set<string>();\n if (existsSync(activeDir)) {\n activeIssueIds = new Set(\n readdirSync(activeDir)\n .filter(f => f.endsWith('.vbrief.json') && !f.startsWith('continue-'))\n .map(f => {\n const parsed = parseVBriefFilename(f);\n return parsed ? parsed.issueId.toUpperCase() : '';\n })\n .filter(Boolean)\n );\n }\n\n const worktreeEntries = readdirSync(workspacesDir, { withFileTypes: true })\n .filter(e => e.isDirectory() && e.name.startsWith('feature-'))\n .map(e => e.name.replace('feature-', '').toUpperCase());\n\n for (const worktreeIssueId of worktreeEntries) {\n if (activeIssueIds.has(worktreeIssueId)) continue;\n\n let trackerOpen = false;\n const ghInfo = resolveGitHubIssueSync(worktreeIssueId);\n if (ghInfo.isGitHub && hasGh) {\n try {\n const state = execSync(\n `gh issue view ${ghInfo.number} --repo ${ghInfo.owner}/${ghInfo.repo} --json state --jq '.state'`,\n { encoding: 'utf-8', timeout: 10000, stdio: 'pipe' }\n ).trim();\n trackerOpen = state.toLowerCase() === 'open';\n } catch { /* skip if gh fails — fall through to heuristic */ }\n }\n\n // Flag if tracker is open OR if we couldn't check tracker (workspace without active vBRIEF is always suspicious)\n if (trackerOpen || !ghInfo.isGitHub || !hasGh) {\n disagreements.push({\n issueId: worktreeIssueId,\n problem: trackerOpen\n ? 'Tracker shows open but no vBRIEF in active/'\n : 'Workspace exists but no vBRIEF in active/',\n fix: `pan scope approve ${worktreeIssueId}`,\n });\n }\n }\n }\n }\n\n if (disagreements.length === 0) {\n auditSpinner.succeed('vBRIEF state audit passed — no disagreements');\n } else {\n auditSpinner.warn(`Found ${disagreements.length} vBRIEF state disagreement(s)`);\n for (const d of disagreements) {\n console.log(chalk.yellow(` ⚠ ${d.issueId}: ${d.problem}`));\n console.log(chalk.dim(` Fix: ${d.fix}`));\n }\n }\n } catch (auditErr: any) {\n console.warn(`[pan sync] vBRIEF audit failed (non-fatal): ${auditErr?.message ?? auditErr}`);\n }\n\n // Record the input hash so a future startup sync can skip when nothing changed.\n time('write-sync-manifest', () => writeSyncManifestSync());\n\n printTimings();\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { t as syncCommand } from "./sync-Bsww9tuj.js";
2
+ export { syncCommand };
@@ -1,8 +1,8 @@
1
1
  import { A as resolveProjectFromIssueSync, f as getProjectSync, v as init_projects, w as loadProjectsConfigSync } from "./projects-D2nMJ9C4.js";
2
2
  import { f as readIssueRecord } from "./record-osVNNpoC.js";
3
- import { n as init_records } from "./records-BIkN1LQ4.js";
4
- import { a as init_review_status, m as setReviewStatusSync } from "./review-status-iJwfEoNX.js";
5
- import { n as collectInFlightIssueIds } from "./records-backfill-HM0Pln6N.js";
3
+ import { n as init_records } from "./records-CTmvuRzz.js";
4
+ import { a as init_review_status, m as setReviewStatusSync } from "./review-status-B_XXomV0.js";
5
+ import { n as collectInFlightIssueIds } from "./records-backfill-BrNsv_zT.js";
6
6
  //#region src/lib/pan-dir/verdict-restore.ts
7
7
  init_projects();
8
8
  init_review_status();
@@ -62,7 +62,7 @@ async function restoreOneIssue(issueId, options) {
62
62
  if (!options.dryRun && update.prUrl && update.prNumber != null) {
63
63
  const repo = parseRepoFromPrUrl(update.prUrl);
64
64
  if (repo) try {
65
- const { refreshMergeStateFromGitHub } = await import("./webhook-handlers-DHBSEDEm.js");
65
+ const { refreshMergeStateFromGitHub } = await import("./webhook-handlers-BjsfIL7u.js");
66
66
  await refreshMergeStateFromGitHub(normalizedId, repo, update.prNumber);
67
67
  } catch (err) {
68
68
  console.warn(`[verdict-restore] GitHub merge-state refresh failed for ${normalizedId}: ${err.message}`);
@@ -109,4 +109,4 @@ async function restoreReviewStatusFromRecords(options = {}) {
109
109
  //#endregion
110
110
  export { restoreReviewStatusFromRecords };
111
111
 
112
- //# sourceMappingURL=verdict-restore-DQiV7_fe.js.map
112
+ //# sourceMappingURL=verdict-restore-DGpX0ZOJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"verdict-restore-DQiV7_fe.js","names":[],"sources":["../src/lib/pan-dir/verdict-restore.ts"],"sourcesContent":["/**\n * PAN-1922: reconstruct the SQLite review_status cache from the durable\n * per-issue git record (PAN-1908) plus a live GitHub re-derivation of\n * PR-owned merge-state.\n *\n * This is the inverse arrow of the PAN-1908 mirror: the mirror writes durable\n * verdicts into the record on every status write; this module reads them back\n * when the cache needs to be rebuilt.\n */\n\nimport {\n getProjectSync,\n loadProjectsConfigSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n} from '../projects.js';\nimport { setReviewStatusSync, type ReviewStatus } from '../review-status.js';\nimport { collectInFlightIssueIds } from './records-backfill.js';\nimport { readIssueRecord, type PanIssuePipelineRecord } from './records.js';\n\nexport interface RestoreVerdictsOptions {\n /** If provided, only restore this issue. */\n issueId?: string;\n /** Show what would be restored without writing. */\n dryRun?: boolean;\n /** Log each processed issue. */\n verbose?: boolean;\n}\n\nexport interface RestoreVerdictsResult {\n restored: number;\n skipped: number;\n failed: number;\n details: Array<{ issueId: string; action: 'restored' | 'skipped' | 'failed'; reason?: string }>;\n}\n\nfunction parseRepoFromPrUrl(prUrl: string): string | null {\n const m = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/\\d+/i);\n return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;\n}\n\n/**\n * Map only the durable verdict columns from a per-issue record pipeline block\n * to a Partial<ReviewStatus> update. Derived/live columns (blockerReasons,\n * readyForMerge) are intentionally omitted.\n */\nfunction pipelineToDurableUpdate(pipeline: PanIssuePipelineRecord): Partial<ReviewStatus> {\n return {\n reviewStatus: pipeline.reviewStatus as ReviewStatus['reviewStatus'],\n testStatus: pipeline.testStatus as ReviewStatus['testStatus'],\n mergeStatus: pipeline.mergeStatus as ReviewStatus['mergeStatus'] ?? undefined,\n inspectStatus: pipeline.inspectStatus as ReviewStatus['inspectStatus'] ?? undefined,\n inspectNotes: pipeline.inspectNotes,\n verificationStatus: pipeline.verificationStatus as ReviewStatus['verificationStatus'] ?? undefined,\n verificationNotes: pipeline.verificationNotes,\n reviewNotes: pipeline.reviewNotes,\n testNotes: pipeline.testNotes,\n mergeNotes: pipeline.mergeNotes,\n prUrl: pipeline.prUrl,\n prNumber: pipeline.prNumber,\n prHeadSha: pipeline.prHeadSha,\n reviewedAtCommit: pipeline.reviewedAtCommit,\n lastVerifiedCommit: pipeline.lastVerifiedCommit,\n autoMerge: pipeline.autoMerge,\n deaconIgnored: pipeline.deaconIgnored,\n deaconIgnoredAt: pipeline.deaconIgnoredAt,\n deaconIgnoredReason: pipeline.deaconIgnoredReason,\n };\n}\n\nasync function restoreOneIssue(\n issueId: string,\n options: RestoreVerdictsOptions,\n): Promise<{ action: 'restored' | 'skipped' | 'failed'; reason?: string }> {\n const normalizedId = issueId.toUpperCase();\n\n const resolved = resolveProjectFromIssueSync(normalizedId);\n if (!resolved) {\n return { action: 'failed', reason: 'could not resolve project' };\n }\n\n const project = getProjectSync(resolved.projectKey);\n if (!project) {\n return { action: 'failed', reason: 'project not found' };\n }\n\n const record = await readIssueRecord(project, normalizedId);\n if (!record || !record.pipeline) {\n return { action: 'skipped', reason: 'no record' };\n }\n\n const update = pipelineToDurableUpdate(record.pipeline);\n\n if (options.dryRun) {\n return { action: 'restored' };\n }\n\n // Write through the normal setReviewStatusSync path on purpose: it recomputes\n // ready_for_merge (PAN-1650 gate + PAN-905 blocker override), re-fires the\n // harmless idempotent PAN-1908 mirror, and seeds ephemeral counters at their\n // schema defaults via the upsert.\n setReviewStatusSync(normalizedId, update);\n\n // Re-derive PR-owned merge-state live for issues with a tracked PR. This is\n // intentionally done AFTER the durable write so the refreshed blocker set is\n // applied to the newly-restored row.\n if (!options.dryRun && update.prUrl && update.prNumber != null) {\n const repo = parseRepoFromPrUrl(update.prUrl);\n if (repo) {\n try {\n const { refreshMergeStateFromGitHub } = await import('../webhook-handlers.js');\n await refreshMergeStateFromGitHub(normalizedId, repo, update.prNumber);\n } catch (err) {\n console.warn(\n `[verdict-restore] GitHub merge-state refresh failed for ${normalizedId}: ${(err as Error).message}`,\n );\n }\n }\n }\n\n return { action: 'restored' };\n}\n\n/**\n * Rebuild durable review_status rows from the per-issue git record.\n *\n * When no issueId is given, every in-flight issue (agent row, review_status\n * row, or continue file) is processed, matching the scope of the PAN-1908\n * backfill command.\n */\nexport async function restoreReviewStatusFromRecords(\n options: RestoreVerdictsOptions = {},\n): Promise<RestoreVerdictsResult> {\n const config = loadProjectsConfigSync();\n const projects = Object.entries(config.projects).map(([key, p]) => ({ ...p, key }));\n\n let issueIds: Set<string>;\n if (options.issueId) {\n issueIds = new Set([options.issueId.toUpperCase()]);\n } else {\n issueIds = await collectInFlightIssueIds(projects);\n }\n\n const result: RestoreVerdictsResult = {\n restored: 0,\n skipped: 0,\n failed: 0,\n details: [],\n };\n\n for (const issueId of issueIds) {\n if (options.verbose) {\n console.log(`[verdict-restore] ${issueId} ...`);\n }\n\n const outcome = await restoreOneIssue(issueId, options);\n result.details.push({ issueId, ...outcome });\n\n if (outcome.action === 'restored') result.restored++;\n else if (outcome.action === 'skipped') result.skipped++;\n else result.failed++;\n\n if (options.verbose) {\n console.log(`[verdict-restore] ${issueId} -> ${outcome.action}${outcome.reason ? ` (${outcome.reason})` : ''}`);\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;eAewB;oBACqD;cAED;AAkB5E,SAAS,mBAAmB,OAA8B;CACxD,MAAM,IAAI,MAAM,MAAM,4CAA4C;AAClE,QAAO,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,KAAK,aAAa,GAAG;;;;;;;AAQ/C,SAAS,wBAAwB,UAAyD;AACxF,QAAO;EACL,cAAc,SAAS;EACvB,YAAY,SAAS;EACrB,aAAa,SAAS,eAA8C,KAAA;EACpE,eAAe,SAAS,iBAAkD,KAAA;EAC1E,cAAc,SAAS;EACvB,oBAAoB,SAAS,sBAA4D,KAAA;EACzF,mBAAmB,SAAS;EAC5B,aAAa,SAAS;EACtB,WAAW,SAAS;EACpB,YAAY,SAAS;EACrB,OAAO,SAAS;EAChB,UAAU,SAAS;EACnB,WAAW,SAAS;EACpB,kBAAkB,SAAS;EAC3B,oBAAoB,SAAS;EAC7B,WAAW,SAAS;EACpB,eAAe,SAAS;EACxB,iBAAiB,SAAS;EAC1B,qBAAqB,SAAS;EAC/B;;AAGH,eAAe,gBACb,SACA,SACyE;CACzE,MAAM,eAAe,QAAQ,aAAa;CAE1C,MAAM,WAAW,4BAA4B,aAAa;AAC1D,KAAI,CAAC,SACH,QAAO;EAAE,QAAQ;EAAU,QAAQ;EAA6B;CAGlE,MAAM,UAAU,eAAe,SAAS,WAAW;AACnD,KAAI,CAAC,QACH,QAAO;EAAE,QAAQ;EAAU,QAAQ;EAAqB;CAG1D,MAAM,SAAS,MAAM,gBAAgB,SAAS,aAAa;AAC3D,KAAI,CAAC,UAAU,CAAC,OAAO,SACrB,QAAO;EAAE,QAAQ;EAAW,QAAQ;EAAa;CAGnD,MAAM,SAAS,wBAAwB,OAAO,SAAS;AAEvD,KAAI,QAAQ,OACV,QAAO,EAAE,QAAQ,YAAY;AAO/B,qBAAoB,cAAc,OAAO;AAKzC,KAAI,CAAC,QAAQ,UAAU,OAAO,SAAS,OAAO,YAAY,MAAM;EAC9D,MAAM,OAAO,mBAAmB,OAAO,MAAM;AAC7C,MAAI,KACF,KAAI;GACF,MAAM,EAAE,gCAAgC,MAAM,OAAO;AACrD,SAAM,4BAA4B,cAAc,MAAM,OAAO,SAAS;WAC/D,KAAK;AACZ,WAAQ,KACN,2DAA2D,aAAa,IAAK,IAAc,UAC5F;;;AAKP,QAAO,EAAE,QAAQ,YAAY;;;;;;;;;AAU/B,eAAsB,+BACpB,UAAkC,EAAE,EACJ;CAChC,MAAM,SAAS,wBAAwB;CACvC,MAAM,WAAW,OAAO,QAAQ,OAAO,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ;EAAE,GAAG;EAAG;EAAK,EAAE;CAEnF,IAAI;AACJ,KAAI,QAAQ,QACV,YAAW,IAAI,IAAI,CAAC,QAAQ,QAAQ,aAAa,CAAC,CAAC;KAEnD,YAAW,MAAM,wBAAwB,SAAS;CAGpD,MAAM,SAAgC;EACpC,UAAU;EACV,SAAS;EACT,QAAQ;EACR,SAAS,EAAE;EACZ;AAED,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,QAAQ,QACV,SAAQ,IAAI,qBAAqB,QAAQ,MAAM;EAGjD,MAAM,UAAU,MAAM,gBAAgB,SAAS,QAAQ;AACvD,SAAO,QAAQ,KAAK;GAAE;GAAS,GAAG;GAAS,CAAC;AAE5C,MAAI,QAAQ,WAAW,WAAY,QAAO;WACjC,QAAQ,WAAW,UAAW,QAAO;MACzC,QAAO;AAEZ,MAAI,QAAQ,QACV,SAAQ,IAAI,qBAAqB,QAAQ,MAAM,QAAQ,SAAS,QAAQ,SAAS,KAAK,QAAQ,OAAO,KAAK,KAAK;;AAInH,QAAO"}
1
+ {"version":3,"file":"verdict-restore-DGpX0ZOJ.js","names":[],"sources":["../src/lib/pan-dir/verdict-restore.ts"],"sourcesContent":["/**\n * PAN-1922: reconstruct the SQLite review_status cache from the durable\n * per-issue git record (PAN-1908) plus a live GitHub re-derivation of\n * PR-owned merge-state.\n *\n * This is the inverse arrow of the PAN-1908 mirror: the mirror writes durable\n * verdicts into the record on every status write; this module reads them back\n * when the cache needs to be rebuilt.\n */\n\nimport {\n getProjectSync,\n loadProjectsConfigSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n} from '../projects.js';\nimport { setReviewStatusSync, type ReviewStatus } from '../review-status.js';\nimport { collectInFlightIssueIds } from './records-backfill.js';\nimport { readIssueRecord, type PanIssuePipelineRecord } from './records.js';\n\nexport interface RestoreVerdictsOptions {\n /** If provided, only restore this issue. */\n issueId?: string;\n /** Show what would be restored without writing. */\n dryRun?: boolean;\n /** Log each processed issue. */\n verbose?: boolean;\n}\n\nexport interface RestoreVerdictsResult {\n restored: number;\n skipped: number;\n failed: number;\n details: Array<{ issueId: string; action: 'restored' | 'skipped' | 'failed'; reason?: string }>;\n}\n\nfunction parseRepoFromPrUrl(prUrl: string): string | null {\n const m = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/\\d+/i);\n return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;\n}\n\n/**\n * Map only the durable verdict columns from a per-issue record pipeline block\n * to a Partial<ReviewStatus> update. Derived/live columns (blockerReasons,\n * readyForMerge) are intentionally omitted.\n */\nfunction pipelineToDurableUpdate(pipeline: PanIssuePipelineRecord): Partial<ReviewStatus> {\n return {\n reviewStatus: pipeline.reviewStatus as ReviewStatus['reviewStatus'],\n testStatus: pipeline.testStatus as ReviewStatus['testStatus'],\n mergeStatus: pipeline.mergeStatus as ReviewStatus['mergeStatus'] ?? undefined,\n inspectStatus: pipeline.inspectStatus as ReviewStatus['inspectStatus'] ?? undefined,\n inspectNotes: pipeline.inspectNotes,\n verificationStatus: pipeline.verificationStatus as ReviewStatus['verificationStatus'] ?? undefined,\n verificationNotes: pipeline.verificationNotes,\n reviewNotes: pipeline.reviewNotes,\n testNotes: pipeline.testNotes,\n mergeNotes: pipeline.mergeNotes,\n prUrl: pipeline.prUrl,\n prNumber: pipeline.prNumber,\n prHeadSha: pipeline.prHeadSha,\n reviewedAtCommit: pipeline.reviewedAtCommit,\n lastVerifiedCommit: pipeline.lastVerifiedCommit,\n autoMerge: pipeline.autoMerge,\n deaconIgnored: pipeline.deaconIgnored,\n deaconIgnoredAt: pipeline.deaconIgnoredAt,\n deaconIgnoredReason: pipeline.deaconIgnoredReason,\n };\n}\n\nasync function restoreOneIssue(\n issueId: string,\n options: RestoreVerdictsOptions,\n): Promise<{ action: 'restored' | 'skipped' | 'failed'; reason?: string }> {\n const normalizedId = issueId.toUpperCase();\n\n const resolved = resolveProjectFromIssueSync(normalizedId);\n if (!resolved) {\n return { action: 'failed', reason: 'could not resolve project' };\n }\n\n const project = getProjectSync(resolved.projectKey);\n if (!project) {\n return { action: 'failed', reason: 'project not found' };\n }\n\n const record = await readIssueRecord(project, normalizedId);\n if (!record || !record.pipeline) {\n return { action: 'skipped', reason: 'no record' };\n }\n\n const update = pipelineToDurableUpdate(record.pipeline);\n\n if (options.dryRun) {\n return { action: 'restored' };\n }\n\n // Write through the normal setReviewStatusSync path on purpose: it recomputes\n // ready_for_merge (PAN-1650 gate + PAN-905 blocker override), re-fires the\n // harmless idempotent PAN-1908 mirror, and seeds ephemeral counters at their\n // schema defaults via the upsert.\n setReviewStatusSync(normalizedId, update);\n\n // Re-derive PR-owned merge-state live for issues with a tracked PR. This is\n // intentionally done AFTER the durable write so the refreshed blocker set is\n // applied to the newly-restored row.\n if (!options.dryRun && update.prUrl && update.prNumber != null) {\n const repo = parseRepoFromPrUrl(update.prUrl);\n if (repo) {\n try {\n const { refreshMergeStateFromGitHub } = await import('../webhook-handlers.js');\n await refreshMergeStateFromGitHub(normalizedId, repo, update.prNumber);\n } catch (err) {\n console.warn(\n `[verdict-restore] GitHub merge-state refresh failed for ${normalizedId}: ${(err as Error).message}`,\n );\n }\n }\n }\n\n return { action: 'restored' };\n}\n\n/**\n * Rebuild durable review_status rows from the per-issue git record.\n *\n * When no issueId is given, every in-flight issue (agent row, review_status\n * row, or continue file) is processed, matching the scope of the PAN-1908\n * backfill command.\n */\nexport async function restoreReviewStatusFromRecords(\n options: RestoreVerdictsOptions = {},\n): Promise<RestoreVerdictsResult> {\n const config = loadProjectsConfigSync();\n const projects = Object.entries(config.projects).map(([key, p]) => ({ ...p, key }));\n\n let issueIds: Set<string>;\n if (options.issueId) {\n issueIds = new Set([options.issueId.toUpperCase()]);\n } else {\n issueIds = await collectInFlightIssueIds(projects);\n }\n\n const result: RestoreVerdictsResult = {\n restored: 0,\n skipped: 0,\n failed: 0,\n details: [],\n };\n\n for (const issueId of issueIds) {\n if (options.verbose) {\n console.log(`[verdict-restore] ${issueId} ...`);\n }\n\n const outcome = await restoreOneIssue(issueId, options);\n result.details.push({ issueId, ...outcome });\n\n if (outcome.action === 'restored') result.restored++;\n else if (outcome.action === 'skipped') result.skipped++;\n else result.failed++;\n\n if (options.verbose) {\n console.log(`[verdict-restore] ${issueId} -> ${outcome.action}${outcome.reason ? ` (${outcome.reason})` : ''}`);\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;eAewB;oBACqD;cAED;AAkB5E,SAAS,mBAAmB,OAA8B;CACxD,MAAM,IAAI,MAAM,MAAM,4CAA4C;AAClE,QAAO,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,KAAK,aAAa,GAAG;;;;;;;AAQ/C,SAAS,wBAAwB,UAAyD;AACxF,QAAO;EACL,cAAc,SAAS;EACvB,YAAY,SAAS;EACrB,aAAa,SAAS,eAA8C,KAAA;EACpE,eAAe,SAAS,iBAAkD,KAAA;EAC1E,cAAc,SAAS;EACvB,oBAAoB,SAAS,sBAA4D,KAAA;EACzF,mBAAmB,SAAS;EAC5B,aAAa,SAAS;EACtB,WAAW,SAAS;EACpB,YAAY,SAAS;EACrB,OAAO,SAAS;EAChB,UAAU,SAAS;EACnB,WAAW,SAAS;EACpB,kBAAkB,SAAS;EAC3B,oBAAoB,SAAS;EAC7B,WAAW,SAAS;EACpB,eAAe,SAAS;EACxB,iBAAiB,SAAS;EAC1B,qBAAqB,SAAS;EAC/B;;AAGH,eAAe,gBACb,SACA,SACyE;CACzE,MAAM,eAAe,QAAQ,aAAa;CAE1C,MAAM,WAAW,4BAA4B,aAAa;AAC1D,KAAI,CAAC,SACH,QAAO;EAAE,QAAQ;EAAU,QAAQ;EAA6B;CAGlE,MAAM,UAAU,eAAe,SAAS,WAAW;AACnD,KAAI,CAAC,QACH,QAAO;EAAE,QAAQ;EAAU,QAAQ;EAAqB;CAG1D,MAAM,SAAS,MAAM,gBAAgB,SAAS,aAAa;AAC3D,KAAI,CAAC,UAAU,CAAC,OAAO,SACrB,QAAO;EAAE,QAAQ;EAAW,QAAQ;EAAa;CAGnD,MAAM,SAAS,wBAAwB,OAAO,SAAS;AAEvD,KAAI,QAAQ,OACV,QAAO,EAAE,QAAQ,YAAY;AAO/B,qBAAoB,cAAc,OAAO;AAKzC,KAAI,CAAC,QAAQ,UAAU,OAAO,SAAS,OAAO,YAAY,MAAM;EAC9D,MAAM,OAAO,mBAAmB,OAAO,MAAM;AAC7C,MAAI,KACF,KAAI;GACF,MAAM,EAAE,gCAAgC,MAAM,OAAO;AACrD,SAAM,4BAA4B,cAAc,MAAM,OAAO,SAAS;WAC/D,KAAK;AACZ,WAAQ,KACN,2DAA2D,aAAa,IAAK,IAAc,UAC5F;;;AAKP,QAAO,EAAE,QAAQ,YAAY;;;;;;;;;AAU/B,eAAsB,+BACpB,UAAkC,EAAE,EACJ;CAChC,MAAM,SAAS,wBAAwB;CACvC,MAAM,WAAW,OAAO,QAAQ,OAAO,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ;EAAE,GAAG;EAAG;EAAK,EAAE;CAEnF,IAAI;AACJ,KAAI,QAAQ,QACV,YAAW,IAAI,IAAI,CAAC,QAAQ,QAAQ,aAAa,CAAC,CAAC;KAEnD,YAAW,MAAM,wBAAwB,SAAS;CAGpD,MAAM,SAAgC;EACpC,UAAU;EACV,SAAS;EACT,QAAQ;EACR,SAAS,EAAE;EACZ;AAED,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,QAAQ,QACV,SAAQ,IAAI,qBAAqB,QAAQ,MAAM;EAGjD,MAAM,UAAU,MAAM,gBAAgB,SAAS,QAAQ;AACvD,SAAO,QAAQ,KAAK;GAAE;GAAS,GAAG;GAAS,CAAC;AAE5C,MAAI,QAAQ,WAAW,WAAY,QAAO;WACjC,QAAQ,WAAW,UAAW,QAAO;MACzC,QAAO;AAEZ,MAAI,QAAQ,QACV,SAAQ,IAAI,qBAAqB,QAAQ,MAAM,QAAQ,SAAS,QAAQ,SAAS,KAAK,QAAQ,OAAO,KAAK,KAAK;;AAInH,QAAO"}
@@ -1,7 +1,7 @@
1
1
  import { m as init_errors } from "./errors-DyEDJ7xB.js";
2
2
  import { v as init_projects } from "./projects-D2nMJ9C4.js";
3
- import { t as init_agents } from "./agents-CQHK0CSA.js";
4
- import { a as init_review_status, p as setReviewStatus, r as getReviewStatus } from "./review-status-iJwfEoNX.js";
3
+ import { t as init_agents } from "./agents-CgVr77Qq.js";
4
+ import { a as init_review_status, p as setReviewStatus, r as getReviewStatus } from "./review-status-B_XXomV0.js";
5
5
  import { i as init_feedback_writer } from "./feedback-writer-DgN4CnWY.js";
6
6
  import { a as init_tracker_config } from "./tracker-config-Cn2U56Fu.js";
7
7
  import { Effect } from "effect";
@@ -146,4 +146,4 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
146
146
  //#endregion
147
147
  export { refreshMergeStateFromGitHub };
148
148
 
149
- //# sourceMappingURL=webhook-handlers-DHBSEDEm.js.map
149
+ //# sourceMappingURL=webhook-handlers-BjsfIL7u.js.map