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,657 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { Router } from 'express';
7
+ import { loadConfig, saveConfig, getWorkspaceSettings, setWorkspaceSettings, deleteWorkspaceSettingKeys } from './config.js';
8
+ import { trackEvent } from './analytics.js';
9
+ import { listBranches, getActivityFeed, getCiStatus, getPrForBranch, getUnresolvedCommentCount, switchBranch, getCurrentBranch } from './git.js';
10
+ import { MOUNTAIN_NAMES } from './types.js';
11
+ const execFileAsync = promisify(execFile);
12
+ const BROWSE_DENYLIST = new Set([
13
+ 'node_modules', '.git', '.Trash', '__pycache__',
14
+ '.cache', '.npm', '.yarn', '.nvm',
15
+ ]);
16
+ const BROWSE_MAX_ENTRIES = 100;
17
+ const BULK_MAX_PATHS = 50;
18
+ // Exported helpers
19
+ /**
20
+ * Resolves and validates a raw workspace path string.
21
+ * Throws with a human-readable message if the path is invalid.
22
+ */
23
+ export async function validateWorkspacePath(rawPath) {
24
+ if (!rawPath || typeof rawPath !== 'string') {
25
+ throw new Error('Path must be a non-empty string');
26
+ }
27
+ const resolved = path.resolve(rawPath);
28
+ let stat;
29
+ try {
30
+ stat = await fs.promises.stat(resolved);
31
+ }
32
+ catch {
33
+ throw new Error(`Path does not exist: ${resolved}`);
34
+ }
35
+ if (!stat.isDirectory()) {
36
+ throw new Error(`Path is not a directory: ${resolved}`);
37
+ }
38
+ return resolved;
39
+ }
40
+ /**
41
+ * Detects whether a directory is the root of a git repository and, if so,
42
+ * what the default branch name is.
43
+ */
44
+ export async function detectGitRepo(dirPath, execAsync = execFileAsync) {
45
+ try {
46
+ await execAsync('git', ['rev-parse', '--git-dir'], { cwd: dirPath });
47
+ }
48
+ catch {
49
+ return { isGitRepo: false, defaultBranch: null };
50
+ }
51
+ // Attempt to determine the default branch from remote HEAD
52
+ let defaultBranch = null;
53
+ try {
54
+ const { stdout } = await execAsync('git', ['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'], { cwd: dirPath });
55
+ const trimmed = stdout.trim();
56
+ // "origin/main" → "main"
57
+ defaultBranch = trimmed.replace(/^origin\//, '') || null;
58
+ }
59
+ catch {
60
+ // Fall back to checking local HEAD
61
+ try {
62
+ const { stdout } = await execAsync('git', ['symbolic-ref', '--short', 'HEAD'], { cwd: dirPath });
63
+ defaultBranch = stdout.trim() || null;
64
+ }
65
+ catch {
66
+ // Cannot determine default branch
67
+ }
68
+ }
69
+ return { isGitRepo: true, defaultBranch };
70
+ }
71
+ // Router factory
72
+ /**
73
+ * Creates and returns an Express Router that handles all /workspaces routes.
74
+ *
75
+ * Caller is responsible for mounting and applying auth middleware:
76
+ * app.use('/workspaces', requireAuth, createWorkspaceRouter({ configPath }));
77
+ */
78
+ export function createWorkspaceRouter(deps) {
79
+ const { configPath } = deps;
80
+ const exec = deps.execAsync ?? execFileAsync;
81
+ const router = Router();
82
+ // Helper: reload config on every request so concurrent changes are reflected
83
+ function getConfig() {
84
+ return loadConfig(configPath);
85
+ }
86
+ // GET /workspaces — list all workspaces with git info
87
+ router.get('/', async (_req, res) => {
88
+ const config = getConfig();
89
+ const workspacePaths = config.workspaces ?? [];
90
+ const results = await Promise.all(workspacePaths.map(async (p) => {
91
+ const name = path.basename(p);
92
+ const { isGitRepo, defaultBranch } = await detectGitRepo(p, exec);
93
+ return { path: p, name, isGitRepo, defaultBranch };
94
+ }));
95
+ res.json({ workspaces: results });
96
+ });
97
+ // POST /workspaces — add a workspace
98
+ router.post('/', async (req, res) => {
99
+ const body = req.body;
100
+ const rawPath = body.path;
101
+ if (typeof rawPath !== 'string' || !rawPath) {
102
+ res.status(400).json({ error: 'path is required' });
103
+ return;
104
+ }
105
+ let resolved;
106
+ try {
107
+ resolved = await validateWorkspacePath(rawPath);
108
+ }
109
+ catch (err) {
110
+ res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
111
+ return;
112
+ }
113
+ const config = getConfig();
114
+ const workspaces = config.workspaces ?? [];
115
+ if (workspaces.includes(resolved)) {
116
+ res.status(409).json({ error: 'Workspace already exists' });
117
+ return;
118
+ }
119
+ const { isGitRepo, defaultBranch } = await detectGitRepo(resolved, exec);
120
+ config.workspaces = [...workspaces, resolved];
121
+ // Store detected default branch in per-workspace settings
122
+ if (isGitRepo && defaultBranch) {
123
+ if (!config.workspaceSettings)
124
+ config.workspaceSettings = {};
125
+ config.workspaceSettings[resolved] = {
126
+ ...config.workspaceSettings[resolved],
127
+ defaultBranch,
128
+ };
129
+ }
130
+ saveConfig(configPath, config);
131
+ trackEvent({ category: 'workspace', action: 'added', target: resolved, properties: { name: path.basename(resolved) } });
132
+ const workspace = {
133
+ path: resolved,
134
+ name: path.basename(resolved),
135
+ isGitRepo,
136
+ defaultBranch,
137
+ };
138
+ res.status(201).json(workspace);
139
+ });
140
+ // DELETE /workspaces — remove a workspace
141
+ router.delete('/', async (req, res) => {
142
+ const body = req.body;
143
+ const rawPath = body.path;
144
+ if (typeof rawPath !== 'string' || !rawPath) {
145
+ res.status(400).json({ error: 'path is required' });
146
+ return;
147
+ }
148
+ const resolved = path.resolve(rawPath);
149
+ const config = getConfig();
150
+ const workspaces = config.workspaces ?? [];
151
+ const idx = workspaces.indexOf(resolved);
152
+ if (idx === -1) {
153
+ res.status(404).json({ error: 'Workspace not found' });
154
+ return;
155
+ }
156
+ config.workspaces = workspaces.filter((p) => p !== resolved);
157
+ saveConfig(configPath, config);
158
+ trackEvent({ category: 'workspace', action: 'removed', target: resolved });
159
+ res.json({ removed: resolved });
160
+ });
161
+ // PUT /workspaces/reorder — reorder workspaces
162
+ router.put('/reorder', async (req, res) => {
163
+ const body = req.body;
164
+ const rawPaths = body.paths;
165
+ if (!Array.isArray(rawPaths)) {
166
+ res.status(400).json({ error: 'paths array is required' });
167
+ return;
168
+ }
169
+ const config = getConfig();
170
+ const current = config.workspaces ?? [];
171
+ // Validate that the submitted paths are the same set as the current workspaces
172
+ if (rawPaths.length !== current.length) {
173
+ res.status(400).json({ error: 'paths must contain the same set of workspaces as the current configuration' });
174
+ return;
175
+ }
176
+ const currentSet = new Set(current);
177
+ for (const p of rawPaths) {
178
+ if (typeof p !== 'string' || !currentSet.has(p)) {
179
+ res.status(400).json({ error: 'paths must contain the same set of workspaces as the current configuration' });
180
+ return;
181
+ }
182
+ }
183
+ config.workspaces = rawPaths;
184
+ saveConfig(configPath, config);
185
+ const results = await Promise.all(rawPaths.map(async (p) => {
186
+ const name = path.basename(p);
187
+ const { isGitRepo, defaultBranch } = await detectGitRepo(p, exec);
188
+ return { path: p, name, isGitRepo, defaultBranch };
189
+ }));
190
+ res.json({ workspaces: results });
191
+ });
192
+ // POST /workspaces/bulk — add multiple workspaces at once
193
+ router.post('/bulk', async (req, res) => {
194
+ const body = req.body;
195
+ const rawPaths = body.paths;
196
+ if (!Array.isArray(rawPaths) || rawPaths.length === 0) {
197
+ res.status(400).json({ error: 'paths array is required' });
198
+ return;
199
+ }
200
+ if (rawPaths.length > BULK_MAX_PATHS) {
201
+ res.status(400).json({ error: `Too many paths (max ${BULK_MAX_PATHS})` });
202
+ return;
203
+ }
204
+ const config = getConfig();
205
+ const existing = new Set(config.workspaces ?? []);
206
+ const added = [];
207
+ const errors = [];
208
+ for (const rawPath of rawPaths) {
209
+ if (typeof rawPath !== 'string' || !rawPath) {
210
+ errors.push({ path: String(rawPath), error: 'Invalid path' });
211
+ continue;
212
+ }
213
+ let resolved;
214
+ try {
215
+ resolved = await validateWorkspacePath(rawPath);
216
+ }
217
+ catch (err) {
218
+ errors.push({ path: rawPath, error: err instanceof Error ? err.message : String(err) });
219
+ continue;
220
+ }
221
+ if (existing.has(resolved)) {
222
+ errors.push({ path: rawPath, error: 'Already exists' });
223
+ continue;
224
+ }
225
+ const { isGitRepo, defaultBranch } = await detectGitRepo(resolved, exec);
226
+ existing.add(resolved);
227
+ added.push({ path: resolved, name: path.basename(resolved), isGitRepo, defaultBranch });
228
+ // Store detected default branch in per-workspace settings
229
+ if (isGitRepo && defaultBranch) {
230
+ if (!config.workspaceSettings)
231
+ config.workspaceSettings = {};
232
+ config.workspaceSettings[resolved] = {
233
+ ...config.workspaceSettings[resolved],
234
+ defaultBranch,
235
+ };
236
+ }
237
+ }
238
+ if (added.length > 0) {
239
+ config.workspaces = [...(config.workspaces ?? []), ...added.map((a) => a.path)];
240
+ saveConfig(configPath, config);
241
+ }
242
+ res.status(201).json({ added, errors });
243
+ });
244
+ // GET /workspaces/dashboard — aggregated PR + activity data for a workspace
245
+ router.get('/dashboard', async (req, res) => {
246
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
247
+ if (!workspacePath) {
248
+ res.status(400).json({ error: 'path query parameter is required' });
249
+ return;
250
+ }
251
+ const fields = 'number,title,url,headRefName,baseRefName,state,author,updatedAt,additions,deletions,reviewDecision,mergeable,mergeStateStatus';
252
+ // Get current GitHub user
253
+ let currentUser = '';
254
+ try {
255
+ const { stdout: whoami } = await exec('gh', ['api', 'user', '--jq', '.login'], { cwd: workspacePath });
256
+ currentUser = whoami.trim();
257
+ }
258
+ catch {
259
+ const response = { prs: [], error: 'gh_not_authenticated' };
260
+ res.json({ pullRequests: response, branches: [] });
261
+ return;
262
+ }
263
+ // Helper to map raw gh JSON to PullRequest
264
+ function mapRawPr(raw, role, fallbackAuthor) {
265
+ return {
266
+ number: raw.number,
267
+ title: raw.title,
268
+ url: raw.url,
269
+ headRefName: raw.headRefName,
270
+ baseRefName: raw.baseRefName ?? '',
271
+ state: raw.state,
272
+ author: raw.author?.login ?? fallbackAuthor,
273
+ role,
274
+ updatedAt: raw.updatedAt,
275
+ additions: raw.additions ?? 0,
276
+ deletions: raw.deletions ?? 0,
277
+ reviewDecision: raw.reviewDecision ?? null,
278
+ mergeable: raw.mergeable ?? null,
279
+ };
280
+ }
281
+ // Fetch authored + review-requested PRs in parallel
282
+ const [authored, reviewing] = await Promise.all([
283
+ (async () => {
284
+ try {
285
+ const { stdout } = await exec('gh', ['pr', 'list', '--author', currentUser, '--state', 'open', '--limit', '30', '--json', fields], { cwd: workspacePath });
286
+ return JSON.parse(stdout).map(pr => mapRawPr(pr, 'author', currentUser));
287
+ }
288
+ catch {
289
+ return [];
290
+ }
291
+ })(),
292
+ (async () => {
293
+ try {
294
+ const { stdout } = await exec('gh', ['pr', 'list', '--search', `review-requested:${currentUser}`, '--state', 'open', '--limit', '30', '--json', fields], { cwd: workspacePath });
295
+ return JSON.parse(stdout).map(pr => mapRawPr(pr, 'reviewer', ''));
296
+ }
297
+ catch {
298
+ return [];
299
+ }
300
+ })(),
301
+ ]);
302
+ // Deduplicate: if a PR appears in both, keep as 'author'
303
+ const seen = new Set(authored.map((pr) => pr.number));
304
+ const combined = [...authored, ...reviewing.filter((pr) => !seen.has(pr.number))];
305
+ // Sort by updatedAt descending
306
+ combined.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
307
+ const pullRequests = { prs: combined };
308
+ // Fetch branches for the workspace
309
+ let branches = [];
310
+ try {
311
+ branches = await listBranches(workspacePath);
312
+ }
313
+ catch { /* not a git repo or git unavailable */ }
314
+ // Fetch recent activity
315
+ let activity = [];
316
+ try {
317
+ activity = await getActivityFeed(workspacePath);
318
+ }
319
+ catch { /* git log unavailable */ }
320
+ res.json({
321
+ pullRequests,
322
+ branches,
323
+ activity,
324
+ });
325
+ });
326
+ function buildMergedSettings(config, workspacePath) {
327
+ const resolved = path.resolve(workspacePath);
328
+ const wsOverrides = config.workspaceSettings?.[resolved] ?? {};
329
+ const effective = getWorkspaceSettings(config, resolved);
330
+ const overridden = [];
331
+ for (const key of ['defaultAgent', 'defaultContinue', 'defaultYolo', 'launchInTmux']) {
332
+ if (wsOverrides[key] !== undefined)
333
+ overridden.push(key);
334
+ }
335
+ return { settings: effective, overridden };
336
+ }
337
+ // GET /workspaces/settings — per-workspace overrides only
338
+ router.get('/settings', async (req, res) => {
339
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
340
+ if (!workspacePath) {
341
+ res.status(400).json({ error: 'path query parameter is required' });
342
+ return;
343
+ }
344
+ // Backward compat: handle merged=true inline (same logic as /settings/merged)
345
+ if (req.query.merged === 'true') {
346
+ res.json(buildMergedSettings(getConfig(), workspacePath));
347
+ return;
348
+ }
349
+ const config = getConfig();
350
+ const resolved = path.resolve(workspacePath);
351
+ const settings = config.workspaceSettings?.[resolved] ?? {};
352
+ res.json(settings);
353
+ });
354
+ // GET /workspaces/settings/merged — effective settings with override tracking
355
+ router.get('/settings/merged', async (req, res) => {
356
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
357
+ if (!workspacePath) {
358
+ res.status(400).json({ error: 'path query parameter is required' });
359
+ return;
360
+ }
361
+ res.json(buildMergedSettings(getConfig(), workspacePath));
362
+ });
363
+ // PATCH /workspaces/settings — update per-workspace settings
364
+ router.patch('/settings', async (req, res) => {
365
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
366
+ if (!workspacePath) {
367
+ res.status(400).json({ error: 'path query parameter is required' });
368
+ return;
369
+ }
370
+ const resolved = path.resolve(workspacePath);
371
+ const updates = req.body;
372
+ const config = getConfig();
373
+ // Separate null values (deletions) from actual updates
374
+ const keysToDelete = [];
375
+ const keysToUpdate = {};
376
+ for (const [key, value] of Object.entries(updates)) {
377
+ if (value === null) {
378
+ keysToDelete.push(key);
379
+ }
380
+ else {
381
+ keysToUpdate[key] = value;
382
+ }
383
+ }
384
+ // Apply deletions first
385
+ if (keysToDelete.length > 0) {
386
+ deleteWorkspaceSettingKeys(configPath, config, resolved, keysToDelete);
387
+ }
388
+ // Apply updates
389
+ if (Object.keys(keysToUpdate).length > 0) {
390
+ setWorkspaceSettings(configPath, config, resolved, keysToUpdate);
391
+ }
392
+ // Return the current raw workspace settings
393
+ const final = config.workspaceSettings?.[resolved] ?? {};
394
+ res.json(final);
395
+ });
396
+ // GET /workspaces/pr — PR info for a specific branch
397
+ router.get('/pr', async (req, res) => {
398
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
399
+ const branch = typeof req.query.branch === 'string' ? req.query.branch : undefined;
400
+ if (!workspacePath || !branch) {
401
+ res.status(400).json({ error: 'path and branch query parameters are required' });
402
+ return;
403
+ }
404
+ try {
405
+ const pr = await getPrForBranch(workspacePath, branch);
406
+ if (pr) {
407
+ if (pr.state === 'OPEN') {
408
+ const unresolvedCommentCount = await getUnresolvedCommentCount(workspacePath, pr.number);
409
+ res.json({ ...pr, unresolvedCommentCount });
410
+ }
411
+ else {
412
+ res.json({ ...pr, unresolvedCommentCount: 0 });
413
+ }
414
+ }
415
+ else {
416
+ res.status(404).json({ error: 'No PR found for branch' });
417
+ }
418
+ }
419
+ catch {
420
+ res.status(404).json({ error: 'No PR found for branch' });
421
+ }
422
+ });
423
+ // GET /workspaces/ci-status — CI check results for a workspace + branch
424
+ router.get('/ci-status', async (req, res) => {
425
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
426
+ const branch = typeof req.query.branch === 'string' ? req.query.branch : undefined;
427
+ if (!workspacePath) {
428
+ res.status(400).json({ error: 'path query parameter is required' });
429
+ return;
430
+ }
431
+ try {
432
+ const status = await getCiStatus(workspacePath, branch ?? 'HEAD');
433
+ res.json(status);
434
+ }
435
+ catch {
436
+ res.json({ total: 0, passing: 0, failing: 0, pending: 0 });
437
+ }
438
+ });
439
+ // POST /workspaces/branch — switch branch for a workspace
440
+ router.post('/branch', async (req, res) => {
441
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
442
+ if (!workspacePath) {
443
+ res.status(400).json({ error: 'path query parameter is required' });
444
+ return;
445
+ }
446
+ const body = req.body;
447
+ const branch = body.branch;
448
+ if (typeof branch !== 'string' || !branch) {
449
+ res.status(400).json({ error: 'branch is required in request body' });
450
+ return;
451
+ }
452
+ const result = await switchBranch(workspacePath, branch);
453
+ if (result.success) {
454
+ res.json({ path: workspacePath, branch });
455
+ }
456
+ else {
457
+ res.status(400).json({ error: result.error ?? `Failed to switch to branch: ${branch}` });
458
+ }
459
+ });
460
+ // POST /workspaces/worktree — create a new worktree with the next mountain name
461
+ router.post('/worktree', async (req, res) => {
462
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
463
+ if (!workspacePath) {
464
+ res.status(400).json({ error: 'path query parameter is required' });
465
+ return;
466
+ }
467
+ const existingBranch = typeof req.body?.branch === 'string' ? req.body.branch : undefined;
468
+ const resolved = path.resolve(workspacePath);
469
+ const config = getConfig();
470
+ const settings = getWorkspaceSettings(config, resolved);
471
+ let branchName;
472
+ let mountainName;
473
+ let gitArgs;
474
+ let nextMountainIndex;
475
+ if (existingBranch) {
476
+ // Checkout existing branch into a worktree — sanitize branch name for use as directory name
477
+ mountainName = existingBranch.replace(/\//g, '-').replace(/[^a-zA-Z0-9._-]/g, '');
478
+ branchName = existingBranch;
479
+ gitArgs = ['worktree', 'add', path.join(resolved, '.worktrees', mountainName), existingBranch];
480
+ }
481
+ else {
482
+ // Create a new branch using the next mountain name
483
+ const index = settings.nextMountainIndex ?? 0;
484
+ mountainName = MOUNTAIN_NAMES[index % MOUNTAIN_NAMES.length] ?? 'everest';
485
+ branchName = (settings.branchPrefix ?? '') + mountainName;
486
+ nextMountainIndex = index + 1;
487
+ // Detect base branch: user setting > git detected > fallback
488
+ let baseBranch = settings.defaultBranch;
489
+ if (!baseBranch) {
490
+ const detected = await detectGitRepo(resolved);
491
+ baseBranch = detected.defaultBranch ?? 'main';
492
+ }
493
+ gitArgs = ['worktree', 'add', '-b', branchName, path.join(resolved, '.worktrees', mountainName), baseBranch];
494
+ }
495
+ const worktreePath = path.join(resolved, '.worktrees', mountainName);
496
+ try {
497
+ // Ensure .worktrees/ is in .gitignore
498
+ const gitignorePath = path.join(resolved, '.gitignore');
499
+ try {
500
+ const existing = await fs.promises.readFile(gitignorePath, 'utf8');
501
+ if (!existing.includes('.worktrees/')) {
502
+ await fs.promises.appendFile(gitignorePath, '\n.worktrees/\n');
503
+ }
504
+ }
505
+ catch {
506
+ await fs.promises.writeFile(gitignorePath, '.worktrees/\n');
507
+ }
508
+ await exec('git', gitArgs, { cwd: resolved });
509
+ }
510
+ catch (err) {
511
+ const msg = err instanceof Error ? err.message : String(err);
512
+ res.status(500).json({ error: `Failed to create worktree: ${msg}` });
513
+ return;
514
+ }
515
+ // Increment mountain counter AFTER successful creation (don't skip names on failure)
516
+ if (nextMountainIndex !== undefined) {
517
+ setWorkspaceSettings(configPath, config, resolved, { nextMountainIndex });
518
+ }
519
+ res.json({ branchName, mountainName, worktreePath });
520
+ });
521
+ // GET /workspaces/current-branch — current checked-out branch for a path
522
+ router.get('/current-branch', async (req, res) => {
523
+ const workspacePath = typeof req.query.path === 'string' ? req.query.path : undefined;
524
+ if (!workspacePath) {
525
+ res.status(400).json({ error: 'path query parameter is required' });
526
+ return;
527
+ }
528
+ const branch = await getCurrentBranch(path.resolve(workspacePath));
529
+ res.json({ branch });
530
+ });
531
+ // GET /workspaces/browse — browse filesystem directories for tree UI
532
+ router.get('/browse', async (req, res) => {
533
+ const rawPath = typeof req.query.path === 'string' ? req.query.path : '~';
534
+ const prefix = typeof req.query.prefix === 'string' ? req.query.prefix : '';
535
+ const showHidden = req.query.showHidden === 'true';
536
+ // Resolve ~ to home directory
537
+ const expanded = rawPath === '~' || rawPath.startsWith('~/')
538
+ ? path.join(os.homedir(), rawPath.slice(1))
539
+ : rawPath;
540
+ const resolved = path.resolve(expanded);
541
+ let stat;
542
+ try {
543
+ stat = await fs.promises.stat(resolved);
544
+ }
545
+ catch (err) {
546
+ const code = err.code;
547
+ if (code === 'EACCES') {
548
+ res.status(403).json({ error: 'Permission denied' });
549
+ }
550
+ else {
551
+ res.status(400).json({ error: `Path does not exist: ${resolved}` });
552
+ }
553
+ return;
554
+ }
555
+ if (!stat.isDirectory()) {
556
+ res.status(400).json({ error: `Not a directory: ${resolved}` });
557
+ return;
558
+ }
559
+ let dirents;
560
+ try {
561
+ dirents = await fs.promises.readdir(resolved, { withFileTypes: true });
562
+ }
563
+ catch {
564
+ res.status(403).json({ error: 'Cannot read directory' });
565
+ return;
566
+ }
567
+ // Filter to directories only, apply denylist, hidden filter, prefix filter
568
+ let dirs = dirents.filter((d) => {
569
+ if (!d.isDirectory())
570
+ return false;
571
+ if (BROWSE_DENYLIST.has(d.name))
572
+ return false;
573
+ // Also check if name contains a path separator component in denylist
574
+ // e.g. "Library/Caches" — we check the full name, not path components
575
+ if (!showHidden && d.name.startsWith('.'))
576
+ return false;
577
+ if (prefix && !d.name.toLowerCase().startsWith(prefix.toLowerCase()))
578
+ return false;
579
+ return true;
580
+ });
581
+ dirs.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
582
+ const total = dirs.length;
583
+ const truncated = dirs.length > BROWSE_MAX_ENTRIES;
584
+ if (truncated)
585
+ dirs = dirs.slice(0, BROWSE_MAX_ENTRIES);
586
+ // Enrich each entry with isGitRepo and hasChildren (parallelized)
587
+ const entries = await Promise.all(dirs.map(async (d) => {
588
+ const entryPath = path.join(resolved, d.name);
589
+ let isGitRepo = false;
590
+ try {
591
+ const gitStat = await fs.promises.stat(path.join(entryPath, '.git'));
592
+ isGitRepo = gitStat.isDirectory();
593
+ }
594
+ catch {
595
+ // not a git repo
596
+ }
597
+ let hasChildren = false;
598
+ try {
599
+ const children = await fs.promises.readdir(entryPath, { withFileTypes: true });
600
+ hasChildren = children.some((c) => c.isDirectory() && !BROWSE_DENYLIST.has(c.name));
601
+ }
602
+ catch {
603
+ // can't read — treat as no children
604
+ }
605
+ return {
606
+ name: d.name,
607
+ path: entryPath,
608
+ isGitRepo,
609
+ hasChildren,
610
+ };
611
+ }));
612
+ res.json({ resolved, entries, truncated, total });
613
+ });
614
+ // GET /workspaces/autocomplete — path prefix autocomplete
615
+ router.get('/autocomplete', async (req, res) => {
616
+ const prefix = typeof req.query.prefix === 'string' ? req.query.prefix : '';
617
+ if (!prefix) {
618
+ res.json({ suggestions: [] });
619
+ return;
620
+ }
621
+ const expanded = prefix.startsWith('~')
622
+ ? path.join(process.env.HOME ?? '~', prefix.slice(1))
623
+ : prefix;
624
+ let dirToRead;
625
+ let partialName;
626
+ if (expanded.endsWith('/') || expanded.endsWith(path.sep)) {
627
+ // User typed a trailing slash — list immediate children of that dir
628
+ dirToRead = expanded;
629
+ partialName = '';
630
+ }
631
+ else {
632
+ dirToRead = path.dirname(expanded);
633
+ partialName = path.basename(expanded).toLowerCase();
634
+ }
635
+ let suggestions = [];
636
+ try {
637
+ const entries = await fs.promises.readdir(dirToRead, { withFileTypes: true });
638
+ suggestions = entries
639
+ .filter((e) => {
640
+ if (!e.isDirectory())
641
+ return false;
642
+ if (e.name.startsWith('.'))
643
+ return false;
644
+ if (!partialName)
645
+ return true;
646
+ return e.name.toLowerCase().startsWith(partialName);
647
+ })
648
+ .map((e) => path.join(dirToRead, e.name))
649
+ .slice(0, 20); // cap results
650
+ }
651
+ catch {
652
+ // Directory doesn't exist or permission denied — return empty
653
+ }
654
+ res.json({ suggestions });
655
+ });
656
+ return router;
657
+ }