@overdeck/desktop 0.45.12 → 0.45.15

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 (355) hide show
  1. package/drizzle/overdeck/0000_overdeck_init.sql +33 -0
  2. package/package.json +7 -2
  3. package/server/{acceptance-criteria-1LHgD9vg.js → acceptance-criteria-DHZk2ceV.js} +2 -2
  4. package/server/{advancing-selfheal-BqsLWqEM.js → advancing-selfheal-CSuZ31bB.js} +5 -5
  5. package/server/{agent-gc-CUb52n7s.js → agent-gc-D_DxfBHR.js} +3 -3
  6. package/server/{agent-state-D-moNwhA.js → agent-state-EP2QFV9n.js} +1 -1
  7. package/server/{agent-state-service-BzESvLOf.js → agent-state-service-BZkvgQf4.js} +5 -5
  8. package/server/{agent-state-service-BaZH7sXJ.js → agent-state-service-C_2ZAQRP.js} +1 -1
  9. package/server/{agents-B5LfpLPv.js → agents-CsOzAmRg.js} +145 -78
  10. package/server/{agents-C4kZ8a2r.js → agents-DCvyTXNU.js} +1 -1
  11. package/server/{archive-planning-BbZe-8IK.js → archive-planning-CTILQRnV.js} +1 -1
  12. package/server/{archive-planning-oX1pj6nY.js → archive-planning-DkivCk1A.js} +4 -4
  13. package/server/{artifact-index-jobs-DGr6bxev.js → artifact-index-jobs-CmsvQ1ey.js} +2 -2
  14. package/server/auto-commit-QAfsu3Sb.js +3 -0
  15. package/server/{backlog-auto-trigger-BlJHikOm.js → backlog-auto-trigger-ueRaT8Dy.js} +2 -2
  16. package/server/{bd-process-lock-i3CYMS-B.js → bd-process-lock-D3S4T4o-.js} +32 -11
  17. package/server/{beads-DD7fYCl5.js → beads-BGGxrkZ5.js} +96 -196
  18. package/server/{beads-BNU-fb48.js → beads-CYsLiyIM.js} +1 -1
  19. package/server/{beads-query-e6oku4P5.js → beads-query-B1DlJVp9.js} +24 -99
  20. package/server/beads-restore-CyygSmmn.js +23 -0
  21. package/server/{behavior-CPf0pbOt.js → behavior-Cpj03N4v.js} +3 -3
  22. package/server/{briefing-freshness-B2FdLwIv.js → briefing-freshness-Kh5V8RTh.js} +4 -4
  23. package/server/{checkpoint-manager-eFgaMMu4.js → checkpoint-manager-CAFos8e1.js} +1 -1
  24. package/server/{checkpoint-manager-JryW6MpM.js → checkpoint-manager-fu9oVsR-.js} +2 -2
  25. package/server/checkpoint-worker.js +1 -1
  26. package/server/{checkpoints-qvOqvMuC.js → checkpoints-1BqOlS4q.js} +2 -2
  27. package/server/{checkpoints-BK17oicz.js → checkpoints-DvhGi8P_.js} +1 -1
  28. package/server/claude-hooks-provision-s_wdpeVj.js +360 -0
  29. package/server/{claude-permissions-B6bWGh9d.js → claude-permissions-CaWqysff.js} +2 -2
  30. package/server/{claude-settings-overlay-BPdhTPjo.js → claude-settings-overlay-BbA4FbVI.js} +1 -1
  31. package/server/{claude-settings-overlay-CRyyKKXQ.js → claude-settings-overlay-CcTf0xzG.js} +2 -2
  32. package/server/{cli-B75AieAM.js → cli-BwCvCUBK.js} +9 -9
  33. package/server/cli-ClIQwgMI.js +3 -0
  34. package/server/{cliproxy-CCzwuSNe.js → cliproxy-BsoNNDAj.js} +2 -2
  35. package/server/{cliproxy-DOQf66G9.js → cliproxy-C2xC-3vH.js} +1 -1
  36. package/server/{close-issue-_X3MXJyG.js → close-issue-BleIwP3f.js} +5 -5
  37. package/server/{closed-issue-reaper-CLR7ClsO.js → closed-issue-reaper-BwUUvjEW.js} +1 -1
  38. package/server/{closed-issue-reaper-BsUfBSjM.js → closed-issue-reaper-mC5pujeb.js} +102 -30
  39. package/server/{codex-CY-QRuU9.js → codex-B7DTpwEw.js} +1 -1
  40. package/server/{codex-B4KEpX5V.js → codex-DW60_5j2.js} +5 -5
  41. package/server/{compact-beads-cZds2MTr.js → compact-beads-CK_hHEnB.js} +1 -1
  42. package/server/{compact-beads-uEDccC76.js → compact-beads-DKL54y6j.js} +15 -8
  43. package/server/{config-DARrpYWA.js → config-BRNQJXhA.js} +1 -1
  44. package/server/{config-8sjaU6jZ.js → config-ZNnMpIlh.js} +2 -2
  45. package/server/{config-yaml-ClpGgrf6.js → config-yaml-Dcz-8uit.js} +29 -3
  46. package/server/{conflict-gate-Ds9_2yjd.js → conflict-gate-DGT8jCLp.js} +1 -1
  47. package/server/{conflict-gate-BtB_o-Vi.js → conflict-gate-DTje2Ll7.js} +3 -3
  48. package/server/{context-layers-r2ywnQYA.js → context-layers-BdUCQ-cl.js} +3 -3
  49. package/server/{context-layers-CRCk1QG7.js → context-layers-ByOWUjUg.js} +2 -2
  50. package/server/{control-settings-D1B3u6HP.js → control-settings-DwkAeooY.js} +4 -4
  51. package/server/{conversation-compaction-D86Zo2Ci.js → conversation-compaction-OTYgd6eg.js} +8 -8
  52. package/server/{conversation-compaction-CZRVk4hg.js → conversation-compaction-WYQZUVDm.js} +1 -1
  53. package/server/{conversation-service-CuTVU8dV.js → conversation-service-BR1OgvxO.js} +5 -5
  54. package/server/{conversation-service-CkL-6MuG.js → conversation-service-C_kNZPLx.js} +1 -1
  55. package/server/{conversations-DfJKBDwB.js → conversations-DG2M8WnT.js} +4 -4
  56. package/server/{conversations-BmWEluPL.js → conversations-izHvSoAR.js} +1 -1
  57. package/server/{cost-DAPYN6iA.js → cost-B2mj17iG.js} +3 -3
  58. package/server/{cost-CCnqa5xK.js → cost-DQ2ajr_m.js} +2 -2
  59. package/server/{cost-sync-BDL10Nwu.js → cost-sync-gTayEuWP.js} +3 -3
  60. package/server/{dag-Dv2r5bVD.js → dag-CqTehWpY.js} +3 -3
  61. package/server/{dangerous-git-ops-Dcw7PbYo.js → dangerous-git-ops-DDWQlPC3.js} +1 -1
  62. package/server/dashboard-db-worker.js +6 -6
  63. package/server/{deacon-DYnZH7kC.js → deacon-BqW9HMew.js} +3 -3
  64. package/server/{deacon-CM0MxbS8.js → deacon-DrKDBeJL.js} +161 -1594
  65. package/server/{deacon-nudge-log-B-naf89q.js → deacon-nudge-log-D1wEK6zF.js} +1 -1
  66. package/server/deacon-swarm-BY5hKNaU.js +2 -0
  67. package/server/{deacon-swarm-BstYoz8Q.js → deacon-swarm-CO_A7xyd.js} +12 -105
  68. package/server/deacon.js +7 -7
  69. package/server/{devcontainer-renderer-CaYOG9JN.js → devcontainer-renderer-BQolp_qZ.js} +3 -3
  70. package/server/{dist-BIC8spJ0.js → dist-Bc32rWJB.js} +1 -1
  71. package/server/dist-DGTwcZhI.js +3 -0
  72. package/server/{docker-DDI1shu3.js → docker-s-esqZ__.js} +67 -28
  73. package/server/{ensure-devcontainer-DCAg3ksz.js → ensure-devcontainer-DA4-jF6R.js} +4 -4
  74. package/server/{ensure-devcontainer-CbSXvN9W.js → ensure-devcontainer-pUVaT0h3.js} +1 -1
  75. package/server/{esm-shims-DiV9jHa_.js → esm-shims-CJHy8CZY.js} +1 -1
  76. package/server/{event-store-DU3HkZlN.js → event-store-CcJAhKCl.js} +1 -1
  77. package/server/{event-store-DCMqyI07.js → event-store-DRte2Rwt.js} +3 -3
  78. package/server/{events-BfiosVAn.js → events-Bej1w_k8.js} +3 -3
  79. package/server/{events-DLRxpSuV.js → events-CtsLScn8.js} +1 -1
  80. package/server/export-Dx_qJ3h8.js +87 -0
  81. package/server/{factory-BMtWV9iv.js → factory-FN727tWr.js} +4 -4
  82. package/server/{feature-registry-population-DA7bO6Zn.js → feature-registry-population-ie-yBcQC.js} +4 -4
  83. package/server/{feature-registry-storage-nAY1fbBt.js → feature-registry-storage-CkZTFSsn.js} +2 -2
  84. package/server/{features-DMu7eGmU.js → features-Dtm9ABuz.js} +2 -2
  85. package/server/{feedback-target-DHqcw3cN.js → feedback-target-BYtpyt1i.js} +6 -6
  86. package/server/{feedback-target-B2SEai8-.js → feedback-target-DVJQDiE4.js} +1 -1
  87. package/server/{feedback-writer-DMBdS_jL.js → feedback-writer-DOUhyIW2.js} +1 -1
  88. package/server/{feedback-writer-qvm7UON7.js → feedback-writer-fdE1BzeL.js} +4 -4
  89. package/server/{flywheel-k4FPtCQ_.js → flywheel-Bi82iiNl.js} +1 -1
  90. package/server/{flywheel-Dfpx4bIV.js → flywheel-Bv3wv2S6.js} +23 -15
  91. package/server/{flywheel-D2LdPSic.js → flywheel-CUErLAyN.js} +13 -13
  92. package/server/{flywheel-merge-order-CB4lbXeJ.js → flywheel-merge-order-BaEN3iyN.js} +7 -7
  93. package/server/{flywheel-merge-order-CttTazRY.js → flywheel-merge-order-DXjeBnJ1.js} +1 -1
  94. package/server/{forge-CoPFXFyl.js → forge-ButEdJ31.js} +2 -2
  95. package/server/forge-XlDgPQWs.js +2 -0
  96. package/server/{from-B0YoJl9-.js → from-CbtTxftl.js} +1 -1
  97. package/server/{fts-db-DAHW1yA_.js → fts-db-CBgBLZcL.js} +2 -2
  98. package/server/{fts-operations-DDXDBi0d.js → fts-operations-CI9Xuo_1.js} +2 -2
  99. package/server/{git-utils-DbfC0zls.js → git-utils-BCLRWQ-y.js} +1 -1
  100. package/server/{git-utils-B90Zjzfl.js → git-utils-ClfpP47N.js} +2 -2
  101. package/server/{github-app-C7lftMNj.js → github-app-DRD2woO0.js} +1 -1
  102. package/server/{github-app-BecAnXRX.js → github-app-DscZ0_B6.js} +2 -2
  103. package/server/{github-client-D21rCIXk.js → github-client-DpiZnxGH.js} +2 -2
  104. package/server/{github-client-BwtKZtM0.js → github-client-iFRw8f7e.js} +1 -1
  105. package/server/{github-pr-lookup-CzVWuysl.js → github-pr-lookup-D-EBexxC.js} +2 -2
  106. package/server/{harness-resolve-SkRJm4a7.js → harness-resolve-iMjyNUJ2.js} +3 -3
  107. package/server/{health-events-DA_JLpgZ.js → health-events-B_WsQyYm.js} +1 -1
  108. package/server/{health-events-BT0aAfeA.js → health-events-CRdxDFN2.js} +2 -2
  109. package/server/{hooks-BQxRXtRb.js → hooks-C1ScyVxP.js} +1 -1
  110. package/server/{hooks-DxEn2BoY.js → hooks-C9b7Vqpw.js} +2 -2
  111. package/server/hume-K1pi8jC4.js +3 -0
  112. package/server/{hume-DkVOs1Qi.js → hume-TQ5pvOZ_.js} +2 -2
  113. package/server/{idle-stack-reaper-DGy7tvC9.js → idle-stack-reaper-B1kG7B_X.js} +2 -2
  114. package/server/{idle-stack-reaper-C7fJL8jH.js → idle-stack-reaper-iFVOAoJy.js} +1 -1
  115. package/server/{infra-DE3C4PMr.js → infra-bpq1MHKU.js} +50 -3
  116. package/server/{injection-Dt22tGvS.js → injection-BLWfwTVZ.js} +1 -1
  117. package/server/{injection-Cc4Qt8LZ.js → injection-DJqWld1v.js} +7 -7
  118. package/server/inspect-agent-477VxQim.js +2 -0
  119. package/server/{inspect-agent-DOaCDkCF.js → inspect-agent-DytCsltn.js} +9 -9
  120. package/server/{internal-token-nobT6FhA.js → internal-token-CCaWaTwE.js} +1 -1
  121. package/server/{internal-token-B_1YLWLI.js → internal-token-CjlhdufS.js} +2 -2
  122. package/server/{io-A7ZuOBkz.js → io-BiQ2Cs3k.js} +2 -2
  123. package/server/{issue-closed-Da3DeomA.js → issue-closed-FApNiOOs.js} +4 -4
  124. package/server/{issue-lifecycle-Bd9XBJAQ.js → issue-lifecycle-BkFWNjlD.js} +1 -1
  125. package/server/{issue-lifecycle-543pQX9D.js → issue-lifecycle-C-K53ASY.js} +21 -21
  126. package/server/{issue-service-singleton-Brc3t0SV.js → issue-service-singleton-CuHGpJJP.js} +8 -8
  127. package/server/{issue-service-singleton-BC2JLZi7.js → issue-service-singleton-bG1WdSdQ.js} +1 -1
  128. package/server/{issues-D3kt9UWE.js → issues-CwQMphDH.js} +2 -2
  129. package/server/jsonl-parser-dNIjHgym.js +245 -0
  130. package/server/{label-cleanup-w9X_T3AQ.js → label-cleanup-DYvAZknd.js} +3 -3
  131. package/server/{layers-Cwe1J-9o.js → layers-CD6FysRU.js} +2 -2
  132. package/server/{lifecycle-f1GbDK33.js → lifecycle-C_73gi-C.js} +7 -7
  133. package/server/{lifecycle-io-D56q8zlO.js → lifecycle-io-C_Wu0_i-.js} +1 -1
  134. package/server/{lifecycle-io-Bqvj0m3i.js → lifecycle-io-CeYxP3cE.js} +4 -4
  135. package/server/{linear-client-Bh4T4S-c.js → linear-client-4qoKnuQu.js} +1 -1
  136. package/server/{linear-client-CfJMTAYd.js → linear-client-EtVyblVz.js} +2 -2
  137. package/server/memory-fts-worker.js +1 -1
  138. package/server/{merge-ZrXFM3jv.js → merge-7rqkTUm2.js} +3 -3
  139. package/server/merge-agent-36wfYm2u.js +3 -0
  140. package/server/{merge-agent-CHHs5S_0.js → merge-agent-4Zhlgvqp.js} +62 -27
  141. package/server/{merge-batch-BwhGdlh4.js → merge-batch-itfnwdsT.js} +1 -1
  142. package/server/{merge-blockers-BMhDpMl_.js → merge-blockers-Bto_Oe_o.js} +2 -2
  143. package/server/{merge-DRMPa8CQ.js → merge-brm721Es.js} +1 -1
  144. package/server/merge-ops-PzqsW69V.js +2 -0
  145. package/server/{merge-rebase-R7MfqpiE.js → merge-rebase-Czfqfe4q.js} +2 -2
  146. package/server/{merge-set-B3Breacq.js → merge-set-BIkGQL9j.js} +9 -5
  147. package/server/{merge-set-D-iXNP0I.js → merge-set-G1RHcu3D.js} +1 -1
  148. package/server/{merge-sync-DHWbGtjR.js → merge-sync-B43Dybea.js} +2 -2
  149. package/server/{merge-sync-bOOtfeXR.js → merge-sync-DIrmlZUr.js} +1 -1
  150. package/server/{merge-train-98ptZrPT.js → merge-train-DGqVuNZV.js} +3 -3
  151. package/server/{merge-train-deps-B5FWJTE2.js → merge-train-deps-q15XLnAS.js} +4 -4
  152. package/server/{merge-verification-BGiuiXAK.js → merge-verification-Cqfxx6bE.js} +4 -4
  153. package/server/{multipart-parser-D7mzNNDm.js → multipart-parser-CiPxxfUH.js} +2 -2
  154. package/server/node_modules/.package-lock.json +1 -1
  155. package/server/{orphan-proposed-reconciler-BSGCNJ6X.js → orphan-proposed-reconciler-BWWHn4Lr.js} +15 -39
  156. package/server/{orphan-proposed-reconciler-CSuxivr9.js → orphan-proposed-reconciler-DAD9FJtQ.js} +1 -1
  157. package/server/package-lock.json +2 -2
  158. package/server/package.json +1 -1
  159. package/server/{pan-dir-DoPtdHrp.js → pan-dir-Bj7Hy8iu.js} +3 -3
  160. package/server/{paths-CVDLPxXp.js → paths-C9DkhIOT.js} +2 -2
  161. package/server/{pickup-KLRUhThD.js → pickup-CKRuGDsB.js} +1 -1
  162. package/server/{pipeline-notifier-BtYHbBPl.js → pipeline-notifier-BkKbQ8ji.js} +1 -1
  163. package/server/{pipeline-notifier-WD2dnOsR.js → pipeline-notifier-CAurUste.js} +2 -2
  164. package/server/project-pipelines-BRWaGmI4.js +162 -0
  165. package/server/projects-KJ94e50I.js +3 -0
  166. package/server/{prompts-Cg7e9FPb.js → prompts-DfI5qbTW.js} +2 -2
  167. package/server/{providers-fLduChnX.js → providers-CWDXoaMA.js} +7 -7
  168. package/server/providers-D-5txBRc.js +3 -0
  169. package/server/public/assets/{chunk-BO2N2NFS-aNlsGDCu.js → chunk-BO2N2NFS-ClukPsZQ.js} +4 -4
  170. package/server/public/assets/{diagram-OG6HWLK6-CnjkrvT8.js → diagram-OG6HWLK6-Avtvq2qI.js} +1 -1
  171. package/server/public/assets/{dist-Bg3Sbsx4.js → dist-BEHQuasD.js} +3 -3
  172. package/server/public/assets/{dist-CGKBCieA.js → dist-BvmY-qb1.js} +1 -1
  173. package/server/public/assets/{ganttDiagram-6RSMTGT7-C8LLKj23.js → ganttDiagram-6RSMTGT7-7KhCd0fH.js} +1 -1
  174. package/server/public/assets/{highlighted-body-OFNGDK62-BSDEUvhW.js → highlighted-body-OFNGDK62-iQoBMTf0.js} +1 -1
  175. package/server/public/assets/index-DUFDmvmH.css +1 -0
  176. package/server/public/assets/index-DnTZ2QeN.js +796 -0
  177. package/server/public/assets/{linear-DEbYZoxg.js → linear-CiJHW9g5.js} +1 -1
  178. package/server/public/assets/mermaid-GHXKKRXX-DZJOwgBG.js +1 -0
  179. package/server/public/assets/{pieDiagram-4H26LBE5-BtZL5pXE.js → pieDiagram-4H26LBE5-BOhmvUhW.js} +1 -1
  180. package/server/public/assets/{prod-DYVvIyuh.js → prod-DmLBBqat.js} +4 -4
  181. package/server/public/assets/{quadrantDiagram-W4KKPZXB-a7vS8Jc8.js → quadrantDiagram-W4KKPZXB-Cjku660G.js} +1 -1
  182. package/server/public/assets/{sankeyDiagram-5OEKKPKP-Ckbq2SWH.js → sankeyDiagram-5OEKKPKP-D52w5oPh.js} +1 -1
  183. package/server/public/assets/{xychartDiagram-2RQKCTM6-DJyeNa99.js → xychartDiagram-2RQKCTM6-BTO07_Iv.js} +1 -1
  184. package/server/public/index.html +2 -2
  185. package/server/{pull-requests-1WXmTBlo.js → pull-requests-Bmre-Xlz.js} +3 -3
  186. package/server/{pull-requests-D2SjJIwY.js → pull-requests-CKZPWhjD.js} +1 -1
  187. package/server/{rally-8aQlLLVj.js → rally-BBd9tQ__.js} +3 -3
  188. package/server/rally-Dc2Wnxw2.js +3 -0
  189. package/server/{rally-api-BtvwdX6d.js → rally-api-DDc85HIz.js} +1 -1
  190. package/server/{rally-api-H5I2CBO0.js → rally-api-DxZFObI_.js} +2 -2
  191. package/server/{rally-client-Bt-qHiOh.js → rally-client-BADHn0m_.js} +1 -1
  192. package/server/{rally-client-33CYZYYu.js → rally-client-DvvLiAQP.js} +4 -4
  193. package/server/{reap-terminal-sessions-CAmURC2h.js → reap-terminal-sessions-D6JDJsqF.js} +1 -1
  194. package/server/{reap-terminal-sessions-BwJb2DLJ.js → reap-terminal-sessions-D_xvy-Mk.js} +1 -1
  195. package/server/{rebuild-stack-KHkkrGjx.js → rebuild-stack-D19i080j.js} +6 -6
  196. package/server/{rebuild-stack-CXSgAGPR.js → rebuild-stack-GOsWqXi3.js} +1 -1
  197. package/server/{reconstruct-cache-D_Ml65_6.js → reconstruct-cache-lQrG4xGg.js} +6 -6
  198. package/server/record-pyB6VA32.js +3 -0
  199. package/server/recovery-trip-Dfh1tNHn.js +98 -0
  200. package/server/release-engine-DxcMWRuK.js +306 -0
  201. package/server/release-set-BYsgX7o6.js +125 -0
  202. package/server/{remote-CGuognN3.js → remote-8W2Yhygw.js} +3 -3
  203. package/server/{remote-D0Old0Cv.js → remote-DmeL21dF.js} +3 -3
  204. package/server/{remote-agents-DfhMq6kQ.js → remote-agents-BfQBE6MN.js} +5 -5
  205. package/server/{remote-agents-UHoi4oGr.js → remote-agents-DlvbzO7m.js} +1 -1
  206. package/server/{remote-completion-RZYePR_0.js → remote-completion-BgkiSQmx.js} +12 -12
  207. package/server/resolver-dTpSSzaH.js +158 -0
  208. package/server/resume-DhcIqsTm.js +3 -0
  209. package/server/{review-agent-sZBCpEVv.js → review-agent-D025Wt7w.js} +22 -22
  210. package/server/{review-agent-uwVftH98.js → review-agent-Rub0OpZc.js} +1 -1
  211. package/server/{review-artifacts-C7a45b1_.js → review-artifacts-CX951jTU.js} +6 -6
  212. package/server/{review-context-CErbQpKL.js → review-context-DGGGR_w5.js} +7 -7
  213. package/server/{review-monitor-DHD9m_kh.js → review-monitor-0_qqMcnS.js} +2 -2
  214. package/server/{review-status-gPndPUBc.js → review-status-4qqLkRMu.js} +24 -17
  215. package/server/{review-status-JhQ3qlDS.js → review-status-Br6lAFGO.js} +1 -1
  216. package/server/{review-status-enrichment-qWvWckYN.js → review-status-enrichment-Cr8m3uVL.js} +3 -3
  217. package/server/{review-status-sync-B_GrEke8.js → review-status-sync-D3KUhsdA.js} +1 -1
  218. package/server/{review-status-sync-D3A9KX95.js → review-status-sync-OC-l1lNt.js} +9 -5
  219. package/server/{review-verdict-feedback-4WZ5ZdbC.js → review-verdict-feedback-DrWMuRaj.js} +6 -6
  220. package/server/{review-verdict-feedback-Zo0r_z4m.js → review-verdict-feedback-YOzmsq9G.js} +1 -1
  221. package/server/{rollup-BrUIKMv_.js → rollup-CnxbwaLH.js} +8 -8
  222. package/server/{rollup-Bmhn80tB.js → rollup-DmazRY0j.js} +1 -1
  223. package/server/{scanner-BIGumel_.js → scanner-DE1MqAaF.js} +7 -7
  224. package/server/{sequence-io-DA0akEQN.js → sequence-io-C0uoqfGx.js} +4 -4
  225. package/server/{sequencer-agent-DeflKafB.js → sequencer-agent-i-b2u4SJ.js} +10 -6
  226. package/server/server.js +1115 -297
  227. package/server/{service-V0sUK-OR.js → service-D0_gh2tl.js} +1668 -1408
  228. package/server/{session-fork-Yx3UcAQf.js → session-fork-DZ12vipL.js} +2 -2
  229. package/server/{settings-CFEeQRj2.js → settings-ByqjPtFO.js} +2 -2
  230. package/server/{settings-CxVate0h.js → settings-DCC4BAqj.js} +3 -3
  231. package/server/settings-yydgaqhv.js +2 -0
  232. package/server/{shadow-engineering-BRxnVSDk.js → shadow-engineering-BhnmtW5m.js} +2 -2
  233. package/server/{shadow-state-1aS8FijU.js → shadow-state-ByXZehsy.js} +1 -1
  234. package/server/{shadow-state-BiMwdyl1.js → shadow-state-CKm8xWX9.js} +2 -2
  235. package/server/{ship-log-CMWH3H5-.js → ship-log-DE_0Cv_i.js} +1 -1
  236. package/server/{smart-compaction-Bw1K_Sbl.js → smart-compaction-D5Zen3AA.js} +7 -7
  237. package/server/{smart-compaction-xblMyVpT.js → smart-compaction-h4_ca6Uk.js} +1 -1
  238. package/server/{spawn-planning-session-CzDFSKAQ.js → spawn-planning-session-Bgi7Ub02.js} +1 -1
  239. package/server/{spawn-planning-session-DxLxcM7m.js → spawn-planning-session-Dueit4WX.js} +15 -15
  240. package/server/{specialist-completion-BiEXUHfD.js → specialist-completion-BZ8M-WZ0.js} +1 -1
  241. package/server/{specialist-context-BfkTA81h.js → specialist-context-BMq83C58.js} +5 -5
  242. package/server/{specialist-handoff-logger-BiKuLtNm.js → specialist-handoff-logger-BxX1YefH.js} +2 -2
  243. package/server/{specialist-logs-CFJjGsLC.js → specialist-logs-BWZYcJ6P.js} +3 -3
  244. package/server/{specialists-DdvpFNt1.js → specialists-8ZAguXef.js} +1 -1
  245. package/server/{specialists-B4byf18k.js → specialists-BudKrjOu.js} +76 -76
  246. package/server/{specialists-DjHbwQ_p.js → specialists-DrPrC9Y9.js} +20 -256
  247. package/server/{specialists-BLIreoE7.js → specialists-ZkW4Mb7U.js} +1 -1
  248. package/server/specs-DwoAEGrW.js +3 -0
  249. package/server/{src-fgbU-jJ-.js → src-DHn4Rd2F.js} +25 -3
  250. package/server/{src-mK_M_ZIj.js → src-DNUUqkec.js} +6 -6
  251. package/server/{src-CALBEUMX.js → src-Dv6L03vW.js} +3 -3
  252. package/server/{stack-health-BFM8YlHw.js → stack-health-Dclm7_tu.js} +1 -1
  253. package/server/{stack-health-Qd7rPiVe.js → stack-health-DxsTx7II.js} +2 -2
  254. package/server/{state-home-C2Bicw9N.js → state-home-CRowbXqU.js} +6 -6
  255. package/server/stuck-remediation-CFWUP8_I.js +1491 -0
  256. package/server/stuck-remediation-CLHd-lrF.js +2 -0
  257. package/server/{summary-fork-EWWBvzgE.js → summary-fork-CKc01Our.js} +2 -2
  258. package/server/{summary-fork-DezPkJSm.js → summary-fork-CtCwBJ2Z.js} +8 -8
  259. package/server/{supervisor-D5GC5jUM.js → supervisor-BA5D72Qa.js} +2 -2
  260. package/server/supervisor-DHjumXlL.js +2 -0
  261. package/server/{swarm-readiness-Cf9mommA.js → swarm-readiness-CL5sM9B1.js} +3 -3
  262. package/server/{system-prerequisites-BsI9hxh9.js → system-prerequisites-DmFSXIE5.js} +13 -1
  263. package/server/{teardown-workspace-CWz1pbEK.js → teardown-workspace-DWmFJzTw.js} +37 -68
  264. package/server/{test-agent-queue-TiVXe3ZT.js → test-agent-queue-DjsQoV5X.js} +6 -6
  265. package/server/{test-verdict-CCDd25uN.js → test-verdict-BSqsQp3G.js} +1 -1
  266. package/server/{test-verdict-Bm4uv_AA.js → test-verdict-D1AmZ2c0.js} +1 -1
  267. package/server/{tldr-daemon-C_1frSDj.js → tldr-daemon-C-MGomL8.js} +2 -2
  268. package/server/{tldr-daemon-Big5IZ_M.js → tldr-daemon-DD9rI5gA.js} +1 -1
  269. package/server/{tmux-D57f-XNv.js → tmux-C_d-lSIq.js} +2 -2
  270. package/server/{tmux-DClqWwb8.js → tmux-x291Gjjb.js} +1 -1
  271. package/server/{token-_qujO3JZ.js → token-DfBf1bsd.js} +1 -1
  272. package/server/{tracker-config-BmdJU_w6.js → tracker-config-CNODpd11.js} +2 -2
  273. package/server/{tracker-config-B_Ubts1Z.js → tracker-config-DItsdelv.js} +1 -1
  274. package/server/{tracker-utils-Bg-Sevcv.js → tracker-utils-B9ZoVA9b.js} +1 -1
  275. package/server/{tracker-utils-CWFt6jjV.js → tracker-utils-Ch2_5GGf.js} +2 -2
  276. package/server/{traefik-connect-5EP1WS_Z.js → traefik-connect-BF4x_LUQ.js} +1 -1
  277. package/server/{tunnel-hOBNQbmF.js → tunnel-YD80_IHr.js} +1 -1
  278. package/server/{tunnel-UBf2vyAL.js → tunnel-r_5nHAI6.js} +3 -3
  279. package/server/{types-B5u3NxMl.js → types-33JEP4LZ.js} +1 -1
  280. package/server/{uat-train-DGkIkLxu.js → uat-train-DDI00aqD.js} +1 -1
  281. package/server/{uat-train-qNanv5hN.js → uat-train-DjkFWXge.js} +25 -25
  282. package/server/{validation-C4wegCvH.js → validation-DUTiJnyz.js} +4 -4
  283. package/server/{validation-B0QaLIHP.js → validation-ict8hnXk.js} +1 -1
  284. package/server/{verification-runner-DdleMNLq.js → verification-runner-ATaLbpr5.js} +1 -1
  285. package/server/{verification-runner-CntudBes.js → verification-runner-inLgeQux.js} +12 -12
  286. package/server/{vtt-parser-CKc-smWM.js → vtt-parser-QnRfcjiU.js} +1 -1
  287. package/server/{work-agent-prompt-CG5jFV9A.js → work-agent-prompt-fZljjA1_.js} +12 -12
  288. package/server/{workflows-BK2xs_a8.js → workflows-oBnJ7Tm0.js} +14 -14
  289. package/server/workflows-tU7KKxQi.js +2 -0
  290. package/server/{workspace-C9lC5o3E.js → workspace-B2WOW9Hw.js} +1 -1
  291. package/server/{workspace-CLrSIRCx.js → workspace-CNuW2j41.js} +3 -3
  292. package/server/{workspace-config-DA8U97k9.js → workspace-config-CBgMGhWs.js} +1 -1
  293. package/server/{workspace-manager-BJhW2sky.js → workspace-manager-CB-R07R3.js} +12 -12
  294. package/server/{workspace-manager-Cb2a7bg9.js → workspace-manager-DjUAJCSs.js} +1 -1
  295. package/server/{workspace-metadata-QQmo_Z0l.js → workspace-metadata-9Nn5kgRl.js} +2 -2
  296. package/server/{workspace-metadata-CTRnr323.js → workspace-metadata-Fd58uUQG.js} +1 -1
  297. package/server/{workspaces-Ca4zZs12.js → workspaces-BjWXntty.js} +106 -120
  298. package/server/writer-CQMGzEkw.js +3 -0
  299. package/server/writer-fe1-8iGc.js +251 -0
  300. package/sync-sources/hooks/ask-user-question-hook +87 -0
  301. package/sync-sources/hooks/auto-approve-hook +96 -0
  302. package/sync-sources/hooks/codex-notify-hook +62 -0
  303. package/sync-sources/hooks/gh-issue-trailer-hook +4 -0
  304. package/sync-sources/hooks/gh-issue-trailer-hook.d.ts +7 -0
  305. package/sync-sources/hooks/gh-issue-trailer-hook.d.ts.map +1 -0
  306. package/sync-sources/hooks/gh-issue-trailer-hook.js +239 -0
  307. package/sync-sources/hooks/gh-issue-trailer-hook.js.map +1 -0
  308. package/sync-sources/hooks/gh-issue-trailer-hook.ts +296 -0
  309. package/sync-sources/hooks/git-hooks/post-checkout +109 -0
  310. package/sync-sources/hooks/heartbeat-hook +113 -0
  311. package/sync-sources/hooks/notification-hook +70 -0
  312. package/sync-sources/hooks/notify-complete +79 -0
  313. package/sync-sources/hooks/pan-hook-lib.sh +153 -0
  314. package/sync-sources/hooks/permission-event-hook +24 -0
  315. package/sync-sources/hooks/post-compact-hook +25 -0
  316. package/sync-sources/hooks/pre-compact-hook +26 -0
  317. package/sync-sources/hooks/pre-tool-hook +35 -0
  318. package/sync-sources/hooks/record-cost-event.d.ts +1 -0
  319. package/sync-sources/hooks/record-cost-event.js +31859 -0
  320. package/sync-sources/hooks/record-cost-event.js.map +1 -0
  321. package/sync-sources/hooks/record-cost-event.ts +267 -0
  322. package/sync-sources/hooks/rtk-bash-filter +152 -0
  323. package/sync-sources/hooks/session-start-hook +80 -0
  324. package/sync-sources/hooks/specialist-stop-hook +106 -0
  325. package/sync-sources/hooks/statusline.sh +169 -0
  326. package/sync-sources/hooks/stop-hook +217 -0
  327. package/sync-sources/hooks/tldr-post-edit +72 -0
  328. package/sync-sources/hooks/tldr-read-enforcer +340 -0
  329. package/sync-sources/hooks/tmux-send-keys-guard +87 -0
  330. package/sync-sources/hooks/tsdown.config.ts +14 -0
  331. package/sync-sources/hooks/user-prompt-submit-hook +57 -0
  332. package/sync-sources/hooks/work-agent-stop-hook +424 -0
  333. package/server/auto-commit-CGQlb4rV.js +0 -3
  334. package/server/bd-mutex-BItGvLQ8.js +0 -35
  335. package/server/beads-restore-CMbDSB5R.js +0 -60
  336. package/server/cli-Ch_BWJB2.js +0 -3
  337. package/server/deacon-swarm-CSWGRfzr.js +0 -2
  338. package/server/dist-BUplB3Od.js +0 -3
  339. package/server/forge-IgLR0Ist.js +0 -2
  340. package/server/hume-x9voULey.js +0 -3
  341. package/server/inspect-agent-CYoWAeeB.js +0 -2
  342. package/server/merge-agent-CfohxFWM.js +0 -3
  343. package/server/merge-ops-Czke6UAp.js +0 -2
  344. package/server/projects-Bqfwuqet.js +0 -3
  345. package/server/providers-JjK6izqX.js +0 -3
  346. package/server/public/assets/index-CZiADHcL.css +0 -1
  347. package/server/public/assets/index-LQIfhmgI.js +0 -796
  348. package/server/public/assets/mermaid-GHXKKRXX-CZOsRYoT.js +0 -1
  349. package/server/rally-Ba9QoNYc.js +0 -3
  350. package/server/record-De2syxNB.js +0 -3
  351. package/server/resume-CmxF3Kjz.js +0 -3
  352. package/server/settings-DEhYpW0k.js +0 -2
  353. package/server/specs-CVH1kvz3.js +0 -3
  354. package/server/supervisor-DQMK2e3I.js +0 -2
  355. package/server/workflows-pKw9yRSV.js +0 -2
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { basename, join } from "node:path";
4
+ import { homedir, tmpdir } from "node:os";
5
+ import { fileURLToPath } from "node:url";
6
+ //#region gh-issue-trailer-hook.ts
7
+ const FLYWHEEL_RUN_ID_PATTERN = /^RUN-\d+$/;
8
+ function shellQuote(value) {
9
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
10
+ }
11
+ function tokenize(command) {
12
+ const tokens = [];
13
+ let text = "";
14
+ let start = -1;
15
+ let quote = null;
16
+ let escape = false;
17
+ const flush = (end) => {
18
+ if (start !== -1) tokens.push({
19
+ text,
20
+ start,
21
+ end
22
+ });
23
+ text = "";
24
+ start = -1;
25
+ };
26
+ for (let index = 0; index < command.length; index += 1) {
27
+ const char = command[index];
28
+ if (start === -1 && !/\s/.test(char)) start = index;
29
+ if (escape) {
30
+ text += char;
31
+ escape = false;
32
+ continue;
33
+ }
34
+ if (quote === "single") {
35
+ if (char === "'") quote = null;
36
+ else text += char;
37
+ continue;
38
+ }
39
+ if (quote === "double") {
40
+ if (char === "\"") quote = null;
41
+ else if (char === "\\") escape = true;
42
+ else text += char;
43
+ continue;
44
+ }
45
+ if (/\s/.test(char)) {
46
+ flush(index);
47
+ continue;
48
+ }
49
+ if (char === "'") {
50
+ quote = "single";
51
+ continue;
52
+ }
53
+ if (char === "\"") {
54
+ quote = "double";
55
+ continue;
56
+ }
57
+ if (char === "\\") {
58
+ escape = true;
59
+ continue;
60
+ }
61
+ text += char;
62
+ }
63
+ flush(command.length);
64
+ return tokens;
65
+ }
66
+ function findGhIssueCreate(tokens) {
67
+ for (let index = 0; index <= tokens.length - 3; index += 1) if (tokens[index].text === "gh" && tokens[index + 1].text === "issue" && tokens[index + 2].text === "create") return index;
68
+ return -1;
69
+ }
70
+ function containsFlywheelRunId(body) {
71
+ return /^Flywheel-Run-Id:/m.test(body);
72
+ }
73
+ function resolveDiscoveredIn(env) {
74
+ const agentId = env.OVERDECK_AGENT_ID;
75
+ if (!agentId || basename(agentId) !== agentId) return null;
76
+ const statePath = join(env.OVERDECK_HOME ?? join(env.HOME ?? homedir(), ".overdeck"), "agents", agentId, "state.json");
77
+ if (!existsSync(statePath)) return null;
78
+ try {
79
+ const parsed = JSON.parse(readFileSync(statePath, "utf-8"));
80
+ return typeof parsed.issueId === "string" && parsed.issueId.trim() ? parsed.issueId.trim() : null;
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+ function buildTrailer(env) {
86
+ const runId = env.OVERDECK_FLYWHEEL_RUN_ID?.trim();
87
+ if (!runId || !FLYWHEEL_RUN_ID_PATTERN.test(runId)) return null;
88
+ const filedBy = env.OVERDECK_FLYWHEEL_AGENT_ROLE === "flywheel" ? "agent" : "operator";
89
+ const discoveredIn = resolveDiscoveredIn(env);
90
+ return `\n\n${[
91
+ "---",
92
+ `Flywheel-Run-Id: ${runId}`,
93
+ `Flywheel-Filed-By: ${filedBy}`,
94
+ discoveredIn ? `Flywheel-Discovered-In: ${discoveredIn}` : void 0
95
+ ].filter((line) => line !== void 0).join("\n")}\n`;
96
+ }
97
+ function replaceSpan(command, start, end, replacement) {
98
+ return `${command.slice(0, start)}${replacement}${command.slice(end)}`;
99
+ }
100
+ function findFlag(tokens, ghIndex, flagName) {
101
+ for (let index = ghIndex + 3; index < tokens.length; index += 1) {
102
+ const token = tokens[index];
103
+ if (token.text === flagName) return {
104
+ flag: token,
105
+ value: tokens[index + 1],
106
+ inline: false
107
+ };
108
+ if (token.text.startsWith(`${flagName}=`)) return {
109
+ flag: token,
110
+ inline: true
111
+ };
112
+ }
113
+ return null;
114
+ }
115
+ function inlineFlagValue(flag) {
116
+ const equalsOffset = flag.text.indexOf("=");
117
+ if (equalsOffset === -1) return null;
118
+ const valueStart = flag.start + equalsOffset + 1;
119
+ return {
120
+ value: flag.text.slice(equalsOffset + 1),
121
+ valueStart,
122
+ valueEnd: flag.end
123
+ };
124
+ }
125
+ function appendBodyFlag(command, tokens, ghIndex, trailer) {
126
+ const bodyFlag = findFlag(tokens, ghIndex, "--body");
127
+ if (!bodyFlag) return null;
128
+ const body = bodyFlag.inline ? inlineFlagValue(bodyFlag.flag) : bodyFlag.value;
129
+ if (!body) return null;
130
+ const bodyText = "text" in body ? body.text : body.value;
131
+ if (containsFlywheelRunId(bodyText)) return command;
132
+ const appended = `${bodyText}${trailer}`;
133
+ if ("text" in body) return replaceSpan(command, body.start, body.end, shellQuote(appended));
134
+ return replaceSpan(command, body.valueStart, body.valueEnd, shellQuote(appended));
135
+ }
136
+ function writeTempBodyFile(body) {
137
+ const path = join(mkdtempSync(join(tmpdir(), "pan-gh-issue-body-")), "body.md");
138
+ writeFileSync(path, body, "utf-8");
139
+ return path;
140
+ }
141
+ function appendBodyFileFlag(command, tokens, ghIndex, trailer) {
142
+ const bodyFileFlag = findFlag(tokens, ghIndex, "--body-file");
143
+ if (!bodyFileFlag) return null;
144
+ const pathValue = bodyFileFlag.inline ? inlineFlagValue(bodyFileFlag.flag) : bodyFileFlag.value;
145
+ if (!pathValue) return null;
146
+ const pathText = "text" in pathValue ? pathValue.text : pathValue.value;
147
+ if (pathText === "-") return appendStdinBody(command, tokens[ghIndex].start, trailer);
148
+ if (!existsSync(pathText)) return null;
149
+ const body = readFileSync(pathText, "utf-8");
150
+ if (containsFlywheelRunId(body)) return command;
151
+ const tempPath = writeTempBodyFile(`${body}${trailer}`);
152
+ if ("text" in pathValue) return replaceSpan(command, pathValue.start, pathValue.end, shellQuote(tempPath));
153
+ return replaceSpan(command, pathValue.valueStart, pathValue.valueEnd, shellQuote(tempPath));
154
+ }
155
+ function findPipeBefore(command, beforeIndex) {
156
+ let quote = null;
157
+ let escape = false;
158
+ let pipeIndex = -1;
159
+ for (let index = 0; index < beforeIndex; index += 1) {
160
+ const char = command[index];
161
+ if (escape) {
162
+ escape = false;
163
+ continue;
164
+ }
165
+ if (quote === "single") {
166
+ if (char === "'") quote = null;
167
+ continue;
168
+ }
169
+ if (quote === "double") {
170
+ if (char === "\"") quote = null;
171
+ else if (char === "\\") escape = true;
172
+ continue;
173
+ }
174
+ if (char === "'") quote = "single";
175
+ else if (char === "\"") quote = "double";
176
+ else if (char === "\\") escape = true;
177
+ else if (char === "|" && command[index + 1] !== "|" && command[index - 1] !== "|") pipeIndex = index;
178
+ }
179
+ return pipeIndex;
180
+ }
181
+ function appendHereDoc(command, trailer) {
182
+ const match = command.match(/(<<-?\s*['"]?([A-Za-z_][A-Za-z0-9_-]*)['"]?[^\n]*\n)([\s\S]*?)(\n\2\s*)$/);
183
+ if (!match || match.index === void 0) return null;
184
+ const body = match[3];
185
+ if (containsFlywheelRunId(body)) return command;
186
+ const bodyStart = match.index + match[1].length;
187
+ return replaceSpan(command, bodyStart, bodyStart + body.length, `${body}${trailer}`);
188
+ }
189
+ function appendStdinBody(command, ghStart, trailer) {
190
+ if (command.includes("Flywheel-Run-Id:")) return command;
191
+ const hereDoc = appendHereDoc(command, trailer);
192
+ if (hereDoc) return hereDoc;
193
+ const pipeIndex = findPipeBefore(command, ghStart);
194
+ if (pipeIndex === -1) return null;
195
+ const producer = command.slice(0, pipeIndex).trim();
196
+ const consumer = command.slice(pipeIndex + 1).trimStart();
197
+ if (!producer || !consumer) return null;
198
+ return `{ ${producer}; printf %s ${shellQuote(trailer)}; } | ${consumer}`;
199
+ }
200
+ function buildUpdatedInput(command) {
201
+ return { hookSpecificOutput: {
202
+ hookEventName: "PreToolUse",
203
+ permissionDecisionReason: "Flywheel issue provenance trailers added",
204
+ updatedInput: { command }
205
+ } };
206
+ }
207
+ function rewriteGhIssueCreateCommand(command, env = process.env) {
208
+ const trailer = buildTrailer(env);
209
+ if (!trailer) return null;
210
+ const tokens = tokenize(command);
211
+ const ghIndex = findGhIssueCreate(tokens);
212
+ if (ghIndex === -1) return null;
213
+ const bodyCommand = appendBodyFlag(command, tokens, ghIndex, trailer);
214
+ if (bodyCommand !== null) return bodyCommand === command ? null : bodyCommand;
215
+ const bodyFileCommand = appendBodyFileFlag(command, tokens, ghIndex, trailer);
216
+ if (bodyFileCommand !== null) return bodyFileCommand === command ? null : bodyFileCommand;
217
+ return null;
218
+ }
219
+ function runGhIssueTrailerHook(input, env = process.env) {
220
+ let event;
221
+ try {
222
+ event = JSON.parse(input);
223
+ } catch {
224
+ return "{}";
225
+ }
226
+ const command = event.tool_name === "Bash" ? event.tool_input?.command : void 0;
227
+ if (typeof command !== "string") return "{}";
228
+ const updatedCommand = rewriteGhIssueCreateCommand(command, env);
229
+ if (!updatedCommand) return "{}";
230
+ return JSON.stringify(buildUpdatedInput(updatedCommand));
231
+ }
232
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
233
+ const input = readFileSync(0, "utf-8");
234
+ process.stdout.write(runGhIssueTrailerHook(input));
235
+ }
236
+ //#endregion
237
+ export { rewriteGhIssueCreateCommand, runGhIssueTrailerHook };
238
+
239
+ //# sourceMappingURL=gh-issue-trailer-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh-issue-trailer-hook.js","names":[],"sources":["gh-issue-trailer-hook.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir, tmpdir } from 'node:os';\nimport { basename, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\ninterface PreToolUseEvent {\n tool_name?: string;\n tool_input?: {\n command?: string;\n [key: string]: unknown;\n };\n}\n\ninterface Token {\n text: string;\n start: number;\n end: number;\n}\n\ntype HookEnv = Record<string, string | undefined>;\n\nconst FLYWHEEL_RUN_ID_PATTERN = /^RUN-\\d+$/;\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\"'\"'`)}'`;\n}\n\nfunction tokenize(command: string): Token[] {\n const tokens: Token[] = [];\n let text = '';\n let start = -1;\n let quote: 'single' | 'double' | null = null;\n let escape = false;\n\n const flush = (end: number): void => {\n if (start !== -1) tokens.push({ text, start, end });\n text = '';\n start = -1;\n };\n\n for (let index = 0; index < command.length; index += 1) {\n const char = command[index];\n if (start === -1 && !/\\s/.test(char)) start = index;\n\n if (escape) {\n text += char;\n escape = false;\n continue;\n }\n\n if (quote === 'single') {\n if (char === \"'\") quote = null;\n else text += char;\n continue;\n }\n\n if (quote === 'double') {\n if (char === '\"') quote = null;\n else if (char === '\\\\') escape = true;\n else text += char;\n continue;\n }\n\n if (/\\s/.test(char)) {\n flush(index);\n continue;\n }\n\n if (char === \"'\") {\n quote = 'single';\n continue;\n }\n\n if (char === '\"') {\n quote = 'double';\n continue;\n }\n\n if (char === '\\\\') {\n escape = true;\n continue;\n }\n\n text += char;\n }\n\n flush(command.length);\n return tokens;\n}\n\nfunction findGhIssueCreate(tokens: Token[]): number {\n for (let index = 0; index <= tokens.length - 3; index += 1) {\n if (tokens[index].text === 'gh' && tokens[index + 1].text === 'issue' && tokens[index + 2].text === 'create') {\n return index;\n }\n }\n return -1;\n}\n\nfunction containsFlywheelRunId(body: string): boolean {\n return /^Flywheel-Run-Id:/m.test(body);\n}\n\nfunction resolveDiscoveredIn(env: HookEnv): string | null {\n const agentId = env.OVERDECK_AGENT_ID;\n if (!agentId || basename(agentId) !== agentId) return null;\n\n const overdeckHome = env.OVERDECK_HOME ?? join(env.HOME ?? homedir(), '.overdeck');\n const statePath = join(overdeckHome, 'agents', agentId, 'state.json');\n if (!existsSync(statePath)) return null;\n\n try {\n const parsed = JSON.parse(readFileSync(statePath, 'utf-8')) as { issueId?: unknown };\n return typeof parsed.issueId === 'string' && parsed.issueId.trim() ? parsed.issueId.trim() : null;\n } catch {\n return null;\n }\n}\n\nfunction buildTrailer(env: HookEnv): string | null {\n const runId = env.OVERDECK_FLYWHEEL_RUN_ID?.trim();\n if (!runId || !FLYWHEEL_RUN_ID_PATTERN.test(runId)) return null;\n\n const filedBy = env.OVERDECK_FLYWHEEL_AGENT_ROLE === 'flywheel' ? 'agent' : 'operator';\n const discoveredIn = resolveDiscoveredIn(env);\n const lines = [\n '---',\n `Flywheel-Run-Id: ${runId}`,\n `Flywheel-Filed-By: ${filedBy}`,\n discoveredIn ? `Flywheel-Discovered-In: ${discoveredIn}` : undefined,\n ].filter((line): line is string => line !== undefined);\n return `\\n\\n${lines.join('\\n')}\\n`;\n}\n\nfunction replaceSpan(command: string, start: number, end: number, replacement: string): string {\n return `${command.slice(0, start)}${replacement}${command.slice(end)}`;\n}\n\nfunction findFlag(tokens: Token[], ghIndex: number, flagName: '--body' | '--body-file'): { flag: Token; value?: Token; inline: boolean } | null {\n for (let index = ghIndex + 3; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (token.text === flagName) return { flag: token, value: tokens[index + 1], inline: false };\n if (token.text.startsWith(`${flagName}=`)) return { flag: token, inline: true };\n }\n return null;\n}\n\nfunction inlineFlagValue(flag: Token): { value: string; valueStart: number; valueEnd: number } | null {\n const equalsOffset = flag.text.indexOf('=');\n if (equalsOffset === -1) return null;\n const valueStart = flag.start + equalsOffset + 1;\n return { value: flag.text.slice(equalsOffset + 1), valueStart, valueEnd: flag.end };\n}\n\nfunction appendBodyFlag(command: string, tokens: Token[], ghIndex: number, trailer: string): string | null {\n const bodyFlag = findFlag(tokens, ghIndex, '--body');\n if (!bodyFlag) return null;\n\n const body = bodyFlag.inline ? inlineFlagValue(bodyFlag.flag) : bodyFlag.value;\n if (!body) return null;\n const bodyText = 'text' in body ? body.text : body.value;\n if (containsFlywheelRunId(bodyText)) return command;\n\n const appended = `${bodyText}${trailer}`;\n if ('text' in body) return replaceSpan(command, body.start, body.end, shellQuote(appended));\n return replaceSpan(command, body.valueStart, body.valueEnd, shellQuote(appended));\n}\n\nfunction writeTempBodyFile(body: string): string {\n const dir = mkdtempSync(join(tmpdir(), 'pan-gh-issue-body-'));\n const path = join(dir, 'body.md');\n writeFileSync(path, body, 'utf-8');\n return path;\n}\n\nfunction appendBodyFileFlag(command: string, tokens: Token[], ghIndex: number, trailer: string): string | null {\n const bodyFileFlag = findFlag(tokens, ghIndex, '--body-file');\n if (!bodyFileFlag) return null;\n\n const pathValue = bodyFileFlag.inline ? inlineFlagValue(bodyFileFlag.flag) : bodyFileFlag.value;\n if (!pathValue) return null;\n const pathText = 'text' in pathValue ? pathValue.text : pathValue.value;\n\n if (pathText === '-') return appendStdinBody(command, tokens[ghIndex].start, trailer);\n if (!existsSync(pathText)) return null;\n\n const body = readFileSync(pathText, 'utf-8');\n if (containsFlywheelRunId(body)) return command;\n\n const tempPath = writeTempBodyFile(`${body}${trailer}`);\n if ('text' in pathValue) return replaceSpan(command, pathValue.start, pathValue.end, shellQuote(tempPath));\n return replaceSpan(command, pathValue.valueStart, pathValue.valueEnd, shellQuote(tempPath));\n}\n\nfunction findPipeBefore(command: string, beforeIndex: number): number {\n let quote: 'single' | 'double' | null = null;\n let escape = false;\n let pipeIndex = -1;\n\n for (let index = 0; index < beforeIndex; index += 1) {\n const char = command[index];\n if (escape) {\n escape = false;\n continue;\n }\n if (quote === 'single') {\n if (char === \"'\") quote = null;\n continue;\n }\n if (quote === 'double') {\n if (char === '\"') quote = null;\n else if (char === '\\\\') escape = true;\n continue;\n }\n if (char === \"'\") quote = 'single';\n else if (char === '\"') quote = 'double';\n else if (char === '\\\\') escape = true;\n else if (char === '|' && command[index + 1] !== '|' && command[index - 1] !== '|') pipeIndex = index;\n }\n\n return pipeIndex;\n}\n\nfunction appendHereDoc(command: string, trailer: string): string | null {\n const match = command.match(/(<<-?\\s*['\"]?([A-Za-z_][A-Za-z0-9_-]*)['\"]?[^\\n]*\\n)([\\s\\S]*?)(\\n\\2\\s*)$/);\n if (!match || match.index === undefined) return null;\n const body = match[3];\n if (containsFlywheelRunId(body)) return command;\n const bodyStart = match.index + match[1].length;\n const bodyEnd = bodyStart + body.length;\n return replaceSpan(command, bodyStart, bodyEnd, `${body}${trailer}`);\n}\n\nfunction appendStdinBody(command: string, ghStart: number, trailer: string): string | null {\n if (command.includes('Flywheel-Run-Id:')) return command;\n\n const hereDoc = appendHereDoc(command, trailer);\n if (hereDoc) return hereDoc;\n\n const pipeIndex = findPipeBefore(command, ghStart);\n if (pipeIndex === -1) return null;\n\n const producer = command.slice(0, pipeIndex).trim();\n const consumer = command.slice(pipeIndex + 1).trimStart();\n if (!producer || !consumer) return null;\n return `{ ${producer}; printf %s ${shellQuote(trailer)}; } | ${consumer}`;\n}\n\nfunction buildUpdatedInput(command: string): Record<string, unknown> {\n return {\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecisionReason: 'Flywheel issue provenance trailers added',\n updatedInput: { command },\n },\n };\n}\n\nexport function rewriteGhIssueCreateCommand(command: string, env: HookEnv = process.env): string | null {\n const trailer = buildTrailer(env);\n if (!trailer) return null;\n\n const tokens = tokenize(command);\n const ghIndex = findGhIssueCreate(tokens);\n if (ghIndex === -1) return null;\n\n const bodyCommand = appendBodyFlag(command, tokens, ghIndex, trailer);\n if (bodyCommand !== null) return bodyCommand === command ? null : bodyCommand;\n\n const bodyFileCommand = appendBodyFileFlag(command, tokens, ghIndex, trailer);\n if (bodyFileCommand !== null) return bodyFileCommand === command ? null : bodyFileCommand;\n\n return null;\n}\n\nexport function runGhIssueTrailerHook(input: string, env: HookEnv = process.env): string {\n let event: PreToolUseEvent;\n try {\n event = JSON.parse(input) as PreToolUseEvent;\n } catch {\n return '{}';\n }\n\n const command = event.tool_name === 'Bash' ? event.tool_input?.command : undefined;\n if (typeof command !== 'string') return '{}';\n\n const updatedCommand = rewriteGhIssueCreateCommand(command, env);\n if (!updatedCommand) return '{}';\n return JSON.stringify(buildUpdatedInput(updatedCommand));\n}\n\nif (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {\n const input = readFileSync(0, 'utf-8');\n process.stdout.write(runGhIssueTrailerHook(input));\n}\n"],"mappings":"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"}
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir, tmpdir } from 'node:os';
4
+ import { basename, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ interface PreToolUseEvent {
8
+ tool_name?: string;
9
+ tool_input?: {
10
+ command?: string;
11
+ [key: string]: unknown;
12
+ };
13
+ }
14
+
15
+ interface Token {
16
+ text: string;
17
+ start: number;
18
+ end: number;
19
+ }
20
+
21
+ type HookEnv = Record<string, string | undefined>;
22
+
23
+ const FLYWHEEL_RUN_ID_PATTERN = /^RUN-\d+$/;
24
+
25
+ function shellQuote(value: string): string {
26
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
27
+ }
28
+
29
+ function tokenize(command: string): Token[] {
30
+ const tokens: Token[] = [];
31
+ let text = '';
32
+ let start = -1;
33
+ let quote: 'single' | 'double' | null = null;
34
+ let escape = false;
35
+
36
+ const flush = (end: number): void => {
37
+ if (start !== -1) tokens.push({ text, start, end });
38
+ text = '';
39
+ start = -1;
40
+ };
41
+
42
+ for (let index = 0; index < command.length; index += 1) {
43
+ const char = command[index];
44
+ if (start === -1 && !/\s/.test(char)) start = index;
45
+
46
+ if (escape) {
47
+ text += char;
48
+ escape = false;
49
+ continue;
50
+ }
51
+
52
+ if (quote === 'single') {
53
+ if (char === "'") quote = null;
54
+ else text += char;
55
+ continue;
56
+ }
57
+
58
+ if (quote === 'double') {
59
+ if (char === '"') quote = null;
60
+ else if (char === '\\') escape = true;
61
+ else text += char;
62
+ continue;
63
+ }
64
+
65
+ if (/\s/.test(char)) {
66
+ flush(index);
67
+ continue;
68
+ }
69
+
70
+ if (char === "'") {
71
+ quote = 'single';
72
+ continue;
73
+ }
74
+
75
+ if (char === '"') {
76
+ quote = 'double';
77
+ continue;
78
+ }
79
+
80
+ if (char === '\\') {
81
+ escape = true;
82
+ continue;
83
+ }
84
+
85
+ text += char;
86
+ }
87
+
88
+ flush(command.length);
89
+ return tokens;
90
+ }
91
+
92
+ function findGhIssueCreate(tokens: Token[]): number {
93
+ for (let index = 0; index <= tokens.length - 3; index += 1) {
94
+ if (tokens[index].text === 'gh' && tokens[index + 1].text === 'issue' && tokens[index + 2].text === 'create') {
95
+ return index;
96
+ }
97
+ }
98
+ return -1;
99
+ }
100
+
101
+ function containsFlywheelRunId(body: string): boolean {
102
+ return /^Flywheel-Run-Id:/m.test(body);
103
+ }
104
+
105
+ function resolveDiscoveredIn(env: HookEnv): string | null {
106
+ const agentId = env.OVERDECK_AGENT_ID;
107
+ if (!agentId || basename(agentId) !== agentId) return null;
108
+
109
+ const overdeckHome = env.OVERDECK_HOME ?? join(env.HOME ?? homedir(), '.overdeck');
110
+ const statePath = join(overdeckHome, 'agents', agentId, 'state.json');
111
+ if (!existsSync(statePath)) return null;
112
+
113
+ try {
114
+ const parsed = JSON.parse(readFileSync(statePath, 'utf-8')) as { issueId?: unknown };
115
+ return typeof parsed.issueId === 'string' && parsed.issueId.trim() ? parsed.issueId.trim() : null;
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+
121
+ function buildTrailer(env: HookEnv): string | null {
122
+ const runId = env.OVERDECK_FLYWHEEL_RUN_ID?.trim();
123
+ if (!runId || !FLYWHEEL_RUN_ID_PATTERN.test(runId)) return null;
124
+
125
+ const filedBy = env.OVERDECK_FLYWHEEL_AGENT_ROLE === 'flywheel' ? 'agent' : 'operator';
126
+ const discoveredIn = resolveDiscoveredIn(env);
127
+ const lines = [
128
+ '---',
129
+ `Flywheel-Run-Id: ${runId}`,
130
+ `Flywheel-Filed-By: ${filedBy}`,
131
+ discoveredIn ? `Flywheel-Discovered-In: ${discoveredIn}` : undefined,
132
+ ].filter((line): line is string => line !== undefined);
133
+ return `\n\n${lines.join('\n')}\n`;
134
+ }
135
+
136
+ function replaceSpan(command: string, start: number, end: number, replacement: string): string {
137
+ return `${command.slice(0, start)}${replacement}${command.slice(end)}`;
138
+ }
139
+
140
+ function findFlag(tokens: Token[], ghIndex: number, flagName: '--body' | '--body-file'): { flag: Token; value?: Token; inline: boolean } | null {
141
+ for (let index = ghIndex + 3; index < tokens.length; index += 1) {
142
+ const token = tokens[index];
143
+ if (token.text === flagName) return { flag: token, value: tokens[index + 1], inline: false };
144
+ if (token.text.startsWith(`${flagName}=`)) return { flag: token, inline: true };
145
+ }
146
+ return null;
147
+ }
148
+
149
+ function inlineFlagValue(flag: Token): { value: string; valueStart: number; valueEnd: number } | null {
150
+ const equalsOffset = flag.text.indexOf('=');
151
+ if (equalsOffset === -1) return null;
152
+ const valueStart = flag.start + equalsOffset + 1;
153
+ return { value: flag.text.slice(equalsOffset + 1), valueStart, valueEnd: flag.end };
154
+ }
155
+
156
+ function appendBodyFlag(command: string, tokens: Token[], ghIndex: number, trailer: string): string | null {
157
+ const bodyFlag = findFlag(tokens, ghIndex, '--body');
158
+ if (!bodyFlag) return null;
159
+
160
+ const body = bodyFlag.inline ? inlineFlagValue(bodyFlag.flag) : bodyFlag.value;
161
+ if (!body) return null;
162
+ const bodyText = 'text' in body ? body.text : body.value;
163
+ if (containsFlywheelRunId(bodyText)) return command;
164
+
165
+ const appended = `${bodyText}${trailer}`;
166
+ if ('text' in body) return replaceSpan(command, body.start, body.end, shellQuote(appended));
167
+ return replaceSpan(command, body.valueStart, body.valueEnd, shellQuote(appended));
168
+ }
169
+
170
+ function writeTempBodyFile(body: string): string {
171
+ const dir = mkdtempSync(join(tmpdir(), 'pan-gh-issue-body-'));
172
+ const path = join(dir, 'body.md');
173
+ writeFileSync(path, body, 'utf-8');
174
+ return path;
175
+ }
176
+
177
+ function appendBodyFileFlag(command: string, tokens: Token[], ghIndex: number, trailer: string): string | null {
178
+ const bodyFileFlag = findFlag(tokens, ghIndex, '--body-file');
179
+ if (!bodyFileFlag) return null;
180
+
181
+ const pathValue = bodyFileFlag.inline ? inlineFlagValue(bodyFileFlag.flag) : bodyFileFlag.value;
182
+ if (!pathValue) return null;
183
+ const pathText = 'text' in pathValue ? pathValue.text : pathValue.value;
184
+
185
+ if (pathText === '-') return appendStdinBody(command, tokens[ghIndex].start, trailer);
186
+ if (!existsSync(pathText)) return null;
187
+
188
+ const body = readFileSync(pathText, 'utf-8');
189
+ if (containsFlywheelRunId(body)) return command;
190
+
191
+ const tempPath = writeTempBodyFile(`${body}${trailer}`);
192
+ if ('text' in pathValue) return replaceSpan(command, pathValue.start, pathValue.end, shellQuote(tempPath));
193
+ return replaceSpan(command, pathValue.valueStart, pathValue.valueEnd, shellQuote(tempPath));
194
+ }
195
+
196
+ function findPipeBefore(command: string, beforeIndex: number): number {
197
+ let quote: 'single' | 'double' | null = null;
198
+ let escape = false;
199
+ let pipeIndex = -1;
200
+
201
+ for (let index = 0; index < beforeIndex; index += 1) {
202
+ const char = command[index];
203
+ if (escape) {
204
+ escape = false;
205
+ continue;
206
+ }
207
+ if (quote === 'single') {
208
+ if (char === "'") quote = null;
209
+ continue;
210
+ }
211
+ if (quote === 'double') {
212
+ if (char === '"') quote = null;
213
+ else if (char === '\\') escape = true;
214
+ continue;
215
+ }
216
+ if (char === "'") quote = 'single';
217
+ else if (char === '"') quote = 'double';
218
+ else if (char === '\\') escape = true;
219
+ else if (char === '|' && command[index + 1] !== '|' && command[index - 1] !== '|') pipeIndex = index;
220
+ }
221
+
222
+ return pipeIndex;
223
+ }
224
+
225
+ function appendHereDoc(command: string, trailer: string): string | null {
226
+ const match = command.match(/(<<-?\s*['"]?([A-Za-z_][A-Za-z0-9_-]*)['"]?[^\n]*\n)([\s\S]*?)(\n\2\s*)$/);
227
+ if (!match || match.index === undefined) return null;
228
+ const body = match[3];
229
+ if (containsFlywheelRunId(body)) return command;
230
+ const bodyStart = match.index + match[1].length;
231
+ const bodyEnd = bodyStart + body.length;
232
+ return replaceSpan(command, bodyStart, bodyEnd, `${body}${trailer}`);
233
+ }
234
+
235
+ function appendStdinBody(command: string, ghStart: number, trailer: string): string | null {
236
+ if (command.includes('Flywheel-Run-Id:')) return command;
237
+
238
+ const hereDoc = appendHereDoc(command, trailer);
239
+ if (hereDoc) return hereDoc;
240
+
241
+ const pipeIndex = findPipeBefore(command, ghStart);
242
+ if (pipeIndex === -1) return null;
243
+
244
+ const producer = command.slice(0, pipeIndex).trim();
245
+ const consumer = command.slice(pipeIndex + 1).trimStart();
246
+ if (!producer || !consumer) return null;
247
+ return `{ ${producer}; printf %s ${shellQuote(trailer)}; } | ${consumer}`;
248
+ }
249
+
250
+ function buildUpdatedInput(command: string): Record<string, unknown> {
251
+ return {
252
+ hookSpecificOutput: {
253
+ hookEventName: 'PreToolUse',
254
+ permissionDecisionReason: 'Flywheel issue provenance trailers added',
255
+ updatedInput: { command },
256
+ },
257
+ };
258
+ }
259
+
260
+ export function rewriteGhIssueCreateCommand(command: string, env: HookEnv = process.env): string | null {
261
+ const trailer = buildTrailer(env);
262
+ if (!trailer) return null;
263
+
264
+ const tokens = tokenize(command);
265
+ const ghIndex = findGhIssueCreate(tokens);
266
+ if (ghIndex === -1) return null;
267
+
268
+ const bodyCommand = appendBodyFlag(command, tokens, ghIndex, trailer);
269
+ if (bodyCommand !== null) return bodyCommand === command ? null : bodyCommand;
270
+
271
+ const bodyFileCommand = appendBodyFileFlag(command, tokens, ghIndex, trailer);
272
+ if (bodyFileCommand !== null) return bodyFileCommand === command ? null : bodyFileCommand;
273
+
274
+ return null;
275
+ }
276
+
277
+ export function runGhIssueTrailerHook(input: string, env: HookEnv = process.env): string {
278
+ let event: PreToolUseEvent;
279
+ try {
280
+ event = JSON.parse(input) as PreToolUseEvent;
281
+ } catch {
282
+ return '{}';
283
+ }
284
+
285
+ const command = event.tool_name === 'Bash' ? event.tool_input?.command : undefined;
286
+ if (typeof command !== 'string') return '{}';
287
+
288
+ const updatedCommand = rewriteGhIssueCreateCommand(command, env);
289
+ if (!updatedCommand) return '{}';
290
+ return JSON.stringify(buildUpdatedInput(updatedCommand));
291
+ }
292
+
293
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
294
+ const input = readFileSync(0, 'utf-8');
295
+ process.stdout.write(runGhIssueTrailerHook(input));
296
+ }
@@ -0,0 +1,109 @@
1
+ #!/bin/bash
2
+ #
3
+ # Git post-checkout hook to guard main project directories
4
+ # Prevents accidental checkouts away from main in the primary project directory
5
+ #
6
+ # Install: ln -sf /path/to/overdeck/scripts/git-hooks/post-checkout .git/hooks/post-checkout
7
+ # Or: pan projects add /path/to/project (auto-installs hooks)
8
+ #
9
+ # This hook only activates in the MAIN project directory (not worktrees).
10
+ # Worktrees can freely checkout any branch - that's their purpose.
11
+ #
12
+ # Auto-revert behavior:
13
+ # - Agents (OVERDECK_AGENT=1): Always auto-revert (agents should never checkout in main)
14
+ # - Manual (OVERDECK_AUTO_REVERT_CHECKOUT=1): Opt-in auto-revert for humans
15
+ # - Default: Warn only, don't revert
16
+
17
+ # Get the previous and new HEAD refs
18
+ PREV_HEAD=$1
19
+ NEW_HEAD=$2
20
+ IS_BRANCH_CHECKOUT=$3 # 1 if branch checkout, 0 if file checkout
21
+
22
+ # Only care about branch checkouts
23
+ if [ "$IS_BRANCH_CHECKOUT" != "1" ]; then
24
+ exit 0
25
+ fi
26
+
27
+ # Check if we're in a worktree (worktrees have .git as a file, not directory)
28
+ if [ -f ".git" ]; then
29
+ # This is a worktree - allow any checkout
30
+ exit 0
31
+ fi
32
+
33
+ # Check if .git is a directory (main repo)
34
+ if [ ! -d ".git" ]; then
35
+ # Not a git repo at all
36
+ exit 0
37
+ fi
38
+
39
+ # Get current branch name
40
+ CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
41
+
42
+ # If we're on main or master, all good
43
+ if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
44
+ exit 0
45
+ fi
46
+
47
+ # We're in the main project directory and NOT on main - this is bad!
48
+ echo ""
49
+ echo "╔══════════════════════════════════════════════════════════════════════════════╗"
50
+ echo "║ ⚠️ WARNING: Main project directory is not on 'main' branch! ║"
51
+ echo "╠══════════════════════════════════════════════════════════════════════════════╣"
52
+ echo "║ ║"
53
+ echo "║ Current branch: $CURRENT_BRANCH"
54
+ echo "║ Location: $(pwd)"
55
+ echo "║ ║"
56
+ echo "║ The main project directory should ALWAYS stay on 'main'. ║"
57
+ echo "║ All feature work should happen in workspaces (git worktrees). ║"
58
+ echo "║ ║"
59
+ echo "║ To fix this: ║"
60
+ echo "║ git checkout main ║"
61
+ echo "║ ║"
62
+ echo "║ To work on a feature: ║"
63
+ echo "║ pan workspace create ISSUE-ID ║"
64
+ echo "║ ║"
65
+ echo "╚══════════════════════════════════════════════════════════════════════════════╝"
66
+ echo ""
67
+
68
+ # Auto-revert for agents and planning sessions
69
+ # They should NEVER be checking out in main project
70
+ IS_OVERDECK_SESSION=0
71
+
72
+ # Check explicit environment variables
73
+ if [ "$OVERDECK_AGENT" = "1" ] || [ -n "$OVERDECK_AGENT_ID" ]; then
74
+ IS_OVERDECK_SESSION=1
75
+ fi
76
+
77
+ # Check for planning session
78
+ if [ "$OVERDECK_PLANNING" = "1" ] || [ -n "$OVERDECK_PLANNING_ISSUE" ]; then
79
+ IS_OVERDECK_SESSION=1
80
+ fi
81
+
82
+ # Check tmux session name pattern (agent-* or planning-* or specialist-*)
83
+ if [ -n "$TMUX" ]; then
84
+ TMUX_SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null || true)
85
+ case "$TMUX_SESSION" in
86
+ agent-*|planning-*|specialist-*)
87
+ IS_OVERDECK_SESSION=1
88
+ ;;
89
+ esac
90
+ fi
91
+
92
+ if [ "$IS_OVERDECK_SESSION" = "1" ]; then
93
+ echo "🤖 Overdeck session detected - auto-reverting to main branch..."
94
+ git checkout main --quiet
95
+ echo "✓ Reverted to main branch. Agents/planning must use workspaces for feature branches."
96
+ exit 0
97
+ fi
98
+
99
+ # Check for manual auto-revert environment variable
100
+ if [ "$OVERDECK_AUTO_REVERT_CHECKOUT" = "1" ]; then
101
+ echo "Auto-reverting to main branch (OVERDECK_AUTO_REVERT_CHECKOUT=1)..."
102
+ git checkout main --quiet
103
+ echo "Reverted to main branch."
104
+ exit 0
105
+ fi
106
+
107
+ # Return success but with warning - don't block the checkout
108
+ # The warning is enough for humans to notice
109
+ exit 0