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,531 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { WORKTREE_DIRS, isValidWorktreePath, parseWorktreeListPorcelain, parseAllWorktrees, findOrCreateWorktreeForBranch, } from '../server/watcher.js';
4
+ import { MOUNTAIN_NAMES } from '../server/types.js';
5
+ import { generateTmuxSessionName } from '../server/pty-handler.js';
6
+ describe('worktree directories constant', () => {
7
+ it('should include both .worktrees and .claude/worktrees', () => {
8
+ assert.deepEqual(WORKTREE_DIRS, ['.worktrees', '.claude/worktrees']);
9
+ });
10
+ });
11
+ describe('isValidWorktreePath', () => {
12
+ it('should reject paths not inside any worktree directory', () => {
13
+ assert.equal(isValidWorktreePath('/some/random/path'), false);
14
+ });
15
+ it('should accept paths inside .worktrees/', () => {
16
+ assert.equal(isValidWorktreePath('/Users/me/code/repo/.worktrees/my-worktree'), true);
17
+ });
18
+ it('should accept paths inside .claude/worktrees/', () => {
19
+ assert.equal(isValidWorktreePath('/Users/me/code/repo/.claude/worktrees/my-worktree'), true);
20
+ });
21
+ it('should not match partial .worktrees paths', () => {
22
+ assert.equal(isValidWorktreePath('/Users/me/.worktrees-fake/foo'), false);
23
+ });
24
+ });
25
+ describe('branch name to directory name', () => {
26
+ it('should replace slashes with dashes', () => {
27
+ const branchName = 'dy/feat/my-feature';
28
+ const dirName = branchName.replace(/\//g, '-');
29
+ assert.equal(dirName, 'dy-feat-my-feature');
30
+ });
31
+ it('should leave flat branch names unchanged', () => {
32
+ const branchName = 'my-feature';
33
+ const dirName = branchName.replace(/\//g, '-');
34
+ assert.equal(dirName, 'my-feature');
35
+ });
36
+ });
37
+ describe('parseWorktreeListPorcelain', () => {
38
+ const repoPath = '/Users/me/code/my-repo';
39
+ it('should parse a single worktree entry', () => {
40
+ const stdout = [
41
+ `worktree ${repoPath}`,
42
+ 'HEAD abc123',
43
+ 'branch refs/heads/main',
44
+ '',
45
+ 'worktree /Users/me/code/my-repo/.worktrees/feat-branch',
46
+ 'HEAD def456',
47
+ 'branch refs/heads/feat/branch',
48
+ '',
49
+ ].join('\n');
50
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
51
+ assert.equal(result.length, 1);
52
+ assert.equal(result[0].path, '/Users/me/code/my-repo/.worktrees/feat-branch');
53
+ assert.equal(result[0].branch, 'feat/branch');
54
+ });
55
+ it('should parse multiple worktree entries', () => {
56
+ const stdout = [
57
+ `worktree ${repoPath}`,
58
+ 'HEAD abc123',
59
+ 'branch refs/heads/main',
60
+ '',
61
+ 'worktree /Users/me/code/my-repo/.worktrees/feat-a',
62
+ 'HEAD def456',
63
+ 'branch refs/heads/feat/a',
64
+ '',
65
+ 'worktree /Users/me/other-path/extend-cli',
66
+ 'HEAD 789abc',
67
+ 'branch refs/heads/dy/feat/worktree-isolation',
68
+ '',
69
+ ].join('\n');
70
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
71
+ assert.equal(result.length, 2);
72
+ assert.equal(result[0].path, '/Users/me/code/my-repo/.worktrees/feat-a');
73
+ assert.equal(result[0].branch, 'feat/a');
74
+ assert.equal(result[1].path, '/Users/me/other-path/extend-cli');
75
+ assert.equal(result[1].branch, 'dy/feat/worktree-isolation');
76
+ });
77
+ it('should skip the main worktree (repo root)', () => {
78
+ const stdout = [
79
+ `worktree ${repoPath}`,
80
+ 'HEAD abc123',
81
+ 'branch refs/heads/main',
82
+ '',
83
+ ].join('\n');
84
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
85
+ assert.equal(result.length, 0);
86
+ });
87
+ it('should skip bare entries', () => {
88
+ const stdout = [
89
+ `worktree ${repoPath}`,
90
+ 'HEAD abc123',
91
+ 'branch refs/heads/main',
92
+ '',
93
+ 'worktree /some/bare/repo',
94
+ 'HEAD def456',
95
+ 'bare',
96
+ '',
97
+ ].join('\n');
98
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
99
+ assert.equal(result.length, 0);
100
+ });
101
+ it('should skip detached HEAD worktrees (no branch line)', () => {
102
+ const stdout = [
103
+ `worktree ${repoPath}`,
104
+ 'HEAD abc123',
105
+ 'branch refs/heads/main',
106
+ '',
107
+ 'worktree /Users/me/code/my-repo/.worktrees/detached',
108
+ 'HEAD def456',
109
+ 'detached',
110
+ '',
111
+ ].join('\n');
112
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
113
+ assert.equal(result.length, 0);
114
+ });
115
+ it('should handle empty output', () => {
116
+ const result = parseWorktreeListPorcelain('', repoPath);
117
+ assert.equal(result.length, 0);
118
+ });
119
+ it('should discover worktrees at arbitrary paths outside .worktrees/', () => {
120
+ const stdout = [
121
+ `worktree ${repoPath}`,
122
+ 'HEAD abc123',
123
+ 'branch refs/heads/main',
124
+ '',
125
+ 'worktree /completely/different/path/project-checkout',
126
+ 'HEAD def456',
127
+ 'branch refs/heads/feature/my-feature',
128
+ '',
129
+ ].join('\n');
130
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
131
+ assert.equal(result.length, 1);
132
+ assert.equal(result[0].path, '/completely/different/path/project-checkout');
133
+ assert.equal(result[0].branch, 'feature/my-feature');
134
+ });
135
+ it('should handle deeply nested branch names', () => {
136
+ const stdout = [
137
+ `worktree ${repoPath}`,
138
+ 'HEAD abc123',
139
+ 'branch refs/heads/main',
140
+ '',
141
+ 'worktree /Users/me/code/my-repo/.worktrees/dy-feat-deep-nesting',
142
+ 'HEAD def456',
143
+ 'branch refs/heads/dy/feat/deep/nesting/here',
144
+ '',
145
+ ].join('\n');
146
+ const result = parseWorktreeListPorcelain(stdout, repoPath);
147
+ assert.equal(result.length, 1);
148
+ assert.equal(result[0].branch, 'dy/feat/deep/nesting/here');
149
+ });
150
+ });
151
+ describe('parseAllWorktrees', () => {
152
+ const repoPath = '/Users/me/code/my-repo';
153
+ it('should include the main worktree with isMain=true', () => {
154
+ const stdout = [
155
+ `worktree ${repoPath}`,
156
+ 'HEAD abc123',
157
+ 'branch refs/heads/main',
158
+ '',
159
+ ].join('\n');
160
+ const result = parseAllWorktrees(stdout, repoPath);
161
+ assert.equal(result.length, 1);
162
+ assert.equal(result[0].path, repoPath);
163
+ assert.equal(result[0].branch, 'main');
164
+ assert.equal(result[0].isMain, true);
165
+ });
166
+ it('should mark non-main worktrees with isMain=false', () => {
167
+ const stdout = [
168
+ `worktree ${repoPath}`,
169
+ 'HEAD abc123',
170
+ 'branch refs/heads/main',
171
+ '',
172
+ 'worktree /Users/me/code/my-repo/.worktrees/feat-branch',
173
+ 'HEAD def456',
174
+ 'branch refs/heads/feat/branch',
175
+ '',
176
+ ].join('\n');
177
+ const result = parseAllWorktrees(stdout, repoPath);
178
+ assert.equal(result.length, 2);
179
+ assert.equal(result[0].isMain, true);
180
+ assert.equal(result[1].isMain, false);
181
+ assert.equal(result[1].path, '/Users/me/code/my-repo/.worktrees/feat-branch');
182
+ assert.equal(result[1].branch, 'feat/branch');
183
+ });
184
+ it('should still skip bare entries', () => {
185
+ const stdout = [
186
+ `worktree ${repoPath}`,
187
+ 'HEAD abc123',
188
+ 'branch refs/heads/main',
189
+ '',
190
+ 'worktree /some/bare/repo',
191
+ 'HEAD def456',
192
+ 'bare',
193
+ '',
194
+ ].join('\n');
195
+ const result = parseAllWorktrees(stdout, repoPath);
196
+ assert.equal(result.length, 1);
197
+ assert.equal(result[0].isMain, true);
198
+ });
199
+ it('should include detached HEAD entries with empty branch', () => {
200
+ const stdout = [
201
+ `worktree ${repoPath}`,
202
+ 'HEAD abc123',
203
+ 'branch refs/heads/main',
204
+ '',
205
+ 'worktree /Users/me/code/my-repo/.worktrees/detached',
206
+ 'HEAD def456',
207
+ 'detached',
208
+ '',
209
+ ].join('\n');
210
+ const result = parseAllWorktrees(stdout, repoPath);
211
+ assert.equal(result.length, 2);
212
+ assert.equal(result[1].branch, '');
213
+ });
214
+ it('should handle empty output', () => {
215
+ const result = parseAllWorktrees('', repoPath);
216
+ assert.equal(result.length, 0);
217
+ });
218
+ it('should find worktree by branch name', () => {
219
+ const stdout = [
220
+ `worktree ${repoPath}`,
221
+ 'HEAD abc123',
222
+ 'branch refs/heads/dy/feat/worktree-isolation',
223
+ '',
224
+ 'worktree /Users/me/code/my-repo/.worktrees/feat-a',
225
+ 'HEAD def456',
226
+ 'branch refs/heads/feat/a',
227
+ '',
228
+ ].join('\n');
229
+ const result = parseAllWorktrees(stdout, repoPath);
230
+ const match = result.find((wt) => wt.branch === 'dy/feat/worktree-isolation');
231
+ assert.ok(match);
232
+ assert.equal(match.path, repoPath);
233
+ assert.equal(match.isMain, true);
234
+ });
235
+ });
236
+ describe('workspace-to-repo merging for worktree discovery', () => {
237
+ // This tests the logic used in GET /worktrees to merge config.workspaces
238
+ // into the rootDir-scanned repo list, preventing the bug where workspaces
239
+ // added directly (not under any rootDir) had invisible worktrees.
240
+ function mergeWorkspacesIntoRepos(reposToScan, configWorkspaces, rootDirs) {
241
+ const result = [...reposToScan];
242
+ const scannedPaths = new Set(result.map((r) => r.path));
243
+ for (const wp of configWorkspaces) {
244
+ if (scannedPaths.has(wp))
245
+ continue;
246
+ const root = rootDirs.find((r) => wp.startsWith(r)) || '';
247
+ result.push({
248
+ path: wp,
249
+ name: wp.split('/').filter(Boolean).pop() || '',
250
+ root,
251
+ });
252
+ }
253
+ return result;
254
+ }
255
+ it('should add workspaces not under any rootDir', () => {
256
+ const reposFromRootDirs = [
257
+ { name: 'repo-a', path: '/root/repo-a', root: '/root' },
258
+ ];
259
+ const configWorkspaces = ['/other/path/my-project'];
260
+ const rootDirs = ['/root'];
261
+ const merged = mergeWorkspacesIntoRepos(reposFromRootDirs, configWorkspaces, rootDirs);
262
+ assert.equal(merged.length, 2);
263
+ assert.equal(merged[1].path, '/other/path/my-project');
264
+ assert.equal(merged[1].name, 'my-project');
265
+ assert.equal(merged[1].root, ''); // not under any rootDir
266
+ });
267
+ it('should deduplicate workspaces already found via rootDir scan', () => {
268
+ const reposFromRootDirs = [
269
+ { name: 'repo-a', path: '/root/repo-a', root: '/root' },
270
+ ];
271
+ const configWorkspaces = ['/root/repo-a']; // same as scanned
272
+ const rootDirs = ['/root'];
273
+ const merged = mergeWorkspacesIntoRepos(reposFromRootDirs, configWorkspaces, rootDirs);
274
+ assert.equal(merged.length, 1); // no duplicate
275
+ });
276
+ it('should handle empty rootDirs with workspaces-only config', () => {
277
+ const reposFromRootDirs = [];
278
+ const configWorkspaces = ['/a/project', '/b/other-project'];
279
+ const rootDirs = [];
280
+ const merged = mergeWorkspacesIntoRepos(reposFromRootDirs, configWorkspaces, rootDirs);
281
+ assert.equal(merged.length, 2);
282
+ assert.equal(merged[0].path, '/a/project');
283
+ assert.equal(merged[1].path, '/b/other-project');
284
+ });
285
+ it('should set root when workspace is under a rootDir', () => {
286
+ const reposFromRootDirs = [];
287
+ const configWorkspaces = ['/root/special-repo'];
288
+ const rootDirs = ['/root'];
289
+ const merged = mergeWorkspacesIntoRepos(reposFromRootDirs, configWorkspaces, rootDirs);
290
+ assert.equal(merged[0].root, '/root');
291
+ });
292
+ });
293
+ describe('CLI worktree arg parsing', () => {
294
+ it('should extract --yolo and leave other args intact', () => {
295
+ const args = [
296
+ 'add',
297
+ './.worktrees/my-feature',
298
+ '-b',
299
+ 'my-feature',
300
+ '--yolo',
301
+ ];
302
+ const hasYolo = args.includes('--yolo');
303
+ const gitArgs = args.filter((a) => a !== '--yolo');
304
+ assert.equal(hasYolo, true);
305
+ assert.deepEqual(gitArgs, [
306
+ 'add',
307
+ './.worktrees/my-feature',
308
+ '-b',
309
+ 'my-feature',
310
+ ]);
311
+ });
312
+ it('should detect missing path for add and use default', () => {
313
+ // args: ['add', '-b', 'my-feature'] — no positional path (first arg after 'add' starts with '-')
314
+ const args = ['add', '-b', 'my-feature'];
315
+ const subArgs = args.slice(1); // after 'add'
316
+ const hasPositionalPath = subArgs.length > 0 && !subArgs[0].startsWith('-');
317
+ assert.equal(hasPositionalPath, false);
318
+ });
319
+ it('should detect path when provided for add', () => {
320
+ const args = ['add', './my-path', '-b', 'my-feature'];
321
+ const subArgs = args.slice(1);
322
+ const hasPositionalPath = subArgs.length > 0 && !subArgs[0].startsWith('-');
323
+ assert.equal(hasPositionalPath, true);
324
+ assert.equal(subArgs[0], './my-path');
325
+ });
326
+ });
327
+ describe('mountain name collision retry', () => {
328
+ it('MOUNTAIN_NAMES is a non-empty array of strings', () => {
329
+ assert.ok(Array.isArray(MOUNTAIN_NAMES), 'MOUNTAIN_NAMES should be an array');
330
+ assert.ok(MOUNTAIN_NAMES.length > 0, 'MOUNTAIN_NAMES should not be empty');
331
+ for (const name of MOUNTAIN_NAMES) {
332
+ assert.equal(typeof name, 'string', `each mountain name should be a string, got: ${typeof name}`);
333
+ assert.ok(name.length > 0, 'each mountain name should be non-empty');
334
+ }
335
+ });
336
+ it('MOUNTAIN_NAMES contains expected well-known peaks', () => {
337
+ assert.ok(MOUNTAIN_NAMES.includes('everest'), 'should include everest');
338
+ assert.ok(MOUNTAIN_NAMES.includes('k2'), 'should include k2');
339
+ assert.ok(MOUNTAIN_NAMES.includes('fuji'), 'should include fuji');
340
+ });
341
+ it('collision retry logic skips taken names and selects the next available one', () => {
342
+ // Simulate the collision retry loop from workspaces.ts
343
+ // The first two names are "taken"; the third should be selected.
344
+ const takenNames = new Set([MOUNTAIN_NAMES[0], MOUNTAIN_NAMES[1]]);
345
+ const baseIndex = 0;
346
+ let selected = null;
347
+ let selectedIndex = -1;
348
+ for (let attempt = 0; attempt < MOUNTAIN_NAMES.length; attempt++) {
349
+ const candidateIndex = (baseIndex + attempt) % MOUNTAIN_NAMES.length;
350
+ const candidateName = MOUNTAIN_NAMES[candidateIndex];
351
+ if (!takenNames.has(candidateName)) {
352
+ selected = candidateName;
353
+ selectedIndex = candidateIndex;
354
+ break;
355
+ }
356
+ }
357
+ assert.ok(selected !== null, 'should find an available name');
358
+ assert.equal(selected, MOUNTAIN_NAMES[2], 'should select the third name after skipping first two');
359
+ assert.equal(selectedIndex, 2);
360
+ });
361
+ it('collision retry wraps around when baseIndex is near the end', () => {
362
+ // baseIndex near the end — should wrap around to the beginning
363
+ const lastIndex = MOUNTAIN_NAMES.length - 1;
364
+ const lastName = MOUNTAIN_NAMES[lastIndex];
365
+ const takenNames = new Set([lastName]);
366
+ const baseIndex = lastIndex;
367
+ let selected = null;
368
+ for (let attempt = 0; attempt < MOUNTAIN_NAMES.length; attempt++) {
369
+ const candidateIndex = (baseIndex + attempt) % MOUNTAIN_NAMES.length;
370
+ const candidateName = MOUNTAIN_NAMES[candidateIndex];
371
+ if (!takenNames.has(candidateName)) {
372
+ selected = candidateName;
373
+ break;
374
+ }
375
+ }
376
+ assert.ok(selected !== null, 'should find an available name after wrap-around');
377
+ // The first candidate tried was lastIndex (taken), so the next is index 0
378
+ assert.equal(selected, MOUNTAIN_NAMES[0], 'should wrap around to the first name');
379
+ });
380
+ it('nextMountainIndex advances to the candidate after the selected one', () => {
381
+ // After selecting candidateIndex N, nextMountainIndex should be N+1
382
+ const baseIndex = 0;
383
+ const takenNames = new Set();
384
+ let nextMountainIndex;
385
+ for (let attempt = 0; attempt < MOUNTAIN_NAMES.length; attempt++) {
386
+ const candidateIndex = (baseIndex + attempt) % MOUNTAIN_NAMES.length;
387
+ const candidateName = MOUNTAIN_NAMES[candidateIndex];
388
+ if (!takenNames.has(candidateName)) {
389
+ nextMountainIndex = candidateIndex + 1;
390
+ break;
391
+ }
392
+ }
393
+ assert.equal(nextMountainIndex, 1, 'nextMountainIndex should be 1 after selecting index 0');
394
+ });
395
+ it('all mountain names are unique', () => {
396
+ const unique = new Set(MOUNTAIN_NAMES);
397
+ assert.equal(unique.size, MOUNTAIN_NAMES.length, 'all mountain names should be unique');
398
+ });
399
+ it('mountain names contain only lowercase letters, digits, and hyphens', () => {
400
+ for (const name of MOUNTAIN_NAMES) {
401
+ assert.ok(/^[a-z0-9-]+$/.test(name), `mountain name "${name}" should only contain lowercase letters, digits, and hyphens`);
402
+ }
403
+ });
404
+ });
405
+ describe('workspace name from git remote', () => {
406
+ it('derives repo name from various git remote URLs', async () => {
407
+ const { repoNameFromRemoteUrl } = await import('../server/workspaces.js');
408
+ const fixtures = [
409
+ {
410
+ url: 'git@github.com:anthropic/claude-remote-cli.git',
411
+ expected: 'claude-remote-cli',
412
+ },
413
+ {
414
+ url: 'https://github.com/anthropic/claude-remote-cli.git',
415
+ expected: 'claude-remote-cli',
416
+ },
417
+ {
418
+ url: 'ssh://git@github.com/anthropic/claude-remote-cli.git',
419
+ expected: 'claude-remote-cli',
420
+ },
421
+ {
422
+ url: 'https://github.com/anthropic/claude-remote-cli',
423
+ expected: 'claude-remote-cli',
424
+ },
425
+ {
426
+ url: 'https://example.com/some-group/another-repo.git',
427
+ expected: 'another-repo',
428
+ },
429
+ {
430
+ url: 'https://example.com/some-group/another-repo/',
431
+ expected: 'another-repo',
432
+ },
433
+ ];
434
+ for (const { url, expected } of fixtures) {
435
+ const name = repoNameFromRemoteUrl(url);
436
+ assert.equal(name, expected, `should extract "${expected}" from "${url}"`);
437
+ assert.ok(name && name.length > 0, `should extract a non-empty name from "${url}"`);
438
+ assert.ok(name && !name.includes('/'), `name "${name}" from "${url}" should not contain slashes`);
439
+ }
440
+ });
441
+ });
442
+ describe('repo-scoped tmux naming', () => {
443
+ it('produces readable tmux names from repo-branch slugs', () => {
444
+ const name = generateTmuxSessionName('claude-remote-cli-nightly', 'a3b4c5d6-1234-5678');
445
+ assert.ok(name.includes('claude-remote-cli-nightly'));
446
+ assert.ok(name.includes('a3b4c5d6'));
447
+ });
448
+ it('sanitizes branch names with special characters', () => {
449
+ const name = generateTmuxSessionName('myapp-fix-auth-flow', 'b4c5d6e7-1234-5678');
450
+ assert.ok(name.includes('myapp-fix-auth-flow'));
451
+ assert.ok(!/[^a-zA-Z0-9-]/.test(name));
452
+ });
453
+ it('truncates long names to 30 chars before appending id', () => {
454
+ const longName = 'a-very-long-repository-name-with-a-very-long-branch-name';
455
+ const name = generateTmuxSessionName(longName, 'c5d6e7f8-1234-5678');
456
+ // Extract the sanitized middle portion: after "crc-" prefix and before "-{8-char-id}"
457
+ const prefix = name
458
+ .replace(/^(?:crcd?-)/, '')
459
+ .replace(/-[a-zA-Z0-9]{8}$/, '');
460
+ assert.ok(prefix.length <= 30, `prefix "${prefix}" (${prefix.length} chars) exceeds 30 chars`);
461
+ });
462
+ it('produces no special characters in output', () => {
463
+ const name = generateTmuxSessionName('repo/with/slashes and spaces', 'deadbeef-0000-1111');
464
+ assert.ok(!/[^a-zA-Z0-9-]/.test(name), `tmux name "${name}" contains invalid characters`);
465
+ });
466
+ });
467
+ describe('findOrCreateWorktreeForBranch', () => {
468
+ it('returns existing: true and isMain: true when branch is in main worktree', async () => {
469
+ const repoPath = '/Users/me/code/my-repo';
470
+ const stdout = [
471
+ `worktree ${repoPath}`,
472
+ 'HEAD abc123',
473
+ 'branch refs/heads/nightly',
474
+ '',
475
+ ].join('\n');
476
+ const exec = async (_cmd, args, _opts) => {
477
+ if (args[0] === 'worktree')
478
+ return { stdout, stderr: '' };
479
+ throw new Error('unexpected call');
480
+ };
481
+ const result = await findOrCreateWorktreeForBranch(repoPath, 'nightly', exec);
482
+ assert.equal(result.existing, true);
483
+ assert.equal(result.isMain, true);
484
+ assert.equal(result.worktreePath, repoPath);
485
+ assert.equal(result.branchName, 'nightly');
486
+ });
487
+ it('returns existing worktree when branch is in a sub-worktree', async () => {
488
+ const repoPath = '/Users/me/code/my-repo';
489
+ const stdout = [
490
+ `worktree ${repoPath}`,
491
+ 'HEAD abc123',
492
+ 'branch refs/heads/main',
493
+ '',
494
+ 'worktree /Users/me/code/my-repo/.worktrees/fix-auth',
495
+ 'HEAD def456',
496
+ 'branch refs/heads/fix/auth',
497
+ '',
498
+ ].join('\n');
499
+ const exec = async (_cmd, args, _opts) => {
500
+ if (args[0] === 'worktree')
501
+ return { stdout, stderr: '' };
502
+ throw new Error('unexpected call');
503
+ };
504
+ const result = await findOrCreateWorktreeForBranch(repoPath, 'fix/auth', exec);
505
+ assert.equal(result.existing, true);
506
+ assert.equal(result.isMain, false);
507
+ assert.equal(result.worktreePath, '/Users/me/code/my-repo/.worktrees/fix-auth');
508
+ });
509
+ it('creates worktree when branch is not checked out anywhere', async () => {
510
+ const repoPath = '/Users/me/code/my-repo';
511
+ const listStdout = [
512
+ `worktree ${repoPath}`,
513
+ 'HEAD abc123',
514
+ 'branch refs/heads/main',
515
+ '',
516
+ ].join('\n');
517
+ const calls = [];
518
+ const exec = async (_cmd, args, _opts) => {
519
+ calls.push(args);
520
+ if (args[0] === 'worktree' && args[1] === 'list')
521
+ return { stdout: listStdout, stderr: '' };
522
+ if (args[0] === 'worktree' && args[1] === 'add')
523
+ return { stdout: '', stderr: '' };
524
+ throw new Error(`unexpected: ${args.join(' ')}`);
525
+ };
526
+ const result = await findOrCreateWorktreeForBranch(repoPath, 'feat/new', exec);
527
+ assert.equal(result.existing, false);
528
+ assert.equal(result.isMain, false);
529
+ assert.equal(result.branchName, 'feat/new');
530
+ });
531
+ });
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "relay-ide",
3
+ "version": "0.1.0",
4
+ "description": "Relay Agentic Development Environment — remote web interface for AI coding agent sessions",
5
+ "type": "module",
6
+ "main": "dist/server/index.js",
7
+ "bin": {
8
+ "relay-ide": "dist/bin/relay-ide.js"
9
+ },
10
+ "files": [
11
+ "dist/"
12
+ ],
13
+ "scripts": {
14
+ "check": "tsc --noEmit",
15
+ "build": "tsc && vite build --config frontend/vite.config.ts",
16
+ "build:server": "tsc",
17
+ "build:frontend": "vite build --config frontend/vite.config.ts",
18
+ "dev": "RELAY_IDE_CONFIG=./config.dev.json NO_PIN=1 RELAY_IDE_PORT=3457 node dist/server/index.js",
19
+ "dev:vite": "vite --config frontend/vite.config.ts",
20
+ "start": "tsc && vite build --config frontend/vite.config.ts && node dist/server/index.js",
21
+ "test": "tsc -p tsconfig.test.json && node --test --test-force-exit dist/test/*.test.js dist/test/**/*.test.js",
22
+ "test:e2e": "playwright test",
23
+ "test:e2e:ui": "playwright test --ui",
24
+ "test:e2e:debug": "playwright test --debug",
25
+ "test:e2e:update": "playwright test --update-snapshots",
26
+ "prepublishOnly": "npm run build",
27
+ "postinstall": "node scripts/postinstall.js"
28
+ },
29
+ "engines": {
30
+ "node": ">=24.0.0"
31
+ },
32
+ "keywords": [
33
+ "relay",
34
+ "relay-ide",
35
+ "agentic",
36
+ "ai",
37
+ "development",
38
+ "remote",
39
+ "terminal",
40
+ "mobile",
41
+ "web",
42
+ "pty"
43
+ ],
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/donovan-yohan/relay-ide.git"
47
+ },
48
+ "license": "MIT",
49
+ "author": "Donovan Yohan",
50
+ "dependencies": {
51
+ "@tanstack/react-query": "^5.96.1",
52
+ "@xterm/addon-fit": "^0.11.0",
53
+ "@xterm/addon-web-links": "^0.12.0",
54
+ "@xterm/xterm": "^6.0.0",
55
+ "better-sqlite3": "^12.8.0",
56
+ "cookie-parser": "^1.4.7",
57
+ "diff2html": "^3.4.56",
58
+ "eslint-plugin-sonarjs": "^4.0.2",
59
+ "express": "^4.21.0",
60
+ "node-pty": "^1.0.0",
61
+ "react": "^19.2.4",
62
+ "react-dom": "^19.2.4",
63
+ "shiki": "^4.0.2",
64
+ "smee-client": "^5.0.0",
65
+ "web-push": "^3.6.7",
66
+ "ws": "^8.18.0",
67
+ "zustand": "^5.0.12"
68
+ },
69
+ "devDependencies": {
70
+ "@eslint/js": "^10.0.1",
71
+ "@playwright/test": "^1.58.2",
72
+ "@types/better-sqlite3": "^7.6.13",
73
+ "@types/cookie-parser": "^1.4.7",
74
+ "@types/express": "^4.17.21",
75
+ "@types/node": "^22.0.0",
76
+ "@types/react": "^19.2.14",
77
+ "@types/react-dom": "^19.2.3",
78
+ "@types/web-push": "^3.6.4",
79
+ "@types/ws": "^8.5.13",
80
+ "@vitejs/plugin-react": "^4.7.0",
81
+ "globals": "^17.4.0",
82
+ "happy-dom": "^20.8.9",
83
+ "playwright": "^1.58.2",
84
+ "typescript": "^5.7.0",
85
+ "typescript-eslint": "^8.58.0",
86
+ "vite": "^6.4.1"
87
+ }
88
+ }