@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.
- package/drizzle/overdeck/0000_overdeck_init.sql +33 -0
- package/package.json +7 -2
- package/server/{acceptance-criteria-1LHgD9vg.js → acceptance-criteria-DHZk2ceV.js} +2 -2
- package/server/{advancing-selfheal-BqsLWqEM.js → advancing-selfheal-CSuZ31bB.js} +5 -5
- package/server/{agent-gc-CUb52n7s.js → agent-gc-D_DxfBHR.js} +3 -3
- package/server/{agent-state-D-moNwhA.js → agent-state-EP2QFV9n.js} +1 -1
- package/server/{agent-state-service-BzESvLOf.js → agent-state-service-BZkvgQf4.js} +5 -5
- package/server/{agent-state-service-BaZH7sXJ.js → agent-state-service-C_2ZAQRP.js} +1 -1
- package/server/{agents-B5LfpLPv.js → agents-CsOzAmRg.js} +145 -78
- package/server/{agents-C4kZ8a2r.js → agents-DCvyTXNU.js} +1 -1
- package/server/{archive-planning-BbZe-8IK.js → archive-planning-CTILQRnV.js} +1 -1
- package/server/{archive-planning-oX1pj6nY.js → archive-planning-DkivCk1A.js} +4 -4
- package/server/{artifact-index-jobs-DGr6bxev.js → artifact-index-jobs-CmsvQ1ey.js} +2 -2
- package/server/auto-commit-QAfsu3Sb.js +3 -0
- package/server/{backlog-auto-trigger-BlJHikOm.js → backlog-auto-trigger-ueRaT8Dy.js} +2 -2
- package/server/{bd-process-lock-i3CYMS-B.js → bd-process-lock-D3S4T4o-.js} +32 -11
- package/server/{beads-DD7fYCl5.js → beads-BGGxrkZ5.js} +96 -196
- package/server/{beads-BNU-fb48.js → beads-CYsLiyIM.js} +1 -1
- package/server/{beads-query-e6oku4P5.js → beads-query-B1DlJVp9.js} +24 -99
- package/server/beads-restore-CyygSmmn.js +23 -0
- package/server/{behavior-CPf0pbOt.js → behavior-Cpj03N4v.js} +3 -3
- package/server/{briefing-freshness-B2FdLwIv.js → briefing-freshness-Kh5V8RTh.js} +4 -4
- package/server/{checkpoint-manager-eFgaMMu4.js → checkpoint-manager-CAFos8e1.js} +1 -1
- package/server/{checkpoint-manager-JryW6MpM.js → checkpoint-manager-fu9oVsR-.js} +2 -2
- package/server/checkpoint-worker.js +1 -1
- package/server/{checkpoints-qvOqvMuC.js → checkpoints-1BqOlS4q.js} +2 -2
- package/server/{checkpoints-BK17oicz.js → checkpoints-DvhGi8P_.js} +1 -1
- package/server/claude-hooks-provision-s_wdpeVj.js +360 -0
- package/server/{claude-permissions-B6bWGh9d.js → claude-permissions-CaWqysff.js} +2 -2
- package/server/{claude-settings-overlay-BPdhTPjo.js → claude-settings-overlay-BbA4FbVI.js} +1 -1
- package/server/{claude-settings-overlay-CRyyKKXQ.js → claude-settings-overlay-CcTf0xzG.js} +2 -2
- package/server/{cli-B75AieAM.js → cli-BwCvCUBK.js} +9 -9
- package/server/cli-ClIQwgMI.js +3 -0
- package/server/{cliproxy-CCzwuSNe.js → cliproxy-BsoNNDAj.js} +2 -2
- package/server/{cliproxy-DOQf66G9.js → cliproxy-C2xC-3vH.js} +1 -1
- package/server/{close-issue-_X3MXJyG.js → close-issue-BleIwP3f.js} +5 -5
- package/server/{closed-issue-reaper-CLR7ClsO.js → closed-issue-reaper-BwUUvjEW.js} +1 -1
- package/server/{closed-issue-reaper-BsUfBSjM.js → closed-issue-reaper-mC5pujeb.js} +102 -30
- package/server/{codex-CY-QRuU9.js → codex-B7DTpwEw.js} +1 -1
- package/server/{codex-B4KEpX5V.js → codex-DW60_5j2.js} +5 -5
- package/server/{compact-beads-cZds2MTr.js → compact-beads-CK_hHEnB.js} +1 -1
- package/server/{compact-beads-uEDccC76.js → compact-beads-DKL54y6j.js} +15 -8
- package/server/{config-DARrpYWA.js → config-BRNQJXhA.js} +1 -1
- package/server/{config-8sjaU6jZ.js → config-ZNnMpIlh.js} +2 -2
- package/server/{config-yaml-ClpGgrf6.js → config-yaml-Dcz-8uit.js} +29 -3
- package/server/{conflict-gate-Ds9_2yjd.js → conflict-gate-DGT8jCLp.js} +1 -1
- package/server/{conflict-gate-BtB_o-Vi.js → conflict-gate-DTje2Ll7.js} +3 -3
- package/server/{context-layers-r2ywnQYA.js → context-layers-BdUCQ-cl.js} +3 -3
- package/server/{context-layers-CRCk1QG7.js → context-layers-ByOWUjUg.js} +2 -2
- package/server/{control-settings-D1B3u6HP.js → control-settings-DwkAeooY.js} +4 -4
- package/server/{conversation-compaction-D86Zo2Ci.js → conversation-compaction-OTYgd6eg.js} +8 -8
- package/server/{conversation-compaction-CZRVk4hg.js → conversation-compaction-WYQZUVDm.js} +1 -1
- package/server/{conversation-service-CuTVU8dV.js → conversation-service-BR1OgvxO.js} +5 -5
- package/server/{conversation-service-CkL-6MuG.js → conversation-service-C_kNZPLx.js} +1 -1
- package/server/{conversations-DfJKBDwB.js → conversations-DG2M8WnT.js} +4 -4
- package/server/{conversations-BmWEluPL.js → conversations-izHvSoAR.js} +1 -1
- package/server/{cost-DAPYN6iA.js → cost-B2mj17iG.js} +3 -3
- package/server/{cost-CCnqa5xK.js → cost-DQ2ajr_m.js} +2 -2
- package/server/{cost-sync-BDL10Nwu.js → cost-sync-gTayEuWP.js} +3 -3
- package/server/{dag-Dv2r5bVD.js → dag-CqTehWpY.js} +3 -3
- package/server/{dangerous-git-ops-Dcw7PbYo.js → dangerous-git-ops-DDWQlPC3.js} +1 -1
- package/server/dashboard-db-worker.js +6 -6
- package/server/{deacon-DYnZH7kC.js → deacon-BqW9HMew.js} +3 -3
- package/server/{deacon-CM0MxbS8.js → deacon-DrKDBeJL.js} +161 -1594
- package/server/{deacon-nudge-log-B-naf89q.js → deacon-nudge-log-D1wEK6zF.js} +1 -1
- package/server/deacon-swarm-BY5hKNaU.js +2 -0
- package/server/{deacon-swarm-BstYoz8Q.js → deacon-swarm-CO_A7xyd.js} +12 -105
- package/server/deacon.js +7 -7
- package/server/{devcontainer-renderer-CaYOG9JN.js → devcontainer-renderer-BQolp_qZ.js} +3 -3
- package/server/{dist-BIC8spJ0.js → dist-Bc32rWJB.js} +1 -1
- package/server/dist-DGTwcZhI.js +3 -0
- package/server/{docker-DDI1shu3.js → docker-s-esqZ__.js} +67 -28
- package/server/{ensure-devcontainer-DCAg3ksz.js → ensure-devcontainer-DA4-jF6R.js} +4 -4
- package/server/{ensure-devcontainer-CbSXvN9W.js → ensure-devcontainer-pUVaT0h3.js} +1 -1
- package/server/{esm-shims-DiV9jHa_.js → esm-shims-CJHy8CZY.js} +1 -1
- package/server/{event-store-DU3HkZlN.js → event-store-CcJAhKCl.js} +1 -1
- package/server/{event-store-DCMqyI07.js → event-store-DRte2Rwt.js} +3 -3
- package/server/{events-BfiosVAn.js → events-Bej1w_k8.js} +3 -3
- package/server/{events-DLRxpSuV.js → events-CtsLScn8.js} +1 -1
- package/server/export-Dx_qJ3h8.js +87 -0
- package/server/{factory-BMtWV9iv.js → factory-FN727tWr.js} +4 -4
- package/server/{feature-registry-population-DA7bO6Zn.js → feature-registry-population-ie-yBcQC.js} +4 -4
- package/server/{feature-registry-storage-nAY1fbBt.js → feature-registry-storage-CkZTFSsn.js} +2 -2
- package/server/{features-DMu7eGmU.js → features-Dtm9ABuz.js} +2 -2
- package/server/{feedback-target-DHqcw3cN.js → feedback-target-BYtpyt1i.js} +6 -6
- package/server/{feedback-target-B2SEai8-.js → feedback-target-DVJQDiE4.js} +1 -1
- package/server/{feedback-writer-DMBdS_jL.js → feedback-writer-DOUhyIW2.js} +1 -1
- package/server/{feedback-writer-qvm7UON7.js → feedback-writer-fdE1BzeL.js} +4 -4
- package/server/{flywheel-k4FPtCQ_.js → flywheel-Bi82iiNl.js} +1 -1
- package/server/{flywheel-Dfpx4bIV.js → flywheel-Bv3wv2S6.js} +23 -15
- package/server/{flywheel-D2LdPSic.js → flywheel-CUErLAyN.js} +13 -13
- package/server/{flywheel-merge-order-CB4lbXeJ.js → flywheel-merge-order-BaEN3iyN.js} +7 -7
- package/server/{flywheel-merge-order-CttTazRY.js → flywheel-merge-order-DXjeBnJ1.js} +1 -1
- package/server/{forge-CoPFXFyl.js → forge-ButEdJ31.js} +2 -2
- package/server/forge-XlDgPQWs.js +2 -0
- package/server/{from-B0YoJl9-.js → from-CbtTxftl.js} +1 -1
- package/server/{fts-db-DAHW1yA_.js → fts-db-CBgBLZcL.js} +2 -2
- package/server/{fts-operations-DDXDBi0d.js → fts-operations-CI9Xuo_1.js} +2 -2
- package/server/{git-utils-DbfC0zls.js → git-utils-BCLRWQ-y.js} +1 -1
- package/server/{git-utils-B90Zjzfl.js → git-utils-ClfpP47N.js} +2 -2
- package/server/{github-app-C7lftMNj.js → github-app-DRD2woO0.js} +1 -1
- package/server/{github-app-BecAnXRX.js → github-app-DscZ0_B6.js} +2 -2
- package/server/{github-client-D21rCIXk.js → github-client-DpiZnxGH.js} +2 -2
- package/server/{github-client-BwtKZtM0.js → github-client-iFRw8f7e.js} +1 -1
- package/server/{github-pr-lookup-CzVWuysl.js → github-pr-lookup-D-EBexxC.js} +2 -2
- package/server/{harness-resolve-SkRJm4a7.js → harness-resolve-iMjyNUJ2.js} +3 -3
- package/server/{health-events-DA_JLpgZ.js → health-events-B_WsQyYm.js} +1 -1
- package/server/{health-events-BT0aAfeA.js → health-events-CRdxDFN2.js} +2 -2
- package/server/{hooks-BQxRXtRb.js → hooks-C1ScyVxP.js} +1 -1
- package/server/{hooks-DxEn2BoY.js → hooks-C9b7Vqpw.js} +2 -2
- package/server/hume-K1pi8jC4.js +3 -0
- package/server/{hume-DkVOs1Qi.js → hume-TQ5pvOZ_.js} +2 -2
- package/server/{idle-stack-reaper-DGy7tvC9.js → idle-stack-reaper-B1kG7B_X.js} +2 -2
- package/server/{idle-stack-reaper-C7fJL8jH.js → idle-stack-reaper-iFVOAoJy.js} +1 -1
- package/server/{infra-DE3C4PMr.js → infra-bpq1MHKU.js} +50 -3
- package/server/{injection-Dt22tGvS.js → injection-BLWfwTVZ.js} +1 -1
- package/server/{injection-Cc4Qt8LZ.js → injection-DJqWld1v.js} +7 -7
- package/server/inspect-agent-477VxQim.js +2 -0
- package/server/{inspect-agent-DOaCDkCF.js → inspect-agent-DytCsltn.js} +9 -9
- package/server/{internal-token-nobT6FhA.js → internal-token-CCaWaTwE.js} +1 -1
- package/server/{internal-token-B_1YLWLI.js → internal-token-CjlhdufS.js} +2 -2
- package/server/{io-A7ZuOBkz.js → io-BiQ2Cs3k.js} +2 -2
- package/server/{issue-closed-Da3DeomA.js → issue-closed-FApNiOOs.js} +4 -4
- package/server/{issue-lifecycle-Bd9XBJAQ.js → issue-lifecycle-BkFWNjlD.js} +1 -1
- package/server/{issue-lifecycle-543pQX9D.js → issue-lifecycle-C-K53ASY.js} +21 -21
- package/server/{issue-service-singleton-Brc3t0SV.js → issue-service-singleton-CuHGpJJP.js} +8 -8
- package/server/{issue-service-singleton-BC2JLZi7.js → issue-service-singleton-bG1WdSdQ.js} +1 -1
- package/server/{issues-D3kt9UWE.js → issues-CwQMphDH.js} +2 -2
- package/server/jsonl-parser-dNIjHgym.js +245 -0
- package/server/{label-cleanup-w9X_T3AQ.js → label-cleanup-DYvAZknd.js} +3 -3
- package/server/{layers-Cwe1J-9o.js → layers-CD6FysRU.js} +2 -2
- package/server/{lifecycle-f1GbDK33.js → lifecycle-C_73gi-C.js} +7 -7
- package/server/{lifecycle-io-D56q8zlO.js → lifecycle-io-C_Wu0_i-.js} +1 -1
- package/server/{lifecycle-io-Bqvj0m3i.js → lifecycle-io-CeYxP3cE.js} +4 -4
- package/server/{linear-client-Bh4T4S-c.js → linear-client-4qoKnuQu.js} +1 -1
- package/server/{linear-client-CfJMTAYd.js → linear-client-EtVyblVz.js} +2 -2
- package/server/memory-fts-worker.js +1 -1
- package/server/{merge-ZrXFM3jv.js → merge-7rqkTUm2.js} +3 -3
- package/server/merge-agent-36wfYm2u.js +3 -0
- package/server/{merge-agent-CHHs5S_0.js → merge-agent-4Zhlgvqp.js} +62 -27
- package/server/{merge-batch-BwhGdlh4.js → merge-batch-itfnwdsT.js} +1 -1
- package/server/{merge-blockers-BMhDpMl_.js → merge-blockers-Bto_Oe_o.js} +2 -2
- package/server/{merge-DRMPa8CQ.js → merge-brm721Es.js} +1 -1
- package/server/merge-ops-PzqsW69V.js +2 -0
- package/server/{merge-rebase-R7MfqpiE.js → merge-rebase-Czfqfe4q.js} +2 -2
- package/server/{merge-set-B3Breacq.js → merge-set-BIkGQL9j.js} +9 -5
- package/server/{merge-set-D-iXNP0I.js → merge-set-G1RHcu3D.js} +1 -1
- package/server/{merge-sync-DHWbGtjR.js → merge-sync-B43Dybea.js} +2 -2
- package/server/{merge-sync-bOOtfeXR.js → merge-sync-DIrmlZUr.js} +1 -1
- package/server/{merge-train-98ptZrPT.js → merge-train-DGqVuNZV.js} +3 -3
- package/server/{merge-train-deps-B5FWJTE2.js → merge-train-deps-q15XLnAS.js} +4 -4
- package/server/{merge-verification-BGiuiXAK.js → merge-verification-Cqfxx6bE.js} +4 -4
- package/server/{multipart-parser-D7mzNNDm.js → multipart-parser-CiPxxfUH.js} +2 -2
- package/server/node_modules/.package-lock.json +1 -1
- package/server/{orphan-proposed-reconciler-BSGCNJ6X.js → orphan-proposed-reconciler-BWWHn4Lr.js} +15 -39
- package/server/{orphan-proposed-reconciler-CSuxivr9.js → orphan-proposed-reconciler-DAD9FJtQ.js} +1 -1
- package/server/package-lock.json +2 -2
- package/server/package.json +1 -1
- package/server/{pan-dir-DoPtdHrp.js → pan-dir-Bj7Hy8iu.js} +3 -3
- package/server/{paths-CVDLPxXp.js → paths-C9DkhIOT.js} +2 -2
- package/server/{pickup-KLRUhThD.js → pickup-CKRuGDsB.js} +1 -1
- package/server/{pipeline-notifier-BtYHbBPl.js → pipeline-notifier-BkKbQ8ji.js} +1 -1
- package/server/{pipeline-notifier-WD2dnOsR.js → pipeline-notifier-CAurUste.js} +2 -2
- package/server/project-pipelines-BRWaGmI4.js +162 -0
- package/server/projects-KJ94e50I.js +3 -0
- package/server/{prompts-Cg7e9FPb.js → prompts-DfI5qbTW.js} +2 -2
- package/server/{providers-fLduChnX.js → providers-CWDXoaMA.js} +7 -7
- package/server/providers-D-5txBRc.js +3 -0
- package/server/public/assets/{chunk-BO2N2NFS-aNlsGDCu.js → chunk-BO2N2NFS-ClukPsZQ.js} +4 -4
- package/server/public/assets/{diagram-OG6HWLK6-CnjkrvT8.js → diagram-OG6HWLK6-Avtvq2qI.js} +1 -1
- package/server/public/assets/{dist-Bg3Sbsx4.js → dist-BEHQuasD.js} +3 -3
- package/server/public/assets/{dist-CGKBCieA.js → dist-BvmY-qb1.js} +1 -1
- package/server/public/assets/{ganttDiagram-6RSMTGT7-C8LLKj23.js → ganttDiagram-6RSMTGT7-7KhCd0fH.js} +1 -1
- package/server/public/assets/{highlighted-body-OFNGDK62-BSDEUvhW.js → highlighted-body-OFNGDK62-iQoBMTf0.js} +1 -1
- package/server/public/assets/index-DUFDmvmH.css +1 -0
- package/server/public/assets/index-DnTZ2QeN.js +796 -0
- package/server/public/assets/{linear-DEbYZoxg.js → linear-CiJHW9g5.js} +1 -1
- package/server/public/assets/mermaid-GHXKKRXX-DZJOwgBG.js +1 -0
- package/server/public/assets/{pieDiagram-4H26LBE5-BtZL5pXE.js → pieDiagram-4H26LBE5-BOhmvUhW.js} +1 -1
- package/server/public/assets/{prod-DYVvIyuh.js → prod-DmLBBqat.js} +4 -4
- package/server/public/assets/{quadrantDiagram-W4KKPZXB-a7vS8Jc8.js → quadrantDiagram-W4KKPZXB-Cjku660G.js} +1 -1
- package/server/public/assets/{sankeyDiagram-5OEKKPKP-Ckbq2SWH.js → sankeyDiagram-5OEKKPKP-D52w5oPh.js} +1 -1
- package/server/public/assets/{xychartDiagram-2RQKCTM6-DJyeNa99.js → xychartDiagram-2RQKCTM6-BTO07_Iv.js} +1 -1
- package/server/public/index.html +2 -2
- package/server/{pull-requests-1WXmTBlo.js → pull-requests-Bmre-Xlz.js} +3 -3
- package/server/{pull-requests-D2SjJIwY.js → pull-requests-CKZPWhjD.js} +1 -1
- package/server/{rally-8aQlLLVj.js → rally-BBd9tQ__.js} +3 -3
- package/server/rally-Dc2Wnxw2.js +3 -0
- package/server/{rally-api-BtvwdX6d.js → rally-api-DDc85HIz.js} +1 -1
- package/server/{rally-api-H5I2CBO0.js → rally-api-DxZFObI_.js} +2 -2
- package/server/{rally-client-Bt-qHiOh.js → rally-client-BADHn0m_.js} +1 -1
- package/server/{rally-client-33CYZYYu.js → rally-client-DvvLiAQP.js} +4 -4
- package/server/{reap-terminal-sessions-CAmURC2h.js → reap-terminal-sessions-D6JDJsqF.js} +1 -1
- package/server/{reap-terminal-sessions-BwJb2DLJ.js → reap-terminal-sessions-D_xvy-Mk.js} +1 -1
- package/server/{rebuild-stack-KHkkrGjx.js → rebuild-stack-D19i080j.js} +6 -6
- package/server/{rebuild-stack-CXSgAGPR.js → rebuild-stack-GOsWqXi3.js} +1 -1
- package/server/{reconstruct-cache-D_Ml65_6.js → reconstruct-cache-lQrG4xGg.js} +6 -6
- package/server/record-pyB6VA32.js +3 -0
- package/server/recovery-trip-Dfh1tNHn.js +98 -0
- package/server/release-engine-DxcMWRuK.js +306 -0
- package/server/release-set-BYsgX7o6.js +125 -0
- package/server/{remote-CGuognN3.js → remote-8W2Yhygw.js} +3 -3
- package/server/{remote-D0Old0Cv.js → remote-DmeL21dF.js} +3 -3
- package/server/{remote-agents-DfhMq6kQ.js → remote-agents-BfQBE6MN.js} +5 -5
- package/server/{remote-agents-UHoi4oGr.js → remote-agents-DlvbzO7m.js} +1 -1
- package/server/{remote-completion-RZYePR_0.js → remote-completion-BgkiSQmx.js} +12 -12
- package/server/resolver-dTpSSzaH.js +158 -0
- package/server/resume-DhcIqsTm.js +3 -0
- package/server/{review-agent-sZBCpEVv.js → review-agent-D025Wt7w.js} +22 -22
- package/server/{review-agent-uwVftH98.js → review-agent-Rub0OpZc.js} +1 -1
- package/server/{review-artifacts-C7a45b1_.js → review-artifacts-CX951jTU.js} +6 -6
- package/server/{review-context-CErbQpKL.js → review-context-DGGGR_w5.js} +7 -7
- package/server/{review-monitor-DHD9m_kh.js → review-monitor-0_qqMcnS.js} +2 -2
- package/server/{review-status-gPndPUBc.js → review-status-4qqLkRMu.js} +24 -17
- package/server/{review-status-JhQ3qlDS.js → review-status-Br6lAFGO.js} +1 -1
- package/server/{review-status-enrichment-qWvWckYN.js → review-status-enrichment-Cr8m3uVL.js} +3 -3
- package/server/{review-status-sync-B_GrEke8.js → review-status-sync-D3KUhsdA.js} +1 -1
- package/server/{review-status-sync-D3A9KX95.js → review-status-sync-OC-l1lNt.js} +9 -5
- package/server/{review-verdict-feedback-4WZ5ZdbC.js → review-verdict-feedback-DrWMuRaj.js} +6 -6
- package/server/{review-verdict-feedback-Zo0r_z4m.js → review-verdict-feedback-YOzmsq9G.js} +1 -1
- package/server/{rollup-BrUIKMv_.js → rollup-CnxbwaLH.js} +8 -8
- package/server/{rollup-Bmhn80tB.js → rollup-DmazRY0j.js} +1 -1
- package/server/{scanner-BIGumel_.js → scanner-DE1MqAaF.js} +7 -7
- package/server/{sequence-io-DA0akEQN.js → sequence-io-C0uoqfGx.js} +4 -4
- package/server/{sequencer-agent-DeflKafB.js → sequencer-agent-i-b2u4SJ.js} +10 -6
- package/server/server.js +1115 -297
- package/server/{service-V0sUK-OR.js → service-D0_gh2tl.js} +1668 -1408
- package/server/{session-fork-Yx3UcAQf.js → session-fork-DZ12vipL.js} +2 -2
- package/server/{settings-CFEeQRj2.js → settings-ByqjPtFO.js} +2 -2
- package/server/{settings-CxVate0h.js → settings-DCC4BAqj.js} +3 -3
- package/server/settings-yydgaqhv.js +2 -0
- package/server/{shadow-engineering-BRxnVSDk.js → shadow-engineering-BhnmtW5m.js} +2 -2
- package/server/{shadow-state-1aS8FijU.js → shadow-state-ByXZehsy.js} +1 -1
- package/server/{shadow-state-BiMwdyl1.js → shadow-state-CKm8xWX9.js} +2 -2
- package/server/{ship-log-CMWH3H5-.js → ship-log-DE_0Cv_i.js} +1 -1
- package/server/{smart-compaction-Bw1K_Sbl.js → smart-compaction-D5Zen3AA.js} +7 -7
- package/server/{smart-compaction-xblMyVpT.js → smart-compaction-h4_ca6Uk.js} +1 -1
- package/server/{spawn-planning-session-CzDFSKAQ.js → spawn-planning-session-Bgi7Ub02.js} +1 -1
- package/server/{spawn-planning-session-DxLxcM7m.js → spawn-planning-session-Dueit4WX.js} +15 -15
- package/server/{specialist-completion-BiEXUHfD.js → specialist-completion-BZ8M-WZ0.js} +1 -1
- package/server/{specialist-context-BfkTA81h.js → specialist-context-BMq83C58.js} +5 -5
- package/server/{specialist-handoff-logger-BiKuLtNm.js → specialist-handoff-logger-BxX1YefH.js} +2 -2
- package/server/{specialist-logs-CFJjGsLC.js → specialist-logs-BWZYcJ6P.js} +3 -3
- package/server/{specialists-DdvpFNt1.js → specialists-8ZAguXef.js} +1 -1
- package/server/{specialists-B4byf18k.js → specialists-BudKrjOu.js} +76 -76
- package/server/{specialists-DjHbwQ_p.js → specialists-DrPrC9Y9.js} +20 -256
- package/server/{specialists-BLIreoE7.js → specialists-ZkW4Mb7U.js} +1 -1
- package/server/specs-DwoAEGrW.js +3 -0
- package/server/{src-fgbU-jJ-.js → src-DHn4Rd2F.js} +25 -3
- package/server/{src-mK_M_ZIj.js → src-DNUUqkec.js} +6 -6
- package/server/{src-CALBEUMX.js → src-Dv6L03vW.js} +3 -3
- package/server/{stack-health-BFM8YlHw.js → stack-health-Dclm7_tu.js} +1 -1
- package/server/{stack-health-Qd7rPiVe.js → stack-health-DxsTx7II.js} +2 -2
- package/server/{state-home-C2Bicw9N.js → state-home-CRowbXqU.js} +6 -6
- package/server/stuck-remediation-CFWUP8_I.js +1491 -0
- package/server/stuck-remediation-CLHd-lrF.js +2 -0
- package/server/{summary-fork-EWWBvzgE.js → summary-fork-CKc01Our.js} +2 -2
- package/server/{summary-fork-DezPkJSm.js → summary-fork-CtCwBJ2Z.js} +8 -8
- package/server/{supervisor-D5GC5jUM.js → supervisor-BA5D72Qa.js} +2 -2
- package/server/supervisor-DHjumXlL.js +2 -0
- package/server/{swarm-readiness-Cf9mommA.js → swarm-readiness-CL5sM9B1.js} +3 -3
- package/server/{system-prerequisites-BsI9hxh9.js → system-prerequisites-DmFSXIE5.js} +13 -1
- package/server/{teardown-workspace-CWz1pbEK.js → teardown-workspace-DWmFJzTw.js} +37 -68
- package/server/{test-agent-queue-TiVXe3ZT.js → test-agent-queue-DjsQoV5X.js} +6 -6
- package/server/{test-verdict-CCDd25uN.js → test-verdict-BSqsQp3G.js} +1 -1
- package/server/{test-verdict-Bm4uv_AA.js → test-verdict-D1AmZ2c0.js} +1 -1
- package/server/{tldr-daemon-C_1frSDj.js → tldr-daemon-C-MGomL8.js} +2 -2
- package/server/{tldr-daemon-Big5IZ_M.js → tldr-daemon-DD9rI5gA.js} +1 -1
- package/server/{tmux-D57f-XNv.js → tmux-C_d-lSIq.js} +2 -2
- package/server/{tmux-DClqWwb8.js → tmux-x291Gjjb.js} +1 -1
- package/server/{token-_qujO3JZ.js → token-DfBf1bsd.js} +1 -1
- package/server/{tracker-config-BmdJU_w6.js → tracker-config-CNODpd11.js} +2 -2
- package/server/{tracker-config-B_Ubts1Z.js → tracker-config-DItsdelv.js} +1 -1
- package/server/{tracker-utils-Bg-Sevcv.js → tracker-utils-B9ZoVA9b.js} +1 -1
- package/server/{tracker-utils-CWFt6jjV.js → tracker-utils-Ch2_5GGf.js} +2 -2
- package/server/{traefik-connect-5EP1WS_Z.js → traefik-connect-BF4x_LUQ.js} +1 -1
- package/server/{tunnel-hOBNQbmF.js → tunnel-YD80_IHr.js} +1 -1
- package/server/{tunnel-UBf2vyAL.js → tunnel-r_5nHAI6.js} +3 -3
- package/server/{types-B5u3NxMl.js → types-33JEP4LZ.js} +1 -1
- package/server/{uat-train-DGkIkLxu.js → uat-train-DDI00aqD.js} +1 -1
- package/server/{uat-train-qNanv5hN.js → uat-train-DjkFWXge.js} +25 -25
- package/server/{validation-C4wegCvH.js → validation-DUTiJnyz.js} +4 -4
- package/server/{validation-B0QaLIHP.js → validation-ict8hnXk.js} +1 -1
- package/server/{verification-runner-DdleMNLq.js → verification-runner-ATaLbpr5.js} +1 -1
- package/server/{verification-runner-CntudBes.js → verification-runner-inLgeQux.js} +12 -12
- package/server/{vtt-parser-CKc-smWM.js → vtt-parser-QnRfcjiU.js} +1 -1
- package/server/{work-agent-prompt-CG5jFV9A.js → work-agent-prompt-fZljjA1_.js} +12 -12
- package/server/{workflows-BK2xs_a8.js → workflows-oBnJ7Tm0.js} +14 -14
- package/server/workflows-tU7KKxQi.js +2 -0
- package/server/{workspace-C9lC5o3E.js → workspace-B2WOW9Hw.js} +1 -1
- package/server/{workspace-CLrSIRCx.js → workspace-CNuW2j41.js} +3 -3
- package/server/{workspace-config-DA8U97k9.js → workspace-config-CBgMGhWs.js} +1 -1
- package/server/{workspace-manager-BJhW2sky.js → workspace-manager-CB-R07R3.js} +12 -12
- package/server/{workspace-manager-Cb2a7bg9.js → workspace-manager-DjUAJCSs.js} +1 -1
- package/server/{workspace-metadata-QQmo_Z0l.js → workspace-metadata-9Nn5kgRl.js} +2 -2
- package/server/{workspace-metadata-CTRnr323.js → workspace-metadata-Fd58uUQG.js} +1 -1
- package/server/{workspaces-Ca4zZs12.js → workspaces-BjWXntty.js} +106 -120
- package/server/writer-CQMGzEkw.js +3 -0
- package/server/writer-fe1-8iGc.js +251 -0
- package/sync-sources/hooks/ask-user-question-hook +87 -0
- package/sync-sources/hooks/auto-approve-hook +96 -0
- package/sync-sources/hooks/codex-notify-hook +62 -0
- package/sync-sources/hooks/gh-issue-trailer-hook +4 -0
- package/sync-sources/hooks/gh-issue-trailer-hook.d.ts +7 -0
- package/sync-sources/hooks/gh-issue-trailer-hook.d.ts.map +1 -0
- package/sync-sources/hooks/gh-issue-trailer-hook.js +239 -0
- package/sync-sources/hooks/gh-issue-trailer-hook.js.map +1 -0
- package/sync-sources/hooks/gh-issue-trailer-hook.ts +296 -0
- package/sync-sources/hooks/git-hooks/post-checkout +109 -0
- package/sync-sources/hooks/heartbeat-hook +113 -0
- package/sync-sources/hooks/notification-hook +70 -0
- package/sync-sources/hooks/notify-complete +79 -0
- package/sync-sources/hooks/pan-hook-lib.sh +153 -0
- package/sync-sources/hooks/permission-event-hook +24 -0
- package/sync-sources/hooks/post-compact-hook +25 -0
- package/sync-sources/hooks/pre-compact-hook +26 -0
- package/sync-sources/hooks/pre-tool-hook +35 -0
- package/sync-sources/hooks/record-cost-event.d.ts +1 -0
- package/sync-sources/hooks/record-cost-event.js +31859 -0
- package/sync-sources/hooks/record-cost-event.js.map +1 -0
- package/sync-sources/hooks/record-cost-event.ts +267 -0
- package/sync-sources/hooks/rtk-bash-filter +152 -0
- package/sync-sources/hooks/session-start-hook +80 -0
- package/sync-sources/hooks/specialist-stop-hook +106 -0
- package/sync-sources/hooks/statusline.sh +169 -0
- package/sync-sources/hooks/stop-hook +217 -0
- package/sync-sources/hooks/tldr-post-edit +72 -0
- package/sync-sources/hooks/tldr-read-enforcer +340 -0
- package/sync-sources/hooks/tmux-send-keys-guard +87 -0
- package/sync-sources/hooks/tsdown.config.ts +14 -0
- package/sync-sources/hooks/user-prompt-submit-hook +57 -0
- package/sync-sources/hooks/work-agent-stop-hook +424 -0
- package/server/auto-commit-CGQlb4rV.js +0 -3
- package/server/bd-mutex-BItGvLQ8.js +0 -35
- package/server/beads-restore-CMbDSB5R.js +0 -60
- package/server/cli-Ch_BWJB2.js +0 -3
- package/server/deacon-swarm-CSWGRfzr.js +0 -2
- package/server/dist-BUplB3Od.js +0 -3
- package/server/forge-IgLR0Ist.js +0 -2
- package/server/hume-x9voULey.js +0 -3
- package/server/inspect-agent-CYoWAeeB.js +0 -2
- package/server/merge-agent-CfohxFWM.js +0 -3
- package/server/merge-ops-Czke6UAp.js +0 -2
- package/server/projects-Bqfwuqet.js +0 -3
- package/server/providers-JjK6izqX.js +0 -3
- package/server/public/assets/index-CZiADHcL.css +0 -1
- package/server/public/assets/index-LQIfhmgI.js +0 -796
- package/server/public/assets/mermaid-GHXKKRXX-CZOsRYoT.js +0 -1
- package/server/rally-Ba9QoNYc.js +0 -3
- package/server/record-De2syxNB.js +0 -3
- package/server/resume-CmxF3Kjz.js +0 -3
- package/server/settings-DEhYpW0k.js +0 -2
- package/server/specs-CVH1kvz3.js +0 -3
- package/server/supervisor-DQMK2e3I.js +0 -2
- 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
|