specrails-desktop 2.15.0 → 2.17.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 (359) hide show
  1. package/client/dist/assets/{ActivityFeedPage-C_clKk8d.js → ActivityFeedPage-BLDMBgwL.js} +1 -1
  2. package/client/dist/assets/AgentsPage-CFaIiyAA.js +86 -0
  3. package/client/dist/assets/{AnalyticsPage-DZkVU3yd.js → AnalyticsPage-DY8ECq2R.js} +1 -1
  4. package/client/dist/assets/{BarChart-Dq1h8eYR.js → BarChart-ouivqe5L.js} +2 -2
  5. package/client/dist/assets/CodePage-DAFtRafz.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-CYhXhJoQ.js → DesktopAnalyticsPage-DIMhuuv0.js} +1 -1
  7. package/client/dist/assets/{DocsDialog-CS3Syf61.js → DocsDialog-DBYhv3SM.js} +2 -2
  8. package/client/dist/assets/{DocsPage-l3ZcTO2E.js → DocsPage-DeLRn8W_.js} +2 -2
  9. package/client/dist/assets/{ExportDropdown-DdB2ncYh.js → ExportDropdown-2XwQ08sz.js} +1 -1
  10. package/client/dist/assets/{IntegrationsPage-CJ_Om1zu.js → IntegrationsPage-DB-a4FDV.js} +3 -3
  11. package/client/dist/assets/JobDetailPage-C3lljMiV.js +16 -0
  12. package/client/dist/assets/JobsPage-CzquwJ6G.js +1 -0
  13. package/client/dist/assets/LoopBuilderPage-TkoalfW5.js +7 -0
  14. package/client/dist/assets/LoopsPage-ypooVAQ5.js +1 -0
  15. package/client/dist/assets/{agents-CQlu1nOm.js → agents-4fnKlwcG.js} +1 -1
  16. package/client/dist/assets/{agents-DcoUUFZl.js → agents-B-rVIhuw.js} +1 -1
  17. package/client/dist/assets/{agents-DVYpVGur.js → agents-BRUz6RJs.js} +1 -1
  18. package/client/dist/assets/{agents-DCX9D3-Q.js → agents-CZQvKDIb.js} +1 -1
  19. package/client/dist/assets/{agents-BYjUOrLD.js → agents-CuRY-Zg_.js} +1 -1
  20. package/client/dist/assets/{agents-Jc-CjBDB.js → agents-D7nWnLg6.js} +1 -1
  21. package/client/dist/assets/{agents-CIhRSOna.js → agents-DzhSlNZd.js} +1 -1
  22. package/client/dist/assets/{agents-1lhqEcXH.js → agents-E-42N3b1.js} +1 -1
  23. package/client/dist/assets/{cssMode-DSHXamUH.js → cssMode-BijCvCxs.js} +1 -1
  24. package/client/dist/assets/{dist-js-Ele_sHO7.js → dist-js-D7m9--SN.js} +1 -1
  25. package/client/dist/assets/{dist-js-D27I-bfi.js → dist-js-nZhsq1qo.js} +1 -1
  26. package/client/dist/assets/{editor.main-Cd8qBRAT.js → editor.main-DMIcqwXb.js} +2 -2
  27. package/client/dist/assets/{freemarker2-CkEKTBm2.js → freemarker2-CP2AizpZ.js} +1 -1
  28. package/client/dist/assets/{handlebars-BRGx0JpL.js → handlebars-8ElanPOm.js} +1 -1
  29. package/client/dist/assets/{html-BfRIaGgN.js → html-Mu3b73tG.js} +1 -1
  30. package/client/dist/assets/{htmlMode-BTpSnsDE.js → htmlMode-tHprGCEv.js} +1 -1
  31. package/client/dist/assets/index-CDEQ4aU_.js +144 -0
  32. package/client/dist/assets/{javascript-DMmE2kKN.js → javascript-DlG0apUe.js} +1 -1
  33. package/client/dist/assets/{jsonMode-hYuaUleY.js → jsonMode-Cr0T4SXO.js} +1 -1
  34. package/client/dist/assets/{lib-qdRfWwE1.js → lib-Bu76JzdB.js} +2 -2
  35. package/client/dist/assets/{liquid-C-VbuTr7.js → liquid-QgTgbClC.js} +1 -1
  36. package/client/dist/assets/{lspLanguageFeatures-BALx-vjn.js → lspLanguageFeatures-D-BxkRza.js} +1 -1
  37. package/client/dist/assets/mcp-BSCAtU6Y.js +1 -0
  38. package/client/dist/assets/mcp-Cb6fUaFJ.js +1 -0
  39. package/client/dist/assets/mcp-CyP0quZ7.js +1 -0
  40. package/client/dist/assets/mcp-D-zQCO5E.js +1 -0
  41. package/client/dist/assets/mcp-DCTNWpfB.js +1 -0
  42. package/client/dist/assets/mcp-DEFGJ1OJ.js +1 -0
  43. package/client/dist/assets/mcp-DFG9OEXM.js +1 -0
  44. package/client/dist/assets/mcp-DyERgHA2.js +1 -0
  45. package/client/dist/assets/{mdx-Cs8MNCzn.js → mdx-B2QTaeRe.js} +1 -1
  46. package/client/dist/assets/{monaco.contribution-CIvCAJs4.js → monaco.contribution-CkdGfXCC.js} +2 -2
  47. package/client/dist/assets/{python-CGShLxuI.js → python-BlF0qnMQ.js} +1 -1
  48. package/client/dist/assets/{razor-CvOBQGd5.js → razor--3PSy3lb.js} +1 -1
  49. package/client/dist/assets/setup-BiQ7ut8d.js +1 -0
  50. package/client/dist/assets/setup-BxvjG5Xp.js +1 -0
  51. package/client/dist/assets/setup-CQ-F6Ciq.js +1 -0
  52. package/client/dist/assets/setup-D2JlpJEz.js +1 -0
  53. package/client/dist/assets/setup-DG1R6J6o.js +1 -0
  54. package/client/dist/assets/setup-DuFJZJsm.js +1 -0
  55. package/client/dist/assets/setup-DyJszDqm.js +1 -0
  56. package/client/dist/assets/setup-Ynw19V8w.js +1 -0
  57. package/client/dist/assets/{tsMode-04uOpiiP.js → tsMode-CHfsvcOI.js} +1 -1
  58. package/client/dist/assets/{typescript-CX0CwyIZ.js → typescript-Bjxf6BOp.js} +1 -1
  59. package/client/dist/assets/{upload-DPG87J4t.js → upload-x42OZJr2.js} +1 -1
  60. package/client/dist/assets/{value-BRVOAvhY.js → value-fEDhg5Bd.js} +1 -1
  61. package/client/dist/assets/{workers-yr2u4c3C.js → workers-CAAvSda9.js} +1 -1
  62. package/client/dist/assets/{xml-C0OFlt5X.js → xml-iYCSJ4oL.js} +1 -1
  63. package/client/dist/assets/{yaml-DgabaPs5.js → yaml-DtO-_yDY.js} +1 -1
  64. package/client/dist/index.html +10 -9
  65. package/docs/README.md +2 -0
  66. package/docs/guide/de/integrations/5-mcp-server.md +74 -0
  67. package/docs/guide/en/integrations/5-mcp-server.md +74 -0
  68. package/docs/guide/es/integrations/5-mcp-server.md +74 -0
  69. package/docs/guide/fr/integrations/5-mcp-server.md +74 -0
  70. package/docs/guide/it/integrations/5-mcp-server.md +74 -0
  71. package/docs/guide/ja/integrations/5-mcp-server.md +74 -0
  72. package/docs/guide/pt/integrations/5-mcp-server.md +74 -0
  73. package/docs/guide/zh/integrations/5-mcp-server.md +74 -0
  74. package/docs/mcp.md +249 -0
  75. package/package.json +8 -5
  76. package/server/dist/index.js +30 -0
  77. package/server/dist/loop-command-catalog.js +30 -1
  78. package/server/dist/loop-run-manager.js +54 -2
  79. package/server/dist/loop-templates.js +73 -0
  80. package/server/dist/mcp/guide.js +82 -0
  81. package/server/dist/mcp/index.js +21 -0
  82. package/server/dist/mcp/mcp-admin-router.js +71 -0
  83. package/server/dist/mcp/mcp-server.js +143 -0
  84. package/server/dist/mcp/mcp-tiers.js +48 -0
  85. package/server/dist/mcp/mcp-token.js +119 -0
  86. package/server/dist/mcp/resources.js +24 -0
  87. package/server/dist/mcp/tools/agents.js +319 -0
  88. package/server/dist/mcp/tools/analytics.js +168 -0
  89. package/server/dist/mcp/tools/app-settings.js +81 -0
  90. package/server/dist/mcp/tools/catalog.js +45 -0
  91. package/server/dist/mcp/tools/chat.js +147 -0
  92. package/server/dist/mcp/tools/code.js +145 -0
  93. package/server/dist/mcp/tools/jira.js +222 -0
  94. package/server/dist/mcp/tools/jobs.js +258 -0
  95. package/server/dist/mcp/tools/loops.js +229 -0
  96. package/server/dist/mcp/tools/meta.js +96 -0
  97. package/server/dist/mcp/tools/plugins.js +83 -0
  98. package/server/dist/mcp/tools/projects.js +62 -0
  99. package/server/dist/mcp/tools/rails.js +181 -0
  100. package/server/dist/mcp/tools/setup.js +187 -0
  101. package/server/dist/mcp/tools/specs.js +327 -0
  102. package/server/dist/mcp/tools/types.js +137 -0
  103. package/server/dist/mcp/tools/watch.js +93 -0
  104. package/server/dist/mobile/mobile-event-bus.js +2 -0
  105. package/server/dist/project-router-tickets.js +2 -2
  106. package/server/dist/rail-isolation.js +8 -7
  107. package/server/dist/rails-router.js +10 -5
  108. package/server/dist/workspace-manager.js +134 -14
  109. package/client/dist/assets/AgentsPage-jcPUtPhP.js +0 -86
  110. package/client/dist/assets/CodePage-BcbZT88K.js +0 -2
  111. package/client/dist/assets/JobDetailPage-D5Cxw0md.js +0 -16
  112. package/client/dist/assets/JobsPage-3v_iX83r.js +0 -1
  113. package/client/dist/assets/LoopBuilderPage-4s_adZy5.js +0 -7
  114. package/client/dist/assets/LoopsPage-DA94-o0j.js +0 -1
  115. package/client/dist/assets/index-meTWPBzY.js +0 -144
  116. package/client/dist/assets/setup-BOmQ4vh2.js +0 -1
  117. package/client/dist/assets/setup-BwO2-kuK.js +0 -1
  118. package/client/dist/assets/setup-CUTNWz-0.js +0 -1
  119. package/client/dist/assets/setup-Cf3hjChU.js +0 -1
  120. package/client/dist/assets/setup-CsuXrbJ2.js +0 -1
  121. package/client/dist/assets/setup-Df4cJOqu.js +0 -1
  122. package/client/dist/assets/setup-Do0fWx6W.js +0 -1
  123. package/client/dist/assets/setup-GlkDhBlJ.js +0 -1
  124. /package/client/dist/assets/{abap-CK68cr9-.js → abap-BFJA_Joo.js} +0 -0
  125. /package/client/dist/assets/{activity-D4dhNEwO.js → activity-BXBMi28x.js} +0 -0
  126. /package/client/dist/assets/{activity-usnNa7FX.js → activity-By6I70Ws.js} +0 -0
  127. /package/client/dist/assets/{activity-BbNkUmW4.js → activity-CfedQaof.js} +0 -0
  128. /package/client/dist/assets/{activity-B2MvEH54.js → activity-D62CDlQz.js} +0 -0
  129. /package/client/dist/assets/{activity-DHjfmAyT.js → activity-DAFuSwnt.js} +0 -0
  130. /package/client/dist/assets/{activity-C7VdmeGb.js → activity-Did0FnrH.js} +0 -0
  131. /package/client/dist/assets/{activity-Di2f50_Z.js → activity-oLgwaVyt.js} +0 -0
  132. /package/client/dist/assets/{addon-image-biWX2UUm.js → addon-image-bPlduuUZ.js} +0 -0
  133. /package/client/dist/assets/{addon-ligatures-D4EiDb5r.js → addon-ligatures-BexpF7xb.js} +0 -0
  134. /package/client/dist/assets/{addon-webgl-CDCIZQ03.js → addon-webgl-Zy4IOieH.js} +0 -0
  135. /package/client/dist/assets/{addspec-BxMn39wn.js → addspec-B2fTENuq.js} +0 -0
  136. /package/client/dist/assets/{addspec-DAveBiCE.js → addspec-BmggyuCD.js} +0 -0
  137. /package/client/dist/assets/{addspec-1HEtmLv1.js → addspec-CFsLeQKV.js} +0 -0
  138. /package/client/dist/assets/{addspec-DfuR6EAl.js → addspec-CkHI5c5H.js} +0 -0
  139. /package/client/dist/assets/{addspec-CO1xv6BL.js → addspec-Cvm06hGe.js} +0 -0
  140. /package/client/dist/assets/{addspec-IozoiiGs.js → addspec-DwoLTocx.js} +0 -0
  141. /package/client/dist/assets/{addspec-CNyAUGz_.js → addspec-gthH-H1g.js} +0 -0
  142. /package/client/dist/assets/{agentstudio-Q0Sq_jTg.js → agentstudio-BtASTJl7.js} +0 -0
  143. /package/client/dist/assets/{agentstudio-9NbsoGXk.js → agentstudio-CIe9C892.js} +0 -0
  144. /package/client/dist/assets/{agentstudio-B-jQgYV-.js → agentstudio-CYGk-jjo.js} +0 -0
  145. /package/client/dist/assets/{agentstudio-Bwa72b1S.js → agentstudio-CwiS3BTp.js} +0 -0
  146. /package/client/dist/assets/{agentstudio-B1y0JWY-.js → agentstudio-D0bQBWpI.js} +0 -0
  147. /package/client/dist/assets/{agentstudio-DCyDW8sy.js → agentstudio-Dq-bZrI3.js} +0 -0
  148. /package/client/dist/assets/{agentstudio-1E-bag77.js → agentstudio-n0pmwz2I.js} +0 -0
  149. /package/client/dist/assets/{aiedit-qTGI_PGs.js → aiedit-1Z-UYKFb.js} +0 -0
  150. /package/client/dist/assets/{aiedit-CGxeml8D.js → aiedit-BAJM9Cch.js} +0 -0
  151. /package/client/dist/assets/{aiedit-CfBNanHE.js → aiedit-CYnsp1Iv.js} +0 -0
  152. /package/client/dist/assets/{aiedit-CvSspQmE.js → aiedit-CZBZAyeK.js} +0 -0
  153. /package/client/dist/assets/{aiedit-kAU7KADg.js → aiedit-CunYxmW0.js} +0 -0
  154. /package/client/dist/assets/{aiedit-DUpe7NxI.js → aiedit-s1T1EMK8.js} +0 -0
  155. /package/client/dist/assets/{aiedit-DSLtsYIu.js → aiedit-uIDh2tlc.js} +0 -0
  156. /package/client/dist/assets/{analytics-DBGO8B6-.js → analytics-B95FiMuD.js} +0 -0
  157. /package/client/dist/assets/{analytics-CgEkAUiP.js → analytics-DPIkvuTS.js} +0 -0
  158. /package/client/dist/assets/{analytics-DDqve9Ma.js → analytics-DPmCEzvJ.js} +0 -0
  159. /package/client/dist/assets/{analytics-D1ElChhs.js → analytics-DRexho82.js} +0 -0
  160. /package/client/dist/assets/{analytics-DEsEAJZL.js → analytics-DVFNjjAv.js} +0 -0
  161. /package/client/dist/assets/{analytics-BDlELxc7.js → analytics-DZKGAUdm.js} +0 -0
  162. /package/client/dist/assets/{analytics-CQqu1yE5.js → analytics-g9ASAR_r.js} +0 -0
  163. /package/client/dist/assets/{apex-BHHyAVNb.js → apex-C1f-z6NO.js} +0 -0
  164. /package/client/dist/assets/{atom-one-dark-DrWoEEbZ.js → atom-one-dark-CT4DolNR.js} +0 -0
  165. /package/client/dist/assets/{attachments-3CuBBRFu.js → attachments--jr2QLGv.js} +0 -0
  166. /package/client/dist/assets/{attachments-pW3zm9nu.js → attachments-BgqfeoOx.js} +0 -0
  167. /package/client/dist/assets/{attachments-BNgH6Tcu.js → attachments-Bj3HHTXV.js} +0 -0
  168. /package/client/dist/assets/{attachments-D5fVsMgb.js → attachments-C3skjFyH.js} +0 -0
  169. /package/client/dist/assets/{attachments-UJtEq892.js → attachments-CnPS8YWu.js} +0 -0
  170. /package/client/dist/assets/{attachments-CppM7de8.js → attachments-DejzxE0C.js} +0 -0
  171. /package/client/dist/assets/{attachments-BLqqPGyy.js → attachments-FFb2xHc_.js} +0 -0
  172. /package/client/dist/assets/{azcli-C56lfIEP.js → azcli-Dr0WBJTP.js} +0 -0
  173. /package/client/dist/assets/{bat-BLamSaf-.js → bat-BKG3k49W.js} +0 -0
  174. /package/client/dist/assets/{bicep-lrOcEItS.js → bicep-DXs2wTcS.js} +0 -0
  175. /package/client/dist/assets/{browser-C91MQBxx.js → browser-BS9t1qMc.js} +0 -0
  176. /package/client/dist/assets/{browser-B7i7Ysc8.js → browser-BpMo-amf.js} +0 -0
  177. /package/client/dist/assets/{browser-DwE289IS.js → browser-CsC3NOZ-.js} +0 -0
  178. /package/client/dist/assets/{browser-C__Vz7KR.js → browser-DS6xnywt.js} +0 -0
  179. /package/client/dist/assets/{browser-DhwTc0ry.js → browser-Dm6F9bqQ.js} +0 -0
  180. /package/client/dist/assets/{browser-EFmPpkvO.js → browser-Dm_Of27q.js} +0 -0
  181. /package/client/dist/assets/{browser-BVQgd1_1.js → browser-U3iouSXn.js} +0 -0
  182. /package/client/dist/assets/{cameligo-BK_p0bgU.js → cameligo-Detknuxp.js} +0 -0
  183. /package/client/dist/assets/{chat-PVakYp92.js → chat-B2tWTbZL.js} +0 -0
  184. /package/client/dist/assets/{chat-BnJPyeUK.js → chat-CXOJHDUZ.js} +0 -0
  185. /package/client/dist/assets/{chat-CE8bx6Rz.js → chat-Cy78lw1n.js} +0 -0
  186. /package/client/dist/assets/{chat-UMl6H55K.js → chat-D78VMi-v.js} +0 -0
  187. /package/client/dist/assets/{chat-BXVx6rb5.js → chat-De1RgOXU.js} +0 -0
  188. /package/client/dist/assets/{chat-C5nJF6L0.js → chat-DyTbH-0F.js} +0 -0
  189. /package/client/dist/assets/{chat-9RcD5Lcn.js → chat-sqZyn0C4.js} +0 -0
  190. /package/client/dist/assets/{clojure-BQ7JOykv.js → clojure-CCu4UNKg.js} +0 -0
  191. /package/client/dist/assets/{clsx-CjLeUJfG.js → clsx-pVRQNNR0.js} +0 -0
  192. /package/client/dist/assets/{code-u9Sg9ohM.js → code-6LokPmIE.js} +0 -0
  193. /package/client/dist/assets/{code-OoLqhjoI.js → code-CLTuhuvM.js} +0 -0
  194. /package/client/dist/assets/{code-Lg4awa35.js → code-CwGlPuwb.js} +0 -0
  195. /package/client/dist/assets/{code-BPMR8_Vp.js → code-DFCDV5e1.js} +0 -0
  196. /package/client/dist/assets/{code-eh4vO2-_.js → code-Djq48g0s.js} +0 -0
  197. /package/client/dist/assets/{code-aCUqkVN_.js → code-Do27r8WG.js} +0 -0
  198. /package/client/dist/assets/{code-BsOBm1Fb.js → code-Drc7f197.js} +0 -0
  199. /package/client/dist/assets/{coffee-BD3DiL5s.js → coffee-Bam7Y9IR.js} +0 -0
  200. /package/client/dist/assets/{commands-B9XEyMj-.js → commands-CxReviyc.js} +0 -0
  201. /package/client/dist/assets/{commands-Ic1DJDxm.js → commands-CzP1qQyD.js} +0 -0
  202. /package/client/dist/assets/{commands-BdI0CtOL.js → commands-DGP46UxM.js} +0 -0
  203. /package/client/dist/assets/{commands-DSX3yJyr.js → commands-DQvoMqa7.js} +0 -0
  204. /package/client/dist/assets/{commands-yNn4IA4C.js → commands-DeKaz04Q.js} +0 -0
  205. /package/client/dist/assets/{commands-BI1Ta-E7.js → commands-DwYhwQsF.js} +0 -0
  206. /package/client/dist/assets/{commands-C2ewlyXo.js → commands-lm6Vlght.js} +0 -0
  207. /package/client/dist/assets/{common-CzCQ0rpW.js → common-B0crwfK0.js} +0 -0
  208. /package/client/dist/assets/{common-CNoRVpnA.js → common-BlqmiTj4.js} +0 -0
  209. /package/client/dist/assets/{common-CcHFVATT.js → common-CIUPtPdu.js} +0 -0
  210. /package/client/dist/assets/{common-LDwESeCX.js → common-C_-0fZsp.js} +0 -0
  211. /package/client/dist/assets/{common-C9VyfY7g.js → common-DH9A-wTo.js} +0 -0
  212. /package/client/dist/assets/{common-Dmpyh323.js → common-DJM41wFl.js} +0 -0
  213. /package/client/dist/assets/{common-ULlk84G4.js → common-SyTaRc_K.js} +0 -0
  214. /package/client/dist/assets/{cpp-splnCwCh.js → cpp-BhOkAe-9.js} +0 -0
  215. /package/client/dist/assets/{csharp-BCpsl-o3.js → csharp-BgHArJNx.js} +0 -0
  216. /package/client/dist/assets/{csp-tkCWrZmb.js → csp-Xzh56_3-.js} +0 -0
  217. /package/client/dist/assets/{css-B_xdnReq.js → css-CD7-RYRS.js} +0 -0
  218. /package/client/dist/assets/{cypher-OL1zq-y3.js → cypher-l2xAdF3X.js} +0 -0
  219. /package/client/dist/assets/{dart-CtTlOkrm.js → dart-CDOWeyzt.js} +0 -0
  220. /package/client/dist/assets/{dashboard-C-dXxut-.js → dashboard-B1rs1wki.js} +0 -0
  221. /package/client/dist/assets/{dashboard-CUjyhohl.js → dashboard-D9QPCmv3.js} +0 -0
  222. /package/client/dist/assets/{dashboard-C52PElPV.js → dashboard-Dxgb47lO.js} +0 -0
  223. /package/client/dist/assets/{dashboard-03EpOoMM.js → dashboard-TuElRCZX.js} +0 -0
  224. /package/client/dist/assets/{dashboard-Ch6H-457.js → dashboard-UWXs9_wy.js} +0 -0
  225. /package/client/dist/assets/{dashboard-zH9quXks.js → dashboard-ajT8usAi.js} +0 -0
  226. /package/client/dist/assets/{dashboard-C2owt3eN.js → dashboard-zwN2xswo.js} +0 -0
  227. /package/client/dist/assets/{dockerfile-DcQ5CNiW.js → dockerfile-Dv8atx7v.js} +0 -0
  228. /package/client/dist/assets/{ecl-DZpiDRt6.js → ecl-DIYTlbD_.js} +0 -0
  229. /package/client/dist/assets/{editor.api2-Cwla1nvV.js → editor.api2-CrNrMVfe.js} +0 -0
  230. /package/client/dist/assets/{elixir-Dzj2lqtK.js → elixir-CR7PLJja.js} +0 -0
  231. /package/client/dist/assets/{explore-4SD1DxR0.js → explore-BYFCCka4.js} +0 -0
  232. /package/client/dist/assets/{explore-xQoaak6e.js → explore-CE0wSNV1.js} +0 -0
  233. /package/client/dist/assets/{explore-CZt4Ncpa.js → explore-CSDcRUNj.js} +0 -0
  234. /package/client/dist/assets/{explore-DQ0XEjed.js → explore-CvM08PpD.js} +0 -0
  235. /package/client/dist/assets/{explore-CCcPXrHg.js → explore-Jkw93usp.js} +0 -0
  236. /package/client/dist/assets/{explore-BxR9q8Hq.js → explore-kK239uoZ.js} +0 -0
  237. /package/client/dist/assets/{explore-B2n8r9Ye.js → explore-mLI5eXks.js} +0 -0
  238. /package/client/dist/assets/{flow9-CFENCHIZ.js → flow9-DCK5-6NG.js} +0 -0
  239. /package/client/dist/assets/{format-command-CMsBTOPN.js → format-command-rVyGPxWE.js} +0 -0
  240. /package/client/dist/assets/{fsharp-D-4bv7FB.js → fsharp-DJ1xiUVU.js} +0 -0
  241. /package/client/dist/assets/{go-djxbTJq7.js → go-BEvbHcbb.js} +0 -0
  242. /package/client/dist/assets/{graphql-DHIFrkZ6.js → graphql-DN1ktXnz.js} +0 -0
  243. /package/client/dist/assets/{hcl-CXVoowNb.js → hcl-DJaIjIEr.js} +0 -0
  244. /package/client/dist/assets/{ini-Crzc5kly.js → ini-PlKfLEnb.js} +0 -0
  245. /package/client/dist/assets/{integrations-A9f08E0o.js → integrations-B8hT0Qf_.js} +0 -0
  246. /package/client/dist/assets/{integrations-DwSVdCpf.js → integrations-Bdk5dMyv.js} +0 -0
  247. /package/client/dist/assets/{integrations-B46ac3sf.js → integrations-CYqz7YFS.js} +0 -0
  248. /package/client/dist/assets/{integrations-9qxUNhPD.js → integrations-Cj_BsXxJ.js} +0 -0
  249. /package/client/dist/assets/{integrations-B17KyA-i.js → integrations-D8M8eJH2.js} +0 -0
  250. /package/client/dist/assets/{integrations-DkBi-Gsh.js → integrations-TJuDBl-9.js} +0 -0
  251. /package/client/dist/assets/{integrations-C7u7SInG.js → integrations-TwQV3sLw.js} +0 -0
  252. /package/client/dist/assets/{java-CYLaJrdE.js → java-3PUwMlx-.js} +0 -0
  253. /package/client/dist/assets/{jira-01H1RfQj.js → jira--rI5PfuE.js} +0 -0
  254. /package/client/dist/assets/{jira-CBU0200d.js → jira-BoRyckbb.js} +0 -0
  255. /package/client/dist/assets/{jira-BpnpUFlb.js → jira-C95FN3yk.js} +0 -0
  256. /package/client/dist/assets/{jira-DzIVvNmy.js → jira-CNUUgTs4.js} +0 -0
  257. /package/client/dist/assets/{jira-Dht8qNck.js → jira-DHFnvEm8.js} +0 -0
  258. /package/client/dist/assets/{jira-4Fin3fu7.js → jira-NhkbrDXe.js} +0 -0
  259. /package/client/dist/assets/{jira-0ldfMByG.js → jira-wiIvGma5.js} +0 -0
  260. /package/client/dist/assets/{jobs-Oz3UtLQn.js → jobs-Bjp7v9aA.js} +0 -0
  261. /package/client/dist/assets/{jobs-jEQidSIb.js → jobs-Bjt5PyVv.js} +0 -0
  262. /package/client/dist/assets/{jobs-BF9raUkL.js → jobs-BmTs4Bn3.js} +0 -0
  263. /package/client/dist/assets/{jobs-DcCONARH.js → jobs-Cchy-18B.js} +0 -0
  264. /package/client/dist/assets/{jobs-DM9owF1j.js → jobs-D2QVTPbz.js} +0 -0
  265. /package/client/dist/assets/{jobs-Bw4hqEro.js → jobs-LK1ImiDH.js} +0 -0
  266. /package/client/dist/assets/{jobs-ym72SIR9.js → jobs-yPz4Dvi-.js} +0 -0
  267. /package/client/dist/assets/{julia-BJ7tUU-T.js → julia-DC6LnY15.js} +0 -0
  268. /package/client/dist/assets/{kotlin-CeWcU3lC.js → kotlin-CoGntbZF.js} +0 -0
  269. /package/client/dist/assets/{less-CviwWNG4.js → less-DCFflTXL.js} +0 -0
  270. /package/client/dist/assets/{lexon-Dt28jqDS.js → lexon-BTOivnjP.js} +0 -0
  271. /package/client/dist/assets/{loops-Fv4Bs-9A.js → loops-BFIcYvpy.js} +0 -0
  272. /package/client/dist/assets/{loops-D_ZxkD4l.js → loops-BU314kSd.js} +0 -0
  273. /package/client/dist/assets/{loops-DE-2cmLw.js → loops-C3OaXou9.js} +0 -0
  274. /package/client/dist/assets/{loops-mcoeFMat.js → loops-Cv6Aeszq.js} +0 -0
  275. /package/client/dist/assets/{loops-B_w7STrw.js → loops-IQe23ef6.js} +0 -0
  276. /package/client/dist/assets/{loops-BYNUJVyD.js → loops-LqZ4MC6J.js} +0 -0
  277. /package/client/dist/assets/{loops-B_gS4Pqu.js → loops-SbkOdciY.js} +0 -0
  278. /package/client/dist/assets/{lua-B6AOaJSi.js → lua-ClKCZMmP.js} +0 -0
  279. /package/client/dist/assets/{m3-VnMkHJcy.js → m3-25hKFWMb.js} +0 -0
  280. /package/client/dist/assets/{markdown-8vKNdvIM.js → markdown-BGAj9q0K.js} +0 -0
  281. /package/client/dist/assets/{mips-DoEcuzan.js → mips-BrVgY2Pf.js} +0 -0
  282. /package/client/dist/assets/{msdax-CNzvHyEp.js → msdax-7EhcRsm8.js} +0 -0
  283. /package/client/dist/assets/{mysql-Buk3vUtL.js → mysql-B5_atCBH.js} +0 -0
  284. /package/client/dist/assets/{nav-_0vAgZ_k.js → nav-B2Cv1vjz.js} +0 -0
  285. /package/client/dist/assets/{nav-Cw3_40fA.js → nav-Br2AivTc.js} +0 -0
  286. /package/client/dist/assets/{nav-DNDJ3H-_.js → nav-CFofO7QO.js} +0 -0
  287. /package/client/dist/assets/{nav-CCz4krPP.js → nav-CyHKTZxn.js} +0 -0
  288. /package/client/dist/assets/{nav-B5W9IMnf.js → nav-DqyLGJmF.js} +0 -0
  289. /package/client/dist/assets/{nav-CrG_mYRr.js → nav-_-siCrRv.js} +0 -0
  290. /package/client/dist/assets/{nav-DbAbG5_k.js → nav-rO4Nqnh-.js} +0 -0
  291. /package/client/dist/assets/{nav-DVp1nEsi.js → nav-zyBhf29M.js} +0 -0
  292. /package/client/dist/assets/{objective-c-DfOmda-5.js → objective-c-CLUvUl9T.js} +0 -0
  293. /package/client/dist/assets/{pascal-DoAA2yjy.js → pascal-BnU1HU42.js} +0 -0
  294. /package/client/dist/assets/{pascaligo-C65Vn1fu.js → pascaligo-dsKvjFzi.js} +0 -0
  295. /package/client/dist/assets/{perl-INlfQ_ro.js → perl-CZne1GYG.js} +0 -0
  296. /package/client/dist/assets/{pgsql-DtwRzglx.js → pgsql-CMa5W_iF.js} +0 -0
  297. /package/client/dist/assets/{php-DfHqVNet.js → php-Cu4mS35J.js} +0 -0
  298. /package/client/dist/assets/{pla-D3dNVRYk.js → pla-B7COwnnm.js} +0 -0
  299. /package/client/dist/assets/{postiats-UKXD1rM4.js → postiats-GLeNtnBQ.js} +0 -0
  300. /package/client/dist/assets/{powerquery-zE3E3O7s.js → powerquery-BqPf3R1W.js} +0 -0
  301. /package/client/dist/assets/{powershell-Df9b3zQl.js → powershell-D9btb5su.js} +0 -0
  302. /package/client/dist/assets/{protobuf-BF0nUVrW.js → protobuf-6GHSuaNm.js} +0 -0
  303. /package/client/dist/assets/{pug-Bj6Yikv_.js → pug-ct6rYKQd.js} +0 -0
  304. /package/client/dist/assets/{qsharp-BI4h-raZ.js → qsharp-CIrAQNPy.js} +0 -0
  305. /package/client/dist/assets/{r-DhpnmaXx.js → r-Bk-a0sON.js} +0 -0
  306. /package/client/dist/assets/{redis-DwYYPr4d.js → redis-C9yfaWYh.js} +0 -0
  307. /package/client/dist/assets/{redshift-D2f3Bc-4.js → redshift-DaOT1jRU.js} +0 -0
  308. /package/client/dist/assets/{restructuredtext-Dkiif6fe.js → restructuredtext-C1t9LQfS.js} +0 -0
  309. /package/client/dist/assets/{ruby-C6hLmHy0.js → ruby-COiPZJjc.js} +0 -0
  310. /package/client/dist/assets/{rust-X8WAGtxB.js → rust--gZOJZzj.js} +0 -0
  311. /package/client/dist/assets/{sb-4M2la0Kr.js → sb-NHf3iazF.js} +0 -0
  312. /package/client/dist/assets/{scala-CaCXz58E.js → scala-D1nO1TBd.js} +0 -0
  313. /package/client/dist/assets/{scheme-CMdjpmP7.js → scheme-Bref42s5.js} +0 -0
  314. /package/client/dist/assets/{scss-QopmvPyp.js → scss-BS9CCLr3.js} +0 -0
  315. /package/client/dist/assets/{settings-DyBxunCh.js → settings-BN4dufu6.js} +0 -0
  316. /package/client/dist/assets/{settings-BN7OusER.js → settings-Bap_q3rt.js} +0 -0
  317. /package/client/dist/assets/{settings-Bi5FFkIw.js → settings-BlYjWMy5.js} +0 -0
  318. /package/client/dist/assets/{settings-iQe5Dim-.js → settings-C7t_uUz4.js} +0 -0
  319. /package/client/dist/assets/{settings-fk-WlnUt.js → settings-CIFrUsKt.js} +0 -0
  320. /package/client/dist/assets/{settings-BA1V6KdK.js → settings-Cbj9VWmI.js} +0 -0
  321. /package/client/dist/assets/{settings-CRJxyTNN.js → settings-D7saHMU8.js} +0 -0
  322. /package/client/dist/assets/{settings-0m2iuTzd.js → settings-DrRjM5e8.js} +0 -0
  323. /package/client/dist/assets/{shell-CDBchS00.js → shell-DT3joMBB.js} +0 -0
  324. /package/client/dist/assets/{solidity-DvBfi5mC.js → solidity-o2Fm9dvg.js} +0 -0
  325. /package/client/dist/assets/{sophia-HSnXR17u.js → sophia-Dj1N6Xqb.js} +0 -0
  326. /package/client/dist/assets/{sparql-DhG_ppup.js → sparql-D2fjpTWm.js} +0 -0
  327. /package/client/dist/assets/{specs-BvsFNKTs.js → specs-C-WXGBqy.js} +0 -0
  328. /package/client/dist/assets/{specs-D4ORogcT.js → specs-C57koADY.js} +0 -0
  329. /package/client/dist/assets/{specs-CnH7dOiF.js → specs-CCXV-eSF.js} +0 -0
  330. /package/client/dist/assets/{specs-CUE3xSFM.js → specs-CYA5Bv7Q.js} +0 -0
  331. /package/client/dist/assets/{specs-C2wW5bq8.js → specs-DFCFg51O.js} +0 -0
  332. /package/client/dist/assets/{specs-C_8j4ELZ.js → specs-Dslng_yW.js} +0 -0
  333. /package/client/dist/assets/{specs-D6Y76Bgi.js → specs-DwRPiJTc.js} +0 -0
  334. /package/client/dist/assets/{specs-Bku8SypT.js → specs-DzlROLy7.js} +0 -0
  335. /package/client/dist/assets/{sql-DHeIS-tf.js → sql-BArhsPUz.js} +0 -0
  336. /package/client/dist/assets/{st-CMWchiRU.js → st-Bd2-tALj.js} +0 -0
  337. /package/client/dist/assets/{swift-CDXvBefj.js → swift-DvEpXBKj.js} +0 -0
  338. /package/client/dist/assets/{systemverilog-0RTukYUE.js → systemverilog-mHEbsnVH.js} +0 -0
  339. /package/client/dist/assets/{tcl-CbBF_Ttl.js → tcl-DePutp1e.js} +0 -0
  340. /package/client/dist/assets/{terminal-CB33IsO1.js → terminal-2S-Wsk6L.js} +0 -0
  341. /package/client/dist/assets/{terminal-Cz_Puz0q.js → terminal-BRQMrU74.js} +0 -0
  342. /package/client/dist/assets/{terminal-DFEH9XTQ.js → terminal-BTiemy98.js} +0 -0
  343. /package/client/dist/assets/{terminal-BTanJIt_.js → terminal-CjQeWfpp.js} +0 -0
  344. /package/client/dist/assets/{terminal-CY_sPfdD.js → terminal-CmP5riTU.js} +0 -0
  345. /package/client/dist/assets/{terminal-BLh00ouM.js → terminal-CnObBCAh.js} +0 -0
  346. /package/client/dist/assets/{terminal-CR6t-Oey.js → terminal-DcPKgKMN.js} +0 -0
  347. /package/client/dist/assets/{terminal-Cj4HzP3p.js → terminal-XciDq6VV.js} +0 -0
  348. /package/client/dist/assets/{tickets-OrJwUxJR.js → tickets-7kl_0jgM.js} +0 -0
  349. /package/client/dist/assets/{tickets-68LH1xp6.js → tickets-BSyaZI8d.js} +0 -0
  350. /package/client/dist/assets/{tickets-blQnNA4N.js → tickets-CcTcvwKS.js} +0 -0
  351. /package/client/dist/assets/{tickets-D6f3GSYn.js → tickets-D75vyZJj.js} +0 -0
  352. /package/client/dist/assets/{tickets-PtHNxQo5.js → tickets-D79ZT2SZ.js} +0 -0
  353. /package/client/dist/assets/{tickets-DBQXMYYA.js → tickets-DbcMWq5L.js} +0 -0
  354. /package/client/dist/assets/{tickets-jxsTrApR.js → tickets-ZA_2iPd1.js} +0 -0
  355. /package/client/dist/assets/{tickets-REr-EQx9.js → tickets-mxd23fWX.js} +0 -0
  356. /package/client/dist/assets/{twig-DCcGd8FE.js → twig-DQ2zIX0b.js} +0 -0
  357. /package/client/dist/assets/{typespec-HTphp-Uh.js → typespec-DPoaiGTJ.js} +0 -0
  358. /package/client/dist/assets/{vb-Cb_yUcVd.js → vb-Jo98wdMr.js} +0 -0
  359. /package/client/dist/assets/{wgsl-DrKU3YXj.js → wgsl-DZc4h_eS.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,137 @@
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
+ /**
12
+ * Calls the app's own REST API over loopback using the MASTER token (held
13
+ * server-side, never exposed to the MCP client). This reuses every router's
14
+ * existing logic/validation rather than re-implementing it in tools — the same
15
+ * loopback-forward pattern the Mobile Gateway uses for REST. `path` is appended
16
+ * after `/api` (e.g. `/projects/<id>/tickets`).
17
+ */
18
+ async function apiCall(ctx, method, path, body) {
19
+ const url = `http://127.0.0.1:${ctx.desktopPort}/api${path}`;
20
+ const res = await fetch(url, {
21
+ method,
22
+ headers: {
23
+ Authorization: `Bearer ${(0, auth_1.loadOrGenerateToken)()}`,
24
+ ...(body !== undefined ? { 'content-type': 'application/json' } : {}),
25
+ },
26
+ body: body !== undefined ? JSON.stringify(body) : undefined,
27
+ });
28
+ const text = await res.text();
29
+ let data;
30
+ try {
31
+ data = text ? JSON.parse(text) : null;
32
+ }
33
+ catch {
34
+ data = text;
35
+ }
36
+ if (!res.ok) {
37
+ const detail = typeof data === 'object' && data !== null ? JSON.stringify(data) : String(data);
38
+ throw new Error(`API ${method} ${path} → ${res.status}: ${detail}`);
39
+ }
40
+ return data;
41
+ }
42
+ /** Convenience: build the `/projects/<id>` path prefix for a resolved project. */
43
+ function projectPath(ctx, projectId) {
44
+ return `/projects/${requireProject(ctx, projectId).project.id}`;
45
+ }
46
+ /** Resolve a per-project context or throw a clear, LLM-readable error. */
47
+ function requireProject(ctx, projectId) {
48
+ const id = projectId ?? getActiveProject(ctx);
49
+ if (!id) {
50
+ throw new Error('No project specified and no active project selected. Call specrails_select_project or pass projectId.');
51
+ }
52
+ const pc = ctx.registry.getContext(id);
53
+ if (!pc)
54
+ throw new Error(`Unknown projectId "${id}". Use specrails_projects(list) to see valid ids.`);
55
+ return pc;
56
+ }
57
+ // ── Active-project stickiness (per server process) ────────────────────────────
58
+ // The active project is a soft default; an explicit projectId always wins.
59
+ let _activeProjectId = null;
60
+ function setActiveProject(id) {
61
+ _activeProjectId = id;
62
+ }
63
+ function getActiveProject(_ctx) {
64
+ return _activeProjectId;
65
+ }
66
+ function tierAnnotations(tier) {
67
+ switch (tier) {
68
+ case 'read':
69
+ return { readOnlyHint: true, openWorldHint: false };
70
+ case 'write':
71
+ return { readOnlyHint: false, openWorldHint: false };
72
+ case 'ai-spawn':
73
+ return { readOnlyHint: false, openWorldHint: true };
74
+ case 'destructive':
75
+ return { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
76
+ }
77
+ }
78
+ function toResult(data) {
79
+ const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
80
+ return { content: [{ type: 'text', text }] };
81
+ }
82
+ function errorResult(message) {
83
+ return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
84
+ }
85
+ /**
86
+ * Broadcasts an app-level `mcp.activity` event after a successful mutating tool
87
+ * call so the UI can surface what an external LLM did — regardless of which
88
+ * project is currently active. Deliberately carries NO top-level `projectId`
89
+ * (so the client's per-project WS filter does not drop it); the affected project
90
+ * is in `affectedProjectId` for cache invalidation. Read-tier calls don't emit.
91
+ * A notify failure must never break the tool, so it's wrapped.
92
+ */
93
+ function emitMcpActivity(ctx, spec, args, tier) {
94
+ try {
95
+ const affectedProjectId = (typeof args.projectId === 'string' ? args.projectId : null) ?? getActiveProject(ctx);
96
+ ctx.broadcast({
97
+ type: 'mcp.activity',
98
+ tool: spec.name,
99
+ action: typeof args.action === 'string' ? args.action : null,
100
+ tier,
101
+ affectedProjectId,
102
+ title: spec.title,
103
+ at: new Date().toISOString(),
104
+ });
105
+ }
106
+ catch {
107
+ /* a notification failure must never fail the tool */
108
+ }
109
+ }
110
+ /**
111
+ * Registers a tool on the McpServer, wrapping the handler with (1) tier
112
+ * enforcement and (2) uniform success/error result shaping. No tool can forget
113
+ * the tier check because it lives here, not in the handler.
114
+ */
115
+ function registerTieredTool(server, ctx, spec) {
116
+ const hintTier = spec.hintTier ?? (typeof spec.tier === 'string' ? spec.tier : 'write');
117
+ server.registerTool(spec.name, {
118
+ title: spec.title,
119
+ description: spec.description,
120
+ inputSchema: spec.inputSchema,
121
+ annotations: tierAnnotations(hintTier),
122
+ }, async (args) => {
123
+ const tier = typeof spec.tier === 'function' ? spec.tier(args ?? {}) : spec.tier;
124
+ if (!(0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier)) {
125
+ return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
126
+ }
127
+ try {
128
+ const data = await spec.handler(ctx, args ?? {});
129
+ if (tier !== 'read')
130
+ emitMcpActivity(ctx, spec, args ?? {}, tier);
131
+ return toResult(data);
132
+ }
133
+ catch (err) {
134
+ return errorResult(err instanceof Error ? err.message : String(err));
135
+ }
136
+ });
137
+ }
@@ -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.
@@ -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;
@@ -28,16 +28,17 @@ function mutatesRepo(loop) {
28
28
  return !loop.readOnly;
29
29
  }
30
30
  /**
31
- * The isolation gate flag. **Opt-in during rollout**: worktree isolation runs
32
- * ONLY when `SPECRAILS_RAIL_WORKTREES` is `1`/`true`/`on`; otherwise every loop run
33
- * keeps the legacy single shared cwd (byte-identical to before this feature). This
34
- * lets the integration land inert and be validated on a live rail before it is
35
- * flipped to default-on. Read per-call so a test can flip the env without
36
- * re-importing.
31
+ * The isolation gate flag. **Default-on kill-switch**: worktree isolation runs for
32
+ * every repo-mutating per-ticket rail UNLESS `SPECRAILS_RAIL_WORKTREES` is set to
33
+ * `0`/`false`/`off`, which restores the legacy single shared cwd (byte-identical to
34
+ * before this feature). Default-on is safe because the launch path degrades
35
+ * gracefully when isolation is unavailable — a non-git repo, an unborn HEAD, or a
36
+ * worktree-allocation error all fall back to the shared cwd (see rails-router).
37
+ * Read per-call so a test can flip the env without re-importing.
37
38
  */
38
39
  function isRailWorktreesEnabled() {
39
40
  const v = (process.env.SPECRAILS_RAIL_WORKTREES ?? '').trim().toLowerCase();
40
- return v === '1' || v === 'true' || v === 'on';
41
+ return v !== '0' && v !== 'false' && v !== 'off';
41
42
  }
42
43
  /**
43
44
  * True when this launch should isolate each ticket's run in its own worktree.
@@ -9,6 +9,7 @@ const feature_flags_1 = require("./feature-flags");
9
9
  const loops_store_1 = require("./loops-store");
10
10
  const loop_runs_store_1 = require("./loop-runs-store");
11
11
  const providers_1 = require("./providers");
12
+ const spec_models_1 = require("./spec-models");
12
13
  const workspace_resolution_1 = require("./workspace-resolution");
13
14
  const loop_factory_1 = require("./loop-factory");
14
15
  const loop_constants_1 = require("./loop-constants");
@@ -366,6 +367,10 @@ function createRailsRouter() {
366
367
  effort = reasoning_effort;
367
368
  }
368
369
  const loopProvider = railProvider ?? c.project.provider ?? 'claude';
370
+ if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
371
+ res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
372
+ return;
373
+ }
369
374
  const loopModel = typeof model === 'string' && model ? model : (0, providers_1.getAdapter)(loopProvider).defaultModel();
370
375
  // The loop's command(s) declare ticket scope + claude-only-ness.
371
376
  const promptsText = loopGraph.nodes
@@ -377,11 +382,11 @@ function createRailsRouter() {
377
382
  return;
378
383
  }
379
384
  const scope = (0, loop_command_catalog_1.dominantTicketScope)(promptsText);
380
- // Parallel isolation (opt-in via SPECRAILS_RAIL_WORKTREES): a per-ticket
381
- // rail fanning out >1 ticket on a repo-mutating loop runs each ticket in
382
- // its own git worktree, then merges the branches back. Inert by default —
383
- // falls through to the shared-cwd path below unless the flag is on; a
384
- // worktree-allocation failure also falls back. See rail-isolation.ts.
385
+ // Parallel isolation (default-on; disable with SPECRAILS_RAIL_WORKTREES=0):
386
+ // a per-ticket rail on a repo-mutating loop runs each ticket in its own git
387
+ // worktree, then merges the branches back. Degrades gracefully — a non-git
388
+ // repo, an unborn HEAD, or a worktree-allocation failure all fall through to
389
+ // the shared-cwd path below. See rail-isolation.ts.
385
390
  let isolationUnavailable;
386
391
  if ((0, rail_isolation_1.isolationApplies)({ loopsEnabled: (0, feature_flags_1.isLoopsEnabled)(), scope, ticketCount: rail.ticketIds.length, readOnly: false })) {
387
392
  // Worktree isolation needs a git repo WITH at least one commit (an