specrails-desktop 2.3.0 → 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 (323) 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-Dyyz1ht3.js → AnalyticsPage-BUd3gWYC.js} +1 -1
  4. package/client/dist/assets/{BarChart-CMdLa6Es.js → BarChart-HDe_YoUD.js} +2 -2
  5. package/client/dist/assets/CodePage-CqPPND47.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-CTNwb639.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-DuoZcdYN.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/{cssMode-Cc6ozl-J.js → cssMode-DzNPAYFh.js} +1 -1
  14. package/client/dist/assets/{dist-js-H6hyhSuv.js → dist-js-COfIfLRE.js} +1 -1
  15. package/client/dist/assets/{dist-js-4UEGaKhD.js → dist-js-CvScGQU_.js} +1 -1
  16. package/client/dist/assets/{editor.main-CfXxHimg.js → editor.main-C7Rmw-hR.js} +2 -2
  17. package/client/dist/assets/{freemarker2-DP7J1gG3.js → freemarker2-Cszs4SVo.js} +1 -1
  18. package/client/dist/assets/{handlebars-BjRlucw6.js → handlebars-Dp7Lsuym.js} +1 -1
  19. package/client/dist/assets/{html-OumBQJ-U.js → html-BURidrEm.js} +1 -1
  20. package/client/dist/assets/{htmlMode-CStc3zXM.js → htmlMode--k5M7GjZ.js} +1 -1
  21. package/client/dist/assets/index-DBpvYrDK.css +2 -0
  22. package/client/dist/assets/index-DGIXKRHE.js +142 -0
  23. package/client/dist/assets/{integrations-Cublz3m6.js → integrations-2C7MkGT0.js} +1 -1
  24. package/client/dist/assets/{integrations-HIlUxXVs.js → integrations-BDC670cg.js} +1 -1
  25. package/client/dist/assets/integrations-BqUmRUef.js +1 -0
  26. package/client/dist/assets/{integrations-DmQYCUvN.js → integrations-C2jQtv-s.js} +1 -1
  27. package/client/dist/assets/{integrations-DRdbki5W.js → integrations-CB98NeH5.js} +1 -1
  28. package/client/dist/assets/{integrations-C3p12Ms6.js → integrations-CX4p_bij.js} +1 -1
  29. package/client/dist/assets/{integrations-DaC4SzzL.js → integrations-_SuVeQIG.js} +1 -1
  30. package/client/dist/assets/{integrations-Cr6hH7XR.js → integrations-eQPHAYsE.js} +1 -1
  31. package/client/dist/assets/{javascript-CMk--e7g.js → javascript-kJQz__44.js} +1 -1
  32. package/client/dist/assets/jira-C-ATCti0.js +1 -0
  33. package/client/dist/assets/jira-CmVfRM-b.js +1 -0
  34. package/client/dist/assets/jira-D7bkKAX8.js +1 -0
  35. package/client/dist/assets/jira-DKImM1YH.js +1 -0
  36. package/client/dist/assets/jira-DOw8bkIR.js +1 -0
  37. package/client/dist/assets/jira-DlA-wGp-.js +1 -0
  38. package/client/dist/assets/jira-Fob8EGxN.js +1 -0
  39. package/client/dist/assets/jira-xZA2lixb.js +1 -0
  40. package/client/dist/assets/{jsonMode-C2h3ZcjZ.js → jsonMode-v5JYPpnz.js} +1 -1
  41. package/client/dist/assets/{lib-Cs5FrUJI.js → lib-Bro9Z0gp.js} +1 -1
  42. package/client/dist/assets/{liquid-mI3KJrBE.js → liquid-Dl9I6gWt.js} +1 -1
  43. package/client/dist/assets/{lspLanguageFeatures-DU09ggWi.js → lspLanguageFeatures-CPlEe0NK.js} +1 -1
  44. package/client/dist/assets/{mdx-C41VDTR_.js → mdx-Byl7TtzQ.js} +1 -1
  45. package/client/dist/assets/{monaco.contribution-CPObAXMC.js → monaco.contribution-YMAkHQcQ.js} +2 -2
  46. package/client/dist/assets/{python-Y27rKQtk.js → python-jWQwT6j2.js} +1 -1
  47. package/client/dist/assets/{razor-Cd5-q9Bp.js → razor-BWS3sP-E.js} +1 -1
  48. package/client/dist/assets/setup-C0dzw8j4.js +1 -0
  49. package/client/dist/assets/setup-C1IA-9YS.js +1 -0
  50. package/client/dist/assets/setup-CpfjaNut.js +1 -0
  51. package/client/dist/assets/setup-D3rNZA9A.js +1 -0
  52. package/client/dist/assets/setup-UD2aanGs.js +1 -0
  53. package/client/dist/assets/setup-WP6WOYQh.js +1 -0
  54. package/client/dist/assets/setup-gzLG8T6F.js +1 -0
  55. package/client/dist/assets/setup-pjgmYHx6.js +1 -0
  56. package/client/dist/assets/specs-4lA_u79w.js +1 -0
  57. package/client/dist/assets/{specs-D2FzlLn9.js → specs-BHjxcjOf.js} +1 -1
  58. package/client/dist/assets/{specs-CZ1PsXsC.js → specs-CXNQzPk9.js} +1 -1
  59. package/client/dist/assets/{specs-Dyc5hYeE.js → specs-DFnc2Huj.js} +1 -1
  60. package/client/dist/assets/{specs-BFfu3u-a.js → specs-DZCLH2-l.js} +1 -1
  61. package/client/dist/assets/{specs-B__C8-8a.js → specs-DgmyAE3N.js} +1 -1
  62. package/client/dist/assets/{specs-DaUTrNF9.js → specs-DicWhvwi.js} +1 -1
  63. package/client/dist/assets/{specs-k0PyLDVt.js → specs-dkro6lSM.js} +1 -1
  64. package/client/dist/assets/{tsMode-B0y_xEci.js → tsMode-BbOGOuSV.js} +1 -1
  65. package/client/dist/assets/{typescript-BzK0OgwW.js → typescript-eBtFQJLs.js} +1 -1
  66. package/client/dist/assets/{useProjectCache-BZWYV-w-.js → useProjectCache-D9juBhsO.js} +1 -1
  67. package/client/dist/assets/{workers-rt--R2Qy.js → workers-BvicOoDf.js} +1 -1
  68. package/client/dist/assets/{xml-eX9QXAmI.js → xml-BJepAPyM.js} +1 -1
  69. package/client/dist/assets/{yaml-fcsNkpOt.js → yaml-DabgV-eA.js} +1 -1
  70. package/client/dist/index.html +12 -11
  71. package/docs/jira-integration-plan.md +321 -0
  72. package/package.json +1 -1
  73. package/server/dist/db.js +80 -0
  74. package/server/dist/feature-flags.js +11 -0
  75. package/server/dist/jira/jira-adf.js +113 -0
  76. package/server/dist/jira/jira-backlog-config.js +58 -0
  77. package/server/dist/jira/jira-client.js +279 -0
  78. package/server/dist/jira/jira-credential-store.js +103 -0
  79. package/server/dist/jira/jira-db.js +341 -0
  80. package/server/dist/jira/jira-issue-fields.js +428 -0
  81. package/server/dist/jira/jira-materializer.js +250 -0
  82. package/server/dist/jira/jira-status-resolver.js +211 -0
  83. package/server/dist/jira/jira-sync-manager.js +1014 -0
  84. package/server/dist/jira/types.js +9 -0
  85. package/server/dist/jira-router.js +304 -0
  86. package/server/dist/project-registry.js +43 -1
  87. package/server/dist/project-router-tickets.js +49 -1
  88. package/server/dist/project-router.js +4 -0
  89. package/server/dist/rails-router.js +12 -0
  90. package/client/dist/assets/ActivityFeedPage-3Veccrvk.js +0 -1
  91. package/client/dist/assets/AgentsPage-2mFPghP4.js +0 -86
  92. package/client/dist/assets/CodePage-D7Xwjhut.js +0 -2
  93. package/client/dist/assets/DocsDialog-D8yoyZDD.js +0 -11
  94. package/client/dist/assets/DocsPage-CeO-fAxy.js +0 -11
  95. package/client/dist/assets/IntegrationsPage-iIZ0UEzf.js +0 -3
  96. package/client/dist/assets/JobDetailPage-DgJHAH2m.js +0 -16
  97. package/client/dist/assets/JobsPage-Bv_RpRAE.js +0 -1
  98. package/client/dist/assets/index-CGHKpC-N.js +0 -142
  99. package/client/dist/assets/index-D17R4Cjc.css +0 -2
  100. package/client/dist/assets/integrations-D28q1kF6.js +0 -1
  101. package/client/dist/assets/setup--FMCsnQS.js +0 -1
  102. package/client/dist/assets/setup-B19ZpBNi.js +0 -1
  103. package/client/dist/assets/setup-BZPmkjSN.js +0 -1
  104. package/client/dist/assets/setup-BqYA02rS.js +0 -1
  105. package/client/dist/assets/setup-ChKQDHN9.js +0 -1
  106. package/client/dist/assets/setup-D2n9jMfM.js +0 -1
  107. package/client/dist/assets/setup-P3r6YP1D.js +0 -1
  108. package/client/dist/assets/setup-fnfEbwlv.js +0 -1
  109. package/client/dist/assets/specs-cKEh2LXt.js +0 -1
  110. /package/client/dist/assets/{abap-Bw6f2wDG.js → abap-s65oMlhi.js} +0 -0
  111. /package/client/dist/assets/{activity-BdrPln96.js → activity-BqqwnH_h.js} +0 -0
  112. /package/client/dist/assets/{activity-BEIp_Y1A.js → activity-C8qqEIoP.js} +0 -0
  113. /package/client/dist/assets/{activity-CpkRS8Sx.js → activity-CZVM4nlJ.js} +0 -0
  114. /package/client/dist/assets/{activity-DOUVEjJi.js → activity-Cyy07Tgo.js} +0 -0
  115. /package/client/dist/assets/{activity-DRwkql_y.js → activity-DlbWCa4y.js} +0 -0
  116. /package/client/dist/assets/{activity-DKCpESPt.js → activity-Dwq0heud.js} +0 -0
  117. /package/client/dist/assets/{activity-DcDQ7tjw.js → activity-qFTcMyW9.js} +0 -0
  118. /package/client/dist/assets/{addon-image-3WCl5Vhd.js → addon-image-CpF0L0jM.js} +0 -0
  119. /package/client/dist/assets/{addon-ligatures-C5OdliKs.js → addon-ligatures-hXysGZrA.js} +0 -0
  120. /package/client/dist/assets/{addon-webgl-BbX6pSjl.js → addon-webgl-Cn1slavz.js} +0 -0
  121. /package/client/dist/assets/{addspec-D33ocMxf.js → addspec-B1FTtI2a.js} +0 -0
  122. /package/client/dist/assets/{addspec-DFswZ0jK.js → addspec-BCT9vm_c.js} +0 -0
  123. /package/client/dist/assets/{addspec-DVZ15Jp8.js → addspec-DeDOztDr.js} +0 -0
  124. /package/client/dist/assets/{addspec-Fkv91Opc.js → addspec-DpRgmfmx.js} +0 -0
  125. /package/client/dist/assets/{addspec-BEeF5-zc.js → addspec-Dw-0Dg-4.js} +0 -0
  126. /package/client/dist/assets/{addspec-B5yl4Loj.js → addspec-rp496P_F.js} +0 -0
  127. /package/client/dist/assets/{addspec-DRE-jZv7.js → addspec-v8j6A7CD.js} +0 -0
  128. /package/client/dist/assets/{agents-DK-Dlc0i.js → agents-23iPejcA.js} +0 -0
  129. /package/client/dist/assets/{agents-Q6Ldfpxx.js → agents-BDx1RXcl.js} +0 -0
  130. /package/client/dist/assets/{agents-TeOSy-ax.js → agents-BFr3kUhK.js} +0 -0
  131. /package/client/dist/assets/{agents-Bm9rPqnt.js → agents-B_1L9xRg.js} +0 -0
  132. /package/client/dist/assets/{agents-1nCDWRmP.js → agents-BlPnx-mz.js} +0 -0
  133. /package/client/dist/assets/{agents-iTqjRajS.js → agents-DcxZHzNr.js} +0 -0
  134. /package/client/dist/assets/{agents-s87sMGzL.js → agents-G3shOewU.js} +0 -0
  135. /package/client/dist/assets/{agentstudio-B6Wb59E7.js → agentstudio-B-CMAQqy.js} +0 -0
  136. /package/client/dist/assets/{agentstudio-D3I62TLJ.js → agentstudio-Bk1eZcv4.js} +0 -0
  137. /package/client/dist/assets/{agentstudio-DuH9TogZ.js → agentstudio-ChxNuGAu.js} +0 -0
  138. /package/client/dist/assets/{agentstudio-Kw88_dUF.js → agentstudio-DNlme601.js} +0 -0
  139. /package/client/dist/assets/{agentstudio-BdidyBzZ.js → agentstudio-DpP9caEE.js} +0 -0
  140. /package/client/dist/assets/{agentstudio-BSnWLR63.js → agentstudio-Y3G0ddJ2.js} +0 -0
  141. /package/client/dist/assets/{agentstudio-BADhZ41e.js → agentstudio-kk9RB7Se.js} +0 -0
  142. /package/client/dist/assets/{aiedit-DJMny-D5.js → aiedit-5ETerMK1.js} +0 -0
  143. /package/client/dist/assets/{aiedit-D2ji6Qy0.js → aiedit-BBCrOpHq.js} +0 -0
  144. /package/client/dist/assets/{aiedit-DAhZTvtk.js → aiedit-BMtcGYNE.js} +0 -0
  145. /package/client/dist/assets/{aiedit-DvrcbwGv.js → aiedit-D9ddlgkM.js} +0 -0
  146. /package/client/dist/assets/{aiedit-WBSjT_C1.js → aiedit-De0SOH6S.js} +0 -0
  147. /package/client/dist/assets/{aiedit-BWxHGsYA.js → aiedit-DrfzQroF.js} +0 -0
  148. /package/client/dist/assets/{aiedit-DOcxERkU.js → aiedit-fMltW101.js} +0 -0
  149. /package/client/dist/assets/{analytics-C9Zc-rkM.js → analytics-BeTyviO8.js} +0 -0
  150. /package/client/dist/assets/{analytics-CrPCZRJ-.js → analytics-C4eEO260.js} +0 -0
  151. /package/client/dist/assets/{analytics-CYj0tfj7.js → analytics-C67cIA1b.js} +0 -0
  152. /package/client/dist/assets/{analytics-C6EzgtdE.js → analytics-CAguvW28.js} +0 -0
  153. /package/client/dist/assets/{analytics-CVx3YOc0.js → analytics-DBtt8Mgk.js} +0 -0
  154. /package/client/dist/assets/{analytics-CnY4kNG3.js → analytics-DUPtODxX.js} +0 -0
  155. /package/client/dist/assets/{analytics-BIdr0YfL.js → analytics-YIpQvPAc.js} +0 -0
  156. /package/client/dist/assets/{apex-Cw8_REBo.js → apex-BLUBIldB.js} +0 -0
  157. /package/client/dist/assets/{attachments-DYHGA2Dj.js → attachments-CCWasu-P.js} +0 -0
  158. /package/client/dist/assets/{attachments-Dd92KpUH.js → attachments-CHaDUfjB.js} +0 -0
  159. /package/client/dist/assets/{attachments-DzdU6DV6.js → attachments-CVSAbGNl.js} +0 -0
  160. /package/client/dist/assets/{attachments-Bcf6BG6V.js → attachments-Chg5poG1.js} +0 -0
  161. /package/client/dist/assets/{attachments-BW4L3l2L.js → attachments-DazTVJbH.js} +0 -0
  162. /package/client/dist/assets/{attachments-COcrGRFz.js → attachments-Dn-JImAK.js} +0 -0
  163. /package/client/dist/assets/{attachments-Bke8sCU4.js → attachments-LDA9kp2X.js} +0 -0
  164. /package/client/dist/assets/{azcli-Cz6HAoOw.js → azcli-DuWxh9mO.js} +0 -0
  165. /package/client/dist/assets/{bat-CcJ-xyqL.js → bat-UKoTejQm.js} +0 -0
  166. /package/client/dist/assets/{bicep-z1WDCKYz.js → bicep-4sTT4B3D.js} +0 -0
  167. /package/client/dist/assets/{browser-DGITz3fC.js → browser-BDd1dbFa.js} +0 -0
  168. /package/client/dist/assets/{browser-JsAIGCEW.js → browser-BWSgbfdX.js} +0 -0
  169. /package/client/dist/assets/{browser-M5-rbPlw.js → browser-D2Y_UAKA.js} +0 -0
  170. /package/client/dist/assets/{browser-BlYF4OOq.js → browser-DH9SGVfM.js} +0 -0
  171. /package/client/dist/assets/{browser-Bc-YdlVg.js → browser-DWOVYMlg.js} +0 -0
  172. /package/client/dist/assets/{browser-CT-ReZGt.js → browser-Dxc_VIRK.js} +0 -0
  173. /package/client/dist/assets/{browser-5ErDlJoR.js → browser-lTQwcDCI.js} +0 -0
  174. /package/client/dist/assets/{cameligo-BRewOpfa.js → cameligo-CAAryRYO.js} +0 -0
  175. /package/client/dist/assets/{chat-DwUm6W9z.js → chat-BO9MvVID.js} +0 -0
  176. /package/client/dist/assets/{chat-BEGuC03z.js → chat-CPgmgZOj.js} +0 -0
  177. /package/client/dist/assets/{chat-CboQguCi.js → chat-CUrG1eVg.js} +0 -0
  178. /package/client/dist/assets/{chat-DRCa9pOt.js → chat-CvOOKB2s.js} +0 -0
  179. /package/client/dist/assets/{chat-BEW60P_u.js → chat-DIh3hr6y.js} +0 -0
  180. /package/client/dist/assets/{chat-yoXwguQu.js → chat-UVVZqA57.js} +0 -0
  181. /package/client/dist/assets/{chat-BQNMD0PL.js → chat-mPn3UlMl.js} +0 -0
  182. /package/client/dist/assets/{clojure-DBjRWN6g.js → clojure-BlMERO1w.js} +0 -0
  183. /package/client/dist/assets/{clsx-DnqN-uhr.js → clsx-CnH-HMk3.js} +0 -0
  184. /package/client/dist/assets/{code-zCwBt3Uu.js → code-BwIz8agY.js} +0 -0
  185. /package/client/dist/assets/{code-g0qFMzyg.js → code-CD7yNSK0.js} +0 -0
  186. /package/client/dist/assets/{code-DDU0CRS0.js → code-CDFlxUFC.js} +0 -0
  187. /package/client/dist/assets/{code-L35Loak_.js → code-Cp3Fdng-.js} +0 -0
  188. /package/client/dist/assets/{code-D1z-YDt-.js → code-D24e1Crx.js} +0 -0
  189. /package/client/dist/assets/{code-BtsmPQLV.js → code-DtZBQTi9.js} +0 -0
  190. /package/client/dist/assets/{code-Coa8f2Sh.js → code-nKa0fkm_.js} +0 -0
  191. /package/client/dist/assets/{coffee-Cfk_XHGR.js → coffee-Cj8D-Wl1.js} +0 -0
  192. /package/client/dist/assets/{commands-sqrqsxyE.js → commands-B-MVT-2F.js} +0 -0
  193. /package/client/dist/assets/{commands-UD1NzmwX.js → commands-B0yFTp7e.js} +0 -0
  194. /package/client/dist/assets/{commands-DLrvnPNg.js → commands-BR1kDkHQ.js} +0 -0
  195. /package/client/dist/assets/{commands-CJxCry-o.js → commands-Cb21pDlG.js} +0 -0
  196. /package/client/dist/assets/{commands-CfgY-_of.js → commands-DWgp-8W1.js} +0 -0
  197. /package/client/dist/assets/{commands-B772IyDa.js → commands-ddsl1V91.js} +0 -0
  198. /package/client/dist/assets/{commands-BDDp6xFG.js → commands-t4frzhB0.js} +0 -0
  199. /package/client/dist/assets/{common-Dmm1GhdD.js → common-5ilvMOcH.js} +0 -0
  200. /package/client/dist/assets/{common-DltqHaAe.js → common-B4sqsKp7.js} +0 -0
  201. /package/client/dist/assets/{common-GbpxfPG8.js → common-BKpVwUIf.js} +0 -0
  202. /package/client/dist/assets/{common-DeDELLZJ.js → common-BzEC3kJU.js} +0 -0
  203. /package/client/dist/assets/{common-DnjcgkPH.js → common-CALKUpYm.js} +0 -0
  204. /package/client/dist/assets/{common-Dard9UNH.js → common-CTEbWVZS.js} +0 -0
  205. /package/client/dist/assets/{common-DCr6VzJ7.js → common-DQiza2Xp.js} +0 -0
  206. /package/client/dist/assets/{cpp-BVob6BaP.js → cpp-BPfKnaj_.js} +0 -0
  207. /package/client/dist/assets/{csharp-C4fbRuOu.js → csharp-gX-x5uD6.js} +0 -0
  208. /package/client/dist/assets/{csp-DthFP_vT.js → csp-DKGVt8SM.js} +0 -0
  209. /package/client/dist/assets/{css-CGMH0hcW.js → css-CPMdnAVq.js} +0 -0
  210. /package/client/dist/assets/{cypher-Pnf68BRV.js → cypher-ClMDrj9S.js} +0 -0
  211. /package/client/dist/assets/{dart-PMMOtxZX.js → dart-C4zbzpVv.js} +0 -0
  212. /package/client/dist/assets/{dashboard-BZBADHSj.js → dashboard--Y6yzMlf.js} +0 -0
  213. /package/client/dist/assets/{dashboard-I19DXBxw.js → dashboard--a4-6oYE.js} +0 -0
  214. /package/client/dist/assets/{dashboard-CB6Le1yN.js → dashboard-BiJ3CDTG.js} +0 -0
  215. /package/client/dist/assets/{dashboard-B4ixDVk8.js → dashboard-CiXjk63Z.js} +0 -0
  216. /package/client/dist/assets/{dashboard-C1MfeUHs.js → dashboard-Cx5VjCea.js} +0 -0
  217. /package/client/dist/assets/{dashboard-C7SK6xu5.js → dashboard-D7jg25XR.js} +0 -0
  218. /package/client/dist/assets/{dashboard-CoTpMOBM.js → dashboard-DpGYK2s1.js} +0 -0
  219. /package/client/dist/assets/{dockerfile-di1nsJCc.js → dockerfile-D9xw73D1.js} +0 -0
  220. /package/client/dist/assets/{ecl-D_WVcB5M.js → ecl-gqO8tIR9.js} +0 -0
  221. /package/client/dist/assets/{editor.api2-XLGzZfbc.js → editor.api2-BPnIxMjz.js} +0 -0
  222. /package/client/dist/assets/{elixir-OAdJEMOn.js → elixir-DSAhVF3_.js} +0 -0
  223. /package/client/dist/assets/{explore-D2EFgt8J.js → explore-BE5UmlbD.js} +0 -0
  224. /package/client/dist/assets/{explore-BV5Xxlsn.js → explore-BmTaI8dX.js} +0 -0
  225. /package/client/dist/assets/{explore-A8Ltoblq.js → explore-CCwkqoWq.js} +0 -0
  226. /package/client/dist/assets/{explore-4mFpnrKU.js → explore-CMdEoPDx.js} +0 -0
  227. /package/client/dist/assets/{explore-C3FSE42C.js → explore-CtdCL4QU.js} +0 -0
  228. /package/client/dist/assets/{explore-B9A3iN2W.js → explore-DHjxSkqQ.js} +0 -0
  229. /package/client/dist/assets/{explore-BrBJvfjP.js → explore-__BeALjE.js} +0 -0
  230. /package/client/dist/assets/{flow9-D3QEZjgn.js → flow9-DeQCSPOd.js} +0 -0
  231. /package/client/dist/assets/{format-command-CwGuwzGA.js → format-command-2VNoNnMv.js} +0 -0
  232. /package/client/dist/assets/{fsharp-BF0k_8N8.js → fsharp-CEfaXL-S.js} +0 -0
  233. /package/client/dist/assets/{go-BAQO5Jsz.js → go-Xp1OkZCh.js} +0 -0
  234. /package/client/dist/assets/{graphql-hdFVFkiV.js → graphql-BwRXrUwe.js} +0 -0
  235. /package/client/dist/assets/{hcl-DWnl1o-X.js → hcl-u06DtVFk.js} +0 -0
  236. /package/client/dist/assets/{ini-CB-6OVu3.js → ini-AmeIpFND.js} +0 -0
  237. /package/client/dist/assets/{java-d1CmfiHX.js → java-CyDbRQjX.js} +0 -0
  238. /package/client/dist/assets/{jobs-DPPT6bV6.js → jobs-8viuHLDV.js} +0 -0
  239. /package/client/dist/assets/{jobs-3j3_npyo.js → jobs-AW2eB5D-.js} +0 -0
  240. /package/client/dist/assets/{jobs-2N3RXDAM.js → jobs-BSm89DL5.js} +0 -0
  241. /package/client/dist/assets/{jobs-BqEbCCxD.js → jobs-BZ3sQHjZ.js} +0 -0
  242. /package/client/dist/assets/{jobs-cHYInoau.js → jobs-Bd8AdOTb.js} +0 -0
  243. /package/client/dist/assets/{jobs-DRzjWI9u.js → jobs-CRtsq_u0.js} +0 -0
  244. /package/client/dist/assets/{jobs-2f6Hdc72.js → jobs-CSRwFQ6K.js} +0 -0
  245. /package/client/dist/assets/{jobs-vGfzIDQa.js → jobs-CbEl7WMI.js} +0 -0
  246. /package/client/dist/assets/{julia-Bgv08lKa.js → julia-BqialFRG.js} +0 -0
  247. /package/client/dist/assets/{kotlin-u98kaVTf.js → kotlin-Dzz8TWAt.js} +0 -0
  248. /package/client/dist/assets/{less-CjYwpgg5.js → less-DHRJD3TR.js} +0 -0
  249. /package/client/dist/assets/{lexon-YTjaAFBB.js → lexon-5Y3QgTmT.js} +0 -0
  250. /package/client/dist/assets/{lua-BzmkWv27.js → lua-sKvhfPn5.js} +0 -0
  251. /package/client/dist/assets/{m3-CFwk9fw0.js → m3-DWDVwkFG.js} +0 -0
  252. /package/client/dist/assets/{markdown-CR5iMpSZ.js → markdown-CD_aSBxW.js} +0 -0
  253. /package/client/dist/assets/{mips-CcEalc17.js → mips-687T03hg.js} +0 -0
  254. /package/client/dist/assets/{msdax-BQbkawnr.js → msdax-C1St-dIV.js} +0 -0
  255. /package/client/dist/assets/{mysql-GTlaaW_P.js → mysql-BG7r8oBS.js} +0 -0
  256. /package/client/dist/assets/{nav-C2YXcbZS.js → nav-B05EYB0b.js} +0 -0
  257. /package/client/dist/assets/{nav-D2bOGSEg.js → nav-BNGCq-0y.js} +0 -0
  258. /package/client/dist/assets/{nav-BEL3MTwK.js → nav-BRInPX8a.js} +0 -0
  259. /package/client/dist/assets/{nav-CtYwmMgu.js → nav-Bf87DRHD.js} +0 -0
  260. /package/client/dist/assets/{nav-iH1V5j6o.js → nav-BkVzzFpc.js} +0 -0
  261. /package/client/dist/assets/{nav-0fwkrgHt.js → nav-BzFLtS1W.js} +0 -0
  262. /package/client/dist/assets/{nav-ClzOE4mA.js → nav-DBDbQOYn.js} +0 -0
  263. /package/client/dist/assets/{nav-B_G-TJDW.js → nav-X9sVtUWC.js} +0 -0
  264. /package/client/dist/assets/{objective-c-Byu1T5if.js → objective-c-Ds1-m05L.js} +0 -0
  265. /package/client/dist/assets/{pascal-BrfzBfRm.js → pascal-BKK9FpIi.js} +0 -0
  266. /package/client/dist/assets/{pascaligo-BXXKFUeo.js → pascaligo-SRS3nwtO.js} +0 -0
  267. /package/client/dist/assets/{perl-B3OikKq-.js → perl-B2hTOlrF.js} +0 -0
  268. /package/client/dist/assets/{pgsql-CTsa0Acc.js → pgsql-DIQJYNpL.js} +0 -0
  269. /package/client/dist/assets/{php-DiQh3FUW.js → php-BEaVe8X2.js} +0 -0
  270. /package/client/dist/assets/{pla-92uH8Fzm.js → pla-oPLHpZ-Q.js} +0 -0
  271. /package/client/dist/assets/{postiats-BbeWkKUr.js → postiats-D_vzrAzD.js} +0 -0
  272. /package/client/dist/assets/{powerquery-DgDMzpsm.js → powerquery-BKG6w-FH.js} +0 -0
  273. /package/client/dist/assets/{powershell-BfdUUzaG.js → powershell-B3dLhDt4.js} +0 -0
  274. /package/client/dist/assets/{protobuf-BojW2ftW.js → protobuf-DC8SGjcl.js} +0 -0
  275. /package/client/dist/assets/{pug-BxqTg3IU.js → pug-D5E-4fI0.js} +0 -0
  276. /package/client/dist/assets/{qsharp-BX_A-MW9.js → qsharp-6vJAWv0x.js} +0 -0
  277. /package/client/dist/assets/{r-D9BMnxvJ.js → r-CDwsEcbM.js} +0 -0
  278. /package/client/dist/assets/{redis-5cJqEQJJ.js → redis-CuQbbESS.js} +0 -0
  279. /package/client/dist/assets/{redshift-d8BBqiwb.js → redshift-B9e1k-qI.js} +0 -0
  280. /package/client/dist/assets/{restructuredtext-C8a6yIcZ.js → restructuredtext-BiJ5IwaU.js} +0 -0
  281. /package/client/dist/assets/{ruby-egeh-6KX.js → ruby-B0UAHY9b.js} +0 -0
  282. /package/client/dist/assets/{rust-a3r9IInB.js → rust-Dg_spmFr.js} +0 -0
  283. /package/client/dist/assets/{sb-y8iRIDei.js → sb-DjU66I8Q.js} +0 -0
  284. /package/client/dist/assets/{scala-BPDK2AmK.js → scala-qvStIdfG.js} +0 -0
  285. /package/client/dist/assets/{scheme-BIWUEoOs.js → scheme-FstEk5Rh.js} +0 -0
  286. /package/client/dist/assets/{scss-CA-PSzwg.js → scss-w0U3rQLK.js} +0 -0
  287. /package/client/dist/assets/{settings-CTcwN9RE.js → settings-5tzo0Rn3.js} +0 -0
  288. /package/client/dist/assets/{settings-D_dujJZI.js → settings-BDAW3trC.js} +0 -0
  289. /package/client/dist/assets/{settings-Bg0A3zoS.js → settings-BEWv3VEu.js} +0 -0
  290. /package/client/dist/assets/{settings-BgPqg2nv.js → settings-BORg56um.js} +0 -0
  291. /package/client/dist/assets/{settings-BSze3_9q.js → settings-D3LurcR5.js} +0 -0
  292. /package/client/dist/assets/{settings-CSJ0ahZ8.js → settings-DcqWIEM6.js} +0 -0
  293. /package/client/dist/assets/{settings-DYIV89nV.js → settings-Dfz8QbZS.js} +0 -0
  294. /package/client/dist/assets/{settings-DDcfx_ca.js → settings-yMubjqYw.js} +0 -0
  295. /package/client/dist/assets/{shell--LiT1Bja.js → shell-DJ78wREd.js} +0 -0
  296. /package/client/dist/assets/{solidity-DdqZccZg.js → solidity-1aGIVsdX.js} +0 -0
  297. /package/client/dist/assets/{sophia-S6-YxNG_.js → sophia-40LqcGjB.js} +0 -0
  298. /package/client/dist/assets/{sparql-BSf5kMp2.js → sparql-Cz5dqG_g.js} +0 -0
  299. /package/client/dist/assets/{sql-D7KgjR8G.js → sql-64f62Ni4.js} +0 -0
  300. /package/client/dist/assets/{st-BnoDa-Ml.js → st-gJe2yG8J.js} +0 -0
  301. /package/client/dist/assets/{swift-DEUHTkUX.js → swift-C6ME22mv.js} +0 -0
  302. /package/client/dist/assets/{systemverilog-Tqb_KPnW.js → systemverilog-CEWz259w.js} +0 -0
  303. /package/client/dist/assets/{tcl-BmBFS2qq.js → tcl-CcLVIi3m.js} +0 -0
  304. /package/client/dist/assets/{terminal-Bje4ziIa.js → terminal-BYtreaaF.js} +0 -0
  305. /package/client/dist/assets/{terminal-CSONJOex.js → terminal-C0xx0SjA.js} +0 -0
  306. /package/client/dist/assets/{terminal-DeWzh6ys.js → terminal-CPpK58RC.js} +0 -0
  307. /package/client/dist/assets/{terminal-C2WYcFHF.js → terminal-CdxkpafL.js} +0 -0
  308. /package/client/dist/assets/{terminal-DEqzGtcr.js → terminal-Ciia0wh2.js} +0 -0
  309. /package/client/dist/assets/{terminal-80yDMgMF.js → terminal-DHIkiWcs.js} +0 -0
  310. /package/client/dist/assets/{terminal-lkZYR4wJ.js → terminal-DY42QANg.js} +0 -0
  311. /package/client/dist/assets/{terminal-YOlsJCQj.js → terminal-DoxtVdma.js} +0 -0
  312. /package/client/dist/assets/{tickets-DYvafSaY.js → tickets-0rM0lIXd.js} +0 -0
  313. /package/client/dist/assets/{tickets-DNOANUXr.js → tickets-1UIGf_oA.js} +0 -0
  314. /package/client/dist/assets/{tickets-DlpC_iTg.js → tickets-9kdPXInd.js} +0 -0
  315. /package/client/dist/assets/{tickets-CF2PYelu.js → tickets-C6pwZwt4.js} +0 -0
  316. /package/client/dist/assets/{tickets-CB7N30gm.js → tickets-DAjtxAVb.js} +0 -0
  317. /package/client/dist/assets/{tickets-DU1aqsbr.js → tickets-DNmXcAwu.js} +0 -0
  318. /package/client/dist/assets/{tickets-clefmXLv.js → tickets-n23kDqJT.js} +0 -0
  319. /package/client/dist/assets/{tickets-DucYgtdl.js → tickets-tGx5AR5b.js} +0 -0
  320. /package/client/dist/assets/{twig-BQV8igWC.js → twig-DvsO-WjW.js} +0 -0
  321. /package/client/dist/assets/{typespec-DlFroUGY.js → typespec-Brkt3IAA.js} +0 -0
  322. /package/client/dist/assets/{vb-BlrJpIMX.js → vb-r121Uzxt.js} +0 -0
  323. /package/client/dist/assets/{wgsl-BWgIc6FZ.js → wgsl-BRX8uYh4.js} +0 -0
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ // Shared types for the per-project Jira integration.
3
+ //
4
+ // Design: Desktop is the sync layer. The local `.specrails/local-tickets.json`
5
+ // store stays the canonical read cache (specrails-core reads it unchanged). Jira
6
+ // is the system of record. Every write to Jira goes through a durable
7
+ // transactional outbox in the per-project `jobs.sqlite`. See
8
+ // docs/jira-integration-plan.md.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createJiraRouter = createJiraRouter;
37
+ const express_1 = require("express");
38
+ const jira_db_1 = require("./jira/jira-db");
39
+ const feature_flags_1 = require("./feature-flags");
40
+ // req.projectCtx is declared in project-router / rails-router.
41
+ /**
42
+ * Per-project Jira router, mounted at /api/projects/:projectId/jira. Gated by
43
+ * SPECRAILS_JIRA_SECTION; 404s entirely when the flag is off. Endpoints back the
44
+ * step-by-step setup wizard (test → pick project → optional status map → connect)
45
+ * and the sync/outbox management surfaces. The token is never returned to the
46
+ * client (connection responses carry `hasToken` only).
47
+ */
48
+ function createJiraRouter() {
49
+ const router = (0, express_1.Router)({ mergeParams: true });
50
+ function ctx(req) {
51
+ return req.projectCtx;
52
+ }
53
+ // Feature-flag gate for every route under /jira.
54
+ router.use((_req, res, next) => {
55
+ if (!(0, feature_flags_1.isJiraEnabled)()) {
56
+ res.status(404).json({ error: 'Jira integration disabled' });
57
+ return;
58
+ }
59
+ next();
60
+ });
61
+ // ─── Connection ──────────────────────────────────────────────────────────────
62
+ // GET /connection — current connection (token redacted) or { connected: false }.
63
+ router.get('/connection', (req, res) => {
64
+ const c = ctx(req);
65
+ const conn = (0, jira_db_1.getConnectionPublic)(c.db, c.project.id);
66
+ if (!conn) {
67
+ res.json({ connected: false });
68
+ return;
69
+ }
70
+ res.json({ connected: true, connection: conn, outbox: c.jiraSyncManager.outboxCounts() });
71
+ });
72
+ // POST /test — wizard step 1: validate credentials without saving.
73
+ router.post('/test', async (req, res) => {
74
+ const { baseUrl, accountEmail, token } = req.body ?? {};
75
+ if (!isNonEmptyString(baseUrl) || !isNonEmptyString(token)) {
76
+ res.status(400).json({ error: 'baseUrl and token are required' });
77
+ return;
78
+ }
79
+ const result = await ctx(req).jiraSyncManager.probeCredentials({
80
+ baseUrl: baseUrl.trim(),
81
+ accountEmail: isNonEmptyString(accountEmail) ? accountEmail.trim() : null,
82
+ token,
83
+ });
84
+ if (!result.ok) {
85
+ res.status(result.status === 401 ? 401 : 400).json({ error: result.error });
86
+ return;
87
+ }
88
+ res.json(result);
89
+ });
90
+ // POST /discover-projects — wizard step 2: list visible projects.
91
+ router.post('/discover-projects', async (req, res) => {
92
+ const { baseUrl, accountEmail, token, query } = req.body ?? {};
93
+ if (!isNonEmptyString(baseUrl) || !isNonEmptyString(token)) {
94
+ res.status(400).json({ error: 'baseUrl and token are required' });
95
+ return;
96
+ }
97
+ const result = await ctx(req).jiraSyncManager.discoverProjects({
98
+ baseUrl: baseUrl.trim(),
99
+ accountEmail: isNonEmptyString(accountEmail) ? accountEmail.trim() : null,
100
+ token,
101
+ query: isNonEmptyString(query) ? query.trim() : undefined,
102
+ });
103
+ if (!result.ok) {
104
+ res.status(400).json({ error: result.error });
105
+ return;
106
+ }
107
+ res.json({ projects: result.projects });
108
+ });
109
+ // POST /discover-statuses — wizard step 3 (optional): the project's statuses.
110
+ router.post('/discover-statuses', async (req, res) => {
111
+ const { baseUrl, accountEmail, token, projectKey } = req.body ?? {};
112
+ if (!isNonEmptyString(baseUrl) || !isNonEmptyString(token) || !isNonEmptyString(projectKey)) {
113
+ res.status(400).json({ error: 'baseUrl, token and projectKey are required' });
114
+ return;
115
+ }
116
+ const result = await ctx(req).jiraSyncManager.discoverStatuses({
117
+ baseUrl: baseUrl.trim(),
118
+ accountEmail: isNonEmptyString(accountEmail) ? accountEmail.trim() : null,
119
+ token,
120
+ projectKey: projectKey.trim(),
121
+ });
122
+ if (!result.ok) {
123
+ res.status(400).json({ error: result.error });
124
+ return;
125
+ }
126
+ res.json({ statuses: result.statuses });
127
+ });
128
+ // POST /connect — wizard final step: validate + persist + start sync.
129
+ router.post('/connect', async (req, res) => {
130
+ const c = ctx(req);
131
+ const { baseUrl, accountEmail, token, jiraProjectKey, statusMap, discardStatus } = req.body ?? {};
132
+ if (!isNonEmptyString(baseUrl) || !isNonEmptyString(token) || !isNonEmptyString(jiraProjectKey)) {
133
+ res.status(400).json({ error: 'baseUrl, token and jiraProjectKey are required' });
134
+ return;
135
+ }
136
+ const cleanMap = sanitizeStatusMap(statusMap);
137
+ const result = await c.jiraSyncManager.connect({
138
+ baseUrl: baseUrl.trim(),
139
+ accountEmail: isNonEmptyString(accountEmail) ? accountEmail.trim() : null,
140
+ token,
141
+ jiraProjectKey: jiraProjectKey.trim(),
142
+ statusMap: cleanMap,
143
+ discardStatus: isNonEmptyString(discardStatus) ? discardStatus.trim() : null,
144
+ });
145
+ if (!result.ok) {
146
+ res.status(result.status === 401 ? 401 : 400).json({ error: result.error });
147
+ return;
148
+ }
149
+ res.status(201).json({ connection: (0, jira_db_1.getConnectionPublic)(c.db, c.project.id) });
150
+ });
151
+ // PATCH /connection — toggle enabled (hot-swap local↔Jira) and/or update the
152
+ // discard "move-to" status. `discardStatus: ''`/null clears it.
153
+ router.patch('/connection', (req, res) => {
154
+ const c = ctx(req);
155
+ const existing = (0, jira_db_1.getConnectionPublic)(c.db, c.project.id);
156
+ if (!existing) {
157
+ res.status(404).json({ error: 'No Jira connection configured' });
158
+ return;
159
+ }
160
+ const { enabled, discardStatus, statusMap } = req.body ?? {};
161
+ if (typeof enabled === 'boolean') {
162
+ c.jiraSyncManager.setEnabled(enabled);
163
+ }
164
+ if (discardStatus !== undefined) {
165
+ c.jiraSyncManager.setDiscardStatus(isNonEmptyString(discardStatus) ? discardStatus.trim() : null);
166
+ }
167
+ if (statusMap !== undefined) {
168
+ c.jiraSyncManager.setStatusMap(sanitizeStatusMap(statusMap));
169
+ }
170
+ res.json({ connection: (0, jira_db_1.getConnectionPublic)(c.db, c.project.id) });
171
+ });
172
+ // GET /statuses — the connected project's real statuses (post-connect picker
173
+ // for the discard "move-to" status). Uses the stored credentials.
174
+ router.get('/statuses', async (req, res) => {
175
+ const result = await ctx(req).jiraSyncManager.listStatusesForConnection();
176
+ if (!result.ok) {
177
+ res.status(400).json({ error: result.error });
178
+ return;
179
+ }
180
+ res.json({ statuses: result.statuses });
181
+ });
182
+ // DELETE /connection — remove the connection + restore local backlog config.
183
+ router.delete('/connection', (req, res) => {
184
+ const c = ctx(req);
185
+ c.jiraSyncManager.disconnect();
186
+ res.json({ connected: false });
187
+ });
188
+ // POST /resume — re-paste of a fresh token after a 401: drain the parked outbox.
189
+ router.post('/resume', (req, res) => {
190
+ ctx(req).jiraSyncManager.resumeAfterReauth();
191
+ res.json({ ok: true });
192
+ });
193
+ // ─── Sync + outbox management ───────────────────────────────────────────────
194
+ // POST /sync — manual "Sync now": a FULL re-fetch (ignores the high-water) so
195
+ // it back-fills any data the cache is missing (e.g. sprint/epic fields).
196
+ router.post('/sync', async (req, res) => {
197
+ const result = await ctx(req).jiraSyncManager.pollOnce(true);
198
+ res.json({ ok: true, upserted: result?.upserted ?? 0 });
199
+ });
200
+ // GET /outbox?state= — list outbox ops (defaults to all).
201
+ router.get('/outbox', (req, res) => {
202
+ const c = ctx(req);
203
+ const state = req.query.state;
204
+ const valid = ['pending', 'inflight', 'done', 'dead'];
205
+ const filter = valid.includes(state) ? state : undefined;
206
+ res.json({ ops: c.jiraSyncManager.listOutbox(filter), counts: c.jiraSyncManager.outboxCounts() });
207
+ });
208
+ // POST /outbox/:id/retry — re-queue a dead-lettered op for a manual retry.
209
+ router.post('/outbox/:id/retry', async (req, res) => {
210
+ const c = ctx(req);
211
+ const id = parseInt(req.params.id, 10);
212
+ if (Number.isNaN(id)) {
213
+ res.status(400).json({ error: 'Invalid op id' });
214
+ return;
215
+ }
216
+ const { retryDeadOutbox } = await Promise.resolve().then(() => __importStar(require('./jira/jira-db')));
217
+ const ok = retryDeadOutbox(c.db, id);
218
+ if (!ok) {
219
+ res.status(404).json({ error: 'Op not found or not in dead state' });
220
+ return;
221
+ }
222
+ void c.jiraSyncManager.drainOnce().catch(() => undefined);
223
+ res.json({ ok: true });
224
+ });
225
+ // POST /specs — Add Spec when the project source is Jira: create the issue in
226
+ // Jira and materialize it locally. Keeps the generic local ticket-create path
227
+ // untouched.
228
+ router.post('/specs', async (req, res) => {
229
+ const c = ctx(req);
230
+ const { title, description, labels, priority, issueType } = req.body ?? {};
231
+ if (!isNonEmptyString(title)) {
232
+ res.status(400).json({ error: 'title is required' });
233
+ return;
234
+ }
235
+ const result = await c.jiraSyncManager.createSpec({
236
+ title: title.trim(),
237
+ description: isNonEmptyString(description) ? description : undefined,
238
+ labels: Array.isArray(labels) ? labels.filter((l) => typeof l === 'string') : undefined,
239
+ priority: isNonEmptyString(priority) ? priority : undefined,
240
+ issueType: isNonEmptyString(issueType) ? issueType : undefined,
241
+ });
242
+ if (!result.ok) {
243
+ res.status(result.status === 401 ? 401 : 400).json({ error: result.error });
244
+ return;
245
+ }
246
+ res.status(201).json({ localId: result.localId, jiraKey: result.jiraKey });
247
+ });
248
+ // POST /specs/:localId/discard — "Move to <discard status>": transition the
249
+ // linked issue to the configured discard status (+ optional reason comment)
250
+ // instead of a destructive delete. Body: { comment?: string }.
251
+ router.post('/specs/:localId/discard', (req, res) => {
252
+ const c = ctx(req);
253
+ const localId = parseInt(req.params.localId, 10);
254
+ if (Number.isNaN(localId)) {
255
+ res.status(400).json({ error: 'Invalid spec id' });
256
+ return;
257
+ }
258
+ const comment = isNonEmptyString(req.body?.comment) ? req.body.comment : null;
259
+ const result = c.jiraSyncManager.discardSpec(localId, comment);
260
+ if (!result.ok) {
261
+ const status = result.reason === 'no-link' ? 404 : 409;
262
+ res.status(status).json({ error: result.reason });
263
+ return;
264
+ }
265
+ res.status(202).json({ ok: true });
266
+ });
267
+ // GET /specs/:localId/details — read-only "Jira details" + "Development" payload
268
+ // for the spec detail modal. Resilient: dev-status failures still return fields.
269
+ router.get('/specs/:localId/details', async (req, res) => {
270
+ const c = ctx(req);
271
+ const localId = parseInt(req.params.localId, 10);
272
+ if (Number.isNaN(localId)) {
273
+ res.status(400).json({ error: 'Invalid spec id' });
274
+ return;
275
+ }
276
+ const result = await c.jiraSyncManager.getSpecDetails(localId);
277
+ if (!result.ok) {
278
+ const status = result.reason === 'no-link' ? 404 : result.reason === 'not-active' ? 409 : (result.status ?? 502);
279
+ res.status(status).json({ error: result.reason });
280
+ return;
281
+ }
282
+ res.json(result.details);
283
+ });
284
+ // GET /links — the spec↔issue map (for the badge / diagnostics).
285
+ router.get('/links', (req, res) => {
286
+ res.json({ links: ctx(req).jiraSyncManager.listLinks() });
287
+ });
288
+ return router;
289
+ }
290
+ function isNonEmptyString(v) {
291
+ return typeof v === 'string' && v.trim().length > 0;
292
+ }
293
+ function sanitizeStatusMap(raw) {
294
+ if (!raw || typeof raw !== 'object')
295
+ return null;
296
+ const states = ['todo', 'in_progress', 'done', 'cancelled'];
297
+ const out = {};
298
+ for (const s of states) {
299
+ const v = raw[s];
300
+ if (typeof v === 'string' && v.trim())
301
+ out[s] = v.trim();
302
+ }
303
+ return Object.keys(out).length > 0 ? out : null;
304
+ }
@@ -24,6 +24,7 @@ const terminal_manager_1 = require("./terminal-manager");
24
24
  const browser_capture_manager_1 = require("./browser-capture-manager");
25
25
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
26
26
  const ticket_store_1 = require("./ticket-store");
27
+ const jira_sync_manager_1 = require("./jira/jira-sync-manager");
27
28
  const rails_store_1 = require("./rails-store");
28
29
  const desktop_db_1 = require("./desktop-db");
29
30
  const config_1 = require("./config");
@@ -115,6 +116,11 @@ class ProjectRegistry {
115
116
  catch { /* ignore */ }
116
117
  // Tear down the embedded browser (closes pages + persistent context).
117
118
  void ctx.browserCaptureManager.shutdown().catch(() => { });
119
+ // Stop the Jira sync poll/drain timers (no children, just intervals).
120
+ try {
121
+ ctx.jiraSyncManager.stop();
122
+ }
123
+ catch { /* ignore */ }
118
124
  // Kill any terminal sessions belonging to this project
119
125
  try {
120
126
  (0, terminal_manager_1.getTerminalManager)().killAllForProject(id);
@@ -201,6 +207,10 @@ class ProjectRegistry {
201
207
  }
202
208
  catch { /* ignore */ }
203
209
  void ctx.browserCaptureManager.shutdown().catch(() => { });
210
+ try {
211
+ ctx.jiraSyncManager.stop();
212
+ }
213
+ catch { /* ignore */ }
204
214
  // Release chokidar watchers + abort in-flight generations so a restart
205
215
  // does not leak handles/children — mirror removeProject()'s per-project teardown.
206
216
  try {
@@ -250,6 +260,14 @@ class ProjectRegistry {
250
260
  catch { /* queue_state table may not exist yet */ }
251
261
  const webhookManager = this._webhookManager;
252
262
  const railJobs = new Map();
263
+ // Jira sync (per-project, inert until a connection is configured). Constructed
264
+ // before QueueManager so the onJobFinished closure can reference it.
265
+ const jiraSyncManager = new jira_sync_manager_1.JiraSyncManager({
266
+ db,
267
+ projectId: project.id,
268
+ projectPath: project.path,
269
+ broadcast: boundBroadcast,
270
+ });
253
271
  const queueManager = new queue_manager_1.QueueManager(boundBroadcast, db, undefined, project.path, {
254
272
  zombieTimeoutMs: projectZombieTimeout,
255
273
  provider: project.provider ?? 'claude',
@@ -318,6 +336,26 @@ class ProjectRegistry {
318
336
  timestamp: ticket.updated_at,
319
337
  });
320
338
  }
339
+ // Jira write-back (inert for non-Jira projects): enqueue the status
340
+ // transition + completion comment per linked ticket. The LOCAL mutation
341
+ // above stays synchronous; only the durable outbox enqueue happens here,
342
+ // wrapped so a Jira failure can never break the job-exit handler.
343
+ if (status === 'completed' || status === 'failed' || status === 'canceled' || status === 'zombie_terminated') {
344
+ try {
345
+ const needsReviewIds = completedTicketIds.filter((tid) => store.tickets[String(tid)]?.needs_review === true);
346
+ jiraSyncManager.onJobOutcome({
347
+ ticketIds: completedTicketIds,
348
+ status,
349
+ jobId,
350
+ costUsd: costUsd ?? null,
351
+ durationMs: jobRow?.duration_ms ?? null,
352
+ needsReviewIds,
353
+ });
354
+ }
355
+ catch (err) {
356
+ console.error('[project-registry] jira onJobOutcome failed:', err);
357
+ }
358
+ }
321
359
  }
322
360
  catch (err) {
323
361
  console.error('[project-registry] failed to apply job outcome to tickets:', err);
@@ -425,6 +463,10 @@ class ProjectRegistry {
425
463
  }
426
464
  const ticketWatcher = new ticket_watcher_1.TicketWatcher(project.path, project.id, boundBroadcast);
427
465
  ticketWatcher.start();
466
+ // Suppress the file-watcher echo for the Jira sync's own writes (the every-60s
467
+ // poll would otherwise trigger a full-board refresh = flicker). Late-bound
468
+ // because the JiraSyncManager is constructed before the watcher.
469
+ jiraSyncManager.setLocalWriteNotifier((rev) => ticketWatcher.notifyDesktopWrite(rev));
428
470
  // BrowserCaptureManager — "Add Spec from browser". Constructed for every
429
471
  // project regardless of the feature flag; the routes + WS endpoint 404 when
430
472
  // the flag is off, and the persistent Chromium context is launched lazily on
@@ -435,7 +477,7 @@ class ProjectRegistry {
435
477
  db,
436
478
  broadcast: boundBroadcast,
437
479
  });
438
- const ctx = { project, db, queueManager, chatManager, setupManager, proposalManager, agentRefineManager, fileSummaryManager, specLauncherManager, ticketWatcher, browserCaptureManager, broadcast: boundBroadcast, railJobs };
480
+ const ctx = { project, db, queueManager, chatManager, setupManager, proposalManager, agentRefineManager, fileSummaryManager, specLauncherManager, ticketWatcher, browserCaptureManager, jiraSyncManager, broadcast: boundBroadcast, railJobs };
439
481
  this._contexts.set(project.id, ctx);
440
482
  return ctx;
441
483
  }
@@ -61,6 +61,27 @@ const tree_kill_1 = __importDefault(require("tree-kill"));
61
61
  const multer_1 = __importDefault(require("multer"));
62
62
  const attachment_manager_1 = require("./attachment-manager");
63
63
  const project_router_helpers_1 = require("./project-router-helpers");
64
+ /**
65
+ * Add Spec on a Jira-backed project: promote the freshly-created LOCAL ticket to
66
+ * a Jira issue (best-effort, server-side). The per-spec `createLocal` escape
67
+ * hatch keeps it local. On any failure the ticket simply stays local and a
68
+ * non-fatal warning is broadcast — a spec is never lost to a Jira error.
69
+ */
70
+ async function maybePromoteSpecToJira(c, ticketId, createLocal, broadcast) {
71
+ // jiraSyncManager is always present in production (constructed per project);
72
+ // guard defensively so partial test contexts and a disabled feature are no-ops.
73
+ if (createLocal || !c.jiraSyncManager?.isActive())
74
+ return;
75
+ try {
76
+ const r = await c.jiraSyncManager.promoteTicketToJira(ticketId);
77
+ if (!r.ok) {
78
+ broadcast({ type: 'jira.sync_error', projectId: c.project.id, reason: `Kept as a local spec — couldn't create it in Jira: ${r.error}` });
79
+ }
80
+ }
81
+ catch (err) {
82
+ console.error('[project-router] jira promote failed:', err);
83
+ }
84
+ }
64
85
  function registerTicketsRoutes(deps) {
65
86
  const { router, registry, ctx, ticketPath } = deps;
66
87
  // ─── Tickets ──────────────────────────────────────────────────────────────────
@@ -490,6 +511,9 @@ function registerTicketsRoutes(deps) {
490
511
  ticket: created, timestamp: new Date().toISOString(),
491
512
  };
492
513
  broadcast(doneMsg);
514
+ // Add Spec → Jira: promote the new ticket to a Jira issue when the
515
+ // project is Jira-backed (unless the user opted to keep it local).
516
+ void maybePromoteSpecToJira(ctx(req), created.id, req.body?.createLocal === true, broadcast);
493
517
  // Quick mode Contract Refine: when toggle is on in the request body
494
518
  // AND the project setting + kill switch permit it, fire the no-resume
495
519
  // Quick refine path asynchronously. Claude-only today — codex
@@ -887,6 +911,9 @@ function registerTicketsRoutes(deps) {
887
911
  }
888
912
  }
889
913
  res.status(201).json({ ticket: created, revision: store.revision });
914
+ // Add Spec (Explore) → Jira: promote the committed ticket to a Jira issue
915
+ // when the project is Jira-backed (unless the user opted to keep it local).
916
+ void maybePromoteSpecToJira(ctx(req), created.id, body.createLocal === true, broadcast);
890
917
  // Fire Contract Refine post-commit (fire-and-forget). Toggle + kill-switch
891
918
  // are checked inside runContractRefine. Claude-only today — codex
892
919
  // contract refine isn't wired (the spawn hardcodes the `claude`
@@ -1329,10 +1356,31 @@ function registerTicketsRoutes(deps) {
1329
1356
  res.status(404).json({ error: 'Ticket not found' });
1330
1357
  return;
1331
1358
  }
1332
- const { broadcast, ticketWatcher } = ctx(req);
1359
+ const { broadcast, ticketWatcher, jiraSyncManager } = ctx(req);
1333
1360
  ticketWatcher.notifyDesktopWrite(store.revision);
1334
1361
  const msg = { type: 'ticket_updated', ticket: updated, projectId: ctx(req).project.id, timestamp: new Date().toISOString() };
1335
1362
  broadcast(msg);
1363
+ // Write the edited fields back to Jira for Jira-backed specs (no-op
1364
+ // otherwise). Uses the FINAL stored values (e.g. acceptance-criteria
1365
+ // folding) for the fields that were actually changed. Never breaks the
1366
+ // local save — a Jira hiccup only fails the enqueue, which is caught.
1367
+ try {
1368
+ const u = updated;
1369
+ const changes = {};
1370
+ if (title !== undefined)
1371
+ changes.title = u.title;
1372
+ if (description !== undefined || acceptanceCriteria !== undefined)
1373
+ changes.description = u.description;
1374
+ if (priority !== undefined)
1375
+ changes.priority = u.priority ?? null;
1376
+ if (labels !== undefined)
1377
+ changes.labels = u.labels;
1378
+ if (Object.keys(changes).length > 0)
1379
+ jiraSyncManager?.onSpecEdited(Number(ticketId), changes);
1380
+ }
1381
+ catch (e) {
1382
+ console.error('[project-router] jira write-back enqueue failed:', e);
1383
+ }
1336
1384
  res.json({ ticket: updated, revision: store.revision });
1337
1385
  }
1338
1386
  catch (err) {
@@ -8,6 +8,7 @@ const rails_router_1 = require("./rails-router");
8
8
  const profiles_router_1 = require("./profiles-router");
9
9
  const plugins_router_1 = require("./plugins-router");
10
10
  const code_explorer_router_1 = require("./code-explorer-router");
11
+ const jira_router_1 = require("./jira-router");
11
12
  const ticket_store_1 = require("./ticket-store");
12
13
  const project_router_jobs_1 = require("./project-router-jobs");
13
14
  const project_router_spending_1 = require("./project-router-spending");
@@ -74,6 +75,9 @@ function createProjectRouter(registry) {
74
75
  // Mount plugins router under each project (per-project marketplace)
75
76
  const pluginsRouter = (0, plugins_router_1.createPluginsRouter)();
76
77
  router.use('/:projectId/plugins', pluginsRouter);
78
+ // Mount Jira router under each project (per-project Jira board sync)
79
+ const jiraRouter = (0, jira_router_1.createJiraRouter)();
80
+ router.use('/:projectId/jira', jiraRouter);
77
81
  // Mount Code-Explorer router. FileSummaryManager comes from ProjectContext.
78
82
  const codeRouterByCtx = new WeakMap();
79
83
  router.use('/:projectId/code', (req, res, next) => {
@@ -270,6 +270,12 @@ function createRailsRouter() {
270
270
  });
271
271
  jobIds.push(job.id);
272
272
  c.railJobs.set(job.id, { railIndex, mode, ticketIds: [ticketId] });
273
+ // Jira write-back: push In Progress for any Jira-linked ticket (inert
274
+ // for non-Jira projects). Best-effort — never blocks the launch.
275
+ try {
276
+ c.jiraSyncManager.onRailLaunch([ticketId], job.id);
277
+ }
278
+ catch { /* non-fatal */ }
273
279
  }
274
280
  jobId = jobIds[0];
275
281
  const startMsg = {
@@ -291,6 +297,12 @@ function createRailsRouter() {
291
297
  const job = c.queueManager.enqueue(command, 'normal', { profileName: resolvedProfile, provider: railProvider });
292
298
  jobId = job.id;
293
299
  c.railJobs.set(jobId, { railIndex, mode, ticketIds: [...rail.ticketIds] });
300
+ // Jira write-back: push In Progress for any Jira-linked ticket (inert for
301
+ // non-Jira projects). Best-effort — never blocks the launch.
302
+ try {
303
+ c.jiraSyncManager.onRailLaunch([...rail.ticketIds], jobId);
304
+ }
305
+ catch { /* non-fatal */ }
294
306
  const startMsg = {
295
307
  type: 'rail.job_started',
296
308
  projectId: c.project.id,
@@ -1 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{Ct as t,Jt as n,M as r,Mt as i,Nt as a,Pt as o,nn as s,ot as c,q as l,st as u,vt as d,xt as f,yt as p}from"./index-CGHKpC-N.js";var m=p(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),h=e(s(),1);function g(e){let t=new Set;return e.filter(e=>{let n=`${e.type}:${e.jobId}`;return t.has(n)?!1:(t.add(n),!0)})}function _({activeProjectId:e,limit:n=50}){let[r,a]=(0,h.useState)([]),[s,c]=(0,h.useState)(!1),[l,u]=(0,h.useState)(!0),d=(0,h.useRef)(e);(0,h.useEffect)(()=>{d.current=e},[e]);let{registerHandler:f,unregisterHandler:p}=i();(0,h.useEffect)(()=>{if(!e){a([]),u(!0);return}let t=!1;c(!0),a([]),u(!0);async function r(){let e=o();try{let r=await fetch(`${e}/activity?limit=${n}`);if(!r.ok||t)return;let i=await r.json();if(t)return;a(i),u(i.length===n)}catch{}finally{t||c(!1)}}return r(),()=>{t=!0}},[e,n]);let m=(0,h.useCallback)(async()=>{if(s||!l)return;c(!0);let e=o();try{a(t=>{let r=t[t.length-1];if(!r)return t;let i=encodeURIComponent(r.timestamp);return fetch(`${e}/activity?limit=${n}&before=${i}`).then(e=>e.json()).then(e=>{a(t=>g([...t,...e])),u(e.length===n),c(!1)}).catch(()=>c(!1)),t})}catch{c(!1)}},[s,l,n]),_=(0,h.useCallback)(e=>{let n=e,r=d.current;if(!(!n||typeof n.type!=`string`)&&!(n.projectId&&n.projectId!==r)){if(n.type===`queue`&&Array.isArray(n.jobs)){let e=[];for(let t of n.jobs){let n=t.status===`completed`?`job_completed`:t.status===`failed`?`job_failed`:t.status===`canceled`?`job_canceled`:`job_started`;e.push({id:`${n}:${t.id}`,type:n,jobId:t.id,jobCommand:t.command??``,timestamp:t.startedAt??new Date().toISOString(),summary:`${n.replace(`_`,` `)}: ${t.command??``}`,costUsd:null})}e.length>0&&a(t=>g([...e,...t]))}if(n.type===`phase`&&n.phase&&n.state&&n.timestamp){let e={id:`phase:${n.phase}:${n.state}:${n.timestamp}`,type:`job_started`,jobId:`phase-${n.phase}`,jobCommand:t.t(`activity:feed.phaseCommand`,{phase:n.phase,state:n.state}),timestamp:n.timestamp,summary:`Phase ${n.phase} is ${n.state}`,costUsd:null};a(t=>g([e,...t]))}}},[]);return(0,h.useLayoutEffect)(()=>(f(`activity`,_),()=>p(`activity`)),[_,f,p]),{items:r,loading:s,hasMore:l,loadMore:m}}var v=a();function y(e,t){let n=Date.now()-new Date(e).getTime(),r=Math.floor(n/1e3);if(r<60)return t(`feed.relativeTime.seconds`,{value:r});let i=Math.floor(r/60);if(i<60)return t(`feed.relativeTime.minutes`,{value:i});let a=Math.floor(i/60);return a<24?t(`feed.relativeTime.hours`,{value:a}):t(`feed.relativeTime.days`,{value:Math.floor(a/24)})}function b({type:e}){switch(e){case`job_completed`:return(0,v.jsx)(u,{className:`w-4 h-4 text-green-500 flex-shrink-0`});case`job_failed`:return(0,v.jsx)(c,{className:`w-4 h-4 text-red-500 flex-shrink-0`});case`job_canceled`:return(0,v.jsx)(m,{className:`w-4 h-4 text-muted-foreground flex-shrink-0`});default:return(0,v.jsx)(r,{className:`w-4 h-4 text-blue-500 flex-shrink-0`})}}function x(e,t){switch(e){case`job_completed`:return t(`common:status.completed`);case`job_failed`:return t(`common:status.failed`);case`job_canceled`:return t(`common:status.canceled`);default:return t(`feed.typeStarted`)}}function S(e){switch(e){case`job_completed`:return`text-green-500`;case`job_failed`:return`text-red-500`;case`job_canceled`:return`text-muted-foreground`;default:return`text-blue-500`}}function C(){let{t:e}=n(`activity`),{activeProjectId:t}=f(),{items:r,loading:i,hasMore:a,loadMore:o}=_({activeProjectId:t}),s=(0,h.useRef)(null);return(0,h.useEffect)(()=>{let e=s.current;if(!e)return;let t=new IntersectionObserver(e=>{e[0].isIntersecting&&a&&!i&&o()},{threshold:.1});return t.observe(e),()=>t.disconnect()},[a,i,o]),(0,v.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden`,children:[(0,v.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 border-b border-border bg-background/50`,children:[(0,v.jsx)(d,{className:`w-4 h-4 text-muted-foreground`}),(0,v.jsx)(`h1`,{className:`text-sm font-medium`,children:e(`feed.title`)})]}),(0,v.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[i&&r.length===0?(0,v.jsx)(`div`,{className:`flex items-center justify-center h-32`,children:(0,v.jsx)(l,{className:`w-4 h-4 animate-spin text-muted-foreground`})}):r.length===0?(0,v.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-48 gap-2 text-muted-foreground`,children:[(0,v.jsx)(d,{className:`w-8 h-8 opacity-40`}),(0,v.jsx)(`p`,{className:`text-sm`,children:e(`feed.emptyTitle`)}),(0,v.jsx)(`p`,{className:`text-xs opacity-70`,children:e(`feed.emptyHint`)})]}):(0,v.jsx)(`ul`,{className:`divide-y divide-border/50`,children:r.map(t=>(0,v.jsxs)(`li`,{className:`flex items-center gap-3 px-4 py-2.5 hover:bg-accent/30 transition-colors`,children:[(0,v.jsx)(b,{type:t.type}),(0,v.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,v.jsx)(`p`,{className:`text-xs text-foreground truncate`,title:t.jobCommand,children:t.jobCommand}),(0,v.jsxs)(`div`,{className:`flex items-center gap-2 mt-0.5`,children:[(0,v.jsx)(`span`,{className:`text-xs font-medium ${S(t.type)}`,children:x(t.type,e)}),t.costUsd!=null&&(0,v.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`$`,t.costUsd.toFixed(4)]})]})]}),(0,v.jsx)(`span`,{className:`text-xs text-muted-foreground flex-shrink-0 tabular-nums`,children:y(t.timestamp,e)})]},`${t.type}:${t.jobId}`))}),(0,v.jsx)(`div`,{ref:s,className:`h-1`}),i&&r.length>0&&(0,v.jsx)(`div`,{className:`flex justify-center py-3`,children:(0,v.jsx)(l,{className:`w-4 h-4 animate-spin text-muted-foreground`})}),!a&&r.length>0&&(0,v.jsx)(`p`,{className:`text-center text-xs text-muted-foreground py-3`,children:e(`feed.allLoaded`)})]})]})}export{C as default};