specrails-desktop 2.2.1 → 2.4.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 (350) hide show
  1. package/client/dist/assets/ActivityFeedPage-DJJlZ3mF.js +1 -0
  2. package/client/dist/assets/AgentsPage-49JaEDjR.js +86 -0
  3. package/client/dist/assets/{AnalyticsPage-BD0paa75.js → AnalyticsPage-BUd3gWYC.js} +1 -1
  4. package/client/dist/assets/{BarChart-D8ZZRab3.js → BarChart-HDe_YoUD.js} +1 -1
  5. package/client/dist/assets/CodePage-CqPPND47.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-mwd8460_.js → DesktopAnalyticsPage-CgvmSvF0.js} +1 -1
  7. package/client/dist/assets/DocsDialog-hHFd3Ejs.js +11 -0
  8. package/client/dist/assets/DocsPage-B4R1aksg.js +11 -0
  9. package/client/dist/assets/{ExportDropdown-CLYmQhic.js → ExportDropdown-f4dwQjlT.js} +1 -1
  10. package/client/dist/assets/IntegrationsPage-CX2Ybxx0.js +3 -0
  11. package/client/dist/assets/JobDetailPage-DN2Jc8Ti.js +16 -0
  12. package/client/dist/assets/JobsPage-DmdpqijT.js +1 -0
  13. package/client/dist/assets/code-BwIz8agY.js +1 -0
  14. package/client/dist/assets/code-CD7yNSK0.js +1 -0
  15. package/client/dist/assets/code-CDFlxUFC.js +1 -0
  16. package/client/dist/assets/code-CY85RXZU.js +1 -0
  17. package/client/dist/assets/code-Cp3Fdng-.js +1 -0
  18. package/client/dist/assets/code-D24e1Crx.js +1 -0
  19. package/client/dist/assets/code-DtZBQTi9.js +1 -0
  20. package/client/dist/assets/code-nKa0fkm_.js +1 -0
  21. package/client/dist/assets/{cssMode-Cc6ozl-J.js → cssMode-DzNPAYFh.js} +1 -1
  22. package/client/dist/assets/{dist-js-D3MxtOYa.js → dist-js-COfIfLRE.js} +1 -1
  23. package/client/dist/assets/{dist-js-BOu_cXw3.js → dist-js-CvScGQU_.js} +1 -1
  24. package/client/dist/assets/{editor.main-CfXxHimg.js → editor.main-C7Rmw-hR.js} +2 -2
  25. package/client/dist/assets/{freemarker2-DP7J1gG3.js → freemarker2-Cszs4SVo.js} +1 -1
  26. package/client/dist/assets/{handlebars-BjRlucw6.js → handlebars-Dp7Lsuym.js} +1 -1
  27. package/client/dist/assets/{html-OumBQJ-U.js → html-BURidrEm.js} +1 -1
  28. package/client/dist/assets/{htmlMode-CStc3zXM.js → htmlMode--k5M7GjZ.js} +1 -1
  29. package/client/dist/assets/index-DBpvYrDK.css +2 -0
  30. package/client/dist/assets/index-DGIXKRHE.js +142 -0
  31. package/client/dist/assets/{integrations-Cublz3m6.js → integrations-2C7MkGT0.js} +1 -1
  32. package/client/dist/assets/{integrations-HIlUxXVs.js → integrations-BDC670cg.js} +1 -1
  33. package/client/dist/assets/integrations-BqUmRUef.js +1 -0
  34. package/client/dist/assets/{integrations-DmQYCUvN.js → integrations-C2jQtv-s.js} +1 -1
  35. package/client/dist/assets/{integrations-DRdbki5W.js → integrations-CB98NeH5.js} +1 -1
  36. package/client/dist/assets/{integrations-C3p12Ms6.js → integrations-CX4p_bij.js} +1 -1
  37. package/client/dist/assets/{integrations-DaC4SzzL.js → integrations-_SuVeQIG.js} +1 -1
  38. package/client/dist/assets/{integrations-Cr6hH7XR.js → integrations-eQPHAYsE.js} +1 -1
  39. package/client/dist/assets/{javascript-CMk--e7g.js → javascript-kJQz__44.js} +1 -1
  40. package/client/dist/assets/jira-C-ATCti0.js +1 -0
  41. package/client/dist/assets/jira-CmVfRM-b.js +1 -0
  42. package/client/dist/assets/jira-D7bkKAX8.js +1 -0
  43. package/client/dist/assets/jira-DKImM1YH.js +1 -0
  44. package/client/dist/assets/jira-DOw8bkIR.js +1 -0
  45. package/client/dist/assets/jira-DlA-wGp-.js +1 -0
  46. package/client/dist/assets/jira-Fob8EGxN.js +1 -0
  47. package/client/dist/assets/jira-xZA2lixb.js +1 -0
  48. package/client/dist/assets/{jsonMode-C2h3ZcjZ.js → jsonMode-v5JYPpnz.js} +1 -1
  49. package/client/dist/assets/{lib-DQ2hrj8m.js → lib-Bro9Z0gp.js} +1 -1
  50. package/client/dist/assets/{liquid-mI3KJrBE.js → liquid-Dl9I6gWt.js} +1 -1
  51. package/client/dist/assets/{lspLanguageFeatures-DU09ggWi.js → lspLanguageFeatures-CPlEe0NK.js} +1 -1
  52. package/client/dist/assets/{mdx-C41VDTR_.js → mdx-Byl7TtzQ.js} +1 -1
  53. package/client/dist/assets/{monaco.contribution-CPObAXMC.js → monaco.contribution-YMAkHQcQ.js} +2 -2
  54. package/client/dist/assets/{python-Y27rKQtk.js → python-jWQwT6j2.js} +1 -1
  55. package/client/dist/assets/{razor-Cd5-q9Bp.js → razor-BWS3sP-E.js} +1 -1
  56. package/client/dist/assets/setup-C0dzw8j4.js +1 -0
  57. package/client/dist/assets/setup-C1IA-9YS.js +1 -0
  58. package/client/dist/assets/setup-CpfjaNut.js +1 -0
  59. package/client/dist/assets/setup-D3rNZA9A.js +1 -0
  60. package/client/dist/assets/setup-UD2aanGs.js +1 -0
  61. package/client/dist/assets/setup-WP6WOYQh.js +1 -0
  62. package/client/dist/assets/setup-gzLG8T6F.js +1 -0
  63. package/client/dist/assets/setup-pjgmYHx6.js +1 -0
  64. package/client/dist/assets/specs-4lA_u79w.js +1 -0
  65. package/client/dist/assets/{specs-D2FzlLn9.js → specs-BHjxcjOf.js} +1 -1
  66. package/client/dist/assets/{specs-CZ1PsXsC.js → specs-CXNQzPk9.js} +1 -1
  67. package/client/dist/assets/{specs-Dyc5hYeE.js → specs-DFnc2Huj.js} +1 -1
  68. package/client/dist/assets/{specs-BFfu3u-a.js → specs-DZCLH2-l.js} +1 -1
  69. package/client/dist/assets/{specs-B__C8-8a.js → specs-DgmyAE3N.js} +1 -1
  70. package/client/dist/assets/{specs-DaUTrNF9.js → specs-DicWhvwi.js} +1 -1
  71. package/client/dist/assets/{specs-k0PyLDVt.js → specs-dkro6lSM.js} +1 -1
  72. package/client/dist/assets/{tsMode-B0y_xEci.js → tsMode-BbOGOuSV.js} +1 -1
  73. package/client/dist/assets/{typescript-BzK0OgwW.js → typescript-eBtFQJLs.js} +1 -1
  74. package/client/dist/assets/{useProjectCache-BxY4aTjd.js → useProjectCache-D9juBhsO.js} +1 -1
  75. package/client/dist/assets/{workers-rt--R2Qy.js → workers-BvicOoDf.js} +1 -1
  76. package/client/dist/assets/{xml-eX9QXAmI.js → xml-BJepAPyM.js} +1 -1
  77. package/client/dist/assets/{yaml-fcsNkpOt.js → yaml-DabgV-eA.js} +1 -1
  78. package/client/dist/index.html +13 -12
  79. package/docs/jira-integration-plan.md +321 -0
  80. package/package.json +1 -1
  81. package/server/dist/agent-refine-manager.js +128 -153
  82. package/server/dist/chat-manager.js +242 -0
  83. package/server/dist/code-explorer-router.js +78 -0
  84. package/server/dist/command-resolver.js +17 -0
  85. package/server/dist/contract-refine-runner.js +42 -10
  86. package/server/dist/db.js +86 -0
  87. package/server/dist/desktop-db.js +3 -0
  88. package/server/dist/explore-stdin-session.js +129 -0
  89. package/server/dist/feature-flags.js +11 -0
  90. package/server/dist/jira/jira-adf.js +113 -0
  91. package/server/dist/jira/jira-backlog-config.js +58 -0
  92. package/server/dist/jira/jira-client.js +279 -0
  93. package/server/dist/jira/jira-credential-store.js +103 -0
  94. package/server/dist/jira/jira-db.js +341 -0
  95. package/server/dist/jira/jira-issue-fields.js +428 -0
  96. package/server/dist/jira/jira-materializer.js +250 -0
  97. package/server/dist/jira/jira-status-resolver.js +211 -0
  98. package/server/dist/jira/jira-sync-manager.js +1014 -0
  99. package/server/dist/jira/types.js +9 -0
  100. package/server/dist/jira-router.js +304 -0
  101. package/server/dist/mobile/mobile-auth.js +16 -0
  102. package/server/dist/project-registry.js +43 -1
  103. package/server/dist/project-router-chat.js +218 -0
  104. package/server/dist/project-router-helpers.js +275 -0
  105. package/server/dist/project-router-jobs.js +389 -0
  106. package/server/dist/project-router-settings.js +312 -0
  107. package/server/dist/project-router-setup.js +456 -0
  108. package/server/dist/project-router-spending.js +320 -0
  109. package/server/dist/project-router-terminals.js +312 -0
  110. package/server/dist/project-router-tickets.js +1815 -0
  111. package/server/dist/project-router.js +31 -3950
  112. package/server/dist/providers/claude-adapter.js +23 -0
  113. package/server/dist/providers/codex-adapter.js +6 -0
  114. package/server/dist/rails-router.js +12 -0
  115. package/server/dist/spawn-lifecycle.js +117 -0
  116. package/client/dist/assets/ActivityFeedPage-BpjXuX2H.js +0 -1
  117. package/client/dist/assets/AgentsPage-D-7fDbTc.js +0 -86
  118. package/client/dist/assets/CodePage-B6q6CiYJ.js +0 -2
  119. package/client/dist/assets/DocsDialog-D_dyF2h9.js +0 -11
  120. package/client/dist/assets/DocsPage-C9-Ru8wE.js +0 -11
  121. package/client/dist/assets/IntegrationsPage-3WWtx9hi.js +0 -3
  122. package/client/dist/assets/JobDetailPage-DgN-79s-.js +0 -16
  123. package/client/dist/assets/JobsPage-Du8_w1ob.js +0 -1
  124. package/client/dist/assets/code-AL1rVIMb.js +0 -1
  125. package/client/dist/assets/code-C0BKpkht.js +0 -1
  126. package/client/dist/assets/code-C0FTS3ew.js +0 -1
  127. package/client/dist/assets/code-CPcHxzxw.js +0 -1
  128. package/client/dist/assets/code-D3ryDniw.js +0 -1
  129. package/client/dist/assets/code-D3zVVQTj.js +0 -1
  130. package/client/dist/assets/code-PCmfS3dn.js +0 -1
  131. package/client/dist/assets/code-exI0G5Wd.js +0 -1
  132. package/client/dist/assets/index-D17R4Cjc.css +0 -2
  133. package/client/dist/assets/index-D9G_K4L-.js +0 -142
  134. package/client/dist/assets/integrations-D28q1kF6.js +0 -1
  135. package/client/dist/assets/setup--FMCsnQS.js +0 -1
  136. package/client/dist/assets/setup-B19ZpBNi.js +0 -1
  137. package/client/dist/assets/setup-BZPmkjSN.js +0 -1
  138. package/client/dist/assets/setup-BqYA02rS.js +0 -1
  139. package/client/dist/assets/setup-ChKQDHN9.js +0 -1
  140. package/client/dist/assets/setup-D2n9jMfM.js +0 -1
  141. package/client/dist/assets/setup-P3r6YP1D.js +0 -1
  142. package/client/dist/assets/setup-fnfEbwlv.js +0 -1
  143. package/client/dist/assets/specs-cKEh2LXt.js +0 -1
  144. /package/client/dist/assets/{abap-Bw6f2wDG.js → abap-s65oMlhi.js} +0 -0
  145. /package/client/dist/assets/{activity-BdrPln96.js → activity-BqqwnH_h.js} +0 -0
  146. /package/client/dist/assets/{activity-BEIp_Y1A.js → activity-C8qqEIoP.js} +0 -0
  147. /package/client/dist/assets/{activity-CpkRS8Sx.js → activity-CZVM4nlJ.js} +0 -0
  148. /package/client/dist/assets/{activity-DOUVEjJi.js → activity-Cyy07Tgo.js} +0 -0
  149. /package/client/dist/assets/{activity-DRwkql_y.js → activity-DlbWCa4y.js} +0 -0
  150. /package/client/dist/assets/{activity-DKCpESPt.js → activity-Dwq0heud.js} +0 -0
  151. /package/client/dist/assets/{activity-DcDQ7tjw.js → activity-qFTcMyW9.js} +0 -0
  152. /package/client/dist/assets/{addon-image-3WCl5Vhd.js → addon-image-CpF0L0jM.js} +0 -0
  153. /package/client/dist/assets/{addon-ligatures-C5OdliKs.js → addon-ligatures-hXysGZrA.js} +0 -0
  154. /package/client/dist/assets/{addon-webgl-BbX6pSjl.js → addon-webgl-Cn1slavz.js} +0 -0
  155. /package/client/dist/assets/{addspec-D33ocMxf.js → addspec-B1FTtI2a.js} +0 -0
  156. /package/client/dist/assets/{addspec-DFswZ0jK.js → addspec-BCT9vm_c.js} +0 -0
  157. /package/client/dist/assets/{addspec-DVZ15Jp8.js → addspec-DeDOztDr.js} +0 -0
  158. /package/client/dist/assets/{addspec-Fkv91Opc.js → addspec-DpRgmfmx.js} +0 -0
  159. /package/client/dist/assets/{addspec-BEeF5-zc.js → addspec-Dw-0Dg-4.js} +0 -0
  160. /package/client/dist/assets/{addspec-B5yl4Loj.js → addspec-rp496P_F.js} +0 -0
  161. /package/client/dist/assets/{addspec-DRE-jZv7.js → addspec-v8j6A7CD.js} +0 -0
  162. /package/client/dist/assets/{agents-DK-Dlc0i.js → agents-23iPejcA.js} +0 -0
  163. /package/client/dist/assets/{agents-Q6Ldfpxx.js → agents-BDx1RXcl.js} +0 -0
  164. /package/client/dist/assets/{agents-TeOSy-ax.js → agents-BFr3kUhK.js} +0 -0
  165. /package/client/dist/assets/{agents-Bm9rPqnt.js → agents-B_1L9xRg.js} +0 -0
  166. /package/client/dist/assets/{agents-1nCDWRmP.js → agents-BlPnx-mz.js} +0 -0
  167. /package/client/dist/assets/{agents-iTqjRajS.js → agents-DcxZHzNr.js} +0 -0
  168. /package/client/dist/assets/{agents-s87sMGzL.js → agents-G3shOewU.js} +0 -0
  169. /package/client/dist/assets/{agentstudio-B6Wb59E7.js → agentstudio-B-CMAQqy.js} +0 -0
  170. /package/client/dist/assets/{agentstudio-D3I62TLJ.js → agentstudio-Bk1eZcv4.js} +0 -0
  171. /package/client/dist/assets/{agentstudio-DuH9TogZ.js → agentstudio-ChxNuGAu.js} +0 -0
  172. /package/client/dist/assets/{agentstudio-Kw88_dUF.js → agentstudio-DNlme601.js} +0 -0
  173. /package/client/dist/assets/{agentstudio-BdidyBzZ.js → agentstudio-DpP9caEE.js} +0 -0
  174. /package/client/dist/assets/{agentstudio-BSnWLR63.js → agentstudio-Y3G0ddJ2.js} +0 -0
  175. /package/client/dist/assets/{agentstudio-BADhZ41e.js → agentstudio-kk9RB7Se.js} +0 -0
  176. /package/client/dist/assets/{aiedit-DJMny-D5.js → aiedit-5ETerMK1.js} +0 -0
  177. /package/client/dist/assets/{aiedit-D2ji6Qy0.js → aiedit-BBCrOpHq.js} +0 -0
  178. /package/client/dist/assets/{aiedit-DAhZTvtk.js → aiedit-BMtcGYNE.js} +0 -0
  179. /package/client/dist/assets/{aiedit-DvrcbwGv.js → aiedit-D9ddlgkM.js} +0 -0
  180. /package/client/dist/assets/{aiedit-WBSjT_C1.js → aiedit-De0SOH6S.js} +0 -0
  181. /package/client/dist/assets/{aiedit-BWxHGsYA.js → aiedit-DrfzQroF.js} +0 -0
  182. /package/client/dist/assets/{aiedit-DOcxERkU.js → aiedit-fMltW101.js} +0 -0
  183. /package/client/dist/assets/{analytics-C9Zc-rkM.js → analytics-BeTyviO8.js} +0 -0
  184. /package/client/dist/assets/{analytics-CrPCZRJ-.js → analytics-C4eEO260.js} +0 -0
  185. /package/client/dist/assets/{analytics-CYj0tfj7.js → analytics-C67cIA1b.js} +0 -0
  186. /package/client/dist/assets/{analytics-C6EzgtdE.js → analytics-CAguvW28.js} +0 -0
  187. /package/client/dist/assets/{analytics-CVx3YOc0.js → analytics-DBtt8Mgk.js} +0 -0
  188. /package/client/dist/assets/{analytics-CnY4kNG3.js → analytics-DUPtODxX.js} +0 -0
  189. /package/client/dist/assets/{analytics-BIdr0YfL.js → analytics-YIpQvPAc.js} +0 -0
  190. /package/client/dist/assets/{apex-Cw8_REBo.js → apex-BLUBIldB.js} +0 -0
  191. /package/client/dist/assets/{attachments-DYHGA2Dj.js → attachments-CCWasu-P.js} +0 -0
  192. /package/client/dist/assets/{attachments-Dd92KpUH.js → attachments-CHaDUfjB.js} +0 -0
  193. /package/client/dist/assets/{attachments-DzdU6DV6.js → attachments-CVSAbGNl.js} +0 -0
  194. /package/client/dist/assets/{attachments-Bcf6BG6V.js → attachments-Chg5poG1.js} +0 -0
  195. /package/client/dist/assets/{attachments-BW4L3l2L.js → attachments-DazTVJbH.js} +0 -0
  196. /package/client/dist/assets/{attachments-COcrGRFz.js → attachments-Dn-JImAK.js} +0 -0
  197. /package/client/dist/assets/{attachments-Bke8sCU4.js → attachments-LDA9kp2X.js} +0 -0
  198. /package/client/dist/assets/{azcli-Cz6HAoOw.js → azcli-DuWxh9mO.js} +0 -0
  199. /package/client/dist/assets/{bat-CcJ-xyqL.js → bat-UKoTejQm.js} +0 -0
  200. /package/client/dist/assets/{bicep-z1WDCKYz.js → bicep-4sTT4B3D.js} +0 -0
  201. /package/client/dist/assets/{browser-DGITz3fC.js → browser-BDd1dbFa.js} +0 -0
  202. /package/client/dist/assets/{browser-JsAIGCEW.js → browser-BWSgbfdX.js} +0 -0
  203. /package/client/dist/assets/{browser-M5-rbPlw.js → browser-D2Y_UAKA.js} +0 -0
  204. /package/client/dist/assets/{browser-BlYF4OOq.js → browser-DH9SGVfM.js} +0 -0
  205. /package/client/dist/assets/{browser-Bc-YdlVg.js → browser-DWOVYMlg.js} +0 -0
  206. /package/client/dist/assets/{browser-CT-ReZGt.js → browser-Dxc_VIRK.js} +0 -0
  207. /package/client/dist/assets/{browser-5ErDlJoR.js → browser-lTQwcDCI.js} +0 -0
  208. /package/client/dist/assets/{cameligo-BRewOpfa.js → cameligo-CAAryRYO.js} +0 -0
  209. /package/client/dist/assets/{chat-DwUm6W9z.js → chat-BO9MvVID.js} +0 -0
  210. /package/client/dist/assets/{chat-BEGuC03z.js → chat-CPgmgZOj.js} +0 -0
  211. /package/client/dist/assets/{chat-CboQguCi.js → chat-CUrG1eVg.js} +0 -0
  212. /package/client/dist/assets/{chat-DRCa9pOt.js → chat-CvOOKB2s.js} +0 -0
  213. /package/client/dist/assets/{chat-BEW60P_u.js → chat-DIh3hr6y.js} +0 -0
  214. /package/client/dist/assets/{chat-yoXwguQu.js → chat-UVVZqA57.js} +0 -0
  215. /package/client/dist/assets/{chat-BQNMD0PL.js → chat-mPn3UlMl.js} +0 -0
  216. /package/client/dist/assets/{clojure-DBjRWN6g.js → clojure-BlMERO1w.js} +0 -0
  217. /package/client/dist/assets/{clsx-DnqN-uhr.js → clsx-CnH-HMk3.js} +0 -0
  218. /package/client/dist/assets/{coffee-Cfk_XHGR.js → coffee-Cj8D-Wl1.js} +0 -0
  219. /package/client/dist/assets/{commands-sqrqsxyE.js → commands-B-MVT-2F.js} +0 -0
  220. /package/client/dist/assets/{commands-UD1NzmwX.js → commands-B0yFTp7e.js} +0 -0
  221. /package/client/dist/assets/{commands-DLrvnPNg.js → commands-BR1kDkHQ.js} +0 -0
  222. /package/client/dist/assets/{commands-CJxCry-o.js → commands-Cb21pDlG.js} +0 -0
  223. /package/client/dist/assets/{commands-CfgY-_of.js → commands-DWgp-8W1.js} +0 -0
  224. /package/client/dist/assets/{commands-B772IyDa.js → commands-ddsl1V91.js} +0 -0
  225. /package/client/dist/assets/{commands-BDDp6xFG.js → commands-t4frzhB0.js} +0 -0
  226. /package/client/dist/assets/{common-Dmm1GhdD.js → common-5ilvMOcH.js} +0 -0
  227. /package/client/dist/assets/{common-DltqHaAe.js → common-B4sqsKp7.js} +0 -0
  228. /package/client/dist/assets/{common-GbpxfPG8.js → common-BKpVwUIf.js} +0 -0
  229. /package/client/dist/assets/{common-DeDELLZJ.js → common-BzEC3kJU.js} +0 -0
  230. /package/client/dist/assets/{common-DnjcgkPH.js → common-CALKUpYm.js} +0 -0
  231. /package/client/dist/assets/{common-Dard9UNH.js → common-CTEbWVZS.js} +0 -0
  232. /package/client/dist/assets/{common-DCr6VzJ7.js → common-DQiza2Xp.js} +0 -0
  233. /package/client/dist/assets/{cpp-BVob6BaP.js → cpp-BPfKnaj_.js} +0 -0
  234. /package/client/dist/assets/{csharp-C4fbRuOu.js → csharp-gX-x5uD6.js} +0 -0
  235. /package/client/dist/assets/{csp-DthFP_vT.js → csp-DKGVt8SM.js} +0 -0
  236. /package/client/dist/assets/{css-CGMH0hcW.js → css-CPMdnAVq.js} +0 -0
  237. /package/client/dist/assets/{cypher-Pnf68BRV.js → cypher-ClMDrj9S.js} +0 -0
  238. /package/client/dist/assets/{dart-PMMOtxZX.js → dart-C4zbzpVv.js} +0 -0
  239. /package/client/dist/assets/{dashboard-BZBADHSj.js → dashboard--Y6yzMlf.js} +0 -0
  240. /package/client/dist/assets/{dashboard-I19DXBxw.js → dashboard--a4-6oYE.js} +0 -0
  241. /package/client/dist/assets/{dashboard-CB6Le1yN.js → dashboard-BiJ3CDTG.js} +0 -0
  242. /package/client/dist/assets/{dashboard-B4ixDVk8.js → dashboard-CiXjk63Z.js} +0 -0
  243. /package/client/dist/assets/{dashboard-C1MfeUHs.js → dashboard-Cx5VjCea.js} +0 -0
  244. /package/client/dist/assets/{dashboard-C7SK6xu5.js → dashboard-D7jg25XR.js} +0 -0
  245. /package/client/dist/assets/{dashboard-CoTpMOBM.js → dashboard-DpGYK2s1.js} +0 -0
  246. /package/client/dist/assets/{dockerfile-di1nsJCc.js → dockerfile-D9xw73D1.js} +0 -0
  247. /package/client/dist/assets/{ecl-D_WVcB5M.js → ecl-gqO8tIR9.js} +0 -0
  248. /package/client/dist/assets/{editor.api2-XLGzZfbc.js → editor.api2-BPnIxMjz.js} +0 -0
  249. /package/client/dist/assets/{elixir-OAdJEMOn.js → elixir-DSAhVF3_.js} +0 -0
  250. /package/client/dist/assets/{explore-D2EFgt8J.js → explore-BE5UmlbD.js} +0 -0
  251. /package/client/dist/assets/{explore-BV5Xxlsn.js → explore-BmTaI8dX.js} +0 -0
  252. /package/client/dist/assets/{explore-A8Ltoblq.js → explore-CCwkqoWq.js} +0 -0
  253. /package/client/dist/assets/{explore-4mFpnrKU.js → explore-CMdEoPDx.js} +0 -0
  254. /package/client/dist/assets/{explore-C3FSE42C.js → explore-CtdCL4QU.js} +0 -0
  255. /package/client/dist/assets/{explore-B9A3iN2W.js → explore-DHjxSkqQ.js} +0 -0
  256. /package/client/dist/assets/{explore-BrBJvfjP.js → explore-__BeALjE.js} +0 -0
  257. /package/client/dist/assets/{flow9-D3QEZjgn.js → flow9-DeQCSPOd.js} +0 -0
  258. /package/client/dist/assets/{format-command-CwGuwzGA.js → format-command-2VNoNnMv.js} +0 -0
  259. /package/client/dist/assets/{fsharp-BF0k_8N8.js → fsharp-CEfaXL-S.js} +0 -0
  260. /package/client/dist/assets/{go-BAQO5Jsz.js → go-Xp1OkZCh.js} +0 -0
  261. /package/client/dist/assets/{graphql-hdFVFkiV.js → graphql-BwRXrUwe.js} +0 -0
  262. /package/client/dist/assets/{hcl-DWnl1o-X.js → hcl-u06DtVFk.js} +0 -0
  263. /package/client/dist/assets/{ini-CB-6OVu3.js → ini-AmeIpFND.js} +0 -0
  264. /package/client/dist/assets/{java-d1CmfiHX.js → java-CyDbRQjX.js} +0 -0
  265. /package/client/dist/assets/{jobs-DPPT6bV6.js → jobs-8viuHLDV.js} +0 -0
  266. /package/client/dist/assets/{jobs-3j3_npyo.js → jobs-AW2eB5D-.js} +0 -0
  267. /package/client/dist/assets/{jobs-2N3RXDAM.js → jobs-BSm89DL5.js} +0 -0
  268. /package/client/dist/assets/{jobs-BqEbCCxD.js → jobs-BZ3sQHjZ.js} +0 -0
  269. /package/client/dist/assets/{jobs-cHYInoau.js → jobs-Bd8AdOTb.js} +0 -0
  270. /package/client/dist/assets/{jobs-DRzjWI9u.js → jobs-CRtsq_u0.js} +0 -0
  271. /package/client/dist/assets/{jobs-2f6Hdc72.js → jobs-CSRwFQ6K.js} +0 -0
  272. /package/client/dist/assets/{jobs-vGfzIDQa.js → jobs-CbEl7WMI.js} +0 -0
  273. /package/client/dist/assets/{julia-Bgv08lKa.js → julia-BqialFRG.js} +0 -0
  274. /package/client/dist/assets/{kotlin-u98kaVTf.js → kotlin-Dzz8TWAt.js} +0 -0
  275. /package/client/dist/assets/{less-CjYwpgg5.js → less-DHRJD3TR.js} +0 -0
  276. /package/client/dist/assets/{lexon-YTjaAFBB.js → lexon-5Y3QgTmT.js} +0 -0
  277. /package/client/dist/assets/{lua-BzmkWv27.js → lua-sKvhfPn5.js} +0 -0
  278. /package/client/dist/assets/{m3-CFwk9fw0.js → m3-DWDVwkFG.js} +0 -0
  279. /package/client/dist/assets/{markdown-CR5iMpSZ.js → markdown-CD_aSBxW.js} +0 -0
  280. /package/client/dist/assets/{mips-CcEalc17.js → mips-687T03hg.js} +0 -0
  281. /package/client/dist/assets/{msdax-BQbkawnr.js → msdax-C1St-dIV.js} +0 -0
  282. /package/client/dist/assets/{mysql-GTlaaW_P.js → mysql-BG7r8oBS.js} +0 -0
  283. /package/client/dist/assets/{nav-C2YXcbZS.js → nav-B05EYB0b.js} +0 -0
  284. /package/client/dist/assets/{nav-D2bOGSEg.js → nav-BNGCq-0y.js} +0 -0
  285. /package/client/dist/assets/{nav-BEL3MTwK.js → nav-BRInPX8a.js} +0 -0
  286. /package/client/dist/assets/{nav-CtYwmMgu.js → nav-Bf87DRHD.js} +0 -0
  287. /package/client/dist/assets/{nav-iH1V5j6o.js → nav-BkVzzFpc.js} +0 -0
  288. /package/client/dist/assets/{nav-0fwkrgHt.js → nav-BzFLtS1W.js} +0 -0
  289. /package/client/dist/assets/{nav-ClzOE4mA.js → nav-DBDbQOYn.js} +0 -0
  290. /package/client/dist/assets/{nav-B_G-TJDW.js → nav-X9sVtUWC.js} +0 -0
  291. /package/client/dist/assets/{objective-c-Byu1T5if.js → objective-c-Ds1-m05L.js} +0 -0
  292. /package/client/dist/assets/{pascal-BrfzBfRm.js → pascal-BKK9FpIi.js} +0 -0
  293. /package/client/dist/assets/{pascaligo-BXXKFUeo.js → pascaligo-SRS3nwtO.js} +0 -0
  294. /package/client/dist/assets/{perl-B3OikKq-.js → perl-B2hTOlrF.js} +0 -0
  295. /package/client/dist/assets/{pgsql-CTsa0Acc.js → pgsql-DIQJYNpL.js} +0 -0
  296. /package/client/dist/assets/{php-DiQh3FUW.js → php-BEaVe8X2.js} +0 -0
  297. /package/client/dist/assets/{pla-92uH8Fzm.js → pla-oPLHpZ-Q.js} +0 -0
  298. /package/client/dist/assets/{postiats-BbeWkKUr.js → postiats-D_vzrAzD.js} +0 -0
  299. /package/client/dist/assets/{powerquery-DgDMzpsm.js → powerquery-BKG6w-FH.js} +0 -0
  300. /package/client/dist/assets/{powershell-BfdUUzaG.js → powershell-B3dLhDt4.js} +0 -0
  301. /package/client/dist/assets/{protobuf-BojW2ftW.js → protobuf-DC8SGjcl.js} +0 -0
  302. /package/client/dist/assets/{pug-BxqTg3IU.js → pug-D5E-4fI0.js} +0 -0
  303. /package/client/dist/assets/{qsharp-BX_A-MW9.js → qsharp-6vJAWv0x.js} +0 -0
  304. /package/client/dist/assets/{r-D9BMnxvJ.js → r-CDwsEcbM.js} +0 -0
  305. /package/client/dist/assets/{redis-5cJqEQJJ.js → redis-CuQbbESS.js} +0 -0
  306. /package/client/dist/assets/{redshift-d8BBqiwb.js → redshift-B9e1k-qI.js} +0 -0
  307. /package/client/dist/assets/{restructuredtext-C8a6yIcZ.js → restructuredtext-BiJ5IwaU.js} +0 -0
  308. /package/client/dist/assets/{ruby-egeh-6KX.js → ruby-B0UAHY9b.js} +0 -0
  309. /package/client/dist/assets/{rust-a3r9IInB.js → rust-Dg_spmFr.js} +0 -0
  310. /package/client/dist/assets/{sb-y8iRIDei.js → sb-DjU66I8Q.js} +0 -0
  311. /package/client/dist/assets/{scala-BPDK2AmK.js → scala-qvStIdfG.js} +0 -0
  312. /package/client/dist/assets/{scheme-BIWUEoOs.js → scheme-FstEk5Rh.js} +0 -0
  313. /package/client/dist/assets/{scss-CA-PSzwg.js → scss-w0U3rQLK.js} +0 -0
  314. /package/client/dist/assets/{settings-CTcwN9RE.js → settings-5tzo0Rn3.js} +0 -0
  315. /package/client/dist/assets/{settings-D_dujJZI.js → settings-BDAW3trC.js} +0 -0
  316. /package/client/dist/assets/{settings-Bg0A3zoS.js → settings-BEWv3VEu.js} +0 -0
  317. /package/client/dist/assets/{settings-BgPqg2nv.js → settings-BORg56um.js} +0 -0
  318. /package/client/dist/assets/{settings-BSze3_9q.js → settings-D3LurcR5.js} +0 -0
  319. /package/client/dist/assets/{settings-CSJ0ahZ8.js → settings-DcqWIEM6.js} +0 -0
  320. /package/client/dist/assets/{settings-DYIV89nV.js → settings-Dfz8QbZS.js} +0 -0
  321. /package/client/dist/assets/{settings-DDcfx_ca.js → settings-yMubjqYw.js} +0 -0
  322. /package/client/dist/assets/{shell--LiT1Bja.js → shell-DJ78wREd.js} +0 -0
  323. /package/client/dist/assets/{solidity-DdqZccZg.js → solidity-1aGIVsdX.js} +0 -0
  324. /package/client/dist/assets/{sophia-S6-YxNG_.js → sophia-40LqcGjB.js} +0 -0
  325. /package/client/dist/assets/{sparql-BSf5kMp2.js → sparql-Cz5dqG_g.js} +0 -0
  326. /package/client/dist/assets/{sql-D7KgjR8G.js → sql-64f62Ni4.js} +0 -0
  327. /package/client/dist/assets/{st-BnoDa-Ml.js → st-gJe2yG8J.js} +0 -0
  328. /package/client/dist/assets/{swift-DEUHTkUX.js → swift-C6ME22mv.js} +0 -0
  329. /package/client/dist/assets/{systemverilog-Tqb_KPnW.js → systemverilog-CEWz259w.js} +0 -0
  330. /package/client/dist/assets/{tcl-BmBFS2qq.js → tcl-CcLVIi3m.js} +0 -0
  331. /package/client/dist/assets/{terminal-Bje4ziIa.js → terminal-BYtreaaF.js} +0 -0
  332. /package/client/dist/assets/{terminal-CSONJOex.js → terminal-C0xx0SjA.js} +0 -0
  333. /package/client/dist/assets/{terminal-DeWzh6ys.js → terminal-CPpK58RC.js} +0 -0
  334. /package/client/dist/assets/{terminal-C2WYcFHF.js → terminal-CdxkpafL.js} +0 -0
  335. /package/client/dist/assets/{terminal-DEqzGtcr.js → terminal-Ciia0wh2.js} +0 -0
  336. /package/client/dist/assets/{terminal-80yDMgMF.js → terminal-DHIkiWcs.js} +0 -0
  337. /package/client/dist/assets/{terminal-lkZYR4wJ.js → terminal-DY42QANg.js} +0 -0
  338. /package/client/dist/assets/{terminal-YOlsJCQj.js → terminal-DoxtVdma.js} +0 -0
  339. /package/client/dist/assets/{tickets-DYvafSaY.js → tickets-0rM0lIXd.js} +0 -0
  340. /package/client/dist/assets/{tickets-DNOANUXr.js → tickets-1UIGf_oA.js} +0 -0
  341. /package/client/dist/assets/{tickets-DlpC_iTg.js → tickets-9kdPXInd.js} +0 -0
  342. /package/client/dist/assets/{tickets-CF2PYelu.js → tickets-C6pwZwt4.js} +0 -0
  343. /package/client/dist/assets/{tickets-CB7N30gm.js → tickets-DAjtxAVb.js} +0 -0
  344. /package/client/dist/assets/{tickets-DU1aqsbr.js → tickets-DNmXcAwu.js} +0 -0
  345. /package/client/dist/assets/{tickets-clefmXLv.js → tickets-n23kDqJT.js} +0 -0
  346. /package/client/dist/assets/{tickets-DucYgtdl.js → tickets-tGx5AR5b.js} +0 -0
  347. /package/client/dist/assets/{twig-BQV8igWC.js → twig-DvsO-WjW.js} +0 -0
  348. /package/client/dist/assets/{typespec-DlFroUGY.js → typespec-Brkt3IAA.js} +0 -0
  349. /package/client/dist/assets/{vb-BlrJpIMX.js → vb-r121Uzxt.js} +0 -0
  350. /package/client/dist/assets/{wgsl-BWgIc6FZ.js → wgsl-BRX8uYh4.js} +0 -0
@@ -539,6 +539,84 @@ function createCodeExplorerRouter(deps) {
539
539
  absolutePath: abs,
540
540
  });
541
541
  });
542
+ // In-app editing (v1): overwrite an existing text file with new content.
543
+ // Same guards as the read path — traversal, deny-list, gitignore, size, and
544
+ // binary refusal — so the editor can never write outside the tree, clobber a
545
+ // secret/lockfile, or corrupt a binary. Creating new files / renames is out of
546
+ // scope here. After a write the existing hash-gated `summaryStale` flag makes
547
+ // the next GET /file surface the summary as stale (regenerate via the existing
548
+ // POST /file/regenerate-summary).
549
+ router.put('/file', (req, res) => {
550
+ const body = (req.body ?? {});
551
+ const relRaw = typeof body.path === 'string' ? body.path : undefined;
552
+ const content = typeof body.content === 'string' ? body.content : undefined;
553
+ if (!relRaw || content === undefined) {
554
+ res.status(400).json({ error: 'path and content are required' });
555
+ return;
556
+ }
557
+ const guard = resolveSafePath(deps.projectPath, relRaw);
558
+ if (!guard) {
559
+ res.status(400).json({ error: 'path traversal not allowed' });
560
+ return;
561
+ }
562
+ if (isDeniedRelPath(relRaw)) {
563
+ res.status(403).json({ error: 'path is excluded by the code-explorer deny-list' });
564
+ return;
565
+ }
566
+ const rel = normalizeRel(relRaw);
567
+ if (isGitIgnored(deps.projectPath, rel)) {
568
+ res.status(403).json({ error: 'path is gitignored' });
569
+ return;
570
+ }
571
+ if (Buffer.byteLength(content, 'utf8') > MAX_FILE_BYTES) {
572
+ res.status(413).json({ error: 'file too large' });
573
+ return;
574
+ }
575
+ if (/[\x00-\x08\x0e-\x1f]/.test(content)) {
576
+ res.status(415).json({ error: 'binary content not allowed' });
577
+ return;
578
+ }
579
+ const abs = guard;
580
+ let stat;
581
+ try {
582
+ stat = fs_1.default.statSync(abs);
583
+ }
584
+ catch {
585
+ res.status(404).json({ error: 'file not found (in-app editing only overwrites existing files)' });
586
+ return;
587
+ }
588
+ if (!stat.isFile()) {
589
+ res.status(400).json({ error: 'path is not a regular file' });
590
+ return;
591
+ }
592
+ // Refuse to overwrite a binary file as text (would corrupt it).
593
+ try {
594
+ const fd = fs_1.default.openSync(abs, 'r');
595
+ try {
596
+ const head = Buffer.alloc(Math.min(BINARY_PROBE_BYTES, stat.size));
597
+ fs_1.default.readSync(fd, head, 0, head.length, 0);
598
+ if (head.includes(0)) {
599
+ res.status(415).json({ error: 'cannot edit a binary file' });
600
+ return;
601
+ }
602
+ }
603
+ finally {
604
+ fs_1.default.closeSync(fd);
605
+ }
606
+ }
607
+ catch {
608
+ res.status(500).json({ error: 'failed to read file' });
609
+ return;
610
+ }
611
+ try {
612
+ fs_1.default.writeFileSync(abs, content, 'utf8');
613
+ }
614
+ catch {
615
+ res.status(500).json({ error: 'failed to write file' });
616
+ return;
617
+ }
618
+ res.json({ ok: true, bytes: Buffer.byteLength(content, 'utf8'), path: rel });
619
+ });
542
620
  router.get('/summary', async (req, res) => {
543
621
  const relRaw = req.query.path;
544
622
  if (!relRaw || typeof relRaw !== 'string') {
@@ -53,6 +53,16 @@ The user's idea follows below. Begin the Explore Spec conversation.
53
53
 
54
54
  ${commandArgs}`.trim();
55
55
  }
56
+ /**
57
+ * A `:`-separated command segment is safe iff it is a plain identifier: no path
58
+ * separators, no `.`/`..`, no NUL. Anything else could escape the
59
+ * commands/skills directory once joined into the lookup path.
60
+ */
61
+ function isSafeSegment(seg) {
62
+ if (seg.length === 0 || seg === '.' || seg === '..')
63
+ return false;
64
+ return !/[/\\\0]/.test(seg);
65
+ }
56
66
  /**
57
67
  * Try to find a command/skill .md file for the given command path parts
58
68
  * within the given base directory. Returns the resolved path or null.
@@ -87,6 +97,13 @@ function resolveCommand(command, cwd) {
87
97
  const builtIn = builtInCommand(commandPath, commandArgs);
88
98
  if (builtIn)
89
99
  return builtIn;
100
+ // Path-traversal guard: each segment is joined verbatim into
101
+ // `<baseDir>/.claude/commands/<...parts>.md`, so a segment like `..`, one
102
+ // containing a path separator, or an absolute fragment would escape the
103
+ // commands/skills directory and read an arbitrary file. Reject and leave the
104
+ // command unresolved (defense-in-depth even though the input is user-typed).
105
+ if (!parts.every(isSafeSegment))
106
+ return command;
90
107
  // 1. Check the project directory
91
108
  let resolvedPath = findCommandFile(cwd, parts);
92
109
  // 2. Fallback: check the app's own directory
@@ -19,6 +19,8 @@ exports.runContractRefineForQuick = runContractRefineForQuick;
19
19
  const node_crypto_1 = require("node:crypto");
20
20
  const node_readline_1 = require("node:readline");
21
21
  const cli_prompt_1 = require("./util/cli-prompt");
22
+ const spawn_lifecycle_1 = require("./spawn-lifecycle");
23
+ const registry_1 = require("./providers/registry");
22
24
  const explore_contract_refine_1 = require("./explore-contract-refine");
23
25
  const db_1 = require("./db");
24
26
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
@@ -242,15 +244,45 @@ async function runContractRefine(deps, conversationId, ticketId) {
242
244
  projectName: deps.projectName,
243
245
  }, conversation);
244
246
  const startedAt = now().toISOString();
245
- let child;
246
- try {
247
- child = spawn('claude', args, {
248
- env: process.env,
249
- stdio: ['ignore', 'pipe', 'pipe'],
250
- cwd,
251
- });
252
- }
253
- catch (err) {
247
+ // Spawn/stream/timeout/settlement is owned by the shared spawn-lifecycle; the
248
+ // contract-refine-specific raw parse (fullText from assistant text blocks,
249
+ // the raw result event) and ALL finalize/record/broadcast logic stay here,
250
+ // byte-for-byte, so behaviour is unchanged (it still records via the legacy
251
+ // recordSafely path).
252
+ let fullText = '';
253
+ let resultEvent = null;
254
+ const run = await (0, spawn_lifecycle_1.runAiCliInvocation)({
255
+ adapter: (0, registry_1.getAdapter)('claude'),
256
+ binary: 'claude',
257
+ argv: args,
258
+ cwd,
259
+ env: process.env,
260
+ spawn,
261
+ timeoutMs,
262
+ onStdoutLine: (line) => {
263
+ let parsed = null;
264
+ try {
265
+ parsed = JSON.parse(line);
266
+ }
267
+ catch {
268
+ return;
269
+ }
270
+ if (!parsed)
271
+ return;
272
+ const type = parsed.type;
273
+ if (type === 'result') {
274
+ resultEvent = parsed;
275
+ }
276
+ else if (type === 'assistant') {
277
+ const message = parsed.message;
278
+ for (const b of (message?.content ?? [])) {
279
+ if (b.type === 'text' && typeof b.text === 'string')
280
+ fullText += b.text;
281
+ }
282
+ }
283
+ },
284
+ });
285
+ if (run.spawnFailed) {
254
286
  recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, now().toISOString(), 'failed', null);
255
287
  deps.broadcast({
256
288
  type: 'explore.contract_refine_failed',
@@ -261,7 +293,7 @@ async function runContractRefine(deps, conversationId, ticketId) {
261
293
  });
262
294
  return { ok: false, reason: 'crashed', ticketId, conversationId };
263
295
  }
264
- const result = await readRefineChildOutput(child, timeoutMs);
296
+ const result = { fullText, resultEvent, code: run.code, timedOut: run.timedOut };
265
297
  const finishedAt = now().toISOString();
266
298
  console.log(`[contract-refine-runner] spawn done code=${result.code} timedOut=${result.timedOut} hasResult=${!!result.resultEvent} textBytes=${result.fullText.length}`);
267
299
  if (result.timedOut) {
package/server/dist/db.js CHANGED
@@ -559,6 +559,86 @@ const MIGRATIONS = [
559
559
  );
560
560
  `);
561
561
  },
562
+ // Migration 29: Jira integration (per-project). Each project syncs with its
563
+ // own Jira board, so every Jira table lives here in the per-project jobs.sqlite
564
+ // and is keyed by nothing but its own rows. See docs/jira-integration-plan.md.
565
+ // - jira_connection: one row, the connection config (token stored encrypted).
566
+ // - jira_links: spec↔issue map keyed on the IMMUTABLE Jira numeric id.
567
+ // - jira_outbox: durable transactional write-back queue (status + comments).
568
+ (db) => {
569
+ db.exec(`
570
+ CREATE TABLE IF NOT EXISTS jira_connection (
571
+ project_id TEXT PRIMARY KEY,
572
+ base_url TEXT NOT NULL,
573
+ deployment TEXT NOT NULL,
574
+ api_version TEXT NOT NULL,
575
+ auth_scheme TEXT NOT NULL,
576
+ account_email TEXT,
577
+ jira_project_key TEXT NOT NULL,
578
+ jira_project_id TEXT NOT NULL,
579
+ encrypted_token TEXT,
580
+ enabled INTEGER NOT NULL DEFAULT 1,
581
+ status_map TEXT,
582
+ high_water_ms INTEGER,
583
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
584
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
585
+ );
586
+
587
+ CREATE TABLE IF NOT EXISTS jira_links (
588
+ local_id INTEGER PRIMARY KEY,
589
+ jira_issue_id TEXT NOT NULL UNIQUE,
590
+ jira_key TEXT,
591
+ jira_project_id TEXT NOT NULL,
592
+ deployment TEXT NOT NULL,
593
+ status_category TEXT,
594
+ state TEXT NOT NULL DEFAULT 'linked',
595
+ tombstoned INTEGER NOT NULL DEFAULT 0,
596
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
597
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
598
+ );
599
+ CREATE INDEX IF NOT EXISTS idx_jira_links_issue ON jira_links(jira_issue_id);
600
+
601
+ CREATE TABLE IF NOT EXISTS jira_outbox (
602
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
603
+ jira_issue_id TEXT NOT NULL,
604
+ op_type TEXT NOT NULL,
605
+ idempotency_key TEXT NOT NULL UNIQUE,
606
+ payload TEXT NOT NULL,
607
+ state TEXT NOT NULL DEFAULT 'pending',
608
+ attempts INTEGER NOT NULL DEFAULT 0,
609
+ next_attempt_at TEXT,
610
+ last_error TEXT,
611
+ dead_reason TEXT,
612
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
613
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
614
+ );
615
+ CREATE INDEX IF NOT EXISTS idx_jira_outbox_state ON jira_outbox(state);
616
+ CREATE INDEX IF NOT EXISTS idx_jira_outbox_issue ON jira_outbox(jira_issue_id);
617
+ `);
618
+ },
619
+ // Migration 30: Jira sprint custom-field id. The field that holds an issue's
620
+ // sprint(s) is a custom field whose id varies per instance; we discover it
621
+ // (schema com.pyxis.greenhopper.jira:gh-sprint) and cache it here. NULL =
622
+ // not yet checked, 'none' = checked and no sprint field exists, '<id>' = found.
623
+ (db) => {
624
+ try {
625
+ db.exec(`ALTER TABLE jira_connection ADD COLUMN sprint_field_id TEXT`);
626
+ }
627
+ catch {
628
+ // Column may already exist on a partially-migrated DB — no-op.
629
+ }
630
+ },
631
+ // Migration 31: Jira discard target status. The user-configured status name to
632
+ // which a discarded spec's issue is transitioned (instead of being deleted) in
633
+ // a Jira-synced project. NULL/empty = not configured (delete behaves normally).
634
+ (db) => {
635
+ try {
636
+ db.exec(`ALTER TABLE jira_connection ADD COLUMN discard_status TEXT`);
637
+ }
638
+ catch {
639
+ // Column may already exist on a partially-migrated DB — no-op.
640
+ }
641
+ },
562
642
  ];
563
643
  function applyMigrations(db) {
564
644
  // Ensure the migrations table exists (migration 1 creates it, but we need
@@ -600,6 +680,12 @@ function initDb(dbPath) {
600
680
  const db = new better_sqlite3_1.default(dbPath);
601
681
  db.pragma('journal_mode = WAL');
602
682
  db.pragma('foreign_keys = ON');
683
+ // Under load, QueueManager / ChatManager / FileSummaryManager write the same
684
+ // per-project DB concurrently with /analytics reads. Wait up to 5s on a lock
685
+ // instead of throwing SQLITE_BUSY, and cap the WAL so a long checkpoint can't
686
+ // grow it without bound.
687
+ db.pragma('busy_timeout = 5000');
688
+ db.pragma('journal_size_limit = 10000000'); // ~10 MB
603
689
  applyMigrations(db);
604
690
  // H-13: restrict the db + its WAL sidecars to 0600 (jobs.sqlite holds chat
605
691
  // transcripts and verbatim terminal command history). After migrations the
@@ -315,6 +315,9 @@ function initDesktopDb(dbPath = getDesktopDbPath()) {
315
315
  const db = new better_sqlite3_1.default(dbPath);
316
316
  db.pragma('journal_mode = WAL');
317
317
  db.pragma('foreign_keys = ON');
318
+ // Wait up to 5s on a lock instead of throwing SQLITE_BUSY, and cap the WAL.
319
+ db.pragma('busy_timeout = 5000');
320
+ db.pragma('journal_size_limit = 10000000'); // ~10 MB
318
321
  applyDesktopMigrations(db);
319
322
  // H-13: desktop.sqlite stores webhook HMAC secrets in plaintext — restrict it
320
323
  // (and its WAL sidecars) to owner read/write.
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ // Persistent-stdin Explore transport (big bet #3).
3
+ //
4
+ // The default Explore turn spawns a fresh `claude` child per message (turns 2+
5
+ // pay `--resume` session-rehydration latency). This module keeps ONE child
6
+ // alive per conversation using claude's `--input-format stream-json` multi-turn
7
+ // transport: each user turn is written as a newline-delimited JSON message to
8
+ // the child's stdin, and the same long-lived child streams the response. The
9
+ // child stays resident between turns, so turns 2+ skip spawn + rehydration.
10
+ //
11
+ // Transport only — it owns spawning, the persistent stdout/stderr fan-out, and
12
+ // stdin framing. ChatManager drives per-turn rendering, persistence, and
13
+ // lifecycle through the handler hooks. Default OFF behind a flag; claude-only
14
+ // (gated by capabilities.persistentStdin); full fallback to the legacy
15
+ // spawn-per-turn path means zero behaviour change when disabled.
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ExploreStdinSessions = void 0;
18
+ exports.isExplorePersistentStdinEnabled = isExplorePersistentStdinEnabled;
19
+ exports.frameStreamJsonUserMessage = frameStreamJsonUserMessage;
20
+ const node_readline_1 = require("node:readline");
21
+ const cli_prompt_1 = require("./util/cli-prompt");
22
+ /**
23
+ * Persistent-stdin Explore is opt-in. Default OFF — set
24
+ * `SPECRAILS_EXPLORE_PERSISTENT_STDIN=1` to enable. Any other value (or unset)
25
+ * keeps the legacy spawn-per-turn path, so this is also the escape hatch.
26
+ */
27
+ function isExplorePersistentStdinEnabled() {
28
+ return process.env.SPECRAILS_EXPLORE_PERSISTENT_STDIN === '1';
29
+ }
30
+ /**
31
+ * Frame one user turn as a stream-json input line for claude's
32
+ * `--input-format stream-json` transport. Newline-terminated so the child reads
33
+ * exactly one message per turn. Content is sent as a plain string (claude
34
+ * accepts string content for user messages).
35
+ */
36
+ function frameStreamJsonUserMessage(text) {
37
+ return JSON.stringify({ type: 'user', message: { role: 'user', content: text } }) + '\n';
38
+ }
39
+ /**
40
+ * Owns the long-lived claude children for persistent-stdin Explore. Keyed by
41
+ * conversation id. One child per conversation; a single stdout reader fans each
42
+ * line out to the conversation's currently-installed turn handler.
43
+ */
44
+ class ExploreStdinSessions {
45
+ _sessions = new Map();
46
+ has(id) {
47
+ return this._sessions.has(id);
48
+ }
49
+ size() {
50
+ return this._sessions.size;
51
+ }
52
+ /**
53
+ * Return the live child for a conversation, spawning one in persistent mode
54
+ * if none exists. `isNew` is true only when a child was just spawned (the
55
+ * caller writes the first turn either way).
56
+ */
57
+ getOrSpawn(id, spec) {
58
+ const existing = this._sessions.get(id);
59
+ if (existing && existing.child.pid && !existing.child.killed) {
60
+ return { child: existing.child, isNew: false };
61
+ }
62
+ const spawn = spec.spawn ?? cli_prompt_1.spawnAiCli;
63
+ const child = spawn(spec.binary, spec.args, {
64
+ env: spec.env ?? process.env,
65
+ // stdin MUST be piped — it is the per-turn transport.
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ cwd: spec.cwd,
68
+ });
69
+ const session = { child, reader: null, handlers: null };
70
+ if (child.stdout) {
71
+ session.reader = (0, node_readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
72
+ session.reader.on('line', (line) => session.handlers?.onLine(line));
73
+ }
74
+ if (child.stderr) {
75
+ child.stderr.on('data', (chunk) => session.handlers?.onStderr(chunk.toString()));
76
+ }
77
+ const onExit = (code) => {
78
+ // Evict first so a handler that re-spawns inside onClose sees a clean slot.
79
+ if (this._sessions.get(id) === session)
80
+ this._sessions.delete(id);
81
+ session.handlers?.onClose(code);
82
+ };
83
+ child.on('close', onExit);
84
+ child.on('error', () => onExit(null));
85
+ this._sessions.set(id, session);
86
+ return { child, isNew: true };
87
+ }
88
+ /** Install the handlers for the current turn (replaces any prior turn's). */
89
+ setHandlers(id, handlers) {
90
+ const s = this._sessions.get(id);
91
+ if (s)
92
+ s.handlers = handlers;
93
+ }
94
+ /** Detach the current turn's handlers (between turns stray lines are dropped). */
95
+ clearHandlers(id) {
96
+ const s = this._sessions.get(id);
97
+ if (s)
98
+ s.handlers = null;
99
+ }
100
+ /** Write one framed user turn to the persistent child's stdin. */
101
+ writeTurn(id, text) {
102
+ const s = this._sessions.get(id);
103
+ if (!s || !s.child.stdin || s.child.stdin.destroyed)
104
+ return false;
105
+ return s.child.stdin.write(frameStreamJsonUserMessage(text));
106
+ }
107
+ /** Kill and forget one conversation's persistent child (idempotent). */
108
+ kill(id) {
109
+ const s = this._sessions.get(id);
110
+ if (!s)
111
+ return;
112
+ this._sessions.delete(id);
113
+ try {
114
+ s.reader?.close();
115
+ }
116
+ catch { /* best-effort */ }
117
+ try {
118
+ if (s.child.pid && !s.child.killed)
119
+ s.child.kill('SIGTERM');
120
+ }
121
+ catch { /* already gone */ }
122
+ }
123
+ /** Kill every persistent child (shutdown / project removal). */
124
+ killAll() {
125
+ for (const id of Array.from(this._sessions.keys()))
126
+ this.kill(id);
127
+ }
128
+ }
129
+ exports.ExploreStdinSessions = ExploreStdinSessions;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isCodeExplorerEnabled = isCodeExplorerEnabled;
4
4
  exports.isBrowserCaptureEnabled = isBrowserCaptureEnabled;
5
+ exports.isJiraEnabled = isJiraEnabled;
5
6
  function isCodeExplorerEnabled() {
6
7
  return process.env.SPECRAILS_CODE_EXPLORER !== 'false';
7
8
  }
@@ -15,3 +16,13 @@ function isCodeExplorerEnabled() {
15
16
  function isBrowserCaptureEnabled() {
16
17
  return process.env.SPECRAILS_BROWSER_CAPTURE !== 'false';
17
18
  }
19
+ /**
20
+ * Jira integration ("spec = Jira issue", per-project hot-swap local↔Jira).
21
+ * Server-side default ON; set SPECRAILS_JIRA_SECTION="false" to 404 the routes
22
+ * and skip all sync (emergency rollback). The feature is inert until a project
23
+ * actually configures a Jira connection, so default-on is safe. The client gates
24
+ * separately on VITE_FEATURE_JIRA.
25
+ */
26
+ function isJiraEnabled() {
27
+ return process.env.SPECRAILS_JIRA_SECTION !== 'false';
28
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ // Atlassian Document Format (ADF) helpers.
3
+ //
4
+ // Jira Cloud (REST v3) requires comment/description bodies in ADF JSON. Jira
5
+ // Server/Data Center (REST v2) expects a plain wiki-markup string. We keep a
6
+ // single internal "text" model and render it to either format at the client
7
+ // boundary (see jira-client.ts `bodyForDeployment`).
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SPECRAILS_COMMENT_PROP_KEY = void 0;
10
+ exports.textToAdf = textToAdf;
11
+ exports.bodyForDeployment = bodyForDeployment;
12
+ exports.commentMarker = commentMarker;
13
+ exports.discardCommentMarker = discardCommentMarker;
14
+ exports.bodyContainsMarker = bodyContainsMarker;
15
+ exports.commentHasMarker = commentHasMarker;
16
+ exports.adfToText = adfToText;
17
+ /** Build a minimal ADF document from plain text (newlines → paragraphs). */
18
+ function textToAdf(text) {
19
+ const paragraphs = text.split('\n');
20
+ return {
21
+ type: 'doc',
22
+ version: 1,
23
+ content: paragraphs.map((line) => line.length === 0
24
+ ? { type: 'paragraph' }
25
+ : { type: 'paragraph', content: [{ type: 'text', text: line }] }),
26
+ };
27
+ }
28
+ /** Render a body for the target deployment: ADF for Cloud v3, plain for DC v2. */
29
+ function bodyForDeployment(text, deployment) {
30
+ return deployment === 'cloud' ? textToAdf(text) : text;
31
+ }
32
+ /**
33
+ * Jira comment-property key under which we store the idempotency marker. Comment
34
+ * properties are metadata that NEVER render in the comment body, so the marker
35
+ * stays invisible to users while still letting us dedup on retry. Supported on
36
+ * both Cloud (v3) and Data Center (v2).
37
+ */
38
+ exports.SPECRAILS_COMMENT_PROP_KEY = 'sh.specrails.marker';
39
+ /**
40
+ * Deterministic idempotency marker. Jira has no native comment idempotency, so
41
+ * before re-posting on retry we GET the issue comments and skip if one already
42
+ * carries this marker (now via an invisible comment property — see
43
+ * SPECRAILS_COMMENT_PROP_KEY — with a legacy body-scan fallback).
44
+ */
45
+ function commentMarker(jobId, ticketId) {
46
+ return `[specrails:job=${jobId}:ticket=${ticketId}]`;
47
+ }
48
+ /**
49
+ * Idempotency marker for a user-initiated "discard / move-to" comment. The
50
+ * `nonce` (captured at enqueue) makes each discard distinct so a later re-discard
51
+ * of the same spec posts a fresh comment instead of being deduped away.
52
+ */
53
+ function discardCommentMarker(ticketId, nonce) {
54
+ return `[specrails:discard=${nonce}:ticket=${ticketId}]`;
55
+ }
56
+ /** True when an ADF doc or wiki string already contains the given marker. */
57
+ function bodyContainsMarker(body, marker) {
58
+ if (typeof body === 'string')
59
+ return body.includes(marker);
60
+ try {
61
+ return JSON.stringify(body).includes(marker);
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ /**
68
+ * True when a fetched comment already carries the marker — preferring the
69
+ * invisible comment property, with a fallback to a legacy body-embedded marker
70
+ * (comments posted before the property move). `comment.properties` comes from
71
+ * `GET …/comment?expand=properties`.
72
+ */
73
+ function commentHasMarker(comment, marker) {
74
+ const prop = comment.properties?.find((p) => p.key === exports.SPECRAILS_COMMENT_PROP_KEY);
75
+ if (prop) {
76
+ try {
77
+ if (JSON.stringify(prop.value ?? '').includes(marker))
78
+ return true;
79
+ }
80
+ catch {
81
+ /* fall through to body scan */
82
+ }
83
+ }
84
+ return bodyContainsMarker(comment.body, marker);
85
+ }
86
+ /**
87
+ * Flatten an ADF document (or plain string) back to text — used to read inbound
88
+ * Jira descriptions/comments into the local cache.
89
+ */
90
+ function adfToText(body) {
91
+ if (body == null)
92
+ return '';
93
+ if (typeof body === 'string')
94
+ return body;
95
+ const out = [];
96
+ const walk = (node) => {
97
+ if (!node || typeof node !== 'object')
98
+ return;
99
+ if (node.type === 'text' && typeof node.text === 'string')
100
+ out.push(node.text);
101
+ if (node.type === 'hardBreak')
102
+ out.push('\n');
103
+ if (Array.isArray(node.content)) {
104
+ for (const child of node.content)
105
+ walk(child);
106
+ // paragraph / block separators
107
+ if (node.type === 'paragraph' || node.type === 'heading')
108
+ out.push('\n');
109
+ }
110
+ };
111
+ walk(body);
112
+ return out.join('').replace(/\n{3,}/g, '\n\n').trim();
113
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ // Writes `.specrails/backlog-config.json` so specrails-core treats the
3
+ // materialized `local-tickets.json` as a plain LOCAL backlog it READS but never
4
+ // mutates. This is the mechanism that keeps core at ZERO changes:
5
+ // - provider:"local" → core reads `.specrails/local-tickets.json` (the cache).
6
+ // - write_access:false → core's implement pipeline enters its read-only branch
7
+ // and never mutates ticket status nor talks to Jira; Desktop's
8
+ // applyJobOutcomeToTickets + the Jira outbox are the sole status authority.
9
+ //
10
+ // We deliberately write provider:"local" (NOT "jira") so core never authenticates
11
+ // to Jira — Desktop is the only thing that holds the credential.
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.backlogConfigPath = backlogConfigPath;
17
+ exports.readBacklogConfig = readBacklogConfig;
18
+ exports.writeJiraBacklogConfig = writeJiraBacklogConfig;
19
+ exports.writeLocalBacklogConfig = writeLocalBacklogConfig;
20
+ const fs_1 = __importDefault(require("fs"));
21
+ const path_1 = __importDefault(require("path"));
22
+ function backlogConfigPath(projectPath) {
23
+ return path_1.default.join(projectPath, '.specrails', 'backlog-config.json');
24
+ }
25
+ function readBacklogConfig(projectPath) {
26
+ try {
27
+ const raw = fs_1.default.readFileSync(backlogConfigPath(projectPath), 'utf-8');
28
+ return JSON.parse(raw);
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /** Idempotently write the Jira-mode backlog config (local provider, read-only). */
35
+ function writeJiraBacklogConfig(projectPath) {
36
+ const target = backlogConfigPath(projectPath);
37
+ const desired = { provider: 'local', write_access: false, git_auto: false };
38
+ const existing = readBacklogConfig(projectPath);
39
+ if (existing && existing.provider === desired.provider && existing.write_access === desired.write_access) {
40
+ return;
41
+ }
42
+ fs_1.default.mkdirSync(path_1.default.dirname(target), { recursive: true });
43
+ const tmp = `${target}.tmp`;
44
+ fs_1.default.writeFileSync(tmp, JSON.stringify(desired, null, 2), 'utf-8');
45
+ fs_1.default.renameSync(tmp, target);
46
+ }
47
+ /**
48
+ * Restore write access (used when hot-swapping a project back to local specs, so
49
+ * core can manage the local backlog normally again).
50
+ */
51
+ function writeLocalBacklogConfig(projectPath) {
52
+ const target = backlogConfigPath(projectPath);
53
+ const desired = { provider: 'local', write_access: true, git_auto: false };
54
+ fs_1.default.mkdirSync(path_1.default.dirname(target), { recursive: true });
55
+ const tmp = `${target}.tmp`;
56
+ fs_1.default.writeFileSync(tmp, JSON.stringify(desired, null, 2), 'utf-8');
57
+ fs_1.default.renameSync(tmp, target);
58
+ }