relay-ide 0.1.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 (506) hide show
  1. package/README.md +259 -0
  2. package/dist/bin/claude-remote-cli.js +390 -0
  3. package/dist/bin/relay-ide.js +390 -0
  4. package/dist/frontend/assets/abap-BdImnpbu.js +1 -0
  5. package/dist/frontend/assets/actionscript-3-CoDkCxhg.js +1 -0
  6. package/dist/frontend/assets/ada-bCR0ucgS.js +1 -0
  7. package/dist/frontend/assets/andromeeda-C4gqWexZ.js +1 -0
  8. package/dist/frontend/assets/angular-html-DA-rfuFy.js +1 -0
  9. package/dist/frontend/assets/angular-ts-BrjP3tb8.js +1 -0
  10. package/dist/frontend/assets/apache-Pmp26Uib.js +1 -0
  11. package/dist/frontend/assets/apex-D8_7TLub.js +1 -0
  12. package/dist/frontend/assets/apl-CORt7UWP.js +1 -0
  13. package/dist/frontend/assets/applescript-Co6uUVPk.js +1 -0
  14. package/dist/frontend/assets/ara-BRHolxvo.js +1 -0
  15. package/dist/frontend/assets/asciidoc-Ve4PFQV2.js +1 -0
  16. package/dist/frontend/assets/asm-D_Q5rh1f.js +1 -0
  17. package/dist/frontend/assets/astro-HNnZUWAn.js +1 -0
  18. package/dist/frontend/assets/aurora-x-D-2ljcwZ.js +1 -0
  19. package/dist/frontend/assets/awk-DMzUqQB5.js +1 -0
  20. package/dist/frontend/assets/ayu-dark-DYE7WIF3.js +1 -0
  21. package/dist/frontend/assets/ayu-light-BA47KaF1.js +1 -0
  22. package/dist/frontend/assets/ayu-mirage-32ctXXKs.js +1 -0
  23. package/dist/frontend/assets/ballerina-BFfxhgS-.js +1 -0
  24. package/dist/frontend/assets/bat-BkioyH1T.js +1 -0
  25. package/dist/frontend/assets/beancount-k_qm7-4y.js +1 -0
  26. package/dist/frontend/assets/berry-uYugtg8r.js +1 -0
  27. package/dist/frontend/assets/bibtex-CHM0blh-.js +1 -0
  28. package/dist/frontend/assets/bicep-Bmn6On1c.js +1 -0
  29. package/dist/frontend/assets/bird2-BIv1doCn.js +1 -0
  30. package/dist/frontend/assets/blade-BjGOyj-B.js +1 -0
  31. package/dist/frontend/assets/bsl-BO_Y6i37.js +1 -0
  32. package/dist/frontend/assets/c-BIGW1oBm.js +1 -0
  33. package/dist/frontend/assets/c3-eo99z4R2.js +1 -0
  34. package/dist/frontend/assets/cadence-Bv_4Rxtq.js +1 -0
  35. package/dist/frontend/assets/cairo-KRGpt6FW.js +1 -0
  36. package/dist/frontend/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  37. package/dist/frontend/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  38. package/dist/frontend/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  39. package/dist/frontend/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  40. package/dist/frontend/assets/clarity-D53aC0YG.js +1 -0
  41. package/dist/frontend/assets/clojure-P80f7IUj.js +1 -0
  42. package/dist/frontend/assets/cmake-D1j8_8rp.js +1 -0
  43. package/dist/frontend/assets/cobol-nBiQ_Alo.js +1 -0
  44. package/dist/frontend/assets/codeowners-Bp6g37R7.js +1 -0
  45. package/dist/frontend/assets/codeql-DsOJ9woJ.js +1 -0
  46. package/dist/frontend/assets/coffee-Ch7k5sss.js +1 -0
  47. package/dist/frontend/assets/common-lisp-Cg-RD9OK.js +1 -0
  48. package/dist/frontend/assets/coq-DkFqJrB1.js +1 -0
  49. package/dist/frontend/assets/cpp-CofmeUqb.js +1 -0
  50. package/dist/frontend/assets/crystal-DNxU26gB.js +1 -0
  51. package/dist/frontend/assets/csharp-COcwbKMJ.js +1 -0
  52. package/dist/frontend/assets/css-CLj8gQPS.js +1 -0
  53. package/dist/frontend/assets/csv-fuZLfV_i.js +1 -0
  54. package/dist/frontend/assets/cue-D82EKSYY.js +1 -0
  55. package/dist/frontend/assets/cypher-COkxafJQ.js +1 -0
  56. package/dist/frontend/assets/d-85-TOEBH.js +1 -0
  57. package/dist/frontend/assets/dark-plus-C3mMm8J8.js +1 -0
  58. package/dist/frontend/assets/dart-bE4Kk8sk.js +1 -0
  59. package/dist/frontend/assets/dax-CEL-wOlO.js +1 -0
  60. package/dist/frontend/assets/desktop-BmXAJ9_W.js +1 -0
  61. package/dist/frontend/assets/diff-D97Zzqfu.js +1 -0
  62. package/dist/frontend/assets/docker-BcOcwvcX.js +1 -0
  63. package/dist/frontend/assets/dotenv-Da5cRb03.js +1 -0
  64. package/dist/frontend/assets/dracula-BzJJZx-M.js +1 -0
  65. package/dist/frontend/assets/dracula-soft-BXkSAIEj.js +1 -0
  66. package/dist/frontend/assets/dream-maker-BtqSS_iP.js +1 -0
  67. package/dist/frontend/assets/edge-FbVlp4U3.js +1 -0
  68. package/dist/frontend/assets/elixir-CkH2-t6x.js +1 -0
  69. package/dist/frontend/assets/elm-DbKCFpqz.js +1 -0
  70. package/dist/frontend/assets/emacs-lisp-CXvaQtF9.js +1 -0
  71. package/dist/frontend/assets/erb-BYCe7drp.js +1 -0
  72. package/dist/frontend/assets/erlang-DsQrWhSR.js +1 -0
  73. package/dist/frontend/assets/everforest-dark-BgDCqdQA.js +1 -0
  74. package/dist/frontend/assets/everforest-light-C8M2exoo.js +1 -0
  75. package/dist/frontend/assets/fennel-BYunw83y.js +1 -0
  76. package/dist/frontend/assets/fish-BvzEVeQv.js +1 -0
  77. package/dist/frontend/assets/fluent-C4IJs8-o.js +1 -0
  78. package/dist/frontend/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  79. package/dist/frontend/assets/fortran-free-form-BxgE0vQu.js +1 -0
  80. package/dist/frontend/assets/fsharp-CXgrBDvD.js +1 -0
  81. package/dist/frontend/assets/gdresource-BOOCDP_w.js +1 -0
  82. package/dist/frontend/assets/gdscript-C5YyOfLZ.js +1 -0
  83. package/dist/frontend/assets/gdshader-DkwncUOv.js +1 -0
  84. package/dist/frontend/assets/genie-D0YGMca9.js +1 -0
  85. package/dist/frontend/assets/gherkin-DyxjwDmM.js +1 -0
  86. package/dist/frontend/assets/git-commit-F4YmCXRG.js +1 -0
  87. package/dist/frontend/assets/git-rebase-r7XF79zn.js +1 -0
  88. package/dist/frontend/assets/github-dark-DHJKELXO.js +1 -0
  89. package/dist/frontend/assets/github-dark-default-Cuk6v7N8.js +1 -0
  90. package/dist/frontend/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  91. package/dist/frontend/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  92. package/dist/frontend/assets/github-light-DAi9KRSo.js +1 -0
  93. package/dist/frontend/assets/github-light-default-D7oLnXFd.js +1 -0
  94. package/dist/frontend/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  95. package/dist/frontend/assets/gleam-BspZqrRM.js +1 -0
  96. package/dist/frontend/assets/glimmer-js-ByusRIyA.js +1 -0
  97. package/dist/frontend/assets/glimmer-ts-BfAWNZQY.js +1 -0
  98. package/dist/frontend/assets/glsl-DplSGwfg.js +1 -0
  99. package/dist/frontend/assets/gn-n2N0HUVH.js +1 -0
  100. package/dist/frontend/assets/gnuplot-DdkO51Og.js +1 -0
  101. package/dist/frontend/assets/go-C27-OAKa.js +1 -0
  102. package/dist/frontend/assets/graphql-ChdNCCLP.js +1 -0
  103. package/dist/frontend/assets/groovy-gcz8RCvz.js +1 -0
  104. package/dist/frontend/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  105. package/dist/frontend/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  106. package/dist/frontend/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  107. package/dist/frontend/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  108. package/dist/frontend/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  109. package/dist/frontend/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  110. package/dist/frontend/assets/hack-i7_Ulhet.js +1 -0
  111. package/dist/frontend/assets/haml-D5jkg6IW.js +1 -0
  112. package/dist/frontend/assets/handlebars-BpdQsYii.js +1 -0
  113. package/dist/frontend/assets/haskell-Df6bDoY_.js +1 -0
  114. package/dist/frontend/assets/haxe-CzTSHFRz.js +1 -0
  115. package/dist/frontend/assets/hcl-BWvSN4gD.js +1 -0
  116. package/dist/frontend/assets/hjson-D5-asLiD.js +1 -0
  117. package/dist/frontend/assets/hlsl-D3lLCCz7.js +1 -0
  118. package/dist/frontend/assets/horizon-BUw7H-hv.js +1 -0
  119. package/dist/frontend/assets/horizon-bright-CUuTKBJd.js +1 -0
  120. package/dist/frontend/assets/houston-DnULxvSX.js +1 -0
  121. package/dist/frontend/assets/html-derivative-DlHx6ybY.js +1 -0
  122. package/dist/frontend/assets/html-pp8916En.js +1 -0
  123. package/dist/frontend/assets/http-jrhK8wxY.js +1 -0
  124. package/dist/frontend/assets/hurl-irOxFIW8.js +1 -0
  125. package/dist/frontend/assets/hxml-Bvhsp5Yf.js +1 -0
  126. package/dist/frontend/assets/hy-DFXneXwc.js +1 -0
  127. package/dist/frontend/assets/imba-DGztddWO.js +1 -0
  128. package/dist/frontend/assets/ini-BEwlwnbL.js +1 -0
  129. package/dist/frontend/assets/java-CylS5w8V.js +1 -0
  130. package/dist/frontend/assets/javascript-wDzz0qaB.js +1 -0
  131. package/dist/frontend/assets/jinja-f2NsQr07.js +1 -0
  132. package/dist/frontend/assets/jison-wvAkD_A8.js +1 -0
  133. package/dist/frontend/assets/json-Cp-IABpG.js +1 -0
  134. package/dist/frontend/assets/json5-C9tS-k6U.js +1 -0
  135. package/dist/frontend/assets/jsonc-Des-eS-w.js +1 -0
  136. package/dist/frontend/assets/jsonl-DcaNXYhu.js +1 -0
  137. package/dist/frontend/assets/jsonnet-DFQXde-d.js +1 -0
  138. package/dist/frontend/assets/jssm-C2t-YnRu.js +1 -0
  139. package/dist/frontend/assets/jsx-g9-lgVsj.js +1 -0
  140. package/dist/frontend/assets/julia-CxzCAyBv.js +1 -0
  141. package/dist/frontend/assets/just-VxiPbLrw.js +1 -0
  142. package/dist/frontend/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  143. package/dist/frontend/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  144. package/dist/frontend/assets/kanagawa-wave-DWedfzmr.js +1 -0
  145. package/dist/frontend/assets/kdl-DV7GczEv.js +1 -0
  146. package/dist/frontend/assets/kotlin-BdnUsdx6.js +1 -0
  147. package/dist/frontend/assets/kusto-wEQ09or8.js +1 -0
  148. package/dist/frontend/assets/laserwave-DUszq2jm.js +1 -0
  149. package/dist/frontend/assets/latex-CWtU0Tv5.js +1 -0
  150. package/dist/frontend/assets/lean-BZvkOJ9d.js +1 -0
  151. package/dist/frontend/assets/less-B1dDrJ26.js +1 -0
  152. package/dist/frontend/assets/light-plus-B7mTdjB0.js +1 -0
  153. package/dist/frontend/assets/liquid-C0sCDyMI.js +1 -0
  154. package/dist/frontend/assets/llvm-DjAJT7YJ.js +1 -0
  155. package/dist/frontend/assets/log-2UxHyX5q.js +1 -0
  156. package/dist/frontend/assets/logo-BtOb2qkB.js +1 -0
  157. package/dist/frontend/assets/lua-BaeVxFsk.js +1 -0
  158. package/dist/frontend/assets/luau-C-HG3fhB.js +1 -0
  159. package/dist/frontend/assets/main-CL5_Wlhv.css +32 -0
  160. package/dist/frontend/assets/main-Czet4Z1x.js +371 -0
  161. package/dist/frontend/assets/make-CHLpvVh8.js +1 -0
  162. package/dist/frontend/assets/markdown-Cvjx9yec.js +1 -0
  163. package/dist/frontend/assets/marko-DjSrsDqO.js +1 -0
  164. package/dist/frontend/assets/material-theme-D5KoaKCx.js +1 -0
  165. package/dist/frontend/assets/material-theme-darker-BfHTSMKl.js +1 -0
  166. package/dist/frontend/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  167. package/dist/frontend/assets/material-theme-ocean-CyktbL80.js +1 -0
  168. package/dist/frontend/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  169. package/dist/frontend/assets/matlab-D7o27uSR.js +1 -0
  170. package/dist/frontend/assets/mdc-DTYItulj.js +1 -0
  171. package/dist/frontend/assets/mdx-Cmh6b_Ma.js +1 -0
  172. package/dist/frontend/assets/mermaid-mWjccvbQ.js +1 -0
  173. package/dist/frontend/assets/min-dark-CafNBF8u.js +1 -0
  174. package/dist/frontend/assets/min-light-CTRr51gU.js +1 -0
  175. package/dist/frontend/assets/mipsasm-CKIfxQSi.js +1 -0
  176. package/dist/frontend/assets/mojo-rZm6bMo-.js +1 -0
  177. package/dist/frontend/assets/monokai-D4h5O-jR.js +1 -0
  178. package/dist/frontend/assets/moonbit-_H4v1dQx.js +1 -0
  179. package/dist/frontend/assets/move-IF9eRakj.js +1 -0
  180. package/dist/frontend/assets/narrat-DRg8JJMk.js +1 -0
  181. package/dist/frontend/assets/nextflow-C-mBbutL.js +1 -0
  182. package/dist/frontend/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  183. package/dist/frontend/assets/nginx-BpAMiNFr.js +1 -0
  184. package/dist/frontend/assets/night-owl-C39BiMTA.js +1 -0
  185. package/dist/frontend/assets/night-owl-light-CMTm3GFP.js +1 -0
  186. package/dist/frontend/assets/nim-BIad80T-.js +1 -0
  187. package/dist/frontend/assets/nix-CwoSXNpI.js +1 -0
  188. package/dist/frontend/assets/nord-Ddv68eIx.js +1 -0
  189. package/dist/frontend/assets/nushell-Cz2AlsmD.js +1 -0
  190. package/dist/frontend/assets/objective-c-DXmwc3jG.js +1 -0
  191. package/dist/frontend/assets/objective-cpp-CLxacb5B.js +1 -0
  192. package/dist/frontend/assets/ocaml-C0hk2d4L.js +1 -0
  193. package/dist/frontend/assets/odin-BBf5iR-q.js +1 -0
  194. package/dist/frontend/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  195. package/dist/frontend/assets/one-light-C3Wv6jpd.js +1 -0
  196. package/dist/frontend/assets/openscad-C4EeE6gA.js +1 -0
  197. package/dist/frontend/assets/pascal-D93ZcfNL.js +1 -0
  198. package/dist/frontend/assets/perl-NvoQZIq0.js +1 -0
  199. package/dist/frontend/assets/php-R6g_5hLQ.js +1 -0
  200. package/dist/frontend/assets/pkl-u5AG7uiY.js +1 -0
  201. package/dist/frontend/assets/plastic-3e1v2bzS.js +1 -0
  202. package/dist/frontend/assets/plsql-ChMvpjG-.js +1 -0
  203. package/dist/frontend/assets/po-BTJTHyun.js +1 -0
  204. package/dist/frontend/assets/poimandres-CS3Unz2-.js +1 -0
  205. package/dist/frontend/assets/polar-C0HS_06l.js +1 -0
  206. package/dist/frontend/assets/postcss-CXtECtnM.js +1 -0
  207. package/dist/frontend/assets/powerquery-CEu0bR-o.js +1 -0
  208. package/dist/frontend/assets/powershell-Dpen1YoG.js +1 -0
  209. package/dist/frontend/assets/prisma-Dd19v3D-.js +1 -0
  210. package/dist/frontend/assets/prolog-CbFg5uaA.js +1 -0
  211. package/dist/frontend/assets/proto-C7zT0LnQ.js +1 -0
  212. package/dist/frontend/assets/pug-DKIMFp6K.js +1 -0
  213. package/dist/frontend/assets/puppet-BMWR74SV.js +1 -0
  214. package/dist/frontend/assets/purescript-CklMAg4u.js +1 -0
  215. package/dist/frontend/assets/python-B6aJPvgy.js +1 -0
  216. package/dist/frontend/assets/qml-3beO22l8.js +1 -0
  217. package/dist/frontend/assets/qmldir-C8lEn-DE.js +1 -0
  218. package/dist/frontend/assets/qss-IeuSbFQv.js +1 -0
  219. package/dist/frontend/assets/r-Dspwwk_N.js +1 -0
  220. package/dist/frontend/assets/racket-BqYA7rlc.js +1 -0
  221. package/dist/frontend/assets/raku-DXvB9xmW.js +1 -0
  222. package/dist/frontend/assets/razor-BDqjjVU7.js +1 -0
  223. package/dist/frontend/assets/red-bN70gL4F.js +1 -0
  224. package/dist/frontend/assets/reg-C-SQnVFl.js +1 -0
  225. package/dist/frontend/assets/regexp-CDVJQ6XC.js +1 -0
  226. package/dist/frontend/assets/rel-C3B-1QV4.js +1 -0
  227. package/dist/frontend/assets/riscv-BM1_JUlF.js +1 -0
  228. package/dist/frontend/assets/ron-D8l8udqQ.js +1 -0
  229. package/dist/frontend/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  230. package/dist/frontend/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  231. package/dist/frontend/assets/rose-pine-qdsjHGoJ.js +1 -0
  232. package/dist/frontend/assets/rosmsg-BJDFO7_C.js +1 -0
  233. package/dist/frontend/assets/rst-CRjBmOyv.js +1 -0
  234. package/dist/frontend/assets/ruby-Wjq7vjNf.js +1 -0
  235. package/dist/frontend/assets/rust-B1yitclQ.js +1 -0
  236. package/dist/frontend/assets/sas-cz2c8ADy.js +1 -0
  237. package/dist/frontend/assets/sass-Cj5Yp3dK.js +1 -0
  238. package/dist/frontend/assets/scala-C151Ov-r.js +1 -0
  239. package/dist/frontend/assets/scheme-C98Dy4si.js +1 -0
  240. package/dist/frontend/assets/scss-D5BDwBP9.js +1 -0
  241. package/dist/frontend/assets/sdbl-DVxCFoDh.js +1 -0
  242. package/dist/frontend/assets/shaderlab-Dg9Lc6iA.js +1 -0
  243. package/dist/frontend/assets/shellscript-Yzrsuije.js +1 -0
  244. package/dist/frontend/assets/shellsession-BADoaaVG.js +1 -0
  245. package/dist/frontend/assets/slack-dark-BthQWCQV.js +1 -0
  246. package/dist/frontend/assets/slack-ochin-DqwNpetd.js +1 -0
  247. package/dist/frontend/assets/smalltalk-BERRCDM3.js +1 -0
  248. package/dist/frontend/assets/snazzy-light-Bw305WKR.js +1 -0
  249. package/dist/frontend/assets/solarized-dark-DXbdFlpD.js +1 -0
  250. package/dist/frontend/assets/solarized-light-L9t79GZl.js +1 -0
  251. package/dist/frontend/assets/solidity-rGO070M0.js +1 -0
  252. package/dist/frontend/assets/soy-8wufbnw4.js +1 -0
  253. package/dist/frontend/assets/sparql-rVzFXLq3.js +1 -0
  254. package/dist/frontend/assets/splunk-BtCnVYZw.js +1 -0
  255. package/dist/frontend/assets/sql-BLtJtn59.js +1 -0
  256. package/dist/frontend/assets/ssh-config-_ykCGR6B.js +1 -0
  257. package/dist/frontend/assets/stata-BH5u7GGu.js +1 -0
  258. package/dist/frontend/assets/stylus-BEDo0Tqx.js +1 -0
  259. package/dist/frontend/assets/surrealql-Bq5Q-fJD.js +1 -0
  260. package/dist/frontend/assets/svelte-Cy7k_4gC.js +1 -0
  261. package/dist/frontend/assets/swift-D82vCrfD.js +1 -0
  262. package/dist/frontend/assets/synthwave-84-CbfX1IO0.js +1 -0
  263. package/dist/frontend/assets/system-verilog-CnnmHF94.js +1 -0
  264. package/dist/frontend/assets/systemd-4A_iFExJ.js +1 -0
  265. package/dist/frontend/assets/talonscript-CkByrt1z.js +1 -0
  266. package/dist/frontend/assets/tasl-QIJgUcNo.js +1 -0
  267. package/dist/frontend/assets/tcl-dwOrl1Do.js +1 -0
  268. package/dist/frontend/assets/templ-DhtptRzy.js +1 -0
  269. package/dist/frontend/assets/terraform-BETggiCN.js +1 -0
  270. package/dist/frontend/assets/tex-idrVyKtj.js +1 -0
  271. package/dist/frontend/assets/tokyo-night-hegEt444.js +1 -0
  272. package/dist/frontend/assets/toml-vGWfd6FD.js +1 -0
  273. package/dist/frontend/assets/ts-tags-DQrlYJgV.js +1 -0
  274. package/dist/frontend/assets/tsv-B_m7g4N7.js +1 -0
  275. package/dist/frontend/assets/tsx-COt5Ahok.js +1 -0
  276. package/dist/frontend/assets/turtle-BsS91CYL.js +1 -0
  277. package/dist/frontend/assets/twig-xg9kU7Mw.js +1 -0
  278. package/dist/frontend/assets/typescript-BPQ3VLAy.js +1 -0
  279. package/dist/frontend/assets/typespec-CAFt9gP4.js +1 -0
  280. package/dist/frontend/assets/typst-DHCkPAjA.js +1 -0
  281. package/dist/frontend/assets/v-BcVCzyr7.js +1 -0
  282. package/dist/frontend/assets/vala-CsfeWuGM.js +1 -0
  283. package/dist/frontend/assets/vb-D17OF-Vu.js +1 -0
  284. package/dist/frontend/assets/verilog-BQ8w6xss.js +1 -0
  285. package/dist/frontend/assets/vesper-DU1UobuO.js +1 -0
  286. package/dist/frontend/assets/vhdl-CeAyd5Ju.js +1 -0
  287. package/dist/frontend/assets/viml-CJc9bBzg.js +1 -0
  288. package/dist/frontend/assets/vitesse-black-Bkuqu6BP.js +1 -0
  289. package/dist/frontend/assets/vitesse-dark-D0r3Knsf.js +1 -0
  290. package/dist/frontend/assets/vitesse-light-CVO1_9PV.js +1 -0
  291. package/dist/frontend/assets/vue-D2xRrEX4.js +1 -0
  292. package/dist/frontend/assets/vue-html-AaS7Mt5G.js +1 -0
  293. package/dist/frontend/assets/vue-vine-BoDAl6tE.js +1 -0
  294. package/dist/frontend/assets/vyper-CDx5xZoG.js +1 -0
  295. package/dist/frontend/assets/wasm-CG6Dc4jp.js +1 -0
  296. package/dist/frontend/assets/wasm-MzD3tlZU.js +1 -0
  297. package/dist/frontend/assets/wenyan-BV7otONQ.js +1 -0
  298. package/dist/frontend/assets/wgsl-Dx-B1_4e.js +1 -0
  299. package/dist/frontend/assets/wikitext-BhOHFoWU.js +1 -0
  300. package/dist/frontend/assets/wit-5i3qLPDT.js +1 -0
  301. package/dist/frontend/assets/wolfram-lXgVvXCa.js +1 -0
  302. package/dist/frontend/assets/xml-sdJ4AIDG.js +1 -0
  303. package/dist/frontend/assets/xsl-CtQFsRM5.js +1 -0
  304. package/dist/frontend/assets/yaml-Buea-lGh.js +1 -0
  305. package/dist/frontend/assets/zenscript-DVFEvuxE.js +1 -0
  306. package/dist/frontend/assets/zig-VOosw3JB.js +1 -0
  307. package/dist/frontend/icon-192.png +0 -0
  308. package/dist/frontend/icon-512.png +0 -0
  309. package/dist/frontend/icon.svg +8 -0
  310. package/dist/frontend/index.html +30 -0
  311. package/dist/frontend/manifest.json +25 -0
  312. package/dist/frontend/sw.js +66 -0
  313. package/dist/server/agent-events.js +39 -0
  314. package/dist/server/analytics.js +885 -0
  315. package/dist/server/auth.js +65 -0
  316. package/dist/server/belayer/executor.js +200 -0
  317. package/dist/server/belayer/intake.js +27 -0
  318. package/dist/server/belayer/pipeline.js +97 -0
  319. package/dist/server/belayer/pr-lifecycle.js +69 -0
  320. package/dist/server/belayer/prompts.js +154 -0
  321. package/dist/server/belayer/types.js +23 -0
  322. package/dist/server/branch-linker.js +137 -0
  323. package/dist/server/browser-content.js +145 -0
  324. package/dist/server/clipboard.js +63 -0
  325. package/dist/server/codex-hooks-adapter.js +93 -0
  326. package/dist/server/config.js +325 -0
  327. package/dist/server/gh-routes.js +163 -0
  328. package/dist/server/gh.js +276 -0
  329. package/dist/server/git-routes.js +154 -0
  330. package/dist/server/git.js +694 -0
  331. package/dist/server/github-app.js +218 -0
  332. package/dist/server/github-graphql.js +178 -0
  333. package/dist/server/hooks.js +373 -0
  334. package/dist/server/index.js +1549 -0
  335. package/dist/server/integration-github.js +137 -0
  336. package/dist/server/integration-jira.js +210 -0
  337. package/dist/server/integration-linear.js +176 -0
  338. package/dist/server/logger.js +18 -0
  339. package/dist/server/mobile-input-pipeline.js +129 -0
  340. package/dist/server/opencode-relay.js +53 -0
  341. package/dist/server/org-dashboard.js +241 -0
  342. package/dist/server/output-parsers/claude-parser.js +56 -0
  343. package/dist/server/output-parsers/codex-parser.js +13 -0
  344. package/dist/server/output-parsers/index.js +14 -0
  345. package/dist/server/output-parsers/null-parser.js +12 -0
  346. package/dist/server/output-parsers/opencode-parser.js +77 -0
  347. package/dist/server/pty-handler.js +586 -0
  348. package/dist/server/push.js +84 -0
  349. package/dist/server/review-poller.js +237 -0
  350. package/dist/server/sdk-handler.js +539 -0
  351. package/dist/server/service.js +189 -0
  352. package/dist/server/sessions.js +638 -0
  353. package/dist/server/telemetry.js +236 -0
  354. package/dist/server/ticket-transitions.js +166 -0
  355. package/dist/server/types.js +146 -0
  356. package/dist/server/utils.js +23 -0
  357. package/dist/server/watcher.js +661 -0
  358. package/dist/server/webhook-manager.js +547 -0
  359. package/dist/server/webhooks.js +73 -0
  360. package/dist/server/workspace-groups.js +363 -0
  361. package/dist/server/workspaces.js +1207 -0
  362. package/dist/server/ws.js +192 -0
  363. package/dist/test/EmptyState.spec.js +51 -0
  364. package/dist/test/action-coverage.test.js +139 -0
  365. package/dist/test/actions/registry.test.js +59 -0
  366. package/dist/test/actions/shortcuts.test.js +79 -0
  367. package/dist/test/agent-events.test.js +151 -0
  368. package/dist/test/analytics.test.js +158 -0
  369. package/dist/test/attention.test.js +91 -0
  370. package/dist/test/auth.test.js +105 -0
  371. package/dist/test/backend-state.test.js +47 -0
  372. package/dist/test/belayer-executor.test.js +33 -0
  373. package/dist/test/belayer-intake.test.js +44 -0
  374. package/dist/test/belayer-pipeline.test.js +113 -0
  375. package/dist/test/belayer-pr-lifecycle.test.js +26 -0
  376. package/dist/test/belayer-prompts.test.js +60 -0
  377. package/dist/test/belayer-types.test.js +69 -0
  378. package/dist/test/bin/claude-remote-cli.js +214 -0
  379. package/dist/test/boot-state.test.js +133 -0
  380. package/dist/test/branch-lifecycle.test.js +75 -0
  381. package/dist/test/branch-linker.test.js +236 -0
  382. package/dist/test/branch-rename.test.js +45 -0
  383. package/dist/test/branch-watcher.test.js +115 -0
  384. package/dist/test/browser-cli.test.js +91 -0
  385. package/dist/test/browser-content.test.js +93 -0
  386. package/dist/test/browser-tabs-ui.test.js +39 -0
  387. package/dist/test/changed-files-api.test.js +140 -0
  388. package/dist/test/clipboard.test.js +12 -0
  389. package/dist/test/codex-hooks-adapter.test.js +237 -0
  390. package/dist/test/components/EmptyState.spec.js +51 -0
  391. package/dist/test/components/ErrorToast.spec.js +65 -0
  392. package/dist/test/components/TuiCheckbox.spec.js +120 -0
  393. package/dist/test/components/TuiInput.spec.js +186 -0
  394. package/dist/test/components/leaf-component-migration.spec.js +104 -0
  395. package/dist/test/config-freshness.test.js +63 -0
  396. package/dist/test/config.test.js +813 -0
  397. package/dist/test/diff-summary.test.js +98 -0
  398. package/dist/test/display-state.test.js +179 -0
  399. package/dist/test/event-message-types.test.js +32 -0
  400. package/dist/test/file-tree-utils.test.js +167 -0
  401. package/dist/test/framework-types.test.js +183 -0
  402. package/dist/test/frameworks-api.test.js +93 -0
  403. package/dist/test/frontend/src/lib/pr-state.js +114 -0
  404. package/dist/test/fs-browse.test.js +246 -0
  405. package/dist/test/fuzzy-scorer.test.js +145 -0
  406. package/dist/test/gh-routes.test.js +156 -0
  407. package/dist/test/git-changed-files.test.js +152 -0
  408. package/dist/test/git-routes.test.js +146 -0
  409. package/dist/test/git-utils.test.js +68 -0
  410. package/dist/test/git-watcher.test.js +110 -0
  411. package/dist/test/git.test.js +140 -0
  412. package/dist/test/github-app.test.js +455 -0
  413. package/dist/test/github-graphql.test.js +301 -0
  414. package/dist/test/greetings.test.js +83 -0
  415. package/dist/test/hooks-agent-event.test.js +412 -0
  416. package/dist/test/hooks.test.js +149 -0
  417. package/dist/test/integration-github.test.js +220 -0
  418. package/dist/test/integration-jira.test.js +238 -0
  419. package/dist/test/integration-linear.test.js +293 -0
  420. package/dist/test/mobile-input.test.js +235 -0
  421. package/dist/test/opencode-relay.test.js +107 -0
  422. package/dist/test/org-dashboard.test.js +349 -0
  423. package/dist/test/output-parser.test.js +217 -0
  424. package/dist/test/paths.test.js +32 -0
  425. package/dist/test/pr-state.test.js +407 -0
  426. package/dist/test/pr-status.test.js +82 -0
  427. package/dist/test/presets.test.js +242 -0
  428. package/dist/test/pty-handler-multi-agent.test.js +149 -0
  429. package/dist/test/pty-handler.test.js +146 -0
  430. package/dist/test/pull-requests.test.js +78 -0
  431. package/dist/test/review-poller.test.js +349 -0
  432. package/dist/test/server/analytics.js +121 -0
  433. package/dist/test/server/auth.js +63 -0
  434. package/dist/test/server/branch-linker.js +124 -0
  435. package/dist/test/server/clipboard.js +56 -0
  436. package/dist/test/server/config.js +137 -0
  437. package/dist/test/server/git.js +308 -0
  438. package/dist/test/server/hooks.js +196 -0
  439. package/dist/test/server/index.js +1124 -0
  440. package/dist/test/server/integration-github.js +117 -0
  441. package/dist/test/server/integration-jira.js +164 -0
  442. package/dist/test/server/integration-linear.js +176 -0
  443. package/dist/test/server/mobile-input-pipeline.js +123 -0
  444. package/dist/test/server/org-dashboard.js +184 -0
  445. package/dist/test/server/output-parsers/claude-parser.js +54 -0
  446. package/dist/test/server/output-parsers/codex-parser.js +13 -0
  447. package/dist/test/server/output-parsers/index.js +7 -0
  448. package/dist/test/server/pty-handler.js +310 -0
  449. package/dist/test/server/push.js +80 -0
  450. package/dist/test/server/review-poller.js +218 -0
  451. package/dist/test/server/service.js +169 -0
  452. package/dist/test/server/sessions.js +434 -0
  453. package/dist/test/server/ticket-transitions.js +216 -0
  454. package/dist/test/server/types.js +20 -0
  455. package/dist/test/server/utils.js +22 -0
  456. package/dist/test/server/watcher.js +139 -0
  457. package/dist/test/server/workspaces.js +657 -0
  458. package/dist/test/server/ws.js +152 -0
  459. package/dist/test/server-startup.test.js +62 -0
  460. package/dist/test/service.test.js +43 -0
  461. package/dist/test/session-analytics-api.test.js +123 -0
  462. package/dist/test/session-analytics.test.js +425 -0
  463. package/dist/test/session-intent.test.js +249 -0
  464. package/dist/test/sessions.test.js +1152 -0
  465. package/dist/test/sidebar-items.test.js +164 -0
  466. package/dist/test/stores/boot-state-store.test.js +165 -0
  467. package/dist/test/stores/sessions-logic.test.js +191 -0
  468. package/dist/test/stores/toasts-store.test.js +66 -0
  469. package/dist/test/stores/ui-store.test.js +203 -0
  470. package/dist/test/stores/unread-store.test.js +97 -0
  471. package/dist/test/telemetry-api.test.js +54 -0
  472. package/dist/test/telemetry-sync.test.js +68 -0
  473. package/dist/test/telemetry.test.js +295 -0
  474. package/dist/test/terminal-zoom.test.js +102 -0
  475. package/dist/test/test/analytics.test.js +152 -0
  476. package/dist/test/test/auth.test.js +95 -0
  477. package/dist/test/test/branch-linker.test.js +231 -0
  478. package/dist/test/test/branch-rename.test.js +45 -0
  479. package/dist/test/test/clipboard.test.js +12 -0
  480. package/dist/test/test/config.test.js +281 -0
  481. package/dist/test/test/fs-browse.test.js +202 -0
  482. package/dist/test/test/git.test.js +67 -0
  483. package/dist/test/test/hooks.test.js +139 -0
  484. package/dist/test/test/integration-github.test.js +203 -0
  485. package/dist/test/test/integration-jira.test.js +294 -0
  486. package/dist/test/test/integration-linear.test.js +293 -0
  487. package/dist/test/test/mobile-input.test.js +193 -0
  488. package/dist/test/test/org-dashboard.test.js +240 -0
  489. package/dist/test/test/output-parser.test.js +95 -0
  490. package/dist/test/test/paths.test.js +32 -0
  491. package/dist/test/test/pr-state.test.js +220 -0
  492. package/dist/test/test/pull-requests.test.js +67 -0
  493. package/dist/test/test/review-poller.test.js +235 -0
  494. package/dist/test/test/service.test.js +43 -0
  495. package/dist/test/test/sessions.test.js +750 -0
  496. package/dist/test/test/ticket-transitions.test.js +130 -0
  497. package/dist/test/test/version.test.js +34 -0
  498. package/dist/test/test/worktrees.test.js +256 -0
  499. package/dist/test/ticket-transitions.test.js +312 -0
  500. package/dist/test/unread.test.js +23 -0
  501. package/dist/test/version.test.js +34 -0
  502. package/dist/test/webhook-manager.test.js +484 -0
  503. package/dist/test/webhooks.test.js +208 -0
  504. package/dist/test/workspace-groups.test.js +377 -0
  505. package/dist/test/worktrees.test.js +531 -0
  506. package/package.json +88 -0
@@ -0,0 +1,220 @@
1
+ import { test, before, after } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import express from 'express';
7
+ import { createIntegrationGitHubRouter, } from '../server/integration-github.js';
8
+ import { saveConfig, DEFAULTS } from '../server/config.js';
9
+ let tmpDir;
10
+ let configPath;
11
+ let server;
12
+ let baseUrl;
13
+ const WORKSPACE_PATH_A = '/fake/workspace/repo-a';
14
+ const WORKSPACE_PATH_B = '/fake/workspace/repo-b';
15
+ /**
16
+ * Builds a minimal GhIssueItem for use in mock stdout payloads.
17
+ */
18
+ function makeIssueItem(overrides) {
19
+ const { number = 1, title = 'Test Issue', url = `https://github.com/fake/repo/issues/${overrides.number ?? 1}`, updatedAt = '2026-03-21T00:00:00Z', createdAt = '2026-03-20T00:00:00Z', } = overrides;
20
+ return {
21
+ number,
22
+ title,
23
+ url,
24
+ state: 'OPEN',
25
+ labels: [],
26
+ assignees: [],
27
+ createdAt,
28
+ updatedAt,
29
+ };
30
+ }
31
+ /**
32
+ * Creates a mock execAsync that routes calls based on the command and cwd.
33
+ * - `gh issue list` in a given cwd → returns configured issues list or throws
34
+ */
35
+ function makeMockExec(opts) {
36
+ return async (cmd, args, options) => {
37
+ const command = cmd;
38
+ const argv = args;
39
+ const cwd = options.cwd ?? '';
40
+ if (command === 'gh' && argv[0] === 'issue' && argv[1] === 'list') {
41
+ if (opts.globalError)
42
+ throw opts.globalError;
43
+ if (opts.errorByPath?.[cwd])
44
+ throw opts.errorByPath[cwd];
45
+ const items = opts.issuesByPath?.[cwd] ?? [];
46
+ return { stdout: JSON.stringify(items), stderr: '' };
47
+ }
48
+ throw new Error(`Unexpected exec call: ${command} ${argv.join(' ')}`);
49
+ };
50
+ }
51
+ function startServer(execAsyncFn) {
52
+ return new Promise((resolve) => {
53
+ const app = express();
54
+ app.use(express.json());
55
+ const deps = {
56
+ configPath,
57
+ execAsync: execAsyncFn,
58
+ };
59
+ app.use('/integration-github', createIntegrationGitHubRouter(deps));
60
+ server = app.listen(0, '127.0.0.1', () => {
61
+ const addr = server.address();
62
+ if (typeof addr === 'object' && addr) {
63
+ baseUrl = `http://127.0.0.1:${addr.port}`;
64
+ }
65
+ resolve();
66
+ });
67
+ });
68
+ }
69
+ function stopServer() {
70
+ return new Promise((resolve) => {
71
+ if (server)
72
+ server.close(() => resolve());
73
+ else
74
+ resolve();
75
+ });
76
+ }
77
+ async function getIssues() {
78
+ const res = await fetch(`${baseUrl}/integration-github/issues`);
79
+ return res.json();
80
+ }
81
+ before(() => {
82
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'integration-github-test-'));
83
+ configPath = path.join(tmpDir, 'config.json');
84
+ });
85
+ after(async () => {
86
+ await stopServer();
87
+ fs.rmSync(tmpDir, { recursive: true, force: true });
88
+ });
89
+ test('returns issues from all workspace repos merged and sorted', async () => {
90
+ await stopServer();
91
+ saveConfig(configPath, {
92
+ ...DEFAULTS,
93
+ repos: [WORKSPACE_PATH_A, WORKSPACE_PATH_B],
94
+ });
95
+ const exec = makeMockExec({
96
+ issuesByPath: {
97
+ [WORKSPACE_PATH_A]: [
98
+ makeIssueItem({
99
+ number: 10,
100
+ title: 'Issue A',
101
+ updatedAt: '2026-03-21T10:00:00Z',
102
+ }),
103
+ makeIssueItem({
104
+ number: 11,
105
+ title: 'Issue A2',
106
+ updatedAt: '2026-03-19T10:00:00Z',
107
+ }),
108
+ ],
109
+ [WORKSPACE_PATH_B]: [
110
+ makeIssueItem({
111
+ number: 20,
112
+ title: 'Issue B',
113
+ updatedAt: '2026-03-20T10:00:00Z',
114
+ }),
115
+ ],
116
+ },
117
+ });
118
+ await startServer(exec);
119
+ const data = await getIssues();
120
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
121
+ assert.equal(data.issues.length, 3, 'Should return all 3 issues from both repos');
122
+ // Verify sorted descending by updatedAt
123
+ const updatedAts = data.issues.map((i) => i.updatedAt);
124
+ assert.deepEqual(updatedAts, [
125
+ '2026-03-21T10:00:00Z',
126
+ '2026-03-20T10:00:00Z',
127
+ '2026-03-19T10:00:00Z',
128
+ ]);
129
+ // Verify repoPath and repoName are attached
130
+ const issue10 = data.issues.find((i) => i.number === 10);
131
+ assert.equal(issue10?.repoPath, WORKSPACE_PATH_A);
132
+ assert.equal(issue10?.repoName, 'repo-a');
133
+ const issue20 = data.issues.find((i) => i.number === 20);
134
+ assert.equal(issue20?.repoPath, WORKSPACE_PATH_B);
135
+ assert.equal(issue20?.repoName, 'repo-b');
136
+ });
137
+ test('returns no_workspaces error when empty', async () => {
138
+ await stopServer();
139
+ saveConfig(configPath, {
140
+ ...DEFAULTS,
141
+ repos: [],
142
+ });
143
+ // execAsync should never be called — pass a mock that always throws to verify early-return
144
+ const exec = makeMockExec({});
145
+ await startServer(exec);
146
+ const data = await getIssues();
147
+ assert.equal(data.error, 'no_workspaces');
148
+ assert.equal(data.issues.length, 0);
149
+ });
150
+ test('returns gh_not_in_path when gh not found', async () => {
151
+ await stopServer();
152
+ saveConfig(configPath, {
153
+ ...DEFAULTS,
154
+ repos: [WORKSPACE_PATH_A],
155
+ });
156
+ const err = new Error('spawn gh ENOENT');
157
+ err.code = 'ENOENT';
158
+ const exec = makeMockExec({ globalError: err });
159
+ await startServer(exec);
160
+ const data = await getIssues();
161
+ assert.equal(data.error, 'gh_not_in_path');
162
+ assert.equal(data.issues.length, 0);
163
+ });
164
+ test('caches per-repo within TTL — gh called once per repo for two requests', async () => {
165
+ await stopServer();
166
+ saveConfig(configPath, {
167
+ ...DEFAULTS,
168
+ repos: [WORKSPACE_PATH_A, WORKSPACE_PATH_B],
169
+ });
170
+ let ghCallCount = 0;
171
+ const baseExec = makeMockExec({
172
+ issuesByPath: {
173
+ [WORKSPACE_PATH_A]: [makeIssueItem({ number: 1 })],
174
+ [WORKSPACE_PATH_B]: [makeIssueItem({ number: 2 })],
175
+ },
176
+ });
177
+ const countingExec = async (...args) => {
178
+ const [cmd, argv] = args;
179
+ if (cmd === 'gh' && argv[0] === 'issue') {
180
+ ghCallCount++;
181
+ }
182
+ return baseExec(...args);
183
+ };
184
+ await startServer(countingExec);
185
+ // First request — populates cache for both repos
186
+ const first = await getIssues();
187
+ assert.equal(first.error, undefined);
188
+ assert.equal(first.issues.length, 2);
189
+ assert.equal(ghCallCount, 2, 'gh should be called once per repo on first request');
190
+ // Second request — should be served from per-repo cache, no additional calls
191
+ const second = await getIssues();
192
+ assert.equal(second.error, undefined);
193
+ assert.equal(second.issues.length, 2);
194
+ assert.equal(ghCallCount, 2, 'gh should not be called again within TTL (cache hit)');
195
+ });
196
+ test('partial failure: repo that throws still returns others', async () => {
197
+ await stopServer();
198
+ saveConfig(configPath, {
199
+ ...DEFAULTS,
200
+ repos: [WORKSPACE_PATH_A, WORKSPACE_PATH_B],
201
+ });
202
+ // repo-b will throw a generic error (not ENOENT, so non-fatal)
203
+ const exec = makeMockExec({
204
+ issuesByPath: {
205
+ [WORKSPACE_PATH_A]: [
206
+ makeIssueItem({ number: 99, title: 'Surviving issue' }),
207
+ ],
208
+ },
209
+ errorByPath: {
210
+ [WORKSPACE_PATH_B]: new Error('git command failed'),
211
+ },
212
+ });
213
+ await startServer(exec);
214
+ const data = await getIssues();
215
+ // No top-level error — partial failures are silent
216
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
217
+ assert.equal(data.issues.length, 1, 'Should return the one issue from the succeeding repo');
218
+ assert.equal(data.issues[0]?.number, 99);
219
+ assert.equal(data.issues[0]?.repoPath, WORKSPACE_PATH_A);
220
+ });
@@ -0,0 +1,238 @@
1
+ import { test, after } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import express from 'express';
4
+ import { createIntegrationJiraRouter, } from '../server/integration-jira.js';
5
+ // ─── Mock stdout constants ────────────────────────────────────────────────────
6
+ const AUTH_STATUS_STDOUT = `✓ Authenticated
7
+ Site: fake-jira.atlassian.net
8
+ Email: test@example.com
9
+ Authentication Type: oauth
10
+ `;
11
+ // ─── Globals ──────────────────────────────────────────────────────────────────
12
+ let server;
13
+ let baseUrl;
14
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
15
+ /**
16
+ * Builds a minimal AcliWorkItem for use in mock stdout payloads.
17
+ */
18
+ function makeAcliWorkItem(overrides) {
19
+ const { key = 'TEST-1', summary = 'Test issue', statusId = '3', statusName = 'In Progress', priorityName = 'Medium', assigneeDisplayName = 'Jane Doe', } = overrides;
20
+ return {
21
+ key,
22
+ fields: {
23
+ summary,
24
+ status: { id: statusId, name: statusName },
25
+ ...(priorityName !== null
26
+ ? { priority: { name: priorityName } }
27
+ : { priority: null }),
28
+ ...(assigneeDisplayName !== null
29
+ ? { assignee: { displayName: assigneeDisplayName } }
30
+ : { assignee: null }),
31
+ },
32
+ };
33
+ }
34
+ /**
35
+ * Creates a mock execAsync that routes calls based on the acli subcommand args:
36
+ * - `acli jira auth status` → returns AUTH_STATUS_STDOUT (or throws)
37
+ * - `acli jira workitem search` → returns the provided items as JSON stdout (or throws)
38
+ */
39
+ function makeMockExec(opts) {
40
+ return async (_cmd, args) => {
41
+ const argv = args;
42
+ if (argv.includes('auth') && argv.includes('status')) {
43
+ if (opts.authError)
44
+ throw opts.authError;
45
+ return { stdout: AUTH_STATUS_STDOUT, stderr: '' };
46
+ }
47
+ if (argv.includes('workitem') && argv.includes('search')) {
48
+ if (opts.searchError)
49
+ throw opts.searchError;
50
+ return { stdout: JSON.stringify(opts.searchItems ?? []), stderr: '' };
51
+ }
52
+ throw new Error(`Unexpected exec call: acli ${argv.join(' ')}`);
53
+ };
54
+ }
55
+ // ─── Server lifecycle ─────────────────────────────────────────────────────────
56
+ function startServer(execAsyncFn) {
57
+ return new Promise((resolve) => {
58
+ const app = express();
59
+ app.use(express.json());
60
+ const deps = {
61
+ configPath: '',
62
+ execAsync: execAsyncFn,
63
+ };
64
+ app.use('/integration-jira', createIntegrationJiraRouter(deps));
65
+ server = app.listen(0, '127.0.0.1', () => {
66
+ const addr = server.address();
67
+ if (typeof addr === 'object' && addr) {
68
+ baseUrl = `http://127.0.0.1:${addr.port}`;
69
+ }
70
+ resolve();
71
+ });
72
+ });
73
+ }
74
+ function stopServer() {
75
+ return new Promise((resolve) => {
76
+ if (server)
77
+ server.close(() => resolve());
78
+ else
79
+ resolve();
80
+ });
81
+ }
82
+ // ─── Suite teardown ───────────────────────────────────────────────────────────
83
+ after(async () => {
84
+ await stopServer();
85
+ });
86
+ // ─── Tests ────────────────────────────────────────────────────────────────────
87
+ test('GET /issues returns mapped JiraIssue[] from mocked acli JSON output', async () => {
88
+ await stopServer();
89
+ const searchItems = [
90
+ makeAcliWorkItem({
91
+ key: 'PROJ-42',
92
+ summary: 'Fix the login bug',
93
+ statusId: '3',
94
+ statusName: 'In Progress',
95
+ priorityName: 'High',
96
+ assigneeDisplayName: 'Alice',
97
+ }),
98
+ makeAcliWorkItem({
99
+ key: 'PROJ-10',
100
+ summary: 'Update docs',
101
+ statusId: '1',
102
+ statusName: 'To Do',
103
+ priorityName: null,
104
+ assigneeDisplayName: null,
105
+ }),
106
+ ];
107
+ const exec = makeMockExec({ searchItems });
108
+ await startServer(exec);
109
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
110
+ assert.equal(res.status, 200);
111
+ const data = (await res.json());
112
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
113
+ assert.equal(data.issues.length, 2);
114
+ const issue42 = data.issues.find((i) => i.key === 'PROJ-42');
115
+ assert.ok(issue42, 'Should contain PROJ-42');
116
+ assert.equal(issue42.title, 'Fix the login bug');
117
+ assert.equal(issue42.status, 'In Progress');
118
+ assert.equal(issue42.priority, 'High');
119
+ assert.equal(issue42.assignee, 'Alice');
120
+ assert.equal(issue42.url, 'https://fake-jira.atlassian.net/browse/PROJ-42');
121
+ assert.equal(issue42.projectKey, 'PROJ');
122
+ assert.equal(issue42.updatedAt, '');
123
+ assert.equal(issue42.sprint, null);
124
+ assert.equal(issue42.storyPoints, null);
125
+ const issue10 = data.issues.find((i) => i.key === 'PROJ-10');
126
+ assert.ok(issue10, 'Should contain PROJ-10');
127
+ assert.equal(issue10.priority, null);
128
+ assert.equal(issue10.assignee, null);
129
+ assert.equal(issue10.url, 'https://fake-jira.atlassian.net/browse/PROJ-10');
130
+ assert.equal(issue10.projectKey, 'PROJ');
131
+ });
132
+ test('GET /issues returns acli_not_in_path when exec throws ENOENT', async () => {
133
+ await stopServer();
134
+ const err = new Error('spawn acli ENOENT');
135
+ err.code = 'ENOENT';
136
+ const exec = makeMockExec({ authError: err });
137
+ await startServer(exec);
138
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
139
+ assert.equal(res.status, 200);
140
+ const data = (await res.json());
141
+ assert.equal(data.error, 'acli_not_in_path');
142
+ assert.equal(data.issues.length, 0);
143
+ });
144
+ test('GET /issues returns acli_not_authenticated when exec stderr contains auth message', async () => {
145
+ await stopServer();
146
+ const err = Object.assign(new Error('acli auth error'), {
147
+ stderr: "not logged in, use 'acli jira auth login' to authenticate",
148
+ });
149
+ const exec = makeMockExec({ authError: err });
150
+ await startServer(exec);
151
+ const res = await fetch(`${baseUrl}/integration-jira/issues`);
152
+ assert.equal(res.status, 200);
153
+ const data = (await res.json());
154
+ assert.equal(data.error, 'acli_not_authenticated');
155
+ assert.equal(data.issues.length, 0);
156
+ });
157
+ test('GET /issues caches results within TTL — exec called only once for two requests', async () => {
158
+ await stopServer();
159
+ let searchCallCount = 0;
160
+ const baseExec = makeMockExec({
161
+ searchItems: [
162
+ makeAcliWorkItem({ key: 'CACHE-1', summary: 'Cached issue' }),
163
+ ],
164
+ });
165
+ const countingExec = async (...args) => {
166
+ const argv = args[1];
167
+ if (argv.includes('workitem') && argv.includes('search')) {
168
+ searchCallCount++;
169
+ }
170
+ return baseExec(...args);
171
+ };
172
+ await startServer(countingExec);
173
+ // First request — populates cache
174
+ const firstRes = await fetch(`${baseUrl}/integration-jira/issues`);
175
+ const first = (await firstRes.json());
176
+ assert.equal(first.error, undefined, `Unexpected error: ${first.error}`);
177
+ assert.equal(first.issues.length, 1);
178
+ assert.equal(searchCallCount, 1, 'search should be called once on first request');
179
+ // Second request — should be served from cache, no additional exec call
180
+ const secondRes = await fetch(`${baseUrl}/integration-jira/issues`);
181
+ const second = (await secondRes.json());
182
+ assert.equal(second.error, undefined, `Unexpected error: ${second.error}`);
183
+ assert.equal(second.issues.length, 1);
184
+ assert.equal(searchCallCount, 1, 'search should not be called again within TTL (cache hit)');
185
+ });
186
+ test('GET /statuses?projectKey=TEST returns deduplicated statuses', async () => {
187
+ await stopServer();
188
+ // Items with overlapping status IDs — deduplication should collapse to 3 unique statuses
189
+ const searchItems = [
190
+ makeAcliWorkItem({ key: 'TEST-1', statusId: '1', statusName: 'To Do' }),
191
+ makeAcliWorkItem({
192
+ key: 'TEST-2',
193
+ statusId: '2',
194
+ statusName: 'In Progress',
195
+ }),
196
+ makeAcliWorkItem({
197
+ key: 'TEST-3',
198
+ statusId: '2',
199
+ statusName: 'In Progress',
200
+ }), // duplicate
201
+ makeAcliWorkItem({ key: 'TEST-4', statusId: '3', statusName: 'Done' }),
202
+ ];
203
+ const exec = makeMockExec({ searchItems });
204
+ await startServer(exec);
205
+ const res = await fetch(`${baseUrl}/integration-jira/statuses?projectKey=TEST`);
206
+ assert.equal(res.status, 200);
207
+ const data = (await res.json());
208
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
209
+ assert.equal(data.statuses.length, 3, 'Should deduplicate statuses by id');
210
+ const ids = data.statuses.map((s) => s.id);
211
+ assert.deepEqual(ids, ['1', '2', '3']);
212
+ const names = data.statuses.map((s) => s.name);
213
+ assert.deepEqual(names, ['To Do', 'In Progress', 'Done']);
214
+ });
215
+ test('GET /statuses returns 400 when projectKey query param is missing', async () => {
216
+ await stopServer();
217
+ const exec = makeMockExec({ searchItems: [] });
218
+ await startServer(exec);
219
+ const res = await fetch(`${baseUrl}/integration-jira/statuses`);
220
+ assert.equal(res.status, 400);
221
+ const data = (await res.json());
222
+ assert.equal(data.error, 'missing_project_key');
223
+ });
224
+ test('GET /statuses returns 400 for invalid projectKey (lowercase or special chars)', async () => {
225
+ await stopServer();
226
+ const exec = makeMockExec({ searchItems: [] });
227
+ await startServer(exec);
228
+ // Lowercase key
229
+ const resLower = await fetch(`${baseUrl}/integration-jira/statuses?projectKey=test`);
230
+ assert.equal(resLower.status, 400);
231
+ const dataLower = (await resLower.json());
232
+ assert.equal(dataLower.error, 'invalid_project_key');
233
+ // Key with special characters
234
+ const resSpecial = await fetch(`${baseUrl}/integration-jira/statuses?projectKey=TEST%20PROJ`);
235
+ assert.equal(resSpecial.status, 400);
236
+ const dataSpecial = (await resSpecial.json());
237
+ assert.equal(dataSpecial.error, 'invalid_project_key');
238
+ });