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,349 @@
1
+ import { test, before, after } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import express from 'express';
7
+ import { createOrgDashboardRouter, } from '../server/org-dashboard.js';
8
+ import { saveConfig, DEFAULTS } from '../server/config.js';
9
+ let tmpDir;
10
+ let configPath;
11
+ let server;
12
+ let baseUrl;
13
+ // A workspace path we can point git remote mocks at
14
+ const WORKSPACE_PATH_A = '/fake/workspace/repo-a';
15
+ const WORKSPACE_PATH_B = '/fake/workspace/repo-b';
16
+ /**
17
+ * Creates a mock execAsync that routes calls based on the command.
18
+ * - `git remote get-url origin` → returns configured remote URL or throws
19
+ * - `gh api user ...` → returns configured user login or throws
20
+ * - `gh api search/issues ...` → returns configured search response or throws
21
+ */
22
+ function makeMockExec(opts) {
23
+ return async (cmd, args, options) => {
24
+ const command = cmd;
25
+ const argv = args;
26
+ if (command === 'git' && argv[0] === 'remote') {
27
+ const cwd = options.cwd ?? '';
28
+ const remote = opts.remotes?.[cwd];
29
+ if (remote)
30
+ return { stdout: remote + '\n', stderr: '' };
31
+ throw Object.assign(new Error('not a git repository'), { code: 128 });
32
+ }
33
+ if (command === 'gh' && argv[0] === 'api' && argv[1] === 'user') {
34
+ if (opts.userError)
35
+ throw opts.userError;
36
+ const login = opts.userLogin ?? 'testuser';
37
+ return { stdout: login + '\n', stderr: '' };
38
+ }
39
+ if (command === 'gh' &&
40
+ argv[0] === 'api' &&
41
+ argv[1]?.startsWith('search/issues')) {
42
+ if (opts.searchError)
43
+ throw opts.searchError;
44
+ const items = opts.searchItems ?? [];
45
+ return { stdout: JSON.stringify({ items }), stderr: '' };
46
+ }
47
+ throw new Error(`Unexpected exec call: ${command} ${argv.join(' ')}`);
48
+ };
49
+ }
50
+ /**
51
+ * Builds a minimal GH search item for a given owner/repo and user.
52
+ */
53
+ function makeSearchItem(overrides) {
54
+ const { ownerRepo, number = 1, title = 'Test PR', author = 'testuser', role = 'author', currentUser = 'testuser', } = overrides;
55
+ return {
56
+ number,
57
+ title,
58
+ html_url: `https://github.com/${ownerRepo}/pull/${number}`,
59
+ state: 'open',
60
+ user: { login: author },
61
+ pull_request: { head: { ref: 'feat/branch' }, base: { ref: 'main' } },
62
+ updated_at: '2026-03-21T00:00:00Z',
63
+ requested_reviewers: role === 'reviewer' ? [{ login: currentUser }] : [],
64
+ repository_url: `https://api.github.com/repos/${ownerRepo}`,
65
+ };
66
+ }
67
+ function startServer(execAsyncFn) {
68
+ return new Promise((resolve) => {
69
+ const app = express();
70
+ app.use(express.json());
71
+ // Cast through unknown: the mock satisfies the runtime contract but the
72
+ // overloaded promisify types don't align across module instances.
73
+ const deps = {
74
+ configPath,
75
+ execAsync: execAsyncFn,
76
+ };
77
+ app.use('/org-dashboard', createOrgDashboardRouter(deps));
78
+ server = app.listen(0, '127.0.0.1', () => {
79
+ const addr = server.address();
80
+ if (typeof addr === 'object' && addr) {
81
+ baseUrl = `http://127.0.0.1:${addr.port}`;
82
+ }
83
+ resolve();
84
+ });
85
+ });
86
+ }
87
+ function stopServer() {
88
+ return new Promise((resolve) => {
89
+ if (server)
90
+ server.close(() => resolve());
91
+ else
92
+ resolve();
93
+ });
94
+ }
95
+ async function getPrs() {
96
+ const res = await fetch(`${baseUrl}/org-dashboard/prs`);
97
+ return res.json();
98
+ }
99
+ before(() => {
100
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'org-dashboard-test-'));
101
+ configPath = path.join(tmpDir, 'config.json');
102
+ });
103
+ after(async () => {
104
+ await stopServer();
105
+ fs.rmSync(tmpDir, { recursive: true, force: true });
106
+ });
107
+ // Each test gets a fresh server with its own router (and thus its own cache).
108
+ // We stop/start the server around each test to reset the in-router cache state.
109
+ test('returns prs filtered to workspace repos', async () => {
110
+ await stopServer();
111
+ saveConfig(configPath, {
112
+ ...DEFAULTS,
113
+ repos: [WORKSPACE_PATH_A, WORKSPACE_PATH_B],
114
+ });
115
+ const exec = makeMockExec({
116
+ remotes: {
117
+ [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git',
118
+ [WORKSPACE_PATH_B]: 'git@github.com:myorg/repo-b.git',
119
+ },
120
+ userLogin: 'testuser',
121
+ searchItems: [
122
+ // Matches WORKSPACE_PATH_A
123
+ makeSearchItem({
124
+ ownerRepo: 'myorg/repo-a',
125
+ number: 10,
126
+ author: 'testuser',
127
+ }),
128
+ // Matches WORKSPACE_PATH_B
129
+ makeSearchItem({
130
+ ownerRepo: 'myorg/repo-b',
131
+ number: 20,
132
+ author: 'testuser',
133
+ }),
134
+ // Not in any workspace — should be excluded
135
+ makeSearchItem({
136
+ ownerRepo: 'myorg/other-repo',
137
+ number: 30,
138
+ author: 'testuser',
139
+ }),
140
+ ],
141
+ });
142
+ await startServer(exec);
143
+ const data = await getPrs();
144
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
145
+ assert.equal(data.prs.length, 2, 'Should return only the 2 workspace-matched PRs');
146
+ const numbers = data.prs.map((p) => p.number).sort((a, b) => a - b);
147
+ assert.deepEqual(numbers, [10, 20]);
148
+ // Verify repoPath is attached
149
+ const pr10 = data.prs.find((p) => p.number === 10);
150
+ assert.equal(pr10?.repoPath, WORKSPACE_PATH_A);
151
+ });
152
+ test('returns gh_not_in_path error when gh not found', async () => {
153
+ await stopServer();
154
+ saveConfig(configPath, {
155
+ ...DEFAULTS,
156
+ repos: [WORKSPACE_PATH_A],
157
+ });
158
+ const notFoundError = Object.assign(new Error('spawn gh ENOENT'), {
159
+ code: 'ENOENT',
160
+ });
161
+ const exec = makeMockExec({
162
+ remotes: { [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git' },
163
+ userError: notFoundError,
164
+ });
165
+ await startServer(exec);
166
+ const data = await getPrs();
167
+ assert.equal(data.error, 'gh_not_in_path');
168
+ assert.equal(data.prs.length, 0);
169
+ });
170
+ test('returns gh_not_authenticated error', async () => {
171
+ await stopServer();
172
+ saveConfig(configPath, {
173
+ ...DEFAULTS,
174
+ repos: [WORKSPACE_PATH_A],
175
+ });
176
+ const authError = new Error('You are not logged into any GitHub hosts. Run gh auth login to authenticate.');
177
+ const exec = makeMockExec({
178
+ remotes: { [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git' },
179
+ userError: authError,
180
+ });
181
+ await startServer(exec);
182
+ const data = await getPrs();
183
+ assert.equal(data.error, 'gh_not_authenticated');
184
+ assert.equal(data.prs.length, 0);
185
+ });
186
+ test('returns empty prs with no_workspaces error when workspaces is empty', async () => {
187
+ await stopServer();
188
+ saveConfig(configPath, {
189
+ ...DEFAULTS,
190
+ repos: [],
191
+ });
192
+ // execAsync should never be called here — pass a mock that always throws to
193
+ // verify the early-return path triggers before any exec
194
+ const exec = makeMockExec({});
195
+ await startServer(exec);
196
+ const data = await getPrs();
197
+ assert.equal(data.error, 'no_workspaces');
198
+ assert.equal(data.prs.length, 0);
199
+ });
200
+ test('detects reviewer role when current user is in requested_reviewers but not the author', async () => {
201
+ await stopServer();
202
+ saveConfig(configPath, {
203
+ ...DEFAULTS,
204
+ repos: [WORKSPACE_PATH_A],
205
+ });
206
+ // PR authored by someone else; testuser is a requested reviewer
207
+ const reviewerItem = makeSearchItem({
208
+ ownerRepo: 'myorg/repo-a',
209
+ number: 42,
210
+ title: 'Review me',
211
+ author: 'otheruser',
212
+ role: 'reviewer',
213
+ currentUser: 'testuser',
214
+ });
215
+ const exec = makeMockExec({
216
+ remotes: { [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git' },
217
+ userLogin: 'testuser',
218
+ searchItems: [reviewerItem],
219
+ });
220
+ await startServer(exec);
221
+ const data = await getPrs();
222
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
223
+ assert.equal(data.prs.length, 1, 'Should return the reviewer PR');
224
+ const pr = data.prs[0];
225
+ assert.equal(pr?.number, 42);
226
+ assert.equal(pr?.role, 'reviewer', 'Role should be reviewer');
227
+ assert.equal(pr?.author, 'otheruser', 'Author should be otheruser, not the current user');
228
+ });
229
+ test('caches results within TTL — exec called only once for two requests', async () => {
230
+ await stopServer();
231
+ saveConfig(configPath, {
232
+ ...DEFAULTS,
233
+ repos: [WORKSPACE_PATH_A],
234
+ });
235
+ let searchCallCount = 0;
236
+ // Wrap makeMockExec with a counter on the search path
237
+ const baseExec = makeMockExec({
238
+ remotes: { [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git' },
239
+ userLogin: 'testuser',
240
+ searchItems: [
241
+ makeSearchItem({
242
+ ownerRepo: 'myorg/repo-a',
243
+ number: 1,
244
+ author: 'testuser',
245
+ }),
246
+ ],
247
+ });
248
+ const countingExec = async (...args) => {
249
+ const [cmd, argv] = args;
250
+ if (cmd === 'gh' &&
251
+ typeof argv[1] === 'string' &&
252
+ argv[1].startsWith('search/issues')) {
253
+ searchCallCount++;
254
+ }
255
+ return baseExec(...args);
256
+ };
257
+ await startServer(countingExec);
258
+ // First request — populates cache
259
+ const first = await getPrs();
260
+ assert.equal(first.error, undefined);
261
+ assert.equal(first.prs.length, 1);
262
+ // Second request — should be served from cache, no additional exec call
263
+ const second = await getPrs();
264
+ assert.equal(second.error, undefined);
265
+ assert.equal(second.prs.length, 1);
266
+ assert.equal(searchCallCount, 1, 'gh search should have been called exactly once (cache hit on second request)');
267
+ });
268
+ test('uses GraphQL path when github accessToken is in config', async () => {
269
+ // Use an isolated tmp dir, config, and server so this test does not
270
+ // interfere with the shared server used by the other tests.
271
+ const gqlTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'org-dashboard-gql-test-'));
272
+ const gqlConfigPath = path.join(gqlTmpDir, 'config.json');
273
+ saveConfig(gqlConfigPath, {
274
+ ...DEFAULTS,
275
+ repos: [WORKSPACE_PATH_A],
276
+ github: { accessToken: 'ghp_test123', username: 'graphqluser' },
277
+ });
278
+ const graphqlPr = {
279
+ number: 99,
280
+ title: 'GraphQL PR',
281
+ url: 'https://github.com/myorg/repo-a/pull/99',
282
+ headRefName: 'feat/graphql',
283
+ baseRefName: 'main',
284
+ state: 'OPEN',
285
+ author: 'graphqluser',
286
+ role: 'author',
287
+ updatedAt: '2026-03-22T00:00:00Z',
288
+ additions: 5,
289
+ deletions: 2,
290
+ reviewDecision: null,
291
+ mergeable: null,
292
+ isDraft: false,
293
+ ciStatus: null,
294
+ repoName: 'repo-a',
295
+ repoPath: WORKSPACE_PATH_A,
296
+ };
297
+ let graphqlCallCount = 0;
298
+ let capturedToken;
299
+ let capturedRepoMap;
300
+ const mockFetchGraphQL = async (token, repoMap) => {
301
+ graphqlCallCount++;
302
+ capturedToken = token;
303
+ capturedRepoMap = repoMap;
304
+ return { prs: [graphqlPr], username: 'graphqluser' };
305
+ };
306
+ // exec mock that handles git remote but should NOT be called for gh user/search
307
+ const exec = makeMockExec({
308
+ remotes: { [WORKSPACE_PATH_A]: 'git@github.com:myorg/repo-a.git' },
309
+ });
310
+ let gqlServer;
311
+ let gqlBaseUrl;
312
+ await new Promise((resolve) => {
313
+ const app = express();
314
+ app.use(express.json());
315
+ const deps = {
316
+ configPath: gqlConfigPath,
317
+ execAsync: exec,
318
+ fetchGraphQL: mockFetchGraphQL,
319
+ };
320
+ app.use('/org-dashboard', createOrgDashboardRouter(deps));
321
+ gqlServer = app.listen(0, '127.0.0.1', () => {
322
+ const addr = gqlServer.address();
323
+ if (typeof addr === 'object' && addr) {
324
+ gqlBaseUrl = `http://127.0.0.1:${addr.port}`;
325
+ }
326
+ resolve();
327
+ });
328
+ });
329
+ try {
330
+ const res = await fetch(`${gqlBaseUrl}/org-dashboard/prs`);
331
+ const data = (await res.json());
332
+ assert.equal(data.error, undefined, `Unexpected error: ${data.error}`);
333
+ assert.equal(data.prs.length, 1, 'Should return the GraphQL PR');
334
+ assert.equal(data.prs[0]?.number, 99, 'PR number should match GraphQL data');
335
+ assert.equal(data.prs[0]?.title, 'GraphQL PR');
336
+ assert.equal(graphqlCallCount, 1, 'fetchGraphQL should have been called exactly once');
337
+ assert.equal(capturedToken, 'ghp_test123', 'fetchGraphQL should receive the configured access token');
338
+ assert.ok(capturedRepoMap instanceof Map, 'fetchGraphQL should receive the repoMap');
339
+ }
340
+ finally {
341
+ await new Promise((resolve) => {
342
+ if (gqlServer)
343
+ gqlServer.close(() => resolve());
344
+ else
345
+ resolve();
346
+ });
347
+ fs.rmSync(gqlTmpDir, { recursive: true, force: true });
348
+ }
349
+ });
@@ -0,0 +1,217 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ClaudeOutputParser } from '../server/output-parsers/claude-parser.js';
4
+ import { CodexOutputParser } from '../server/output-parsers/codex-parser.js';
5
+ import { OpencodeOutputParser } from '../server/output-parsers/opencode-parser.js';
6
+ import { NullOutputParser } from '../server/output-parsers/null-parser.js';
7
+ import { outputParsers } from '../server/output-parsers/index.js';
8
+ describe('ClaudeOutputParser', () => {
9
+ it('starts in initializing state', () => {
10
+ const parser = new ClaudeOutputParser();
11
+ assert.equal(parser.state, 'initializing');
12
+ });
13
+ it('transitions to waiting-for-input on > prompt', () => {
14
+ const parser = new ClaudeOutputParser();
15
+ const result = parser.onData('>\n', []);
16
+ assert.deepEqual(result, { state: 'waiting-for-input' });
17
+ });
18
+ it('transitions to waiting-for-input on greeting', () => {
19
+ const parser = new ClaudeOutputParser();
20
+ const result = parser.onData('How can I help you today?', []);
21
+ assert.deepEqual(result, { state: 'waiting-for-input' });
22
+ });
23
+ it('transitions to processing after first prompt when output arrives', () => {
24
+ const parser = new ClaudeOutputParser();
25
+ // First: see the prompt
26
+ parser.onData('>\n', []);
27
+ // Then: output starts
28
+ const result = parser.onData('I will help you with that task...', []);
29
+ assert.deepEqual(result, { state: 'processing' });
30
+ });
31
+ it('transitions back to waiting-for-input after processing', () => {
32
+ const parser = new ClaudeOutputParser();
33
+ parser.onData('>\n', []);
34
+ parser.onData('Working on it...', []);
35
+ const result = parser.onData('>\n', []);
36
+ assert.deepEqual(result, { state: 'waiting-for-input' });
37
+ });
38
+ it('detects permission prompt', () => {
39
+ const parser = new ClaudeOutputParser();
40
+ parser.onData('>\n', []);
41
+ const result = parser.onData('Allow tool access to /usr/bin? Allow / Deny', []);
42
+ assert.deepEqual(result, { state: 'permission-prompt' });
43
+ });
44
+ it('detects error state', () => {
45
+ const parser = new ClaudeOutputParser();
46
+ parser.onData('>\n', []);
47
+ const result = parser.onData('Error: something went wrong', []);
48
+ assert.deepEqual(result, { state: 'error' });
49
+ });
50
+ it('ignores pure ANSI escape sequences', () => {
51
+ const parser = new ClaudeOutputParser();
52
+ const result = parser.onData('\x1b[32m\x1b[0m', []);
53
+ assert.equal(result, null);
54
+ });
55
+ it('returns null when state does not change', () => {
56
+ const parser = new ClaudeOutputParser();
57
+ parser.onData('>\n', []);
58
+ // Already in waiting-for-input, send another prompt
59
+ const result = parser.onData('>\n', []);
60
+ assert.equal(result, null);
61
+ });
62
+ it('reset returns to initializing', () => {
63
+ const parser = new ClaudeOutputParser();
64
+ parser.onData('>\n', []);
65
+ assert.equal(parser.state, 'waiting-for-input');
66
+ parser.reset();
67
+ assert.equal(parser.state, 'initializing');
68
+ });
69
+ it('stays initializing before first prompt', () => {
70
+ const parser = new ClaudeOutputParser();
71
+ const result = parser.onData('Loading configuration...', []);
72
+ // Still initializing since no prompt seen
73
+ assert.equal(result, null); // already in initializing, no change
74
+ });
75
+ });
76
+ describe('CodexOutputParser', () => {
77
+ it('always returns null', () => {
78
+ const parser = new CodexOutputParser();
79
+ assert.equal(parser.onData('any output', []), null);
80
+ assert.equal(parser.onData('>\n', []), null);
81
+ assert.equal(parser.onData('Error: something', []), null);
82
+ });
83
+ it('reset is a no-op', () => {
84
+ const parser = new CodexOutputParser();
85
+ parser.reset(); // should not throw
86
+ });
87
+ });
88
+ describe('outputParsers registry', () => {
89
+ it('creates ClaudeOutputParser for claude', () => {
90
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
91
+ const parser = outputParsers['claude']();
92
+ assert.ok(parser instanceof ClaudeOutputParser);
93
+ });
94
+ it('creates CodexOutputParser for codex', () => {
95
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
96
+ const parser = outputParsers['codex']();
97
+ assert.ok(parser instanceof CodexOutputParser);
98
+ });
99
+ it('creates OpencodeOutputParser for opencode', () => {
100
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
101
+ const parser = outputParsers['opencode']();
102
+ assert.ok(parser instanceof OpencodeOutputParser);
103
+ });
104
+ it('creates NullOutputParser for none', () => {
105
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
106
+ const parser = outputParsers['none']();
107
+ assert.ok(parser instanceof NullOutputParser);
108
+ });
109
+ it('returns undefined for unknown keys', () => {
110
+ assert.equal(outputParsers['unknown-agent-xyz'], undefined);
111
+ });
112
+ });
113
+ describe('NullOutputParser', () => {
114
+ it('always returns null for any input', () => {
115
+ const parser = new NullOutputParser();
116
+ assert.equal(parser.onData('any output', []), null);
117
+ assert.equal(parser.onData('>\n', []), null);
118
+ assert.equal(parser.onData('Error: something', []), null);
119
+ assert.equal(parser.onData('', []), null);
120
+ });
121
+ it('reset is a no-op', () => {
122
+ const parser = new NullOutputParser();
123
+ parser.reset(); // should not throw
124
+ });
125
+ });
126
+ describe('OpencodeOutputParser', () => {
127
+ it('starts in initializing state', () => {
128
+ const parser = new OpencodeOutputParser();
129
+ assert.equal(parser.state, 'initializing');
130
+ });
131
+ it('detects permission prompt from ! permission requested line', () => {
132
+ const parser = new OpencodeOutputParser();
133
+ const result = parser.onData('! permission requested: bash (*); allow this tool?', []);
134
+ assert.deepEqual(result, { state: 'permission-prompt' });
135
+ });
136
+ it('recognizes agent header and sets hasSeenFirstPrompt (no state change from initializing)', () => {
137
+ const parser = new OpencodeOutputParser();
138
+ // Parser starts in initializing; header keeps it in initializing — no state change, returns null
139
+ const result = parser.onData('> build . anthropic/claude-sonnet-4-5', []);
140
+ assert.equal(result, null);
141
+ assert.equal(parser.state, 'initializing');
142
+ });
143
+ it('detects bash tool icon as processing', () => {
144
+ const parser = new OpencodeOutputParser();
145
+ // Simulate having seen first prompt first
146
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
147
+ // Now a bash tool invocation
148
+ const result = parser.onData('$ npm run build', []);
149
+ assert.deepEqual(result, { state: 'processing' });
150
+ });
151
+ it('detects file edit tool icon as processing', () => {
152
+ const parser = new OpencodeOutputParser();
153
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
154
+ const result = parser.onData('<- src/index.ts', []);
155
+ assert.deepEqual(result, { state: 'processing' });
156
+ });
157
+ it('detects file read tool icon as processing', () => {
158
+ const parser = new OpencodeOutputParser();
159
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
160
+ const result = parser.onData('-> src/index.ts', []);
161
+ assert.deepEqual(result, { state: 'processing' });
162
+ });
163
+ it('detects glob/grep tool icon as processing', () => {
164
+ const parser = new OpencodeOutputParser();
165
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
166
+ const result = parser.onData('* **/*.ts', []);
167
+ assert.deepEqual(result, { state: 'processing' });
168
+ });
169
+ it('detects webfetch tool icon as processing', () => {
170
+ const parser = new OpencodeOutputParser();
171
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
172
+ const result = parser.onData('% https://example.com', []);
173
+ assert.deepEqual(result, { state: 'processing' });
174
+ });
175
+ it('detects waiting-for-input on > prompt without provider info', () => {
176
+ const parser = new OpencodeOutputParser();
177
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
178
+ parser.onData('$ npm install', []);
179
+ const result = parser.onData('>\n', []);
180
+ assert.deepEqual(result, { state: 'waiting-for-input' });
181
+ });
182
+ it('detects waiting-for-input on "Ready" pattern', () => {
183
+ const parser = new OpencodeOutputParser();
184
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
185
+ parser.onData('doing work...', []);
186
+ const result = parser.onData('Ready', []);
187
+ assert.deepEqual(result, { state: 'waiting-for-input' });
188
+ });
189
+ it('strips ANSI sequences before matching', () => {
190
+ const parser = new OpencodeOutputParser();
191
+ // Pure ANSI should return null
192
+ const result = parser.onData('\x1b[32m\x1b[0m', []);
193
+ assert.equal(result, null);
194
+ });
195
+ it('permission prompt has highest priority over processing', () => {
196
+ const parser = new OpencodeOutputParser();
197
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
198
+ // Even if there is tool-like content, the permission pattern wins
199
+ const result = parser.onData('! permission requested: bash (*); $ npm run test', []);
200
+ assert.deepEqual(result, { state: 'permission-prompt' });
201
+ });
202
+ it('returns null when state does not change', () => {
203
+ const parser = new OpencodeOutputParser();
204
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
205
+ // Still initializing, another header line should return null (already initializing)
206
+ const result = parser.onData('> build . anthropic/claude-3-5-sonnet', []);
207
+ assert.equal(result, null);
208
+ });
209
+ it('reset returns to initializing state', () => {
210
+ const parser = new OpencodeOutputParser();
211
+ parser.onData('> build . anthropic/claude-3-5-sonnet', []);
212
+ parser.onData('>\n', []);
213
+ assert.equal(parser.state, 'waiting-for-input');
214
+ parser.reset();
215
+ assert.equal(parser.state, 'initializing');
216
+ });
217
+ });
@@ -0,0 +1,32 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ // At runtime, __dirname resolves to dist/test/.
7
+ // The server runs from dist/server/, which uses path.join(__dirname, '..', '..', ...)
8
+ // to reach the project root. This test verifies that relationship is correct.
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ // This test file is at dist/test/, server is at dist/server/ — same depth
12
+ const projectRoot = path.resolve(__dirname, '..', '..');
13
+ test('project root from dist/ contains frontend/ directory', () => {
14
+ const frontendDir = path.join(projectRoot, 'frontend');
15
+ assert.ok(fs.existsSync(frontendDir), `Expected frontend/ at ${frontendDir}`);
16
+ });
17
+ test('project root from dist/ contains frontend/index.html', () => {
18
+ const indexHtml = path.join(projectRoot, 'frontend', 'index.html');
19
+ assert.ok(fs.existsSync(indexHtml), `Expected frontend/index.html at ${indexHtml}`);
20
+ });
21
+ test('dist/server/ exists after compilation', () => {
22
+ const serverDir = path.join(projectRoot, 'dist', 'server');
23
+ assert.ok(fs.existsSync(serverDir), `Expected dist/server/ at ${serverDir}`);
24
+ });
25
+ test('server index.ts uses correct path depth to reach dist/frontend/', async () => {
26
+ // Read the source file and verify the path pattern
27
+ const indexSource = fs.readFileSync(path.join(projectRoot, 'server', 'index.ts'), 'utf8');
28
+ // Static serving must go up one level from dist/server/ to dist/, then into frontend/
29
+ assert.ok(indexSource.includes("path.join(__dirname, '..', 'frontend')"), 'express.static must resolve dist/frontend/ one level up from dist/server/');
30
+ // Config fallback must also go up two levels
31
+ assert.ok(indexSource.includes("path.join(__dirname, '..', '..', 'config.json')"), 'CONFIG_PATH must resolve config.json two levels up from dist/server/');
32
+ });