specrails-desktop 2.16.0 → 2.18.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 (424) hide show
  1. package/client/dist/assets/{ActivityFeedPage-C_clKk8d.js → ActivityFeedPage-mGnv5SOe.js} +1 -1
  2. package/client/dist/assets/AgentsPage-BH1isg5j.js +86 -0
  3. package/client/dist/assets/AnalyticsPage-ByyshYRI.js +1 -0
  4. package/client/dist/assets/{BarChart-Dq1h8eYR.js → BarChart-BqHUOnl9.js} +3 -3
  5. package/client/dist/assets/CodePage-CU7T_HYs.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-CYhXhJoQ.js → DesktopAnalyticsPage-CR-m97Ui.js} +1 -1
  7. package/client/dist/assets/DocsDialog-CEmZp_8V.js +11 -0
  8. package/client/dist/assets/DocsPage-DYbAQ1AT.js +11 -0
  9. package/client/dist/assets/{ExportDropdown-DdB2ncYh.js → ExportDropdown-C-o1Q4ef.js} +1 -1
  10. package/client/dist/assets/IntegrationsPage-C6fKdwVH.js +3 -0
  11. package/client/dist/assets/JobDetailPage-CKfIfBgE.js +16 -0
  12. package/client/dist/assets/JobsPage-nBng0riZ.js +1 -0
  13. package/client/dist/assets/LoopBuilderPage-Co_TYqYk.js +7 -0
  14. package/client/dist/assets/LoopsPage-a2hd37Ya.js +1 -0
  15. package/client/dist/assets/{activity-Ca2XoBi-.js → activity-D8vQJrpW.js} +1 -1
  16. package/client/dist/assets/{addon-image-biWX2UUm.js → addon-image-Ba-TY3wr.js} +1 -1
  17. package/client/dist/assets/{addon-ligatures-D4EiDb5r.js → addon-ligatures-T4ARLiuu.js} +1 -1
  18. package/client/dist/assets/{addon-webgl-CDCIZQ03.js → addon-webgl-Cd73n8eh.js} +1 -1
  19. package/client/dist/assets/{addspec-DE_VfIyP.js → addspec-BloGn_ey.js} +1 -1
  20. package/client/dist/assets/agent-9WdSxPU5.js +1 -0
  21. package/client/dist/assets/agent-ASIyVcg0.js +1 -0
  22. package/client/dist/assets/agent-Bm2tPrdr.js +1 -0
  23. package/client/dist/assets/agent-C4bO1l_v.js +1 -0
  24. package/client/dist/assets/agent-CgKsh88Z2.js +1 -0
  25. package/client/dist/assets/agent-DMUrz9ZS2.js +1 -0
  26. package/client/dist/assets/agent-Dwkz5mPS.js +1 -0
  27. package/client/dist/assets/agent-pO8x4jbR.js +1 -0
  28. package/client/dist/assets/{agents-DcoUUFZl.js → agents-52t8NVpS.js} +1 -1
  29. package/client/dist/assets/{agents-DCX9D3-Q.js → agents-BAxAwHy-.js} +1 -1
  30. package/client/dist/assets/{agents-Jc-CjBDB.js → agents-BUYBb825.js} +1 -1
  31. package/client/dist/assets/{agents-CIhRSOna.js → agents-BjT_xLjv.js} +1 -1
  32. package/client/dist/assets/{agents-BYjUOrLD.js → agents-C07al8rI.js} +1 -1
  33. package/client/dist/assets/{agents-1lhqEcXH.js → agents-DfGvYtpq2.js} +1 -1
  34. package/client/dist/assets/{agents-CQlu1nOm.js → agents-Dli56gr82.js} +1 -1
  35. package/client/dist/assets/{agents-DVYpVGur.js → agents-Eqw_AQ6H.js} +1 -1
  36. package/client/dist/assets/{agentstudio-DCTpIqOx.js → agentstudio-CZm2rOzt.js} +1 -1
  37. package/client/dist/assets/{aiedit-Dirqf3Bm.js → aiedit-DYtnZU9H.js} +1 -1
  38. package/client/dist/assets/{analytics-BE49LbUk.js → analytics-B4FJ_pry.js} +1 -1
  39. package/client/dist/assets/{attachments-C2JPppoP.js → attachments-Ci6_42Kh.js} +1 -1
  40. package/client/dist/assets/{browser-Dpo_ug4c.js → browser-ZB1afzjZ.js} +1 -1
  41. package/client/dist/assets/{chat-mq1x9cxZ.js → chat-VMflhzxn.js} +1 -1
  42. package/client/dist/assets/chunk-BNv3lrIs.js +1 -0
  43. package/client/dist/assets/{code-DtIatVES.js → code-wtcs0ivb.js} +1 -1
  44. package/client/dist/assets/commands-Bc8y9eNo2.js +1 -0
  45. package/client/dist/assets/commands-Bl3-KVfj.js +1 -0
  46. package/client/dist/assets/commands-C5WQ9blx2.js +1 -0
  47. package/client/dist/assets/commands-CM99Upsz2.js +1 -0
  48. package/client/dist/assets/commands-Cc2WpuXa.js +1 -0
  49. package/client/dist/assets/commands-CckaBWdV.js +1 -0
  50. package/client/dist/assets/commands-DASXJD-l.js +1 -0
  51. package/client/dist/assets/commands-k9wbNYoi.js +1 -0
  52. package/client/dist/assets/{common-kftoE0Q-.js → common-BPQQkgJZ.js} +1 -1
  53. package/client/dist/assets/{cssMode-DSHXamUH.js → cssMode-DsArsSCT.js} +1 -1
  54. package/client/dist/assets/{dashboard-BQ3Oa8CE.js → dashboard-Cyzp4ox9.js} +1 -1
  55. package/client/dist/assets/{dist-js-Ele_sHO7.js → dist-js-BHhtSerX.js} +1 -1
  56. package/client/dist/assets/{dist-js-D27I-bfi.js → dist-js-CUegfF6k.js} +1 -1
  57. package/client/dist/assets/{editor.api2-Cwla1nvV.js → editor.api2-T7u8kQWG.js} +1 -1
  58. package/client/dist/assets/{editor.main-Cd8qBRAT.js → editor.main-G3Sl9C67.js} +2 -2
  59. package/client/dist/assets/{explore-CBYU76JD.js → explore-BKVV5JSu.js} +1 -1
  60. package/client/dist/assets/{freemarker2-CkEKTBm2.js → freemarker2-pBzQNgFi.js} +1 -1
  61. package/client/dist/assets/{handlebars-BRGx0JpL.js → handlebars-CtBTCnBI.js} +1 -1
  62. package/client/dist/assets/{html-BfRIaGgN.js → html-Dx3DYBzY.js} +1 -1
  63. package/client/dist/assets/{htmlMode-BTpSnsDE.js → htmlMode-In7frFOo.js} +1 -1
  64. package/client/dist/assets/index-B0FxQALR.css +2 -0
  65. package/client/dist/assets/index-B7vB20Jw.js +152 -0
  66. package/client/dist/assets/{integrations-jJGaKwff.js → integrations-CuWnMR5L.js} +1 -1
  67. package/client/dist/assets/{javascript-DMmE2kKN.js → javascript-BKcn-YkN.js} +1 -1
  68. package/client/dist/assets/{jira-BwACz875.js → jira-DXMoAKWD.js} +1 -1
  69. package/client/dist/assets/{jobs-DRInW0AE.js → jobs---dwa4Nf.js} +1 -1
  70. package/client/dist/assets/{jsonMode-hYuaUleY.js → jsonMode-jNREoQ3C.js} +1 -1
  71. package/client/dist/assets/{lib-qdRfWwE1.js → lib-PQoHlR5u.js} +2 -2
  72. package/client/dist/assets/{liquid-C-VbuTr7.js → liquid-CtRkGqBu.js} +1 -1
  73. package/client/dist/assets/{loops-DfGadne0.js → loops-D8Iu6Jss.js} +1 -1
  74. package/client/dist/assets/{lspLanguageFeatures-BALx-vjn.js → lspLanguageFeatures-Cl3gvD_W.js} +1 -1
  75. package/client/dist/assets/mcp-B4irfLJ2.js +1 -0
  76. package/client/dist/assets/mcp-B6nwJJmR.js +1 -0
  77. package/client/dist/assets/mcp-BOoEGTSQ.js +1 -0
  78. package/client/dist/assets/mcp-BP7PpbDr2.js +1 -0
  79. package/client/dist/assets/mcp-CIH0pH1K.js +1 -0
  80. package/client/dist/assets/mcp-CNgFiTEh.js +1 -0
  81. package/client/dist/assets/mcp-D3E4_Mmk2.js +1 -0
  82. package/client/dist/assets/mcp-mupPuPDO2.js +1 -0
  83. package/client/dist/assets/{mdx-Cs8MNCzn.js → mdx-B_nJldHz.js} +1 -1
  84. package/client/dist/assets/{monaco.contribution-CIvCAJs4.js → monaco.contribution-PUeaZk8T.js} +2 -2
  85. package/client/dist/assets/{nav-CCz4krPP.js → nav-DE3yWQw6.js} +1 -1
  86. package/client/dist/assets/{python-CGShLxuI.js → python-DS1Ce-A6.js} +1 -1
  87. package/client/dist/assets/{razor-CvOBQGd5.js → razor-L41Qg9OK.js} +1 -1
  88. package/client/dist/assets/settings-BKmORxlB2.js +1 -0
  89. package/client/dist/assets/settings-BVv2JgQ8.js +1 -0
  90. package/client/dist/assets/settings-Bghm3pAV.js +1 -0
  91. package/client/dist/assets/settings-BuC_aplX.js +1 -0
  92. package/client/dist/assets/settings-Bw_Qk8E32.js +1 -0
  93. package/client/dist/assets/settings-CAKgJv7p.js +1 -0
  94. package/client/dist/assets/settings-CEUgk1Ck.js +1 -0
  95. package/client/dist/assets/settings-C_PDE20s2.js +1 -0
  96. package/client/dist/assets/setup-4us-Bz3H.js +1 -0
  97. package/client/dist/assets/setup-BYoDYoPY2.js +1 -0
  98. package/client/dist/assets/setup-Bf9eLKxU.js +1 -0
  99. package/client/dist/assets/setup-C5UbM0RP.js +1 -0
  100. package/client/dist/assets/setup-CqGrWb6G.js +1 -0
  101. package/client/dist/assets/setup-JFkWWZnR.js +1 -0
  102. package/client/dist/assets/setup-faMApo0U2.js +1 -0
  103. package/client/dist/assets/setup-tloaTobG2.js +1 -0
  104. package/client/dist/assets/{specs-C_8j4ELZ.js → specs-BYeAbIEy.js} +1 -1
  105. package/client/dist/assets/{terminal-Cj4HzP3p.js → terminal-DFPNRLKp.js} +1 -1
  106. package/client/dist/assets/{tickets-DBQXMYYA.js → tickets-CkfbISuK.js} +1 -1
  107. package/client/dist/assets/{tsMode-04uOpiiP.js → tsMode-BC1g186U.js} +1 -1
  108. package/client/dist/assets/{typescript-CX0CwyIZ.js → typescript-BGlRGvJW.js} +1 -1
  109. package/client/dist/assets/upload-PnJDPHZH.js +1 -0
  110. package/client/dist/assets/{value-BRVOAvhY.js → value-BTDefcOV.js} +1 -1
  111. package/client/dist/assets/{workers-yr2u4c3C.js → workers-CXcXtO3b.js} +1 -1
  112. package/client/dist/assets/{xml-C0OFlt5X.js → xml-JdZZaIhJ.js} +1 -1
  113. package/client/dist/assets/{yaml-DgabaPs5.js → yaml-Cm82BVBt.js} +1 -1
  114. package/client/dist/index.html +30 -28
  115. package/docs/README.md +2 -0
  116. package/docs/guide/de/integrations/5-mcp-server.md +74 -0
  117. package/docs/guide/de/integrations/6-agent-chat.md +64 -0
  118. package/docs/guide/en/integrations/5-mcp-server.md +74 -0
  119. package/docs/guide/en/integrations/6-agent-chat.md +64 -0
  120. package/docs/guide/es/integrations/5-mcp-server.md +74 -0
  121. package/docs/guide/es/integrations/6-agent-chat.md +64 -0
  122. package/docs/guide/fr/integrations/5-mcp-server.md +74 -0
  123. package/docs/guide/fr/integrations/6-agent-chat.md +64 -0
  124. package/docs/guide/it/integrations/5-mcp-server.md +74 -0
  125. package/docs/guide/it/integrations/6-agent-chat.md +64 -0
  126. package/docs/guide/ja/integrations/5-mcp-server.md +74 -0
  127. package/docs/guide/ja/integrations/6-agent-chat.md +64 -0
  128. package/docs/guide/pt/integrations/5-mcp-server.md +74 -0
  129. package/docs/guide/pt/integrations/6-agent-chat.md +64 -0
  130. package/docs/guide/zh/integrations/5-mcp-server.md +74 -0
  131. package/docs/guide/zh/integrations/6-agent-chat.md +64 -0
  132. package/docs/mcp.md +249 -0
  133. package/package.json +8 -5
  134. package/server/dist/agent-chat-manager.js +224 -0
  135. package/server/dist/agent-chat-router.js +148 -0
  136. package/server/dist/agent-cwd-manager.js +94 -0
  137. package/server/dist/agent-mcp-config.js +208 -0
  138. package/server/dist/agent-store.js +59 -0
  139. package/server/dist/agent-tier.js +86 -0
  140. package/server/dist/desktop-db.js +27 -0
  141. package/server/dist/index.js +38 -0
  142. package/server/dist/mcp/guide.js +82 -0
  143. package/server/dist/mcp/index.js +21 -0
  144. package/server/dist/mcp/mcp-admin-router.js +71 -0
  145. package/server/dist/mcp/mcp-server.js +143 -0
  146. package/server/dist/mcp/mcp-tiers.js +48 -0
  147. package/server/dist/mcp/mcp-token.js +119 -0
  148. package/server/dist/mcp/resources.js +24 -0
  149. package/server/dist/mcp/tools/agents.js +319 -0
  150. package/server/dist/mcp/tools/analytics.js +168 -0
  151. package/server/dist/mcp/tools/app-settings.js +81 -0
  152. package/server/dist/mcp/tools/catalog.js +45 -0
  153. package/server/dist/mcp/tools/chat.js +147 -0
  154. package/server/dist/mcp/tools/code.js +145 -0
  155. package/server/dist/mcp/tools/jira.js +222 -0
  156. package/server/dist/mcp/tools/jobs.js +258 -0
  157. package/server/dist/mcp/tools/loops.js +229 -0
  158. package/server/dist/mcp/tools/meta.js +96 -0
  159. package/server/dist/mcp/tools/plugins.js +83 -0
  160. package/server/dist/mcp/tools/projects.js +62 -0
  161. package/server/dist/mcp/tools/rails.js +181 -0
  162. package/server/dist/mcp/tools/setup.js +167 -0
  163. package/server/dist/mcp/tools/specs.js +327 -0
  164. package/server/dist/mcp/tools/types.js +156 -0
  165. package/server/dist/mcp/tools/watch.js +93 -0
  166. package/server/dist/mobile/mobile-event-bus.js +2 -0
  167. package/server/dist/project-router-setup.js +15 -1
  168. package/server/dist/project-router-tickets.js +2 -2
  169. package/server/dist/providers/codex-adapter.js +19 -3
  170. package/server/dist/rails-router.js +5 -0
  171. package/server/dist/setup-manager.js +28 -0
  172. package/server/dist/spawn-lifecycle.js +21 -13
  173. package/server/dist/workspace-manager.js +11 -0
  174. package/client/dist/assets/AgentsPage-jcPUtPhP.js +0 -86
  175. package/client/dist/assets/AnalyticsPage-DZkVU3yd.js +0 -1
  176. package/client/dist/assets/CodePage-BcbZT88K.js +0 -2
  177. package/client/dist/assets/DocsDialog-CS3Syf61.js +0 -11
  178. package/client/dist/assets/DocsPage-l3ZcTO2E.js +0 -11
  179. package/client/dist/assets/IntegrationsPage-CJ_Om1zu.js +0 -3
  180. package/client/dist/assets/JobDetailPage-D5Cxw0md.js +0 -16
  181. package/client/dist/assets/JobsPage-3v_iX83r.js +0 -1
  182. package/client/dist/assets/LoopBuilderPage-4s_adZy5.js +0 -7
  183. package/client/dist/assets/LoopsPage-DA94-o0j.js +0 -1
  184. package/client/dist/assets/chunk-CilyBKbf.js +0 -1
  185. package/client/dist/assets/commands-B9XEyMj-.js +0 -1
  186. package/client/dist/assets/commands-BI1Ta-E7.js +0 -1
  187. package/client/dist/assets/commands-BdI0CtOL.js +0 -1
  188. package/client/dist/assets/commands-C2ewlyXo.js +0 -1
  189. package/client/dist/assets/commands-CwypztRT.js +0 -1
  190. package/client/dist/assets/commands-DSX3yJyr.js +0 -1
  191. package/client/dist/assets/commands-Ic1DJDxm.js +0 -1
  192. package/client/dist/assets/commands-yNn4IA4C.js +0 -1
  193. package/client/dist/assets/index-BRMyOS1B.css +0 -2
  194. package/client/dist/assets/index-meTWPBzY.js +0 -144
  195. package/client/dist/assets/settings-0m2iuTzd.js +0 -1
  196. package/client/dist/assets/settings-BA1V6KdK.js +0 -1
  197. package/client/dist/assets/settings-BN7OusER.js +0 -1
  198. package/client/dist/assets/settings-Bi5FFkIw.js +0 -1
  199. package/client/dist/assets/settings-CRJxyTNN.js +0 -1
  200. package/client/dist/assets/settings-DyBxunCh.js +0 -1
  201. package/client/dist/assets/settings-fk-WlnUt.js +0 -1
  202. package/client/dist/assets/settings-iQe5Dim-.js +0 -1
  203. package/client/dist/assets/setup-BOmQ4vh2.js +0 -1
  204. package/client/dist/assets/setup-BwO2-kuK.js +0 -1
  205. package/client/dist/assets/setup-CUTNWz-0.js +0 -1
  206. package/client/dist/assets/setup-Cf3hjChU.js +0 -1
  207. package/client/dist/assets/setup-CsuXrbJ2.js +0 -1
  208. package/client/dist/assets/setup-Df4cJOqu.js +0 -1
  209. package/client/dist/assets/setup-Do0fWx6W.js +0 -1
  210. package/client/dist/assets/setup-GlkDhBlJ.js +0 -1
  211. package/client/dist/assets/upload-DPG87J4t.js +0 -1
  212. /package/client/dist/assets/{abap-CK68cr9-.js → abap-CpHUwCVK.js} +0 -0
  213. /package/client/dist/assets/{activity-Di2f50_Z.js → activity-B82z3Pna.js} +0 -0
  214. /package/client/dist/assets/{activity-B2MvEH54.js → activity-BId0pwuS2.js} +0 -0
  215. /package/client/dist/assets/{activity-D4dhNEwO.js → activity-BVpZrs0J.js} +0 -0
  216. /package/client/dist/assets/{activity-DHjfmAyT.js → activity-BySXFT2J2.js} +0 -0
  217. /package/client/dist/assets/{activity-usnNa7FX.js → activity-DIWnV0Pl.js} +0 -0
  218. /package/client/dist/assets/{activity-C7VdmeGb.js → activity-Uj576ckT.js} +0 -0
  219. /package/client/dist/assets/{activity-BbNkUmW4.js → activity-w1qMFwxh.js} +0 -0
  220. /package/client/dist/assets/{addspec-CO1xv6BL.js → addspec-B9dY_F5T.js} +0 -0
  221. /package/client/dist/assets/{addspec-CNyAUGz_.js → addspec-B9nhMikb.js} +0 -0
  222. /package/client/dist/assets/{addspec-IozoiiGs.js → addspec-BtNaJHu2.js} +0 -0
  223. /package/client/dist/assets/{addspec-BxMn39wn.js → addspec-D43TlMVp2.js} +0 -0
  224. /package/client/dist/assets/{addspec-1HEtmLv1.js → addspec-DfgIZ5lO.js} +0 -0
  225. /package/client/dist/assets/{addspec-DfuR6EAl.js → addspec-W0r16BOC2.js} +0 -0
  226. /package/client/dist/assets/{addspec-DAveBiCE.js → addspec-tbxr3sHa.js} +0 -0
  227. /package/client/dist/assets/{agentstudio-DCyDW8sy.js → agentstudio-0o3XKrcy.js} +0 -0
  228. /package/client/dist/assets/{agentstudio-9NbsoGXk.js → agentstudio-Bu8m5jDw.js} +0 -0
  229. /package/client/dist/assets/{agentstudio-B1y0JWY-.js → agentstudio-C7xxqVWn.js} +0 -0
  230. /package/client/dist/assets/{agentstudio-1E-bag77.js → agentstudio-D-UoNAIA.js} +0 -0
  231. /package/client/dist/assets/{agentstudio-B-jQgYV-.js → agentstudio-OJjjz6Uf2.js} +0 -0
  232. /package/client/dist/assets/{agentstudio-Q0Sq_jTg.js → agentstudio-ObTpVbBP.js} +0 -0
  233. /package/client/dist/assets/{agentstudio-Bwa72b1S.js → agentstudio-PzFB3vfh2.js} +0 -0
  234. /package/client/dist/assets/{aiedit-DUpe7NxI.js → aiedit-BIQqv2XM2.js} +0 -0
  235. /package/client/dist/assets/{aiedit-kAU7KADg.js → aiedit-BaLZalLa.js} +0 -0
  236. /package/client/dist/assets/{aiedit-CvSspQmE.js → aiedit-BjlUzasF.js} +0 -0
  237. /package/client/dist/assets/{aiedit-DSLtsYIu.js → aiedit-BmDufyGU.js} +0 -0
  238. /package/client/dist/assets/{aiedit-qTGI_PGs.js → aiedit-DAM_WRAl2.js} +0 -0
  239. /package/client/dist/assets/{aiedit-CfBNanHE.js → aiedit-DRrNiaB4.js} +0 -0
  240. /package/client/dist/assets/{aiedit-CGxeml8D.js → aiedit-sP_iao2j.js} +0 -0
  241. /package/client/dist/assets/{analytics-CgEkAUiP.js → analytics-BDqqCzN9.js} +0 -0
  242. /package/client/dist/assets/{analytics-DEsEAJZL.js → analytics-B_rxNC9X.js} +0 -0
  243. /package/client/dist/assets/{analytics-D1ElChhs.js → analytics-C4z_NgJZ.js} +0 -0
  244. /package/client/dist/assets/{analytics-DDqve9Ma.js → analytics-DL2vJCbq.js} +0 -0
  245. /package/client/dist/assets/{analytics-CQqu1yE5.js → analytics-Ds_PNxtO2.js} +0 -0
  246. /package/client/dist/assets/{analytics-BDlELxc7.js → analytics-MVJRjcY52.js} +0 -0
  247. /package/client/dist/assets/{analytics-DBGO8B6-.js → analytics-e1mkNTgy2.js} +0 -0
  248. /package/client/dist/assets/{apex-BHHyAVNb.js → apex-DlD7oJX6.js} +0 -0
  249. /package/client/dist/assets/{atom-one-dark-DrWoEEbZ.js → atom-one-dark-BrRN8wBy.js} +0 -0
  250. /package/client/dist/assets/{attachments-BNgH6Tcu.js → attachments-8QCKHCEF2.js} +0 -0
  251. /package/client/dist/assets/{attachments-BLqqPGyy.js → attachments-BP0Ac1yt.js} +0 -0
  252. /package/client/dist/assets/{attachments-UJtEq892.js → attachments-CJaO1vCH.js} +0 -0
  253. /package/client/dist/assets/{attachments-D5fVsMgb.js → attachments-CPBVeHX72.js} +0 -0
  254. /package/client/dist/assets/{attachments-3CuBBRFu.js → attachments-CV2uaTGE.js} +0 -0
  255. /package/client/dist/assets/{attachments-CppM7de8.js → attachments-CaOjdN332.js} +0 -0
  256. /package/client/dist/assets/{attachments-pW3zm9nu.js → attachments-Db7JfA8a.js} +0 -0
  257. /package/client/dist/assets/{azcli-C56lfIEP.js → azcli-CmXFAbc-.js} +0 -0
  258. /package/client/dist/assets/{bat-BLamSaf-.js → bat-DdNJq4hH.js} +0 -0
  259. /package/client/dist/assets/{bicep-lrOcEItS.js → bicep-Bppz7WOA.js} +0 -0
  260. /package/client/dist/assets/{browser-C__Vz7KR.js → browser-0xeW8fUa2.js} +0 -0
  261. /package/client/dist/assets/{browser-EFmPpkvO.js → browser-543jbxE4.js} +0 -0
  262. /package/client/dist/assets/{browser-DhwTc0ry.js → browser-C9rUUxo4.js} +0 -0
  263. /package/client/dist/assets/{browser-C91MQBxx.js → browser-CEqx11iy.js} +0 -0
  264. /package/client/dist/assets/{browser-B7i7Ysc8.js → browser-CLC0etB_2.js} +0 -0
  265. /package/client/dist/assets/{browser-DwE289IS.js → browser-DyG2l9Wa.js} +0 -0
  266. /package/client/dist/assets/{browser-BVQgd1_1.js → browser-ExmELH702.js} +0 -0
  267. /package/client/dist/assets/{cameligo-BK_p0bgU.js → cameligo-0MRMdSmg.js} +0 -0
  268. /package/client/dist/assets/{chat-C5nJF6L0.js → chat-CCytD3P8.js} +0 -0
  269. /package/client/dist/assets/{chat-UMl6H55K.js → chat-CN8BMAMc.js} +0 -0
  270. /package/client/dist/assets/{chat-BXVx6rb5.js → chat-Cu6F3d6g2.js} +0 -0
  271. /package/client/dist/assets/{chat-CE8bx6Rz.js → chat-DagUyoVA.js} +0 -0
  272. /package/client/dist/assets/{chat-PVakYp92.js → chat-IjutwlBg.js} +0 -0
  273. /package/client/dist/assets/{chat-9RcD5Lcn.js → chat-NQPCBQo72.js} +0 -0
  274. /package/client/dist/assets/{chat-BnJPyeUK.js → chat-vVpwkOp52.js} +0 -0
  275. /package/client/dist/assets/{clojure-BQ7JOykv.js → clojure-B5dKj863.js} +0 -0
  276. /package/client/dist/assets/{clsx-CjLeUJfG.js → clsx-1dnHRn8W.js} +0 -0
  277. /package/client/dist/assets/{code-OoLqhjoI.js → code-32l6Naxu2.js} +0 -0
  278. /package/client/dist/assets/{code-Lg4awa35.js → code-BaG05zK62.js} +0 -0
  279. /package/client/dist/assets/{code-BPMR8_Vp.js → code-C5cibF2F.js} +0 -0
  280. /package/client/dist/assets/{code-aCUqkVN_.js → code-Ce-nBIwj.js} +0 -0
  281. /package/client/dist/assets/{code-BsOBm1Fb.js → code-Dap54vOb.js} +0 -0
  282. /package/client/dist/assets/{code-u9Sg9ohM.js → code-Dhb6jbAU.js} +0 -0
  283. /package/client/dist/assets/{code-eh4vO2-_.js → code-_NOCo-Ol2.js} +0 -0
  284. /package/client/dist/assets/{coffee-BD3DiL5s.js → coffee-DSbWjzk7.js} +0 -0
  285. /package/client/dist/assets/{common-C9VyfY7g.js → common-0aY9HQBP.js} +0 -0
  286. /package/client/dist/assets/{common-LDwESeCX.js → common-5jScBJqt.js} +0 -0
  287. /package/client/dist/assets/{common-ULlk84G4.js → common-BdaRciTu.js} +0 -0
  288. /package/client/dist/assets/{common-CzCQ0rpW.js → common-CHARW6MZ2.js} +0 -0
  289. /package/client/dist/assets/{common-CNoRVpnA.js → common-CQCJzAQq2.js} +0 -0
  290. /package/client/dist/assets/{common-Dmpyh323.js → common-DNhhqjlJ.js} +0 -0
  291. /package/client/dist/assets/{common-CcHFVATT.js → common-YdyZtmaI2.js} +0 -0
  292. /package/client/dist/assets/{cpp-splnCwCh.js → cpp-SBF8SAh9.js} +0 -0
  293. /package/client/dist/assets/{csharp-BCpsl-o3.js → csharp-Lzuq_1t-.js} +0 -0
  294. /package/client/dist/assets/{csp-tkCWrZmb.js → csp-BEzxZqi1.js} +0 -0
  295. /package/client/dist/assets/{css-B_xdnReq.js → css-C3D3PKln.js} +0 -0
  296. /package/client/dist/assets/{cypher-OL1zq-y3.js → cypher-CQ3dEPmv.js} +0 -0
  297. /package/client/dist/assets/{dart-CtTlOkrm.js → dart-DCPmxolI.js} +0 -0
  298. /package/client/dist/assets/{dashboard-C52PElPV.js → dashboard-BTiH361F.js} +0 -0
  299. /package/client/dist/assets/{dashboard-C-dXxut-.js → dashboard-COSm_14E.js} +0 -0
  300. /package/client/dist/assets/{dashboard-CUjyhohl.js → dashboard-CfOs70FN2.js} +0 -0
  301. /package/client/dist/assets/{dashboard-zH9quXks.js → dashboard-CzHDp14_2.js} +0 -0
  302. /package/client/dist/assets/{dashboard-03EpOoMM.js → dashboard-D6Woy2B5.js} +0 -0
  303. /package/client/dist/assets/{dashboard-C2owt3eN.js → dashboard-DwbH4oVH.js} +0 -0
  304. /package/client/dist/assets/{dashboard-Ch6H-457.js → dashboard-cWhU3krA2.js} +0 -0
  305. /package/client/dist/assets/{dockerfile-DcQ5CNiW.js → dockerfile-B_2_aaTO.js} +0 -0
  306. /package/client/dist/assets/{ecl-DZpiDRt6.js → ecl-rADUeXsV.js} +0 -0
  307. /package/client/dist/assets/{elixir-Dzj2lqtK.js → elixir-DOd-TuJO.js} +0 -0
  308. /package/client/dist/assets/{explore-CCcPXrHg.js → explore-B82VMUJ_.js} +0 -0
  309. /package/client/dist/assets/{explore-DQ0XEjed.js → explore-BGz1rvQE.js} +0 -0
  310. /package/client/dist/assets/{explore-xQoaak6e.js → explore-BzdMEpuf.js} +0 -0
  311. /package/client/dist/assets/{explore-B2n8r9Ye.js → explore-CbW0381i2.js} +0 -0
  312. /package/client/dist/assets/{explore-4SD1DxR0.js → explore-ChtxIiSM.js} +0 -0
  313. /package/client/dist/assets/{explore-BxR9q8Hq.js → explore-DfcUfiPw2.js} +0 -0
  314. /package/client/dist/assets/{explore-CZt4Ncpa.js → explore-yRF7wFSp2.js} +0 -0
  315. /package/client/dist/assets/{flow9-CFENCHIZ.js → flow9-Bf7zg_j0.js} +0 -0
  316. /package/client/dist/assets/{format-command-CMsBTOPN.js → format-command-D96da4fZ.js} +0 -0
  317. /package/client/dist/assets/{fsharp-D-4bv7FB.js → fsharp-BqROZjbA.js} +0 -0
  318. /package/client/dist/assets/{go-djxbTJq7.js → go-oQTP5uLe.js} +0 -0
  319. /package/client/dist/assets/{graphql-DHIFrkZ6.js → graphql-CSHfTU3q.js} +0 -0
  320. /package/client/dist/assets/{hcl-CXVoowNb.js → hcl-CeG-RoFJ.js} +0 -0
  321. /package/client/dist/assets/{ini-Crzc5kly.js → ini-B2Dv4ohc.js} +0 -0
  322. /package/client/dist/assets/{integrations-C7u7SInG.js → integrations-B0OBPU9_.js} +0 -0
  323. /package/client/dist/assets/{integrations-B17KyA-i.js → integrations-BA5pM05t.js} +0 -0
  324. /package/client/dist/assets/{integrations-DkBi-Gsh.js → integrations-BJvrlcc7.js} +0 -0
  325. /package/client/dist/assets/{integrations-DwSVdCpf.js → integrations-CPKYkG36.js} +0 -0
  326. /package/client/dist/assets/{integrations-A9f08E0o.js → integrations-CnrX7vqw2.js} +0 -0
  327. /package/client/dist/assets/{integrations-9qxUNhPD.js → integrations-DBou159M2.js} +0 -0
  328. /package/client/dist/assets/{integrations-B46ac3sf.js → integrations-DeJFRyXz2.js} +0 -0
  329. /package/client/dist/assets/{java-CYLaJrdE.js → java-aUDDeufb.js} +0 -0
  330. /package/client/dist/assets/{jira-Dht8qNck.js → jira-BDgxAFTF2.js} +0 -0
  331. /package/client/dist/assets/{jira-4Fin3fu7.js → jira-BwLBgmfI2.js} +0 -0
  332. /package/client/dist/assets/{jira-0ldfMByG.js → jira-D3GGzXOI.js} +0 -0
  333. /package/client/dist/assets/{jira-BpnpUFlb.js → jira-DeotPx6P2.js} +0 -0
  334. /package/client/dist/assets/{jira-DzIVvNmy.js → jira-NwlUCcQD.js} +0 -0
  335. /package/client/dist/assets/{jira-CBU0200d.js → jira-Srx2P5zS.js} +0 -0
  336. /package/client/dist/assets/{jira-01H1RfQj.js → jira-mi37H4Kt.js} +0 -0
  337. /package/client/dist/assets/{jobs-jEQidSIb.js → jobs-B56vQEkh2.js} +0 -0
  338. /package/client/dist/assets/{jobs-DM9owF1j.js → jobs-BYIk6Tg_.js} +0 -0
  339. /package/client/dist/assets/{jobs-Bw4hqEro.js → jobs-C-lM-iax2.js} +0 -0
  340. /package/client/dist/assets/{jobs-BF9raUkL.js → jobs-CYdevGgT.js} +0 -0
  341. /package/client/dist/assets/{jobs-ym72SIR9.js → jobs-D8adm2BX.js} +0 -0
  342. /package/client/dist/assets/{jobs-DcCONARH.js → jobs-K_Y8KAZ2.js} +0 -0
  343. /package/client/dist/assets/{jobs-Oz3UtLQn.js → jobs-OwM0ZWhI2.js} +0 -0
  344. /package/client/dist/assets/{julia-BJ7tUU-T.js → julia-C5LbruO5.js} +0 -0
  345. /package/client/dist/assets/{kotlin-CeWcU3lC.js → kotlin-C6mOuSKk.js} +0 -0
  346. /package/client/dist/assets/{less-CviwWNG4.js → less-D5qC3caW.js} +0 -0
  347. /package/client/dist/assets/{lexon-Dt28jqDS.js → lexon-Dr2La-fj.js} +0 -0
  348. /package/client/dist/assets/{loops-DE-2cmLw.js → loops-BDhEfnx82.js} +0 -0
  349. /package/client/dist/assets/{loops-mcoeFMat.js → loops-BM5QggwH.js} +0 -0
  350. /package/client/dist/assets/{loops-D_ZxkD4l.js → loops-CPjLAAxj.js} +0 -0
  351. /package/client/dist/assets/{loops-BYNUJVyD.js → loops-DKnk79TB2.js} +0 -0
  352. /package/client/dist/assets/{loops-B_gS4Pqu.js → loops-DLW0aFMm.js} +0 -0
  353. /package/client/dist/assets/{loops-Fv4Bs-9A.js → loops-Dcv3dFy_2.js} +0 -0
  354. /package/client/dist/assets/{loops-B_w7STrw.js → loops-y7wFRx6M.js} +0 -0
  355. /package/client/dist/assets/{lua-B6AOaJSi.js → lua-Zy6u5z6Z.js} +0 -0
  356. /package/client/dist/assets/{m3-VnMkHJcy.js → m3-C7XHeDz_.js} +0 -0
  357. /package/client/dist/assets/{markdown-8vKNdvIM.js → markdown-PiF7r-2h.js} +0 -0
  358. /package/client/dist/assets/{mips-DoEcuzan.js → mips-nn07GiyE.js} +0 -0
  359. /package/client/dist/assets/{msdax-CNzvHyEp.js → msdax-DaKFVMST.js} +0 -0
  360. /package/client/dist/assets/{mysql-Buk3vUtL.js → mysql-C6d6xrbU.js} +0 -0
  361. /package/client/dist/assets/{nav-Cw3_40fA.js → nav-1hj2izqB2.js} +0 -0
  362. /package/client/dist/assets/{nav-DNDJ3H-_.js → nav-BBUKMAu-2.js} +0 -0
  363. /package/client/dist/assets/{nav-CrG_mYRr.js → nav-CLqkP-26.js} +0 -0
  364. /package/client/dist/assets/{nav-DVp1nEsi.js → nav-Cv46L837.js} +0 -0
  365. /package/client/dist/assets/{nav-DbAbG5_k.js → nav-DgA-rjLk2.js} +0 -0
  366. /package/client/dist/assets/{nav-B5W9IMnf.js → nav-EG2pr6vN.js} +0 -0
  367. /package/client/dist/assets/{nav-_0vAgZ_k.js → nav-r3mIJVfU.js} +0 -0
  368. /package/client/dist/assets/{objective-c-DfOmda-5.js → objective-c-B6KMtI-k.js} +0 -0
  369. /package/client/dist/assets/{pascal-DoAA2yjy.js → pascal-DYr8MPE7.js} +0 -0
  370. /package/client/dist/assets/{pascaligo-C65Vn1fu.js → pascaligo-B3pd-jJ_.js} +0 -0
  371. /package/client/dist/assets/{perl-INlfQ_ro.js → perl-z2xUzoaL.js} +0 -0
  372. /package/client/dist/assets/{pgsql-DtwRzglx.js → pgsql-DUvT-09-.js} +0 -0
  373. /package/client/dist/assets/{php-DfHqVNet.js → php-CGyOmJfy.js} +0 -0
  374. /package/client/dist/assets/{pla-D3dNVRYk.js → pla-CMqgvyD0.js} +0 -0
  375. /package/client/dist/assets/{postiats-UKXD1rM4.js → postiats-Dx5LUn4Z.js} +0 -0
  376. /package/client/dist/assets/{powerquery-zE3E3O7s.js → powerquery-DazYxvJ6.js} +0 -0
  377. /package/client/dist/assets/{powershell-Df9b3zQl.js → powershell-B3nwlDka.js} +0 -0
  378. /package/client/dist/assets/{protobuf-BF0nUVrW.js → protobuf-BGuu2u2s.js} +0 -0
  379. /package/client/dist/assets/{pug-Bj6Yikv_.js → pug-Dbte1MXA.js} +0 -0
  380. /package/client/dist/assets/{qsharp-BI4h-raZ.js → qsharp-DJLwBOvG.js} +0 -0
  381. /package/client/dist/assets/{r-DhpnmaXx.js → r-DmGBRSHj.js} +0 -0
  382. /package/client/dist/assets/{redis-DwYYPr4d.js → redis-VvUYn0a4.js} +0 -0
  383. /package/client/dist/assets/{redshift-D2f3Bc-4.js → redshift-6GdDrxg3.js} +0 -0
  384. /package/client/dist/assets/{restructuredtext-Dkiif6fe.js → restructuredtext-CfAB8aRU.js} +0 -0
  385. /package/client/dist/assets/{ruby-C6hLmHy0.js → ruby-CIYK6MXY.js} +0 -0
  386. /package/client/dist/assets/{rust-X8WAGtxB.js → rust-CTHZfjj9.js} +0 -0
  387. /package/client/dist/assets/{sb-4M2la0Kr.js → sb-CkKTdkz_.js} +0 -0
  388. /package/client/dist/assets/{scala-CaCXz58E.js → scala-Bx-Wr9TM.js} +0 -0
  389. /package/client/dist/assets/{scheme-CMdjpmP7.js → scheme-Dcfi6f83.js} +0 -0
  390. /package/client/dist/assets/{scss-QopmvPyp.js → scss-ByL-7PFQ.js} +0 -0
  391. /package/client/dist/assets/{shell-CDBchS00.js → shell-Csnb9Yqd.js} +0 -0
  392. /package/client/dist/assets/{solidity-DvBfi5mC.js → solidity-Dbw5-0QP.js} +0 -0
  393. /package/client/dist/assets/{sophia-HSnXR17u.js → sophia-ZQ3saZQx.js} +0 -0
  394. /package/client/dist/assets/{sparql-DhG_ppup.js → sparql-BEtOel8u.js} +0 -0
  395. /package/client/dist/assets/{specs-CUE3xSFM.js → specs-B5XOoH-02.js} +0 -0
  396. /package/client/dist/assets/{specs-C2wW5bq8.js → specs-B8mwScoM2.js} +0 -0
  397. /package/client/dist/assets/{specs-Bku8SypT.js → specs-Bj_3fDrQ.js} +0 -0
  398. /package/client/dist/assets/{specs-CnH7dOiF.js → specs-CWNKAScX2.js} +0 -0
  399. /package/client/dist/assets/{specs-BvsFNKTs.js → specs-D4z5d8uq.js} +0 -0
  400. /package/client/dist/assets/{specs-D4ORogcT.js → specs-DgvIRZtM.js} +0 -0
  401. /package/client/dist/assets/{specs-D6Y76Bgi.js → specs-DuRzE5wE.js} +0 -0
  402. /package/client/dist/assets/{sql-DHeIS-tf.js → sql-DGPDbzB7.js} +0 -0
  403. /package/client/dist/assets/{st-CMWchiRU.js → st-GeW2MOgh.js} +0 -0
  404. /package/client/dist/assets/{swift-CDXvBefj.js → swift-BVYTN8jq.js} +0 -0
  405. /package/client/dist/assets/{systemverilog-0RTukYUE.js → systemverilog-Deg2a8Lr.js} +0 -0
  406. /package/client/dist/assets/{tcl-CbBF_Ttl.js → tcl-6DnLmROF.js} +0 -0
  407. /package/client/dist/assets/{terminal-BLh00ouM.js → terminal-B2sExUis.js} +0 -0
  408. /package/client/dist/assets/{terminal-Cz_Puz0q.js → terminal-CH0fdHvP.js} +0 -0
  409. /package/client/dist/assets/{terminal-DFEH9XTQ.js → terminal-CPMigUX7.js} +0 -0
  410. /package/client/dist/assets/{terminal-CB33IsO1.js → terminal-DOwfkxqa2.js} +0 -0
  411. /package/client/dist/assets/{terminal-CR6t-Oey.js → terminal-DjSSa5qP.js} +0 -0
  412. /package/client/dist/assets/{terminal-CY_sPfdD.js → terminal-DoMTHedv2.js} +0 -0
  413. /package/client/dist/assets/{terminal-BTanJIt_.js → terminal-w0i-OCp32.js} +0 -0
  414. /package/client/dist/assets/{tickets-D6f3GSYn.js → tickets-CQTana1u.js} +0 -0
  415. /package/client/dist/assets/{tickets-blQnNA4N.js → tickets-D00mSt832.js} +0 -0
  416. /package/client/dist/assets/{tickets-68LH1xp6.js → tickets-D7CSuWjO.js} +0 -0
  417. /package/client/dist/assets/{tickets-OrJwUxJR.js → tickets-Dq7HDy8n.js} +0 -0
  418. /package/client/dist/assets/{tickets-PtHNxQo5.js → tickets-DvbFg_-c2.js} +0 -0
  419. /package/client/dist/assets/{tickets-REr-EQx9.js → tickets-heDAaj8C2.js} +0 -0
  420. /package/client/dist/assets/{tickets-jxsTrApR.js → tickets-onKZz6Dd.js} +0 -0
  421. /package/client/dist/assets/{twig-DCcGd8FE.js → twig-XbbXW9Yz.js} +0 -0
  422. /package/client/dist/assets/{typespec-HTphp-Uh.js → typespec-CRlV8GhI.js} +0 -0
  423. /package/client/dist/assets/{vb-Cb_yUcVd.js → vb-BHE2izPH.js} +0 -0
  424. /package/client/dist/assets/{wgsl-DrKU3YXj.js → wgsl-Bf5vkfmY.js} +0 -0
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.specsTools = specsTools;
4
+ const zod_1 = require("zod");
5
+ const types_1 = require("./types");
6
+ // Per-action danger classification for the specs (tickets) facade.
7
+ const DESTRUCTIVE_ACTIONS = new Set(['delete', 'smash_undo', 'delete_epic_children']);
8
+ // `create` routes through Quick Add Spec (generate-spec) so MCP-created specs
9
+ // match the app's "Add Spec → Quick" flow — hence ai-spawn, not write.
10
+ const AI_SPAWN_ACTIONS = new Set(['create', 'generate', 'ai_edit', 'contract_refine', 'smash']);
11
+ // `commit_draft` (POST /tickets/from-draft) may fire-and-forget a Contract
12
+ // Refine spawn as a side-effect, but the call itself is a synchronous store
13
+ // mutation (a write), so it is classified as `write`.
14
+ const WRITE_ACTIONS = new Set([
15
+ 'update', 'from_prompt', 'save_draft', 'commit_draft', 'cancel_ai_edit',
16
+ ]);
17
+ function resolveSpecsTier(action) {
18
+ if (DESTRUCTIVE_ACTIONS.has(action))
19
+ return 'destructive';
20
+ if (AI_SPAWN_ACTIONS.has(action))
21
+ return 'ai-spawn';
22
+ if (WRITE_ACTIONS.has(action))
23
+ return 'write';
24
+ return 'read';
25
+ }
26
+ const WATCH_HINT = 'Returns 202 immediately; the actual result streams over the WebSocket. ' +
27
+ 'Use specrails_watch with the returned requestId/scheduled ref to await completion ' +
28
+ '(WS events: spec_gen_*, ticket_ai_edit_*, explore.contract_refine_*, smash.*).';
29
+ function specsTools() {
30
+ return [
31
+ {
32
+ name: 'specrails_specs',
33
+ title: 'Specs (Tickets)',
34
+ description: 'Manage the spec/ticket backlog for a project (the local-tickets.json store). ' +
35
+ 'Actions: list, get, ' +
36
+ 'create (ai-spawn — THE way to add a spec: routes through Quick Add Spec, so the specrails agent generates a structured spec with AI from the user request, exactly matching the app\'s "Add Spec → Quick" flow; pass the request as title/description, it becomes the idea; async), ' +
37
+ 'update, delete (destructive), ' +
38
+ 'from_prompt (verbatim insert, no AI — use ONLY when you already have a complete finished spec to store as-is), ' +
39
+ 'save_draft (persist an Explore conversation as a draft), commit_draft (flip a draft to a real spec), ' +
40
+ 'generate (alias of create — Quick AI spec generation), ai_edit (ai-spawn, AI-edit a ticket title+description), ' +
41
+ 'cancel_ai_edit (abort an in-flight ai-edit), contract_refine (ai-spawn, append a Contract Layer — Claude-only), ' +
42
+ 'smash (ai-spawn, decompose a spec into N child sub-specs under an epic — Claude-only), ' +
43
+ 'smash_undo (destructive, reverse a prior SMASH), delete_epic_children (destructive, delete all children of an epic), ' +
44
+ 'spending_summary (per-ticket AI spend), files_touched (provenance of files an AI job touched for this ticket), ' +
45
+ 'list_attachments, get_attachment. The generate/ai_edit/contract_refine/smash actions are async (HTTP 202) — ' +
46
+ 'results arrive over the WebSocket, not the HTTP response.',
47
+ hintTier: 'read',
48
+ tier: (args) => resolveSpecsTier(args.action),
49
+ inputSchema: {
50
+ action: zod_1.z
51
+ .enum([
52
+ 'list',
53
+ 'get',
54
+ 'create',
55
+ 'update',
56
+ 'delete',
57
+ 'from_prompt',
58
+ 'save_draft',
59
+ 'commit_draft',
60
+ 'generate',
61
+ 'ai_edit',
62
+ 'cancel_ai_edit',
63
+ 'contract_refine',
64
+ 'smash',
65
+ 'smash_undo',
66
+ 'delete_epic_children',
67
+ 'spending_summary',
68
+ 'files_touched',
69
+ 'list_attachments',
70
+ 'get_attachment',
71
+ ])
72
+ .describe('Operation to perform'),
73
+ projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
74
+ // ── Ticket identity ──────────────────────────────────────────────
75
+ id: zod_1.z
76
+ .number()
77
+ .int()
78
+ .optional()
79
+ .describe('Numeric ticket id (get/update/delete/contract_refine/smash/smash_undo/delete_epic_children/spending_summary/files_touched/ai_edit)'),
80
+ // ── list filters ─────────────────────────────────────────────────
81
+ status: zod_1.z
82
+ .string()
83
+ .optional()
84
+ .describe('list: filter by status (CSV: draft|todo|in_progress|done|cancelled). create/update: set ticket status'),
85
+ label: zod_1.z.string().optional().describe('list: filter by label (CSV)'),
86
+ q: zod_1.z.string().optional().describe('list: free-text search over title + description'),
87
+ // ── create / update / from_prompt / commit_draft fields ──────────
88
+ title: zod_1.z.string().optional().describe('Ticket title (required for create / commit_draft)'),
89
+ description: zod_1.z
90
+ .string()
91
+ .optional()
92
+ .describe('Ticket description. For ai_edit this is the REQUIRED current baseline description'),
93
+ priority: zod_1.z
94
+ .enum(['critical', 'high', 'medium', 'low'])
95
+ .optional()
96
+ .describe('Ticket priority (defaults to medium; may be null only for drafts)'),
97
+ labels: zod_1.z.array(zod_1.z.string()).optional().describe('Labels array'),
98
+ assignee: zod_1.z.string().optional().describe('create/update: assignee'),
99
+ prerequisites: zod_1.z.array(zod_1.z.number().int()).optional().describe('create/update: prerequisite ticket ids'),
100
+ metadata: zod_1.z.record(zod_1.z.unknown()).optional().describe('create/update: free-form metadata object'),
101
+ source: zod_1.z.string().optional().describe('create: source tag'),
102
+ acceptanceCriteria: zod_1.z
103
+ .array(zod_1.z.string())
104
+ .optional()
105
+ .describe('update/commit_draft: acceptance-criteria bullets folded into the description'),
106
+ shortSummary: zod_1.z.string().optional().describe('commit_draft: explicit short summary (camelCase key)'),
107
+ short_summary: zod_1.z.string().optional().describe('update: explicit short summary (snake_case key; null clears)'),
108
+ structured: zod_1.z.boolean().optional().describe('from_prompt: lightly structure the verbatim prompt'),
109
+ createLocal: zod_1.z.boolean().optional().describe('commit_draft/generate: keep the spec local (skip Jira promote)'),
110
+ // ── draft linkage ────────────────────────────────────────────────
111
+ conversationId: zod_1.z
112
+ .string()
113
+ .optional()
114
+ .describe('save_draft (required) / commit_draft: origin Explore conversation id'),
115
+ draftTicketId: zod_1.z.number().int().optional().describe('commit_draft: explicit draft ticket id to flip in place'),
116
+ editTicketId: zod_1.z.number().int().optional().describe('save_draft: existing ticket id to demote to a draft in place'),
117
+ pendingSpecId: zod_1.z
118
+ .string()
119
+ .optional()
120
+ .describe('generate/from_prompt/commit_draft: pending-spec UUID owning pre-creation attachments'),
121
+ // ── generate (Quick AI spec) ─────────────────────────────────────
122
+ idea: zod_1.z.string().optional().describe('generate: the idea/prompt to turn into a spec (required)'),
123
+ model: zod_1.z.string().optional().describe('generate/smash: model override (validated against the provider catalog)'),
124
+ aiEngine: zod_1.z
125
+ .string()
126
+ .optional()
127
+ .describe('generate: provider/engine override (must be an installed provider; defaults to primary)'),
128
+ contextScope: zod_1.z
129
+ .record(zod_1.z.unknown())
130
+ .optional()
131
+ .describe('generate: context-awareness flags {specrails,openspec,full,mcp,userMcp,contractRefine}'),
132
+ contractRefine: zod_1.z.boolean().optional().describe('generate: enrich the spec with a Contract Layer (Claude-only)'),
133
+ attachmentIds: zod_1.z.array(zod_1.z.string()).optional().describe('generate: attachment ids (requires pendingSpecId)'),
134
+ // ── ai_edit ──────────────────────────────────────────────────────
135
+ instructions: zod_1.z.string().optional().describe('ai_edit: natural-language editing instructions (required)'),
136
+ priorInstructions: zod_1.z.array(zod_1.z.string()).optional().describe('ai_edit: prior refinement turns for an iterative edit'),
137
+ priorProposal: zod_1.z.string().optional().describe('ai_edit: the previous turn proposal to refine'),
138
+ requestId: zod_1.z.string().optional().describe('cancel_ai_edit: requestId of the in-flight ai-edit to abort (required)'),
139
+ // ── smash ────────────────────────────────────────────────────────
140
+ mode: zod_1.z.enum(['simple', 'full']).optional().describe('smash: decomposition mode (default simple)'),
141
+ smashedAt: zod_1.z.string().optional().describe('smash_undo: the smashedAt timestamp of the SMASH to reverse (required)'),
142
+ // ── attachments ──────────────────────────────────────────────────
143
+ ticketId: zod_1.z
144
+ .string()
145
+ .optional()
146
+ .describe('list_attachments/get_attachment: ticket numeric id or pending-spec UUID'),
147
+ attachmentId: zod_1.z.string().optional().describe('get_attachment: attachment id (required)'),
148
+ },
149
+ async handler(ctx, args) {
150
+ const base = (0, types_1.projectPath)(ctx, args.projectId);
151
+ const action = args.action;
152
+ const requireId = () => {
153
+ const id = args.id;
154
+ if (typeof id !== 'number' || !Number.isInteger(id)) {
155
+ throw new Error(`Action "${action}" requires a numeric "id".`);
156
+ }
157
+ return id;
158
+ };
159
+ switch (action) {
160
+ // ── reads ──────────────────────────────────────────────────────
161
+ case 'list': {
162
+ const qs = new URLSearchParams();
163
+ if (typeof args.status === 'string')
164
+ qs.set('status', args.status);
165
+ if (typeof args.label === 'string')
166
+ qs.set('label', args.label);
167
+ if (typeof args.q === 'string')
168
+ qs.set('q', args.q);
169
+ const suffix = qs.toString() ? `?${qs.toString()}` : '';
170
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets${suffix}`);
171
+ }
172
+ case 'get':
173
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${requireId()}`);
174
+ case 'spending_summary':
175
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${requireId()}/spending-summary`);
176
+ case 'files_touched':
177
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/code/provenance?ticketId=${requireId()}`);
178
+ case 'list_attachments': {
179
+ const ticketId = args.ticketId;
180
+ if (!ticketId)
181
+ throw new Error('list_attachments requires a "ticketId" (numeric id or UUID).');
182
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${encodeURIComponent(ticketId)}/attachments`);
183
+ }
184
+ case 'get_attachment': {
185
+ const ticketId = args.ticketId;
186
+ const attachmentId = args.attachmentId;
187
+ if (!ticketId)
188
+ throw new Error('get_attachment requires a "ticketId".');
189
+ if (!attachmentId)
190
+ throw new Error('get_attachment requires an "attachmentId".');
191
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${encodeURIComponent(ticketId)}/attachments/${encodeURIComponent(attachmentId)}`);
192
+ }
193
+ // ── writes (synchronous store mutations) ────────────────────────
194
+ case 'create': {
195
+ // Route through Quick Add Spec (POST /tickets/generate-spec) so an
196
+ // MCP-created spec matches the app's "Add Spec → Quick" flow: the
197
+ // specrails agent generates a structured spec (source 'propose-spec')
198
+ // following the project's conventions, instead of a raw insert of
199
+ // whatever the external LLM wrote. The user's request becomes the
200
+ // `idea`. For a verbatim insert with no AI, use `from_prompt`.
201
+ const idea = [args.title, args.description]
202
+ .filter((v) => typeof v === 'string' && v.trim().length > 0)
203
+ .join('\n\n')
204
+ .trim();
205
+ if (!idea)
206
+ throw new Error('create requires a "title" and/or "description" to use as the spec idea.');
207
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/generate-spec`, {
208
+ idea,
209
+ model: args.model,
210
+ aiEngine: args.aiEngine,
211
+ provider: args.provider,
212
+ contractRefine: args.contractRefine,
213
+ });
214
+ return { ...r, hint: WATCH_HINT };
215
+ }
216
+ case 'update':
217
+ return (0, types_1.apiCall)(ctx, 'PATCH', `${base}/tickets/${requireId()}`, {
218
+ title: args.title,
219
+ description: args.description,
220
+ status: args.status,
221
+ priority: args.priority,
222
+ labels: args.labels,
223
+ assignee: args.assignee,
224
+ prerequisites: args.prerequisites,
225
+ metadata: args.metadata,
226
+ acceptanceCriteria: args.acceptanceCriteria,
227
+ short_summary: args.short_summary,
228
+ });
229
+ case 'from_prompt':
230
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/from-prompt`, {
231
+ description: args.description,
232
+ title: args.title,
233
+ labels: args.labels,
234
+ priority: args.priority,
235
+ structured: args.structured,
236
+ pendingSpecId: args.pendingSpecId,
237
+ });
238
+ case 'save_draft': {
239
+ if (!args.conversationId)
240
+ throw new Error('save_draft requires a "conversationId".');
241
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/save-as-draft`, {
242
+ conversationId: args.conversationId,
243
+ title: args.title,
244
+ description: args.description,
245
+ labels: args.labels,
246
+ editTicketId: args.editTicketId,
247
+ });
248
+ }
249
+ case 'commit_draft':
250
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/from-draft`, {
251
+ title: args.title,
252
+ description: args.description,
253
+ draftTicketId: args.draftTicketId,
254
+ conversationId: args.conversationId,
255
+ pendingSpecId: args.pendingSpecId,
256
+ labels: args.labels,
257
+ acceptanceCriteria: args.acceptanceCriteria,
258
+ priority: args.priority,
259
+ shortSummary: args.shortSummary,
260
+ createLocal: args.createLocal,
261
+ });
262
+ case 'cancel_ai_edit': {
263
+ if (!args.requestId)
264
+ throw new Error('cancel_ai_edit requires a "requestId".');
265
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/tickets/${requireId()}/ai-edit?requestId=${encodeURIComponent(args.requestId)}`);
266
+ }
267
+ // ── destructive ────────────────────────────────────────────────
268
+ case 'delete':
269
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/tickets/${requireId()}`);
270
+ case 'smash_undo': {
271
+ if (!args.smashedAt)
272
+ throw new Error('smash_undo requires the "smashedAt" timestamp.');
273
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/smash/undo`, {
274
+ smashedAt: args.smashedAt,
275
+ });
276
+ }
277
+ case 'delete_epic_children':
278
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/tickets/${requireId()}/children`);
279
+ // ── ai-spawn (async 202 → WS) ──────────────────────────────────
280
+ case 'generate': {
281
+ if (!args.idea)
282
+ throw new Error('generate requires an "idea".');
283
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/generate-spec`, {
284
+ idea: args.idea,
285
+ model: args.model,
286
+ aiEngine: args.aiEngine,
287
+ contextScope: args.contextScope,
288
+ contractRefine: args.contractRefine,
289
+ attachmentIds: args.attachmentIds,
290
+ pendingSpecId: args.pendingSpecId,
291
+ createLocal: args.createLocal,
292
+ });
293
+ return { ...r, hint: WATCH_HINT };
294
+ }
295
+ case 'ai_edit': {
296
+ if (!args.instructions)
297
+ throw new Error('ai_edit requires "instructions".');
298
+ if (!args.description)
299
+ throw new Error('ai_edit requires the current "description" baseline.');
300
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/ai-edit`, {
301
+ instructions: args.instructions,
302
+ description: args.description,
303
+ title: args.title,
304
+ attachmentIds: args.attachmentIds,
305
+ priorInstructions: args.priorInstructions,
306
+ priorProposal: args.priorProposal,
307
+ });
308
+ return { ...r, hint: WATCH_HINT };
309
+ }
310
+ case 'contract_refine': {
311
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/contract-refine`);
312
+ return { ...r, hint: WATCH_HINT };
313
+ }
314
+ case 'smash': {
315
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/smash`, {
316
+ mode: args.mode,
317
+ model: args.model,
318
+ });
319
+ return { ...r, hint: WATCH_HINT };
320
+ }
321
+ default:
322
+ throw new Error(`Unknown action "${action}".`);
323
+ }
324
+ },
325
+ },
326
+ ];
327
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiCall = apiCall;
4
+ exports.projectPath = projectPath;
5
+ exports.requireProject = requireProject;
6
+ exports.setActiveProject = setActiveProject;
7
+ exports.getActiveProject = getActiveProject;
8
+ exports.registerTieredTool = registerTieredTool;
9
+ const mcp_tiers_1 = require("../mcp-tiers");
10
+ const auth_1 = require("../../auth");
11
+ const agent_tier_1 = require("../../agent-tier");
12
+ /**
13
+ * Calls the app's own REST API over loopback using the MASTER token (held
14
+ * server-side, never exposed to the MCP client). This reuses every router's
15
+ * existing logic/validation rather than re-implementing it in tools — the same
16
+ * loopback-forward pattern the Mobile Gateway uses for REST. `path` is appended
17
+ * after `/api` (e.g. `/projects/<id>/tickets`).
18
+ */
19
+ async function apiCall(ctx, method, path, body) {
20
+ const url = `http://127.0.0.1:${ctx.desktopPort}/api${path}`;
21
+ const res = await fetch(url, {
22
+ method,
23
+ headers: {
24
+ Authorization: `Bearer ${(0, auth_1.loadOrGenerateToken)()}`,
25
+ ...(body !== undefined ? { 'content-type': 'application/json' } : {}),
26
+ },
27
+ body: body !== undefined ? JSON.stringify(body) : undefined,
28
+ });
29
+ const text = await res.text();
30
+ let data;
31
+ try {
32
+ data = text ? JSON.parse(text) : null;
33
+ }
34
+ catch {
35
+ data = text;
36
+ }
37
+ if (!res.ok) {
38
+ const detail = typeof data === 'object' && data !== null ? JSON.stringify(data) : String(data);
39
+ throw new Error(`API ${method} ${path} → ${res.status}: ${detail}`);
40
+ }
41
+ return data;
42
+ }
43
+ /** Convenience: build the `/projects/<id>` path prefix for a resolved project. */
44
+ function projectPath(ctx, projectId) {
45
+ return `/projects/${requireProject(ctx, projectId).project.id}`;
46
+ }
47
+ /** Resolve a per-project context or throw a clear, LLM-readable error. */
48
+ function requireProject(ctx, projectId) {
49
+ const id = projectId ?? getActiveProject(ctx);
50
+ if (!id) {
51
+ throw new Error('No project specified and no active project selected. Call specrails_select_project or pass projectId.');
52
+ }
53
+ const pc = ctx.registry.getContext(id);
54
+ if (!pc)
55
+ throw new Error(`Unknown projectId "${id}". Use specrails_projects(list) to see valid ids.`);
56
+ return pc;
57
+ }
58
+ // ── Active-project stickiness (per server process) ────────────────────────────
59
+ // The active project is a soft default; an explicit projectId always wins.
60
+ let _activeProjectId = null;
61
+ function setActiveProject(id) {
62
+ _activeProjectId = id;
63
+ }
64
+ function getActiveProject(_ctx) {
65
+ return _activeProjectId;
66
+ }
67
+ function tierAnnotations(tier) {
68
+ switch (tier) {
69
+ case 'read':
70
+ return { readOnlyHint: true, openWorldHint: false };
71
+ case 'write':
72
+ return { readOnlyHint: false, openWorldHint: false };
73
+ case 'ai-spawn':
74
+ return { readOnlyHint: false, openWorldHint: true };
75
+ case 'destructive':
76
+ return { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
77
+ }
78
+ }
79
+ function toResult(data) {
80
+ const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
81
+ return { content: [{ type: 'text', text }] };
82
+ }
83
+ function errorResult(message) {
84
+ return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
85
+ }
86
+ /**
87
+ * Broadcasts an app-level `mcp.activity` event after a successful mutating tool
88
+ * call so the UI can surface what an external LLM did — regardless of which
89
+ * project is currently active. Deliberately carries NO top-level `projectId`
90
+ * (so the client's per-project WS filter does not drop it); the affected project
91
+ * is in `affectedProjectId` for cache invalidation. Read-tier calls don't emit.
92
+ * A notify failure must never break the tool, so it's wrapped.
93
+ */
94
+ function emitMcpActivity(ctx, spec, args, tier) {
95
+ try {
96
+ const affectedProjectId = (typeof args.projectId === 'string' ? args.projectId : null) ?? getActiveProject(ctx);
97
+ ctx.broadcast({
98
+ type: 'mcp.activity',
99
+ tool: spec.name,
100
+ action: typeof args.action === 'string' ? args.action : null,
101
+ tier,
102
+ affectedProjectId,
103
+ title: spec.title,
104
+ at: new Date().toISOString(),
105
+ });
106
+ }
107
+ catch {
108
+ /* a notification failure must never fail the tool */
109
+ }
110
+ }
111
+ /**
112
+ * Registers a tool on the McpServer, wrapping the handler with (1) tier
113
+ * enforcement and (2) uniform success/error result shaping. No tool can forget
114
+ * the tier check because it lives here, not in the handler.
115
+ */
116
+ function registerTieredTool(server, ctx, spec) {
117
+ const hintTier = spec.hintTier ?? (typeof spec.tier === 'string' ? spec.tier : 'write');
118
+ server.registerTool(spec.name, {
119
+ title: spec.title,
120
+ description: spec.description,
121
+ inputSchema: spec.inputSchema,
122
+ annotations: tierAnnotations(hintTier),
123
+ }, async (args, extra) => {
124
+ // In-app agent: a per-request active-project header (loopback-only, set by
125
+ // the agent's bridge from the Cursor-style selector) pins the project for
126
+ // this call so project-scoped tools resolve without specrails_select_project.
127
+ const projectHeader = extra?.requestInfo?.headers?.[agent_tier_1.AGENT_PROJECT_HEADER];
128
+ if (projectHeader != null) {
129
+ const pid = Array.isArray(projectHeader) ? projectHeader[0] : projectHeader;
130
+ if (typeof pid === 'string' && pid.trim())
131
+ setActiveProject(pid.trim());
132
+ }
133
+ const tier = typeof spec.tier === 'function' ? spec.tier(args ?? {}) : spec.tier;
134
+ // In-app agent: when the request carries the loopback-only agent-tier header,
135
+ // enforce the cumulative ladder instead of the external Settings checkboxes
136
+ // (design D4). Absent header → external client → unchanged Settings gate.
137
+ const agentLevel = (0, agent_tier_1.levelFromHeader)(extra?.requestInfo?.headers?.[agent_tier_1.AGENT_TIER_HEADER]);
138
+ if (agentLevel !== null) {
139
+ if (!(0, agent_tier_1.levelAllowsTier)(agentLevel, tier)) {
140
+ return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
141
+ }
142
+ }
143
+ else if (!(0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier)) {
144
+ return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
145
+ }
146
+ try {
147
+ const data = await spec.handler(ctx, args ?? {});
148
+ if (tier !== 'read')
149
+ emitMcpActivity(ctx, spec, args ?? {}, tier);
150
+ return toResult(data);
151
+ }
152
+ catch (err) {
153
+ return errorResult(err instanceof Error ? err.message : String(err));
154
+ }
155
+ });
156
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchTool = watchTool;
4
+ const zod_1 = require("zod");
5
+ // Event `type` suffixes/strings that mean an async operation has settled.
6
+ const TERMINAL_PATTERNS = [
7
+ 'job.finalized',
8
+ 'rail.job_completed',
9
+ 'rail.job_stopped',
10
+ 'chat_done',
11
+ 'chat_error',
12
+ 'spec_gen_done',
13
+ 'spec_gen_error',
14
+ 'ticket_ai_edit_done',
15
+ 'ticket_ai_edit_error',
16
+ 'agent_refine_ready',
17
+ 'agent_refine_error',
18
+ 'smash.completed',
19
+ 'smash.failed',
20
+ 'loop.run_completed',
21
+ 'loop.run_stopped',
22
+ 'explore.contract_refine_failed',
23
+ 'file.summary_updated',
24
+ 'file.summary_failed',
25
+ ];
26
+ function isTerminal(type) {
27
+ return TERMINAL_PATTERNS.some((p) => type === p || type.endsWith(p));
28
+ }
29
+ function mentionsRef(msg, ref) {
30
+ // Heuristic: the reference (jobId / requestId / loopRunId) appears somewhere
31
+ // in the message payload. Cheap and robust across the heterogeneous WS union.
32
+ try {
33
+ return JSON.stringify(msg).includes(ref);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ function watchTool() {
40
+ return {
41
+ name: 'specrails_watch',
42
+ title: 'Watch async result',
43
+ description: 'Wait for an asynchronous operation (rail launch, spec generation, chat turn, agent refine, SMASH, loop run) to settle ' +
44
+ 'and return its events. Pass the projectId and the reference (jobId / requestId) returned by the action. ' +
45
+ 'Resolves when a terminal event for that reference is seen, or after untilMs.',
46
+ tier: 'read',
47
+ inputSchema: {
48
+ projectId: zod_1.z.string().describe('Project id the operation runs in'),
49
+ ref: zod_1.z.string().describe('jobId / requestId / loopRunId returned by the async action'),
50
+ untilMs: zod_1.z.number().int().min(1000).max(600000).default(120000).describe('Max time to wait (ms)'),
51
+ },
52
+ handler: (ctx, args) => {
53
+ const projectId = args.projectId;
54
+ const ref = args.ref;
55
+ const untilMs = args.untilMs ?? 120000;
56
+ return new Promise((resolve) => {
57
+ const events = [];
58
+ let done = false;
59
+ const finish = (settled, reason) => {
60
+ if (done)
61
+ return;
62
+ done = true;
63
+ clearTimeout(timer);
64
+ unsub();
65
+ resolve({
66
+ settled,
67
+ reason,
68
+ ref,
69
+ projectId,
70
+ eventCount: events.length,
71
+ terminalEvent: settled ? events[events.length - 1] : null,
72
+ events: events.slice(-50),
73
+ });
74
+ };
75
+ const unsub = ctx.eventBus.onMessage((msg) => {
76
+ const msgProjectId = msg.projectId;
77
+ if (msgProjectId && msgProjectId !== projectId)
78
+ return;
79
+ if (!mentionsRef(msg, ref))
80
+ return;
81
+ events.push(msg);
82
+ const type = msg.type ?? '';
83
+ if (isTerminal(type))
84
+ finish(true, `terminal:${type}`);
85
+ });
86
+ const timer = setTimeout(() => finish(false, 'timeout'), untilMs);
87
+ // Do not keep the event loop alive solely for this watch.
88
+ if (typeof timer.unref === 'function')
89
+ timer.unref();
90
+ });
91
+ },
92
+ };
93
+ }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MobileEventBus = void 0;
3
4
  exports.getMobileEventBus = getMobileEventBus;
4
5
  const events_1 = require("events");
5
6
  // In-process fan-out of every WS broadcast to the Mobile Gateway.
@@ -28,6 +29,7 @@ class MobileEventBus extends events_1.EventEmitter {
28
29
  return () => this.off('message', cb);
29
30
  }
30
31
  }
32
+ exports.MobileEventBus = MobileEventBus;
31
33
  // EventEmitter defaults to 10 listeners; the gateway attaches one shared listener
32
34
  // that fans out to many sockets, so the default is plenty — but lift it anyway
33
35
  // to be safe against future multi-listener designs.
@@ -67,7 +67,21 @@ function registerSetupRoutes(deps) {
67
67
  const requested = (req.body ?? {});
68
68
  const provider = typeof requested.provider === 'string' ? requested.provider : undefined;
69
69
  res.status(202).json({ ok: true });
70
- setupManager.startInstall(project.id, project.path, project.slug, provider);
70
+ // One-shot multi-provider install: an explicit `providers` list, or `all:true`
71
+ // to install every provider the project is registered with (used by the MCP/
72
+ // agent so a single `install` provisions them all). Else single-provider.
73
+ const explicit = Array.isArray(requested.providers)
74
+ ? requested.providers.filter((p) => typeof p === 'string')
75
+ : undefined;
76
+ if (explicit && explicit.length > 0) {
77
+ setupManager.startInstallProviders(project.id, project.path, project.slug, explicit);
78
+ }
79
+ else if (requested.all === true) {
80
+ setupManager.startInstallProviders(project.id, project.path, project.slug, project.providers);
81
+ }
82
+ else {
83
+ setupManager.startInstall(project.id, project.path, project.slug, provider);
84
+ }
71
85
  });
72
86
  router.post('/:projectId/enrich/start', (req, res) => {
73
87
  const { project, setupManager } = ctx(req);
@@ -1322,8 +1322,8 @@ function registerTicketsRoutes(deps) {
1322
1322
  short_summary: null,
1323
1323
  created_at: now,
1324
1324
  updated_at: now,
1325
- created_by: 'hub', // legacy on-disk wire value (tickets.json, shared with specrails-core) — do not rename
1326
- source: source === 'product-backlog' || source === 'propose-spec' || source === 'manual' ? source : 'hub', // legacy on-disk wire value — do not rename
1325
+ created_by: source === 'mcp' ? 'mcp' : 'hub', // 'hub' = legacy on-disk wire value (tickets.json, shared with specrails-core) — do not rename
1326
+ source: source === 'product-backlog' || source === 'propose-spec' || source === 'manual' || source === 'mcp' ? source : 'hub', // 'hub' legacy wire value — do not rename
1327
1327
  };
1328
1328
  s.tickets[String(id)] = ticket;
1329
1329
  created = ticket;