specrails-desktop 2.26.1 → 2.27.0

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 (265) hide show
  1. package/client/dist/assets/{ActivityFeedPage-C1LftFSE.js → ActivityFeedPage-BlkNVXCv.js} +1 -1
  2. package/client/dist/assets/AgentBrowserCapture-76Ri2QR1.js +1 -0
  3. package/client/dist/assets/AgentIntegrationsModal-D9hVgaBR.js +1 -0
  4. package/client/dist/assets/{AgentModeCodePane-b6UmdjwB.js → AgentModeCodePane-BUR_QxqR.js} +2 -2
  5. package/client/dist/assets/AgentModeJobsPane-CnRXelns.js +1 -0
  6. package/client/dist/assets/AgentsPage-DEt9vpzh.js +86 -0
  7. package/client/dist/assets/{AnalyticsPage-DjFf1OxU.js → AnalyticsPage-C_eT3I3i.js} +1 -1
  8. package/client/dist/assets/{BarChart-nmDbbUiR.js → BarChart-DuzYxiJ6.js} +1 -1
  9. package/client/dist/assets/CodePage-72-FLBNw.js +2 -0
  10. package/client/dist/assets/{DesktopAnalyticsPage-ee0bpk-F.js → DesktopAnalyticsPage-BSeBkF2n.js} +1 -1
  11. package/client/dist/assets/{DocsDialog-CGUzB1zX.js → DocsDialog-CGzqDyN3.js} +2 -2
  12. package/client/dist/assets/DocsPage-BB3VGFM9.js +11 -0
  13. package/client/dist/assets/{ExportDropdown-Dzk2Lkxw.js → ExportDropdown-IehskH-7.js} +1 -1
  14. package/client/dist/assets/IntegrationsPage-X8bLyuDk.js +3 -0
  15. package/client/dist/assets/InteractiveJobComposer-D1HUALME.js +19 -0
  16. package/client/dist/assets/JobDetailModal-CMg6aKSO.js +1 -0
  17. package/client/dist/assets/JobDetailPage-Bg01rzeu.js +1 -0
  18. package/client/dist/assets/JobsPage-Cwkm9yYc.js +1 -0
  19. package/client/dist/assets/{LoopBuilderPage-5ucN9Hq9.js → LoopBuilderPage-4uPNN8Xn.js} +2 -2
  20. package/client/dist/assets/{LoopPreviewModal-wKAts-Wf.js → LoopPreviewModal-BNUQ9iL8.js} +1 -1
  21. package/client/dist/assets/LoopsPage-DLXESZxq.js +1 -0
  22. package/client/dist/assets/ProjectSettingsDialog-DP6hAug-.js +1 -0
  23. package/client/dist/assets/{TemplatePreviewModal-_Dy6ceXg.js → TemplatePreviewModal-C6mndL8D.js} +1 -1
  24. package/client/dist/assets/{agent-gFi-qvxO2.js → agent-B0Rg_eLw2.js} +1 -1
  25. package/client/dist/assets/{agent-BmH52J4j.js → agent-B8f79myL.js} +1 -1
  26. package/client/dist/assets/{agent-DolxzTol.js → agent-BCH7a3z-.js} +1 -1
  27. package/client/dist/assets/{agent-Cg49-TGZ.js → agent-BELAd4RF.js} +1 -1
  28. package/client/dist/assets/{agent-DwDR2Exf2.js → agent-BrCnHlvX2.js} +1 -1
  29. package/client/dist/assets/{agent-DgDtg-0Y2.js → agent-CrcIj6WE2.js} +1 -1
  30. package/client/dist/assets/{agent-_tfCgFUk.js → agent-CtwLUmjl.js} +1 -1
  31. package/client/dist/assets/{agent-Bxb9RRNW.js → agent-xG4qLg4R.js} +1 -1
  32. package/client/dist/assets/{brain-BJ2Gd98j.js → brain-Dia32foa.js} +1 -1
  33. package/client/dist/assets/dashboard-5C2FOZQX.js +1 -0
  34. package/client/dist/assets/dashboard-B6iQ-IL_.js +1 -0
  35. package/client/dist/assets/dashboard-BFfnCtq52.js +1 -0
  36. package/client/dist/assets/dashboard-BIC8KtRf2.js +1 -0
  37. package/client/dist/assets/dashboard-BawQCyrV.js +1 -0
  38. package/client/dist/assets/dashboard-CEgg9NAr.js +1 -0
  39. package/client/dist/assets/dashboard-CiOyDh9L.js +1 -0
  40. package/client/dist/assets/dashboard-D6x5OnNj2.js +1 -0
  41. package/client/dist/assets/{dist-js-D7smLA35.js → dist-js-DN7Pxudf.js} +1 -1
  42. package/client/dist/assets/{dist-js-CXAsO0Z8.js → dist-js-D_4k5prK.js} +1 -1
  43. package/client/dist/assets/{index-B3ejvKSt.js → index-BbVOi-dY.js} +57 -55
  44. package/client/dist/assets/{index-C695aPdV.css → index-o3Q2Lii5.css} +1 -1
  45. package/client/dist/assets/{lib-BIHmUkcn.js → lib-BxSZPd0W.js} +1 -1
  46. package/client/dist/assets/settings-BFv6UbKV.js +1 -0
  47. package/client/dist/assets/{settings-BHdPchJP.js → settings-B_v3zgJf.js} +1 -1
  48. package/client/dist/assets/{settings-BeCczBFS2.js → settings-BemR_e3F.js} +1 -1
  49. package/client/dist/assets/{settings-D54_cgl12.js → settings-Bp24JHlv2.js} +1 -1
  50. package/client/dist/assets/{settings-C_nY14Yz.js → settings-BvIwQ3A1.js} +1 -1
  51. package/client/dist/assets/{settings-BlLi719f.js → settings-Bx2ObkTR2.js} +1 -1
  52. package/client/dist/assets/{settings-CaZ26a0S.js → settings-C_T0-j9d.js} +1 -1
  53. package/client/dist/assets/{settings-cn0tbve62.js → settings-DhaFKpcw2.js} +1 -1
  54. package/client/dist/assets/{upload-xdQuJvXN.js → upload-4u055Eea.js} +1 -1
  55. package/client/dist/assets/{value-Bk2f9AUg.js → value-C3Qe3VXm.js} +1 -1
  56. package/client/dist/index.html +5 -5
  57. package/package.json +1 -1
  58. package/server/dist/agent-operator-prompt.js +10 -1
  59. package/server/dist/db.js +51 -2
  60. package/server/dist/dev-ports.js +35 -0
  61. package/server/dist/index.js +2 -6
  62. package/server/dist/mcp/tools/catalog.js +2 -0
  63. package/server/dist/mcp/tools/env.js +72 -0
  64. package/server/dist/mcp/tools/jobs.js +66 -1
  65. package/server/dist/mcp/tools/rails.js +2 -0
  66. package/server/dist/path-resolver.js +74 -15
  67. package/server/dist/pr-publisher.js +17 -15
  68. package/server/dist/project-env.js +30 -0
  69. package/server/dist/project-git.js +57 -0
  70. package/server/dist/project-registry.js +4 -2
  71. package/server/dist/project-router-jobs.js +76 -0
  72. package/server/dist/project-router-settings.js +10 -1
  73. package/server/dist/queue-manager.js +9 -1
  74. package/server/dist/rail-isolated-launch.js +27 -6
  75. package/server/dist/rail-pr-decision.js +96 -18
  76. package/server/dist/rail-pr-store.js +36 -0
  77. package/server/dist/rail-worktree-release.js +25 -0
  78. package/server/dist/rail-worktrees-store.js +2 -2
  79. package/server/dist/rails-router.js +95 -9
  80. package/server/dist/transient-children.js +126 -8
  81. package/server/dist/workspace-resolution.js +3 -3
  82. package/server/dist/worktree-env-discovery.js +172 -0
  83. package/client/dist/assets/AgentBrowserCapture-B5prW-V0.js +0 -1
  84. package/client/dist/assets/AgentModeJobsPane-DW3Wj5oi.js +0 -1
  85. package/client/dist/assets/AgentsPage-DBMnhdlq.js +0 -86
  86. package/client/dist/assets/CodePage-DHd46Sm7.js +0 -2
  87. package/client/dist/assets/DocsPage-96iws84I.js +0 -11
  88. package/client/dist/assets/IntegrationsPage-Bta2lAhq.js +0 -3
  89. package/client/dist/assets/InteractiveJobComposer-B9S_ZGJS.js +0 -19
  90. package/client/dist/assets/JobDetailModal-DhrwU2z0.js +0 -1
  91. package/client/dist/assets/JobDetailPage-BnhoXkql.js +0 -1
  92. package/client/dist/assets/JobsPage-BBT9fyhg.js +0 -1
  93. package/client/dist/assets/LoopsPage-COyxf6Pa.js +0 -1
  94. package/client/dist/assets/ProjectSettingsDialog-CcToBI-Z.js +0 -1
  95. package/client/dist/assets/dashboard-90r0QcUc2.js +0 -1
  96. package/client/dist/assets/dashboard-B0C6mJP-.js +0 -1
  97. package/client/dist/assets/dashboard-BOx0FR1L2.js +0 -1
  98. package/client/dist/assets/dashboard-BcjUOLJr.js +0 -1
  99. package/client/dist/assets/dashboard-C27kNpjn.js +0 -1
  100. package/client/dist/assets/dashboard-DJAj2EbH.js +0 -1
  101. package/client/dist/assets/dashboard-DNd1Gobs.js +0 -1
  102. package/client/dist/assets/dashboard-DZWrDAYm2.js +0 -1
  103. package/client/dist/assets/settings-D9bDDe2O.js +0 -1
  104. /package/client/dist/assets/{activity-BZNfOdiS.js → activity-BZZlEG3a.js} +0 -0
  105. /package/client/dist/assets/{activity--iN4Ad2R2.js → activity-BbWiDfn-2.js} +0 -0
  106. /package/client/dist/assets/{activity-CIOLxGvh.js → activity-D3Ic-HTS.js} +0 -0
  107. /package/client/dist/assets/{activity-DtxrAUMo2.js → activity-DFbvNqhE2.js} +0 -0
  108. /package/client/dist/assets/{activity-BuVo5sPB.js → activity-DtzRy92x.js} +0 -0
  109. /package/client/dist/assets/{activity-6hM_ZSg9.js → activity-DvECTYGt.js} +0 -0
  110. /package/client/dist/assets/{activity-D2r2h3SE2.js → activity-FleOlK0l2.js} +0 -0
  111. /package/client/dist/assets/{addspec-D-JLDsUV.js → addspec-BKrH2Ucz.js} +0 -0
  112. /package/client/dist/assets/{addspec-DfhDbg5c.js → addspec-B_LQMHW3.js} +0 -0
  113. /package/client/dist/assets/{addspec-bsNDdqRm2.js → addspec-Bw_YQwo62.js} +0 -0
  114. /package/client/dist/assets/{addspec-BMtLZLW62.js → addspec-CFxGvmJR2.js} +0 -0
  115. /package/client/dist/assets/{addspec-mgPOt5q02.js → addspec-CR8MB5Fr2.js} +0 -0
  116. /package/client/dist/assets/{addspec-CP0HRuxY.js → addspec-DaA-CbTt.js} +0 -0
  117. /package/client/dist/assets/{addspec-CRuvWn8d.js → addspec-NBbcTbLc.js} +0 -0
  118. /package/client/dist/assets/{agents-BpoUaoW92.js → agents-B9pFSJQI2.js} +0 -0
  119. /package/client/dist/assets/{agents-78bTZGZh.js → agents-BHgn--3j.js} +0 -0
  120. /package/client/dist/assets/{agents-BUdEi3-N.js → agents-B_qGaB0F.js} +0 -0
  121. /package/client/dist/assets/{agents-bvyRLczL.js → agents-CP8_ezYG.js} +0 -0
  122. /package/client/dist/assets/{agents-BLhWCmDS2.js → agents-CV-_xBvX2.js} +0 -0
  123. /package/client/dist/assets/{agents-CWo-KyAB.js → agents-HFCqQH0Z.js} +0 -0
  124. /package/client/dist/assets/{agents-DDtCJ5M02.js → agents-VsyFhGOS2.js} +0 -0
  125. /package/client/dist/assets/{agentstudio-BMQxc_bz.js → agentstudio-BEp7Sb46.js} +0 -0
  126. /package/client/dist/assets/{agentstudio-Dtq3dGYk.js → agentstudio-BOjG6tgJ.js} +0 -0
  127. /package/client/dist/assets/{agentstudio-C_Jq8Tl4.js → agentstudio-Br3WD7Xs.js} +0 -0
  128. /package/client/dist/assets/{agentstudio-sI27TpBu2.js → agentstudio-CuTt3ZGe2.js} +0 -0
  129. /package/client/dist/assets/{agentstudio-CWgXG5W52.js → agentstudio-D2A5peWV2.js} +0 -0
  130. /package/client/dist/assets/{agentstudio-COZriimI2.js → agentstudio-RfZavULt2.js} +0 -0
  131. /package/client/dist/assets/{agentstudio-jKMTdT70.js → agentstudio-RsxnZtUA.js} +0 -0
  132. /package/client/dist/assets/{aiedit-igmw-ZoZ2.js → aiedit-26p5A_HY2.js} +0 -0
  133. /package/client/dist/assets/{aiedit-6CHdb_iv2.js → aiedit-CV520D4W2.js} +0 -0
  134. /package/client/dist/assets/{aiedit-pc5JDUq1.js → aiedit-CcugGPbS.js} +0 -0
  135. /package/client/dist/assets/{aiedit-eMN4eQPF.js → aiedit-D0Syl-mC.js} +0 -0
  136. /package/client/dist/assets/{aiedit-hknZlly82.js → aiedit-DnBzjlVz2.js} +0 -0
  137. /package/client/dist/assets/{aiedit-B03pD_Na.js → aiedit-DrIjSojo.js} +0 -0
  138. /package/client/dist/assets/{aiedit-BtRqRxML.js → aiedit-a-Z-g5iC.js} +0 -0
  139. /package/client/dist/assets/{analytics-C5dpiS0o.js → analytics-4-eskbtX.js} +0 -0
  140. /package/client/dist/assets/{analytics-CR1TJqUi2.js → analytics-4HvXb0Vn2.js} +0 -0
  141. /package/client/dist/assets/{analytics-rmHuJv0V.js → analytics-Bd9rHGjV.js} +0 -0
  142. /package/client/dist/assets/{analytics-BWMUZ2Pq2.js → analytics-Bpe41fnX2.js} +0 -0
  143. /package/client/dist/assets/{analytics-Bj-SmKHa.js → analytics-CpSTNfui.js} +0 -0
  144. /package/client/dist/assets/{analytics-CCprAqk3.js → analytics-DEe5BMz8.js} +0 -0
  145. /package/client/dist/assets/{analytics-DfNdXR0y2.js → analytics-DcOITHla2.js} +0 -0
  146. /package/client/dist/assets/{attachments-BjyMmrwE.js → attachments-CA2f3qL3.js} +0 -0
  147. /package/client/dist/assets/{attachments-DCZOL1RU.js → attachments-CHDbhKeH.js} +0 -0
  148. /package/client/dist/assets/{attachments-rNn1ekRy2.js → attachments-CKAY5aYG2.js} +0 -0
  149. /package/client/dist/assets/{attachments-BgBNSqQd.js → attachments-CgOQ9K-6.js} +0 -0
  150. /package/client/dist/assets/{attachments-DPCmkFZu2.js → attachments-D2jwJXQ42.js} +0 -0
  151. /package/client/dist/assets/{attachments-C9GTTJsx2.js → attachments-DlEQu56g2.js} +0 -0
  152. /package/client/dist/assets/{attachments-DL-bPy9E.js → attachments-jVdKzAXn.js} +0 -0
  153. /package/client/dist/assets/{browser-Cvg-QHvd2.js → browser-BQFDxrRq.js} +0 -0
  154. /package/client/dist/assets/{browser-rWKj-RR_2.js → browser-BbJ_q_OT2.js} +0 -0
  155. /package/client/dist/assets/{browser-Cc0iDqUi.js → browser-C4VJ5CW12.js} +0 -0
  156. /package/client/dist/assets/{browser-BPkhILSJ.js → browser-Cl-FwPXs.js} +0 -0
  157. /package/client/dist/assets/{browser-FRv8P02E.js → browser-Do2jf8_N.js} +0 -0
  158. /package/client/dist/assets/{browser-BqLqJVVT.js → browser-JsiohNmW.js} +0 -0
  159. /package/client/dist/assets/{browser-BijVYG0Y2.js → browser-kA31eYhu2.js} +0 -0
  160. /package/client/dist/assets/{chat-DdwZjT8K2.js → chat-BMWD_Rx12.js} +0 -0
  161. /package/client/dist/assets/{chat-Bom6bqK3.js → chat-BalviUAD.js} +0 -0
  162. /package/client/dist/assets/{chat-DsbwJhus.js → chat-CJddjN3D2.js} +0 -0
  163. /package/client/dist/assets/{chat-Dnwg-NpQ.js → chat-D5xEtsf3.js} +0 -0
  164. /package/client/dist/assets/{chat-vylxqR0P2.js → chat-Ds8GvCnv.js} +0 -0
  165. /package/client/dist/assets/{chat-BgL3zv6x2.js → chat-SaG-ng1x2.js} +0 -0
  166. /package/client/dist/assets/{chat-Bh3NdgJs.js → chat-n43nZGvn.js} +0 -0
  167. /package/client/dist/assets/{code-DzC1rMk12.js → code-Bit_z2Xd2.js} +0 -0
  168. /package/client/dist/assets/{code-BRcDlze52.js → code-BotxM43N2.js} +0 -0
  169. /package/client/dist/assets/{code-UPVvmAKM2.js → code-C9U6M5-e2.js} +0 -0
  170. /package/client/dist/assets/{code-BdMy3Ffq.js → code-CIPmDEd0.js} +0 -0
  171. /package/client/dist/assets/{code-DgeHz9CB.js → code-DUe1Bouz.js} +0 -0
  172. /package/client/dist/assets/{code-CwnBPPyA.js → code-F_KjccGf.js} +0 -0
  173. /package/client/dist/assets/{code-CeQ0FZpC.js → code-HDK4P6Rx.js} +0 -0
  174. /package/client/dist/assets/{commands-DkU0qliq.js → commands-BFQZc9AG.js} +0 -0
  175. /package/client/dist/assets/{commands-u-4eEHCN.js → commands-BMkC5-ki.js} +0 -0
  176. /package/client/dist/assets/{commands-CSDs9q_2.js → commands-BllkJauO.js} +0 -0
  177. /package/client/dist/assets/{commands-xHDDGfcP2.js → commands-CWX9WmDX2.js} +0 -0
  178. /package/client/dist/assets/{commands-C4Y3C-PF2.js → commands-CzFSoIiq2.js} +0 -0
  179. /package/client/dist/assets/{commands-JWMfGDmr.js → commands-KBbhsnw8.js} +0 -0
  180. /package/client/dist/assets/{commands-RHOchN4n2.js → commands-cKTaLwwV2.js} +0 -0
  181. /package/client/dist/assets/{common-DmqBPwZG.js → common-BfQD173j.js} +0 -0
  182. /package/client/dist/assets/{common-DhWTkocg.js → common-CF8iGXrG.js} +0 -0
  183. /package/client/dist/assets/{common-Bwafujnc2.js → common-Cfmwcrl52.js} +0 -0
  184. /package/client/dist/assets/{common-C-vU-j-I.js → common-D2cimsul.js} +0 -0
  185. /package/client/dist/assets/{common-C5HKSbbz.js → common-DH7fjyL1.js} +0 -0
  186. /package/client/dist/assets/{common-BM3LFLCE2.js → common-DZbXlmq72.js} +0 -0
  187. /package/client/dist/assets/{common-DiPTT7V_2.js → common-DtXmzX2o2.js} +0 -0
  188. /package/client/dist/assets/{explore-UvW1jEMa.js → explore-BEpIs343.js} +0 -0
  189. /package/client/dist/assets/{explore-CNBzrEPS.js → explore-BO_OinBD.js} +0 -0
  190. /package/client/dist/assets/{explore-C0k8HrkN.js → explore-Bhy7WWXv.js} +0 -0
  191. /package/client/dist/assets/{explore-B7fblXBb2.js → explore-CAc8Qzu32.js} +0 -0
  192. /package/client/dist/assets/{explore-D_mWBYCS2.js → explore-DCZL9gRN2.js} +0 -0
  193. /package/client/dist/assets/{explore-Dahc1lOx.js → explore-DW_YTkQh.js} +0 -0
  194. /package/client/dist/assets/{explore-BHtEpoNN2.js → explore-ELbrSZyQ2.js} +0 -0
  195. /package/client/dist/assets/{format-command-CuWuRvka.js → format-command-Db1viey1.js} +0 -0
  196. /package/client/dist/assets/{integrations-BNuauK3o.js → integrations-BQNkrfr7.js} +0 -0
  197. /package/client/dist/assets/{integrations-Cl_wD5AI.js → integrations-BdN2f6bP.js} +0 -0
  198. /package/client/dist/assets/{integrations-CHy8tnpV.js → integrations-BrIUJmVf.js} +0 -0
  199. /package/client/dist/assets/{integrations-Cy8nZ_dv2.js → integrations-Cyp7cSYv2.js} +0 -0
  200. /package/client/dist/assets/{integrations-4KZnYECp.js → integrations-DDR3OHdq.js} +0 -0
  201. /package/client/dist/assets/{integrations-7W266oqF2.js → integrations-DVxi8b0j2.js} +0 -0
  202. /package/client/dist/assets/{integrations-zMqY4VU12.js → integrations-RuNEX3La2.js} +0 -0
  203. /package/client/dist/assets/{jira-uNH1gETL2.js → jira-B5wszJ1-2.js} +0 -0
  204. /package/client/dist/assets/{jira-BHAKtc4B.js → jira-C5UjxHE4.js} +0 -0
  205. /package/client/dist/assets/{jira-D5efgwp_.js → jira-CQ7mrZde.js} +0 -0
  206. /package/client/dist/assets/{jira-bDs7Im2C.js → jira-DIRkZk9U.js} +0 -0
  207. /package/client/dist/assets/{jira-CPpgA-5z2.js → jira-DTYu3hJi2.js} +0 -0
  208. /package/client/dist/assets/{jira-DS7ownDy.js → jira-DtZpokjJ.js} +0 -0
  209. /package/client/dist/assets/{jira-Be369RIR2.js → jira-ZdKb-50F2.js} +0 -0
  210. /package/client/dist/assets/{jobs-CE8qeIYj.js → jobs-BFIdeDTw.js} +0 -0
  211. /package/client/dist/assets/{jobs-BgQzB0Sx2.js → jobs-BJ-Gcao22.js} +0 -0
  212. /package/client/dist/assets/{jobs-1GV2-4zd2.js → jobs-DAGiPVIS2.js} +0 -0
  213. /package/client/dist/assets/{jobs-26F9gGcE.js → jobs-DAU8C_tZ.js} +0 -0
  214. /package/client/dist/assets/{jobs-BqWui9_f.js → jobs-Da0GaaSc.js} +0 -0
  215. /package/client/dist/assets/{jobs-Wh7dn28q2.js → jobs-P3unxtxo2.js} +0 -0
  216. /package/client/dist/assets/{jobs-DzeuASRf.js → jobs-p360zdf1.js} +0 -0
  217. /package/client/dist/assets/{loops-CvOWsi5r.js → loops-Bi4ywYGn.js} +0 -0
  218. /package/client/dist/assets/{loops-BvqQ-bHR2.js → loops-BjK8lmoZ2.js} +0 -0
  219. /package/client/dist/assets/{loops-K5_HXfJa.js → loops-D3wdUkZV.js} +0 -0
  220. /package/client/dist/assets/{loops-D6D3XHjj.js → loops-DDs4KPu_.js} +0 -0
  221. /package/client/dist/assets/{loops-LIgO_-eM.js → loops-DKzYFcq6.js} +0 -0
  222. /package/client/dist/assets/{loops-DJNhp17p2.js → loops-Du-bNVpc2.js} +0 -0
  223. /package/client/dist/assets/{loops-vhJdbcz92.js → loops-DvGbSfxw2.js} +0 -0
  224. /package/client/dist/assets/{mcp-XJNRfxVp2.js → mcp-BG-OCJca2.js} +0 -0
  225. /package/client/dist/assets/{mcp-DtbxuH3V2.js → mcp-CbJ04oFS2.js} +0 -0
  226. /package/client/dist/assets/{mcp-sRmWE8WJ.js → mcp-CrWdq5db.js} +0 -0
  227. /package/client/dist/assets/{mcp-H-gnQUdN.js → mcp-Dd844F5_.js} +0 -0
  228. /package/client/dist/assets/{mcp-Dcl-XxkR.js → mcp-DfmzoT5t.js} +0 -0
  229. /package/client/dist/assets/{mcp-CjVKXAhD2.js → mcp-DyULurYS2.js} +0 -0
  230. /package/client/dist/assets/{mcp-N1nzp8oL.js → mcp-zSJ2TsJj.js} +0 -0
  231. /package/client/dist/assets/{nav-EI_OhEAo.js → nav-BFCUtHUQ2.js} +0 -0
  232. /package/client/dist/assets/{nav-CGy4yx3C.js → nav-BouzorQ5.js} +0 -0
  233. /package/client/dist/assets/{nav-40HzpIFb.js → nav-Bpr-K0tD.js} +0 -0
  234. /package/client/dist/assets/{nav-B6PkGfTk2.js → nav-CpFrRDXd2.js} +0 -0
  235. /package/client/dist/assets/{nav-CXEDcRdv2.js → nav-Dkv4Vlr_2.js} +0 -0
  236. /package/client/dist/assets/{nav-8yPhBn00.js → nav-GG6azCbh.js} +0 -0
  237. /package/client/dist/assets/{nav-BHqMLrS72.js → nav-prHkMqcr.js} +0 -0
  238. /package/client/dist/assets/{setup-BuQNKztt2.js → setup-1vN48B-m2.js} +0 -0
  239. /package/client/dist/assets/{setup-BwMovM9z2.js → setup-BmJ0Kx16.js} +0 -0
  240. /package/client/dist/assets/{setup-BXpw07r-.js → setup-DQWsu2TR.js} +0 -0
  241. /package/client/dist/assets/{setup-D84y3wx-.js → setup-JeBwoXoM.js} +0 -0
  242. /package/client/dist/assets/{setup-DU0ULX8q2.js → setup-T_ApeQDV2.js} +0 -0
  243. /package/client/dist/assets/{setup-BiduSX9d.js → setup-mXnzww4f.js} +0 -0
  244. /package/client/dist/assets/{setup-7J8TjG4O.js → setup-q0wRHUp1.js} +0 -0
  245. /package/client/dist/assets/{specs-CWtKcVDC.js → specs-BGRKhM8t.js} +0 -0
  246. /package/client/dist/assets/{specs-CBDvEYKv.js → specs-C6J2zrLM.js} +0 -0
  247. /package/client/dist/assets/{specs-D2clrt2c2.js → specs-CT8exGOy2.js} +0 -0
  248. /package/client/dist/assets/{specs-C9Sd7BhH2.js → specs-Ce5y2iFc2.js} +0 -0
  249. /package/client/dist/assets/{specs-CIPr9reA2.js → specs-DZDoZsDq.js} +0 -0
  250. /package/client/dist/assets/{specs-PozUNhqb.js → specs-DbtIzV8a.js} +0 -0
  251. /package/client/dist/assets/{specs-DTplt2XM.js → specs-GkiL-snY.js} +0 -0
  252. /package/client/dist/assets/{terminal-xGbCZJHg.js → terminal-BNSg2lR7.js} +0 -0
  253. /package/client/dist/assets/{terminal-De2Brd23.js → terminal-BTpE7iki.js} +0 -0
  254. /package/client/dist/assets/{terminal-BRm03iFA2.js → terminal-Cvabyzm42.js} +0 -0
  255. /package/client/dist/assets/{terminal-DxQWCqSD2.js → terminal-D0z12_sJ2.js} +0 -0
  256. /package/client/dist/assets/{terminal-Z7KsDM_n.js → terminal-DDk_bTw0.js} +0 -0
  257. /package/client/dist/assets/{terminal-cqfd3EDV.js → terminal-DEyt0prF.js} +0 -0
  258. /package/client/dist/assets/{terminal-WSAvHXoU.js → terminal-a6Uq8B0p.js} +0 -0
  259. /package/client/dist/assets/{tickets-C3b8cHhd2.js → tickets-BIxU86j12.js} +0 -0
  260. /package/client/dist/assets/{tickets-SuX_HkK4.js → tickets-C2BhwzwR.js} +0 -0
  261. /package/client/dist/assets/{tickets-DTHo9c3C.js → tickets-D1IeX2EW.js} +0 -0
  262. /package/client/dist/assets/{tickets-BoXom7l8.js → tickets-DZut8Ryt.js} +0 -0
  263. /package/client/dist/assets/{tickets-CWYFvx1W2.js → tickets-LfK6LzKl2.js} +0 -0
  264. /package/client/dist/assets/{tickets-CFXnJsYF.js → tickets-dQfsc7Ph.js} +0 -0
  265. /package/client/dist/assets/{tickets-B7zT4IRf.js → tickets-sVun0I6v.js} +0 -0
@@ -1,8 +1,42 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.jobsTools = jobsTools;
4
7
  const zod_1 = require("zod");
5
8
  const types_1 = require("./types");
9
+ const path_1 = __importDefault(require("path"));
10
+ const transient_children_1 = require("../../transient-children");
11
+ function resolveBackgroundCwd(projectRoot, cwd) {
12
+ const resolved = path_1.default.resolve(projectRoot, cwd && cwd.trim() ? cwd : '.');
13
+ const root = path_1.default.resolve(projectRoot);
14
+ if (resolved !== root && !resolved.startsWith(root + path_1.default.sep)) {
15
+ throw new Error('background_start cwd must stay within the selected project.');
16
+ }
17
+ return resolved;
18
+ }
19
+ function backgroundHooks(broadcast) {
20
+ return {
21
+ onStarted: (process) => broadcast({
22
+ type: 'background_process.started',
23
+ process,
24
+ projectId: process.projectId,
25
+ timestamp: new Date().toISOString(),
26
+ }),
27
+ onOutput: (event) => broadcast({
28
+ type: 'background_process.output',
29
+ ...event,
30
+ timestamp: new Date().toISOString(),
31
+ }),
32
+ onExited: (process) => broadcast({
33
+ type: 'background_process.exited',
34
+ process,
35
+ projectId: process.projectId,
36
+ timestamp: new Date().toISOString(),
37
+ }),
38
+ };
39
+ }
6
40
  /**
7
41
  * Jobs domain facade. Maps every in-scope jobs/queue action to its REST
8
42
  * endpoint on the loopback API (server/project-router-jobs.ts, plus the
@@ -20,6 +54,7 @@ function jobsTools() {
20
54
  title: 'Jobs & Queue',
21
55
  description: 'Manage a project\'s AI-pipeline job queue and individual jobs. ' +
22
56
  'Actions: list, get, queue, spawn (ai-spawn — enqueues an arbitrary slash-command job, returns {jobId,position}, async; from the in-app agent chat the engine defaults to your conversation\'s provider — pass aiEngine to override), ' +
57
+ 'background_start (operate-level shell command tied to the current agent chat; requires explicit user confirmation before use), background_kill, ' +
23
58
  'cancel (destructive — cancels a running/queued job or deletes a terminal one), ' +
24
59
  'purge (destructive — bulk-delete persisted job rows in a date range), ' +
25
60
  'pause / resume (queue), reorder (queued-job order), priority (change a queued job\'s priority), ' +
@@ -34,7 +69,7 @@ function jobsTools() {
34
69
  const action = a.action;
35
70
  if (['cancel', 'purge'].includes(action))
36
71
  return 'destructive';
37
- if (['spawn', 'interactive_turn'].includes(action))
72
+ if (['spawn', 'interactive_turn', 'background_start', 'background_kill'].includes(action))
38
73
  return 'ai-spawn';
39
74
  if (['pause', 'resume', 'reorder', 'priority', 'finalize'].includes(action))
40
75
  return 'write';
@@ -64,6 +99,8 @@ function jobsTools() {
64
99
  'default_spec_model',
65
100
  'interactive_turn',
66
101
  'finalize',
102
+ 'background_start',
103
+ 'background_kill',
67
104
  ])
68
105
  .describe('Operation to perform'),
69
106
  projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
@@ -99,6 +136,9 @@ function jobsTools() {
99
136
  .describe('reorder: exact set of currently-queued job ids in the desired order; compare: exactly 2 job ids'),
100
137
  // ── interactive_turn ──
101
138
  text: zod_1.z.string().optional().describe('Prompt text for interactive_turn'),
139
+ chatId: zod_1.z.string().optional().describe('Agent chat conversation id for background_start/background_kill ownership; defaults to the in-app agent conversation that called the tool'),
140
+ cwd: zod_1.z.string().optional().describe('Optional cwd for background_start; resolved inside the selected project root'),
141
+ pid: zod_1.z.number().optional().describe('Background process pid for background_kill'),
102
142
  // ── export ──
103
143
  format: zod_1.z.enum(['json', 'csv']).optional().describe('Export format (default json)'),
104
144
  // ── default_spec_model ──
@@ -270,6 +310,31 @@ function jobsTools() {
270
310
  throw new Error('finalize requires a "jobId".');
271
311
  return (0, types_1.apiCall)(ctx, 'POST', `${base}/jobs/${encodeURIComponent(id)}/finalize`);
272
312
  }
313
+ case 'background_start': {
314
+ const command = args.command;
315
+ if (!command || !command.trim())
316
+ throw new Error('background_start requires a "command".');
317
+ const chatId = args.chatId ?? ctx.originConversationId ?? undefined;
318
+ if (!chatId || !chatId.trim())
319
+ throw new Error('background_start requires a "chatId".');
320
+ const projectCtx = (0, types_1.requireProject)(ctx, args.projectId);
321
+ const cwd = resolveBackgroundCwd(projectCtx.project.path, args.cwd);
322
+ const process = (0, transient_children_1.startBackgroundProcess)(command.trim(), cwd, chatId.trim(), projectCtx.project.id, backgroundHooks(ctx.broadcast));
323
+ return { ok: true, process };
324
+ }
325
+ case 'background_kill': {
326
+ const pid = args.pid;
327
+ if (typeof pid !== 'number' || !Number.isFinite(pid))
328
+ throw new Error('background_kill requires a numeric "pid".');
329
+ const chatId = args.chatId ?? ctx.originConversationId ?? undefined;
330
+ if (!chatId || !chatId.trim())
331
+ throw new Error('background_kill requires a "chatId".');
332
+ const projectCtx = (0, types_1.requireProject)(ctx, args.projectId);
333
+ const killed = (0, transient_children_1.killOwnedBackgroundProcess)(pid, { projectId: projectCtx.project.id, chatId: chatId.trim() });
334
+ if (!killed)
335
+ throw new Error('background_kill requires a registered pid in the same project/chat.');
336
+ return { ok: true, pid, status: 'killing' };
337
+ }
273
338
  default:
274
339
  throw new Error(`Unknown action "${action}".`);
275
340
  }
@@ -20,6 +20,8 @@ function prDeliveryContinuesTickets(raw, ticketIds) {
20
20
  return false;
21
21
  if (typeof raw.branch !== 'string' || !raw.branch)
22
22
  return false;
23
+ if (raw.prState !== 'pr-created')
24
+ return false;
23
25
  if (!Array.isArray(raw.ticketIds))
24
26
  return false;
25
27
  const covered = new Set(raw.ticketIds.filter((id) => typeof id === 'number'));
@@ -11,6 +11,8 @@ exports.resolveStartupPath = resolveStartupPath;
11
11
  exports.parseLoginShellOutput = parseLoginShellOutput;
12
12
  exports.augmentPathFromLoginShell = augmentPathFromLoginShell;
13
13
  exports.parseLoginShellEnv = parseLoginShellEnv;
14
+ exports.augmentEnvFromLoginShell = augmentEnvFromLoginShell;
15
+ exports.augmentEnvFromLoginShellSync = augmentEnvFromLoginShellSync;
14
16
  exports.augmentAuthEnvFromLoginShell = augmentAuthEnvFromLoginShell;
15
17
  exports.getPathDiagnostic = getPathDiagnostic;
16
18
  exports.__resetPathResolverForTest = __resetPathResolverForTest;
@@ -464,6 +466,24 @@ exports.AUTH_ENV_VARS = [
464
466
  'GOOGLE_CLOUD_LOCATION',
465
467
  'GOOGLE_APPLICATION_CREDENTIALS',
466
468
  ];
469
+ const LOGIN_SHELL_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
470
+ const loginShellEnvProbeCache = new Set();
471
+ function normalizeLoginShellEnvNames(names) {
472
+ const out = [];
473
+ for (const raw of names) {
474
+ const name = String(raw).trim();
475
+ if (!LOGIN_SHELL_ENV_NAME_RE.test(name) || out.includes(name))
476
+ continue;
477
+ out.push(name);
478
+ }
479
+ return out;
480
+ }
481
+ function buildLoginShellEnvCommand(names) {
482
+ // `printf` reuses POSIX positional `$VAR` expansion (works in sh/bash/zsh).
483
+ const fmt = ENV_BEGIN + names.map((v) => `${v}=%s\n`).join('') + ENV_END;
484
+ const refs = names.map((v) => `"$${v}"`).join(' ');
485
+ return `printf '${fmt}' ${refs}`;
486
+ }
467
487
  /** Parse the `KEY=value` block emitted between the env sentinels. */
468
488
  function parseLoginShellEnv(stdout) {
469
489
  const begin = stdout.indexOf(ENV_BEGIN);
@@ -486,27 +506,23 @@ function parseLoginShellEnv(stdout) {
486
506
  }
487
507
  return out;
488
508
  }
489
- /**
490
- * Spawn the user's login shell once and backfill any provider auth env vars it
491
- * exposes into `process.env` (only when not already set — never override an
492
- * explicit value). POSIX-only and runs REGARDLESS of bundle state (unlike the
493
- * PATH augmentation, which is skipped when the bundle is active): macOS desktop
494
- * builds bundle node/git, so the PATH login-shell probe is skipped, yet we still
495
- * need to recover the gemini key. Async, fire-and-forget — must not block
496
- * startup. No-op on Windows (GUI inherits user env) and in tests.
497
- */
498
- async function augmentAuthEnvFromLoginShell(opts = {}) {
509
+ /** Backfill arbitrary, pre-validated environment variable names from the user's
510
+ * login shell into `process.env` (only when not already set — never override an
511
+ * explicit value). POSIX-only and runs regardless of bundle state, so macOS GUI
512
+ * launches can recover tokens exported in `.zshrc`/`.bashrc` without hardcoding
513
+ * every possible package-manager/provider variable. No-op on Windows and tests. */
514
+ async function augmentEnvFromLoginShell(names, opts = {}) {
499
515
  if (process.platform === 'win32')
500
516
  return;
501
517
  if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true')
502
518
  return;
519
+ const wanted = normalizeLoginShellEnvNames(names).filter((name) => !process.env[name]);
520
+ if (wanted.length === 0)
521
+ return;
503
522
  const spawnFn = opts.spawnFn ?? child_process_1.spawn;
504
523
  const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
505
524
  const shell = process.env.SHELL || '/bin/sh';
506
- // `printf` reuses POSIX positional `$VAR` expansion (works in sh/bash/zsh).
507
- const fmt = ENV_BEGIN + exports.AUTH_ENV_VARS.map((v) => `${v}=%s\n`).join('') + ENV_END;
508
- const refs = exports.AUTH_ENV_VARS.map((v) => `"$${v}"`).join(' ');
509
- const command = `printf '${fmt}' ${refs}`;
525
+ const command = buildLoginShellEnvCommand(wanted);
510
526
  await new Promise((resolve) => {
511
527
  let child;
512
528
  try {
@@ -536,7 +552,7 @@ async function augmentAuthEnvFromLoginShell(opts = {}) {
536
552
  child.on('error', finish);
537
553
  child.on('close', () => {
538
554
  const recovered = parseLoginShellEnv(stdout);
539
- for (const key of exports.AUTH_ENV_VARS) {
555
+ for (const key of wanted) {
540
556
  const val = recovered[key];
541
557
  // Backfill only when unset/empty — never clobber an explicit value.
542
558
  if (val && !process.env[key])
@@ -546,6 +562,48 @@ async function augmentAuthEnvFromLoginShell(opts = {}) {
546
562
  });
547
563
  });
548
564
  }
565
+ /** Synchronous, cached variant for spawn-time env assembly. It probes each
566
+ * missing name at most once per process to keep loops from paying the login
567
+ * shell timeout on every step. */
568
+ function augmentEnvFromLoginShellSync(names, opts = {}) {
569
+ if (process.platform === 'win32')
570
+ return;
571
+ if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true')
572
+ return;
573
+ const wanted = normalizeLoginShellEnvNames(names).filter((name) => !process.env[name] && !loginShellEnvProbeCache.has(name));
574
+ if (wanted.length === 0)
575
+ return;
576
+ for (const name of wanted)
577
+ loginShellEnvProbeCache.add(name);
578
+ const spawnSyncFn = opts.spawnSyncFn ?? child_process_1.spawnSync;
579
+ const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
580
+ const shell = process.env.SHELL || '/bin/sh';
581
+ const command = buildLoginShellEnvCommand(wanted);
582
+ let res;
583
+ try {
584
+ res = spawnSyncFn(shell, ['-l', '-i', '-c', command], {
585
+ encoding: 'utf8',
586
+ stdio: ['ignore', 'pipe', 'pipe'],
587
+ timeout: timeoutMs,
588
+ });
589
+ }
590
+ catch {
591
+ return;
592
+ }
593
+ if (res.error)
594
+ return;
595
+ const stdout = typeof res.stdout === 'string' ? res.stdout : res.stdout?.toString('utf8') ?? '';
596
+ const recovered = parseLoginShellEnv(stdout);
597
+ for (const key of wanted) {
598
+ const val = recovered[key];
599
+ if (val && !process.env[key])
600
+ process.env[key] = val;
601
+ }
602
+ }
603
+ /** Startup backfill for the built-in provider auth variables. */
604
+ async function augmentAuthEnvFromLoginShell(opts = {}) {
605
+ await augmentEnvFromLoginShell(exports.AUTH_ENV_VARS, opts);
606
+ }
549
607
  function mergeLoginShellPath(rawPath) {
550
608
  const current = splitPath(process.env.PATH);
551
609
  const currentSet = new Set(current);
@@ -582,4 +640,5 @@ function __resetPathResolverForTest() {
582
640
  diagnostic = { pathSegments: [], pathSources: [], loginShellStatus: 'skipped' };
583
641
  warnedLoginShell = false;
584
642
  bundledRuntimesActive = false;
643
+ loginShellEnvProbeCache.clear();
585
644
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultExec = void 0;
4
4
  exports.parsePrUrl = parsePrUrl;
5
+ exports.pushBranch = pushBranch;
5
6
  exports.publishDraftPr = publishDraftPr;
6
7
  /**
7
8
  * App-owned "deliver a mutating loop's result as a DRAFT PR" primitive.
@@ -46,32 +47,33 @@ function parsePrUrl(stdout) {
46
47
  const m = stdout.match(/https?:\/\/\S+\/pull\/\d+/);
47
48
  return m ? m[0] : undefined;
48
49
  }
49
- async function publishDraftPr(exec, input) {
50
+ /** Push a committed delivery branch, including the gh-credential retry used by
51
+ * draft PR creation. This is also used for follow-up commits on an existing PR:
52
+ * that path must update the same remote PR branch without trying to create a
53
+ * second PR. */
54
+ async function pushBranch(exec, input) {
50
55
  const remote = input.remote ?? 'origin';
51
56
  const { repoDir, branch, baseBranch } = input;
52
- // 1. Push the branch (set upstream). Failure → local-only, never throw.
53
- // Guardrail: never force-push, never push the integration branch itself.
54
57
  const pushArgs = ['push', '-u', remote, branch];
55
58
  (0, git_guardrails_1.assertGitAllowed)('git', pushArgs, { protectedBranch: baseBranch });
56
59
  let push = await exec.run('git', pushArgs, repoDir);
57
60
  if (push.code !== 0 && isCredentialFailure(push)) {
58
- // The push failed authenticating over HTTPS — typically the machine's
59
- // configured credential helper (e.g. git-credential-osxkeychain) is not on
60
- // the app's PATH (GUI-launch PATH divergence: the sidecar inherits launchd's
61
- // PATH, not the user's login shell). Retry borrowing gh's credentials — the
62
- // SAME auth we use for `gh pr create` below, so if the PR can be created the
63
- // push can succeed — instead of depending on a machine credential helper.
64
- // `-c credential.helper=` first RESETS the (broken) inherited helper list,
65
- // then adds gh's. Harmless for SSH remotes (git ignores credential.helper).
66
- // The semantic push (refspec/remote) is unchanged, so the guardrail asserted
67
- // above still holds; no-op when gh is absent (retry fails the same way).
68
61
  const retry = await exec.run('git', ['-c', 'credential.helper=', '-c', 'credential.helper=!gh auth git-credential', ...pushArgs], repoDir);
69
62
  if (retry.code === 0)
70
63
  push = retry;
71
64
  }
72
- if (push.code !== 0) {
65
+ if (push.code !== 0)
73
66
  return { state: 'local-only', branch, reason: reasonFrom(push) };
74
- }
67
+ return { state: 'pushed', branch };
68
+ }
69
+ async function publishDraftPr(exec, input) {
70
+ const remote = input.remote ?? 'origin';
71
+ const { repoDir, branch, baseBranch } = input;
72
+ // 1. Push the branch (set upstream). Failure → local-only, never throw.
73
+ // Guardrail: never force-push, never push the integration branch itself.
74
+ const pushed = await pushBranch(exec, { repoDir, branch, baseBranch, remote });
75
+ if (pushed.state === 'local-only')
76
+ return pushed;
75
77
  // 2. Open a DRAFT PR. Failure (no gh / not authed / perms) → pushed, never throw.
76
78
  const pr = await exec.run('gh', ['pr', 'create', '--draft', '--base', baseBranch, '--head', branch, '--title', input.title, '--body', input.body], repoDir);
77
79
  if (pr.code !== 0) {
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveWorktreeEnvPassthrough = resolveWorktreeEnvPassthrough;
4
+ exports.applyWorktreeEnvPassthrough = applyWorktreeEnvPassthrough;
5
+ const db_1 = require("./db");
6
+ const path_resolver_1 = require("./path-resolver");
7
+ /** Resolve the per-project env passthrough overlay for a spawn.
8
+ *
9
+ * The project setting stores names only. Values are read from `sourceEnv` at
10
+ * spawn time so credentials can rotate without touching SQLite. When the
11
+ * source is `process.env`, missing configured names get one bounded login-shell
12
+ * recovery attempt first; unresolved names are still ignored instead of
13
+ * blocking the run.
14
+ */
15
+ function resolveWorktreeEnvPassthrough(db, sourceEnv = process.env) {
16
+ const out = {};
17
+ const names = (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
18
+ if (sourceEnv === process.env)
19
+ (0, path_resolver_1.augmentEnvFromLoginShellSync)(names);
20
+ for (const name of names) {
21
+ const value = sourceEnv[name];
22
+ if (value !== undefined)
23
+ out[name] = value;
24
+ }
25
+ return out;
26
+ }
27
+ function applyWorktreeEnvPassthrough(db, env, sourceEnv = process.env) {
28
+ const passthrough = resolveWorktreeEnvPassthrough(db, sourceEnv);
29
+ return Object.keys(passthrough).length === 0 ? env : { ...env, ...passthrough };
30
+ }
@@ -4,8 +4,10 @@ exports.parseWorktreePorcelain = parseWorktreePorcelain;
4
4
  exports.getProjectGitInfo = getProjectGitInfo;
5
5
  exports.compactCheckoutError = compactCheckoutError;
6
6
  exports.checkoutProjectBranch = checkoutProjectBranch;
7
+ exports.checkoutProjectReviewBranch = checkoutProjectReviewBranch;
7
8
  const child_process_1 = require("child_process");
8
9
  const file_provenance_1 = require("./file-provenance");
10
+ const integration_branch_1 = require("./integration-branch");
9
11
  // ─── Project git info + branch switch (Agent-Mode git bar) ────────────────────
10
12
  //
11
13
  // Read the repo's current branch / last commit / local branches, and check out
@@ -145,3 +147,58 @@ async function checkoutProjectBranch(repoDir, branch) {
145
147
  return { ok: false, error: compactCheckoutError(err instanceof Error ? err.message : 'git checkout failed') };
146
148
  }
147
149
  }
150
+ async function remoteBranchExists(repoDir, branch) {
151
+ try {
152
+ await git(repoDir, ['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${branch}`]);
153
+ return true;
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ /** Checkout a PR/review branch into the user's main repo. Unlike the small
160
+ * AgentGitBar switcher, this path may materialize a local tracking branch from
161
+ * `origin/<branch>` because a PR delivery can be remote-only after worktree
162
+ * cleanup or a fresh clone. It never overwrites dirty local work. */
163
+ async function checkoutProjectReviewBranch(repoDir, branch) {
164
+ const target = branch.trim();
165
+ if (!(0, integration_branch_1.isValidBranchName)(target))
166
+ return { ok: false, error: `Invalid branch name: ${branch}` };
167
+ let dirty = false;
168
+ try {
169
+ dirty = (await git(repoDir, ['status', '--porcelain'])) !== '';
170
+ }
171
+ catch (err) {
172
+ return { ok: false, error: err instanceof Error ? err.message : 'git status failed' };
173
+ }
174
+ if (dirty)
175
+ return { ok: false, error: 'Working tree has uncommitted changes. Commit or stash them before checkout.' };
176
+ // Best effort: refresh remote-tracking refs before deciding whether a branch
177
+ // can be materialized. Failure degrades to local-only checkout.
178
+ await git(repoDir, ['fetch', 'origin']).catch(() => { });
179
+ let branches;
180
+ try {
181
+ branches = await listLocalBranches(repoDir);
182
+ }
183
+ catch (err) {
184
+ return { ok: false, error: err instanceof Error ? err.message : 'git failed' };
185
+ }
186
+ try {
187
+ if (branches.includes(target)) {
188
+ await git(repoDir, ['checkout', target]);
189
+ }
190
+ else if (await remoteBranchExists(repoDir, target)) {
191
+ await git(repoDir, ['checkout', '-b', target, '--track', `origin/${target}`]);
192
+ }
193
+ else {
194
+ return { ok: false, error: `Unknown branch: ${target}` };
195
+ }
196
+ }
197
+ catch (err) {
198
+ return { ok: false, error: compactCheckoutError(err instanceof Error ? err.message : 'git checkout failed') };
199
+ }
200
+ // A failed ff-only pull should not undo a successful checkout; the branch is
201
+ // already where the user can inspect/fix it. Surface only hard checkout errors.
202
+ await git(repoDir, ['pull', '--ff-only']).catch(() => { });
203
+ return { ok: true };
204
+ }
@@ -29,6 +29,7 @@ const transient_children_1 = require("./transient-children");
29
29
  const telemetry_receiver_1 = require("./telemetry-receiver");
30
30
  const artifact_registry_1 = require("./artifact-registry");
31
31
  const workspace_resolution_1 = require("./workspace-resolution");
32
+ const project_env_1 = require("./project-env");
32
33
  const workspace_manager_1 = require("./workspace-manager");
33
34
  const ticket_store_1 = require("./ticket-store");
34
35
  const jira_sync_manager_1 = require("./jira/jira-sync-manager");
@@ -689,9 +690,10 @@ class ProjectRegistry {
689
690
  // `${ENV:-legacy}` defaults resolve inside the worktree, where only tracked
690
691
  // files exist — still reads the real project state. SPECRAILS_REPO_DIR stays
691
692
  // per-run (the worktree for isolated runs). Legacy projects keep process.env
692
- // byte-identical. See resolveLoopBaseEnv.
693
+ // byte-identical except for the explicit per-project passthrough overlay.
694
+ // See resolveLoopBaseEnv.
693
695
  const loopRunManager = new loop_run_manager_1.LoopRunManager(db, boundBroadcast, (0, loop_executors_1.createLoopExecutors)({
694
- env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }),
696
+ env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }, undefined, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
695
697
  }));
696
698
  const getTicketSpec = (ticketId) => {
697
699
  try {
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.registerJobsRoutes = registerJobsRoutes;
7
+ const path_1 = __importDefault(require("path"));
4
8
  const db_1 = require("./db");
5
9
  const queue_manager_1 = require("./queue-manager");
6
10
  const feature_flags_1 = require("./feature-flags");
@@ -12,6 +16,7 @@ const provider_selection_1 = require("./provider-selection");
12
16
  const loop_runs_store_1 = require("./loop-runs-store");
13
17
  const metrics_1 = require("./metrics");
14
18
  const ticket_store_1 = require("./ticket-store");
19
+ const transient_children_1 = require("./transient-children");
15
20
  const project_router_helpers_1 = require("./project-router-helpers");
16
21
  function registerJobsRoutes(deps) {
17
22
  const { router, registry, ctx, ticketPath } = deps;
@@ -86,6 +91,77 @@ function registerJobsRoutes(deps) {
86
91
  },
87
92
  });
88
93
  });
94
+ router.get('/:projectId/background-processes', (req, res) => {
95
+ const c = ctx(req);
96
+ const chatId = typeof req.query.chatId === 'string' && req.query.chatId.trim() ? req.query.chatId.trim() : undefined;
97
+ res.json({
98
+ processes: (0, transient_children_1.listBackgroundProcesses)({
99
+ projectId: c.project.id,
100
+ ...(chatId ? { chatId } : {}),
101
+ }),
102
+ });
103
+ });
104
+ router.post('/:projectId/background-processes', (req, res) => {
105
+ const { command, cwd, chatId } = req.body ?? {};
106
+ const c = ctx(req);
107
+ if (!command || typeof command !== 'string' || !command.trim()) {
108
+ res.status(400).json({ error: 'command is required' });
109
+ return;
110
+ }
111
+ if (!chatId || typeof chatId !== 'string' || !chatId.trim()) {
112
+ res.status(400).json({ error: 'chatId is required' });
113
+ return;
114
+ }
115
+ const resolvedCwd = path_1.default.resolve(c.project.path, typeof cwd === 'string' && cwd.trim() ? cwd : '.');
116
+ const root = path_1.default.resolve(c.project.path);
117
+ if (resolvedCwd !== root && !resolvedCwd.startsWith(root + path_1.default.sep)) {
118
+ res.status(400).json({ error: 'cwd must stay within the project' });
119
+ return;
120
+ }
121
+ try {
122
+ const process = (0, transient_children_1.startBackgroundProcess)(command.trim(), resolvedCwd, chatId.trim(), c.project.id, {
123
+ onStarted: (process) => c.broadcast({
124
+ type: 'background_process.started',
125
+ process,
126
+ projectId: process.projectId,
127
+ timestamp: new Date().toISOString(),
128
+ }),
129
+ onOutput: (event) => c.broadcast({
130
+ type: 'background_process.output',
131
+ ...event,
132
+ timestamp: new Date().toISOString(),
133
+ }),
134
+ onExited: (process) => c.broadcast({
135
+ type: 'background_process.exited',
136
+ process,
137
+ projectId: process.projectId,
138
+ timestamp: new Date().toISOString(),
139
+ }),
140
+ });
141
+ res.status(202).json({ ok: true, process });
142
+ }
143
+ catch (err) {
144
+ res.status(500).json({ error: err instanceof Error ? err.message : 'background process failed to start' });
145
+ }
146
+ });
147
+ router.delete('/:projectId/background-processes/:pid', (req, res) => {
148
+ const c = ctx(req);
149
+ const pid = Number(req.params.pid);
150
+ const chatId = typeof req.query.chatId === 'string' ? req.query.chatId : '';
151
+ if (!Number.isFinite(pid)) {
152
+ res.status(400).json({ error: 'pid must be numeric' });
153
+ return;
154
+ }
155
+ if (!chatId.trim()) {
156
+ res.status(400).json({ error: 'chatId is required' });
157
+ return;
158
+ }
159
+ if (!(0, transient_children_1.killOwnedBackgroundProcess)(pid, { projectId: c.project.id, chatId: chatId.trim() })) {
160
+ res.status(404).json({ error: 'background process not found for project/chat' });
161
+ return;
162
+ }
163
+ res.json({ ok: true, pid, status: 'killing' });
164
+ });
89
165
  // Returns the resolved default model for Add Spec + the full provider
90
166
  // allow-list so the modal can render its picker without maintaining its
91
167
  // own copy of the model lists. Source of truth is `server/spec-models.ts`.
@@ -76,7 +76,7 @@ function registerSettingsRoutes(deps) {
76
76
  res.json({ scope: merged });
77
77
  });
78
78
  router.patch('/:projectId/settings', (req, res) => {
79
- const { pipelineTelemetryEnabled, orchestratorModel, prePrompt, freestylePrePrompt } = req.body ?? {};
79
+ const { pipelineTelemetryEnabled, orchestratorModel, prePrompt, freestylePrePrompt, worktreeEnvPassthrough } = req.body ?? {};
80
80
  const patch = {};
81
81
  if (pipelineTelemetryEnabled !== undefined) {
82
82
  patch.pipelineTelemetryEnabled = Boolean(pipelineTelemetryEnabled);
@@ -103,6 +103,15 @@ function registerSettingsRoutes(deps) {
103
103
  }
104
104
  patch.freestylePrePrompt = freestylePrePrompt;
105
105
  }
106
+ if (worktreeEnvPassthrough !== undefined) {
107
+ try {
108
+ patch.worktreeEnvPassthrough = (0, db_1.normalizeWorktreeEnvPassthrough)(worktreeEnvPassthrough);
109
+ }
110
+ catch (err) {
111
+ res.status(400).json({ error: err instanceof Error ? err.message : 'invalid worktreeEnvPassthrough' });
112
+ return;
113
+ }
114
+ }
106
115
  if (req.body?.integrationBranch !== undefined) {
107
116
  const ib = req.body.integrationBranch;
108
117
  if (typeof ib !== 'string') {
@@ -33,6 +33,7 @@ const binary_probe_1 = require("./binary-probe");
33
33
  const workspace_manager_1 = require("./workspace-manager");
34
34
  const claude_trust_1 = require("./claude-trust");
35
35
  const workspace_resolution_1 = require("./workspace-resolution");
36
+ const project_env_1 = require("./project-env");
36
37
  const framework_manager_1 = require("./framework-manager");
37
38
  const openspec_shim_1 = require("./openspec-shim");
38
39
  const artifact_registry_1 = require("./artifact-registry");
@@ -1650,6 +1651,13 @@ class QueueManager {
1650
1651
  // claude honours OTEL_* env vars natively; codex does not and instead
1651
1652
  // gets signals synthesised by the codex-otel-bridge attached below.
1652
1653
  let spawnEnv = process.env;
1654
+ // Per-project worktree/job env passthrough. The setting stores names only;
1655
+ // values are read from the server env (with login-shell recovery for missing
1656
+ // names) at spawn time. Apply this before Specrails' own env overlays so
1657
+ // internal SPECRAILS_* control-plane values always win.
1658
+ if (this._db) {
1659
+ spawnEnv = (0, project_env_1.applyWorktreeEnvPassthrough)(this._db, spawnEnv);
1660
+ }
1653
1661
  const telemetryEnabled = !!(this._projectId && this._db && (0, db_1.getProjectSettings)(this._db).pipelineTelemetryEnabled);
1654
1662
  // Resolve the framework version ONCE at spawn time — `framework/current`
1655
1663
  // is read from `~/.specrails/framework/current`. A concurrent atomic swap
@@ -1668,7 +1676,7 @@ class QueueManager {
1668
1676
  if (frameworkVersion)
1669
1677
  extra['specrails.framework_version'] = frameworkVersion;
1670
1678
  spawnEnv = {
1671
- ...process.env,
1679
+ ...spawnEnv,
1672
1680
  ...buildTelemetryEnv(jobId, this._projectId, this._desktopPort, extra, adapter.id),
1673
1681
  };
1674
1682
  }