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,661 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { EventEmitter } from 'node:events';
6
+ import { createLogger } from './logger.js';
7
+ const execFileAsync = promisify(execFile);
8
+ const logger = createLogger('watcher');
9
+ export const WORKTREE_DIRS = ['.worktrees', '.claude/worktrees'];
10
+ function closeWatchers(watchers) {
11
+ for (const w of watchers) {
12
+ try {
13
+ w.close();
14
+ }
15
+ catch (_) { }
16
+ }
17
+ }
18
+ export function isValidWorktreePath(worktreePath) {
19
+ const resolved = path.resolve(worktreePath);
20
+ return WORKTREE_DIRS.some(function (dir) {
21
+ return resolved.includes(path.sep + dir + path.sep);
22
+ });
23
+ }
24
+ function parseWorktreeBlocks(stdout) {
25
+ return stdout
26
+ .split('\n\n')
27
+ .filter(Boolean)
28
+ .map((block) => {
29
+ let path = '';
30
+ let branch = '';
31
+ let bare = false;
32
+ for (const line of block.split('\n')) {
33
+ if (line.startsWith('worktree '))
34
+ path = line.slice(9);
35
+ else if (line.startsWith('branch refs/heads/'))
36
+ branch = line.slice(18);
37
+ else if (line === 'bare')
38
+ bare = true;
39
+ }
40
+ return { path, branch, bare };
41
+ });
42
+ }
43
+ /**
44
+ * Parse `git worktree list --porcelain` output into ALL entries (including main worktree).
45
+ * Skips bare entries. Detached HEAD entries get empty branch string.
46
+ */
47
+ export function parseAllWorktrees(stdout, repoPath) {
48
+ return parseWorktreeBlocks(stdout)
49
+ .filter((b) => b.path && !b.bare)
50
+ .map((b) => ({
51
+ path: b.path,
52
+ branch: b.branch,
53
+ isMain: b.path === repoPath,
54
+ }));
55
+ }
56
+ /**
57
+ * Parse `git worktree list --porcelain` output into structured entries.
58
+ * Skips the main worktree (matching repoPath) and bare/detached entries.
59
+ */
60
+ export function parseWorktreeListPorcelain(stdout, repoPath) {
61
+ return parseWorktreeBlocks(stdout)
62
+ .filter((b) => b.path && b.path !== repoPath && !b.bare && b.branch)
63
+ .map((b) => ({ path: b.path, branch: b.branch }));
64
+ }
65
+ /**
66
+ * Find an existing worktree for a branch, or create a new one.
67
+ * Prevents "fatal: branch is already used by worktree" errors by
68
+ * checking `git worktree list` before attempting `git worktree add`.
69
+ * Throws branch_checked_out_in_main error if the branch is checked out
70
+ * in the main worktree (caller should open a repo-root session instead).
71
+ */
72
+ export async function findOrCreateWorktreeForBranch(repoPath, branch, execFn) {
73
+ // Check if branch is already checked out in ANY worktree (including the main repo)
74
+ try {
75
+ const { stdout } = await execFn('git', ['worktree', 'list', '--porcelain'], { cwd: repoPath });
76
+ const allWorktrees = parseAllWorktrees(stdout, repoPath);
77
+ const match = allWorktrees.find((wt) => wt.branch === branch);
78
+ if (match) {
79
+ return {
80
+ worktreePath: match.path,
81
+ branchName: match.branch,
82
+ dirName: match.path.split('/').pop() || '',
83
+ existing: true,
84
+ isMain: match.isMain,
85
+ };
86
+ }
87
+ }
88
+ catch (err) {
89
+ // git worktree list failed — proceed with creation attempt
90
+ logger.warn('[watcher] git worktree list failed, proceeding with creation attempt:', err instanceof Error ? err.message : err);
91
+ }
92
+ // Sanitize branch name for directory
93
+ const dirName = branch.replace(/\//g, '-').replace(/[^a-zA-Z0-9._-]/g, '');
94
+ const worktreePath = path.join(repoPath, '.worktrees', dirName);
95
+ // Ensure .worktrees/ is in .gitignore (best-effort — skip if directory doesn't exist)
96
+ try {
97
+ const gitignorePath = path.join(repoPath, '.gitignore');
98
+ try {
99
+ const content = fs.readFileSync(gitignorePath, 'utf8');
100
+ if (!content.includes('.worktrees/')) {
101
+ fs.appendFileSync(gitignorePath, '\n.worktrees/\n');
102
+ }
103
+ }
104
+ catch {
105
+ fs.writeFileSync(gitignorePath, '.worktrees/\n');
106
+ }
107
+ }
108
+ catch {
109
+ // Directory may not exist in test environments
110
+ }
111
+ await execFn('git', ['worktree', 'add', worktreePath, branch], {
112
+ cwd: repoPath,
113
+ });
114
+ return {
115
+ worktreePath,
116
+ branchName: branch,
117
+ dirName,
118
+ existing: false,
119
+ isMain: false,
120
+ };
121
+ }
122
+ export class WorktreeWatcher extends EventEmitter {
123
+ _watchers;
124
+ _debounceTimer;
125
+ constructor() {
126
+ super();
127
+ this._watchers = [];
128
+ this._debounceTimer = null;
129
+ }
130
+ rebuild(rootDirs) {
131
+ this._closeAll();
132
+ for (const rootDir of rootDirs) {
133
+ let entries;
134
+ try {
135
+ entries = fs.readdirSync(rootDir, { withFileTypes: true });
136
+ }
137
+ catch (_) {
138
+ continue;
139
+ }
140
+ for (const entry of entries) {
141
+ if (!entry.isDirectory() || entry.name.startsWith('.'))
142
+ continue;
143
+ const repoPath = path.join(rootDir, entry.name);
144
+ if (!fs.existsSync(path.join(repoPath, '.git')))
145
+ continue;
146
+ this._watchRepo(repoPath);
147
+ }
148
+ }
149
+ }
150
+ _watchRepo(repoPath) {
151
+ let anyWatched = false;
152
+ for (const dir of WORKTREE_DIRS) {
153
+ const worktreeDir = path.join(repoPath, dir);
154
+ if (fs.existsSync(worktreeDir)) {
155
+ this._addWatch(worktreeDir);
156
+ anyWatched = true;
157
+ }
158
+ }
159
+ if (!anyWatched) {
160
+ // Watch repo root so we detect when either dir is first created
161
+ this._addWatch(repoPath);
162
+ }
163
+ }
164
+ _addWatch(dirPath) {
165
+ try {
166
+ const watcher = fs.watch(dirPath, { persistent: false }, () => {
167
+ this._debouncedEmit();
168
+ });
169
+ watcher.on('error', () => { });
170
+ this._watchers.push(watcher);
171
+ }
172
+ catch (_) { }
173
+ }
174
+ _debouncedEmit() {
175
+ if (this._debounceTimer)
176
+ clearTimeout(this._debounceTimer);
177
+ this._debounceTimer = setTimeout(() => {
178
+ this.emit('worktrees-changed');
179
+ }, 500);
180
+ }
181
+ _closeAll() {
182
+ closeWatchers(this._watchers);
183
+ this._watchers = [];
184
+ if (this._debounceTimer) {
185
+ clearTimeout(this._debounceTimer);
186
+ this._debounceTimer = null;
187
+ }
188
+ }
189
+ close() {
190
+ this._closeAll();
191
+ }
192
+ }
193
+ export class BranchWatcher {
194
+ // Map headPath → { watcher, cwdPath } so we can recreate individual watchers
195
+ // after detection (git's atomic checkout can change the inode, killing kqueue watchers)
196
+ _watcherMap = new Map();
197
+ _debounceTimers = new Map();
198
+ _lastBranch = new Map();
199
+ _callback;
200
+ /** Monotonic generation counter. Incremented on rebuild()/close() so that
201
+ * in-flight async _readAndEmit calls from a prior generation don't
202
+ * recreate watchers into the new (or cleared) _watcherMap. */
203
+ _generation = 0;
204
+ constructor(callback) {
205
+ this._callback = callback;
206
+ }
207
+ rebuild(rootDirs) {
208
+ this._closeAll();
209
+ for (const rootDir of rootDirs) {
210
+ let entries;
211
+ try {
212
+ entries = fs.readdirSync(rootDir, { withFileTypes: true });
213
+ }
214
+ catch (_) {
215
+ continue;
216
+ }
217
+ for (const entry of entries) {
218
+ if (!entry.isDirectory() || entry.name.startsWith('.'))
219
+ continue;
220
+ const repoPath = path.join(rootDir, entry.name);
221
+ if (!fs.existsSync(path.join(repoPath, '.git')))
222
+ continue;
223
+ this._watchRepoHeads(repoPath);
224
+ }
225
+ }
226
+ }
227
+ _watchRepoHeads(repoPath) {
228
+ // Watch main repo HEAD
229
+ const mainHead = path.join(repoPath, '.git', 'HEAD');
230
+ this._watchHeadFile(mainHead, repoPath);
231
+ // Watch worktree HEADs: <repoPath>/.git/worktrees/*/HEAD
232
+ const worktreesGitDir = path.join(repoPath, '.git', 'worktrees');
233
+ let wtEntries;
234
+ try {
235
+ wtEntries = fs.readdirSync(worktreesGitDir, { withFileTypes: true });
236
+ }
237
+ catch (_) {
238
+ return; // No worktrees
239
+ }
240
+ for (const entry of wtEntries) {
241
+ if (!entry.isDirectory())
242
+ continue;
243
+ const wtGitDir = path.join(worktreesGitDir, entry.name);
244
+ const headFile = path.join(wtGitDir, 'HEAD');
245
+ if (!fs.existsSync(headFile))
246
+ continue;
247
+ // Map worktree git dir back to checkout path via gitdir file
248
+ const gitdirFile = path.join(wtGitDir, 'gitdir');
249
+ let checkoutPath;
250
+ try {
251
+ const gitdirContent = fs.readFileSync(gitdirFile, 'utf-8').trim();
252
+ // gitdir contains <checkoutPath>/.git — strip the /.git suffix
253
+ checkoutPath = gitdirContent.replace(/\/\.git\/?$/, '');
254
+ }
255
+ catch (_) {
256
+ continue;
257
+ }
258
+ this._watchHeadFile(headFile, checkoutPath);
259
+ }
260
+ }
261
+ _watchHeadFile(headPath, cwdPath) {
262
+ // Seed initial branch to avoid false-positive on first change detection
263
+ try {
264
+ const content = fs.readFileSync(headPath, 'utf-8').trim();
265
+ const match = content.match(/^ref: refs\/heads\/(.+)$/);
266
+ if (match)
267
+ this._lastBranch.set(cwdPath, match[1]);
268
+ }
269
+ catch (_) { }
270
+ this._createWatcher(headPath, cwdPath);
271
+ }
272
+ /**
273
+ * Create (or recreate) an fs.watch() for a HEAD file. Tracked by headPath
274
+ * so we can close and recreate after detection — git's atomic checkout
275
+ * (write HEAD.lock, rename to HEAD) can change the file's inode, which
276
+ * silently kills kqueue-based watchers on macOS.
277
+ */
278
+ _createWatcher(headPath, cwdPath) {
279
+ // Close existing watcher for this path if any
280
+ const existing = this._watcherMap.get(headPath);
281
+ if (existing) {
282
+ try {
283
+ existing.watcher.close();
284
+ }
285
+ catch (_) { }
286
+ }
287
+ try {
288
+ const watcher = fs.watch(headPath, { persistent: false }, () => {
289
+ this._debouncedCheck(headPath, cwdPath);
290
+ });
291
+ watcher.on('error', () => { });
292
+ this._watcherMap.set(headPath, { watcher, cwdPath });
293
+ }
294
+ catch (_) { }
295
+ }
296
+ _debouncedCheck(headPath, cwdPath) {
297
+ const existing = this._debounceTimers.get(cwdPath);
298
+ if (existing)
299
+ clearTimeout(existing);
300
+ // Capture generation so the async callback can detect stale invocations
301
+ const gen = this._generation;
302
+ this._debounceTimers.set(cwdPath, setTimeout(() => {
303
+ this._debounceTimers.delete(cwdPath);
304
+ this._readAndEmit(headPath, cwdPath, gen);
305
+ }, 300));
306
+ }
307
+ async _readAndEmit(headPath, cwdPath, gen) {
308
+ try {
309
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: cwdPath });
310
+ const newBranch = stdout.trim();
311
+ const lastBranch = this._lastBranch.get(cwdPath);
312
+ if (newBranch && newBranch !== lastBranch) {
313
+ this._lastBranch.set(cwdPath, newBranch);
314
+ this._callback(cwdPath, newBranch);
315
+ }
316
+ }
317
+ catch (_) {
318
+ // Non-fatal — repo may be in detached HEAD or mid-rebase
319
+ }
320
+ // Recreate the watcher — the inode may have changed due to atomic rename.
321
+ // Only recreate if the generation hasn't changed (rebuild/close didn't happen
322
+ // while git rev-parse was in flight).
323
+ if (this._generation === gen) {
324
+ this._createWatcher(headPath, cwdPath);
325
+ }
326
+ }
327
+ _closeAll() {
328
+ this._generation++;
329
+ for (const { watcher } of this._watcherMap.values()) {
330
+ try {
331
+ watcher.close();
332
+ }
333
+ catch (_) { }
334
+ }
335
+ this._watcherMap.clear();
336
+ for (const timer of this._debounceTimers.values()) {
337
+ clearTimeout(timer);
338
+ }
339
+ this._debounceTimers.clear();
340
+ this._lastBranch.clear();
341
+ }
342
+ close() {
343
+ this._closeAll();
344
+ }
345
+ }
346
+ /**
347
+ * Resolve the git directory for a checkout path, handling both regular repos
348
+ * and worktrees. For worktrees, follows the `commondir` file to find the main
349
+ * repo's git dir (where remote refs live).
350
+ */
351
+ export function resolveGitDir(cwdPath) {
352
+ const dotGit = path.join(cwdPath, '.git');
353
+ let stat;
354
+ try {
355
+ stat = fs.statSync(dotGit, { throwIfNoEntry: false });
356
+ }
357
+ catch (_) {
358
+ return null; // EACCES, ENOTDIR, etc.
359
+ }
360
+ if (!stat)
361
+ return null;
362
+ if (stat.isDirectory())
363
+ return dotGit;
364
+ // Worktree: .git is a file containing "gitdir: <path>"
365
+ let content;
366
+ try {
367
+ content = fs.readFileSync(dotGit, 'utf-8').trim();
368
+ }
369
+ catch (_) {
370
+ return null;
371
+ }
372
+ const match = content.match(/^gitdir:\s*(.+)$/);
373
+ if (!match)
374
+ return null;
375
+ const worktreeGitDir = path.resolve(cwdPath, match[1]);
376
+ // Follow commondir to find the main repo's git dir (where refs/remotes/ lives)
377
+ const commondirFile = path.join(worktreeGitDir, 'commondir');
378
+ try {
379
+ const commondir = fs.readFileSync(commondirFile, 'utf-8').trim();
380
+ return path.resolve(worktreeGitDir, commondir);
381
+ }
382
+ catch (_) {
383
+ // No commondir — fall back to the worktree git dir itself
384
+ return worktreeGitDir;
385
+ }
386
+ }
387
+ export class RefWatcher {
388
+ _watchers = [];
389
+ _debounceTimers = new Map();
390
+ _lastSha = new Map();
391
+ _entries = new Map();
392
+ _callback;
393
+ constructor(callback) {
394
+ this._callback = callback;
395
+ }
396
+ async rebuild(entries) {
397
+ this._closeAll();
398
+ // Dedupe entries — multiple sessions can share the same cwdPath:branch
399
+ const seen = new Set();
400
+ for (const { cwdPath, branch } of entries) {
401
+ const dedupeKey = `${cwdPath}:${branch}`;
402
+ if (seen.has(dedupeKey))
403
+ continue;
404
+ seen.add(dedupeKey);
405
+ // Resolve the upstream tracking ref
406
+ let upstreamRef;
407
+ try {
408
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--symbolic-full-name', '@{u}'], { cwd: cwdPath });
409
+ upstreamRef = stdout.trim();
410
+ if (!upstreamRef)
411
+ continue;
412
+ }
413
+ catch (_) {
414
+ // No upstream (detached HEAD, unpushed branch) — skip
415
+ continue;
416
+ }
417
+ const key = `${cwdPath}:${branch}`;
418
+ this._entries.set(key, { cwdPath, branch, upstreamRef });
419
+ // Seed last known SHA
420
+ try {
421
+ const { stdout } = await execFileAsync('git', ['rev-parse', upstreamRef], { cwd: cwdPath });
422
+ this._lastSha.set(key, stdout.trim());
423
+ }
424
+ catch (_) {
425
+ this._lastSha.set(key, null);
426
+ }
427
+ // Resolve git dir (handles worktrees via commondir)
428
+ const gitDir = resolveGitDir(cwdPath);
429
+ if (!gitDir)
430
+ continue;
431
+ // Watch the loose ref file if it exists (e.g. refs/remotes/origin/feature-x)
432
+ // upstreamRef is like "refs/remotes/origin/feature-x"
433
+ const refFile = path.join(gitDir, upstreamRef);
434
+ this._addWatch(refFile, key);
435
+ // Watch the remote's ref directory to catch new ref creation
436
+ const refDir = path.dirname(refFile);
437
+ this._addWatch(refDir, key);
438
+ }
439
+ }
440
+ _addWatch(target, key) {
441
+ try {
442
+ if (!fs.existsSync(target))
443
+ return;
444
+ const watcher = fs.watch(target, { persistent: false }, () => {
445
+ this._debouncedCheck(key);
446
+ });
447
+ watcher.on('error', () => { });
448
+ this._watchers.push(watcher);
449
+ }
450
+ catch (_) { }
451
+ }
452
+ _debouncedCheck(key) {
453
+ const existing = this._debounceTimers.get(key);
454
+ if (existing)
455
+ clearTimeout(existing);
456
+ this._debounceTimers.set(key, setTimeout(() => {
457
+ this._debounceTimers.delete(key);
458
+ this._checkAndEmit(key);
459
+ }, 300));
460
+ }
461
+ async _checkAndEmit(key) {
462
+ const entry = this._entries.get(key);
463
+ if (!entry)
464
+ return;
465
+ let newSha;
466
+ try {
467
+ const { stdout } = await execFileAsync('git', ['rev-parse', entry.upstreamRef], { cwd: entry.cwdPath });
468
+ newSha = stdout.trim();
469
+ }
470
+ catch (_) {
471
+ newSha = null; // Ref deleted or pruned
472
+ }
473
+ const lastSha = this._lastSha.get(key);
474
+ if (newSha !== lastSha) {
475
+ this._lastSha.set(key, newSha);
476
+ this._callback(entry.cwdPath, entry.branch);
477
+ }
478
+ }
479
+ _closeAll() {
480
+ closeWatchers(this._watchers);
481
+ this._watchers = [];
482
+ for (const timer of this._debounceTimers.values()) {
483
+ clearTimeout(timer);
484
+ }
485
+ this._debounceTimers.clear();
486
+ this._lastSha.clear();
487
+ this._entries.clear();
488
+ }
489
+ close() {
490
+ this._closeAll();
491
+ }
492
+ }
493
+ // Directories to ignore when watching the working tree.
494
+ // These never contain user-authored source files worth diffing.
495
+ const IGNORED_DIRS = new Set([
496
+ '.git',
497
+ 'node_modules',
498
+ '.next',
499
+ '.nuxt',
500
+ '.svelte-kit',
501
+ 'dist',
502
+ 'build',
503
+ 'out',
504
+ '.output',
505
+ 'coverage',
506
+ '__pycache__',
507
+ '.pytest_cache',
508
+ '.venv',
509
+ 'venv',
510
+ '.turbo',
511
+ '.cache',
512
+ '.parcel-cache',
513
+ ]);
514
+ export class GitWatcher extends EventEmitter {
515
+ _watchers = new Map();
516
+ _debounceTimers = new Map();
517
+ watch(workspacePath) {
518
+ const existing = this._watchers.get(workspacePath);
519
+ if (existing) {
520
+ existing.refCount++;
521
+ return;
522
+ }
523
+ let treeWatcher;
524
+ let headWatcher;
525
+ // 1. Watch the working tree recursively for file edits.
526
+ // macOS uses FSEvents (single kernel subscription). Linux uses inotify
527
+ // (one watch per directory, can hit fs.inotify.max_user_watches limit).
528
+ // .git/ changes are filtered out so git-status calls never cause feedback loops.
529
+ try {
530
+ treeWatcher = fs.watch(workspacePath, { persistent: false, recursive: true }, (_event, filename) => {
531
+ if (!filename)
532
+ return;
533
+ const firstSegment = filename.split(path.sep)[0] ?? '';
534
+ if (IGNORED_DIRS.has(firstSegment))
535
+ return;
536
+ this._debouncedEmit(workspacePath);
537
+ });
538
+ treeWatcher.on('error', (err) => {
539
+ logger.warn('[GitWatcher] working-tree watch failed for', workspacePath, '—', err.message);
540
+ logger.warn('[GitWatcher] changed-files will not auto-refresh for this workspace. On Linux, try: sysctl fs.inotify.max_user_watches=524288');
541
+ });
542
+ }
543
+ catch (err) {
544
+ const msg = err instanceof Error ? err.message : String(err);
545
+ logger.warn('[GitWatcher] could not watch working tree for', workspacePath, '—', msg);
546
+ }
547
+ // 2. Watch .git/HEAD for commits and branch switches.
548
+ // Single-file watch, no feedback loop risk (HEAD only changes on checkout/commit).
549
+ const gitDir = path.join(workspacePath, '.git');
550
+ try {
551
+ const stat = fs.statSync(gitDir);
552
+ let headPath;
553
+ if (stat.isFile()) {
554
+ // Worktree: .git is a file pointing to the real git dir
555
+ const content = fs.readFileSync(gitDir, 'utf-8').trim();
556
+ const match = content.match(/^gitdir:\s*(.+)$/);
557
+ headPath = match
558
+ ? path.join(path.resolve(workspacePath, match[1]), 'HEAD')
559
+ : '';
560
+ }
561
+ else {
562
+ headPath = path.join(gitDir, 'HEAD');
563
+ }
564
+ if (headPath && fs.existsSync(headPath)) {
565
+ headWatcher = fs.watch(headPath, { persistent: false }, () => {
566
+ this._debouncedEmit(workspacePath);
567
+ });
568
+ headWatcher.on('error', () => {
569
+ /* HEAD watch is best-effort */
570
+ });
571
+ }
572
+ }
573
+ catch {
574
+ // .git doesn't exist or isn't readable — skip HEAD watching
575
+ }
576
+ if (!treeWatcher && !headWatcher)
577
+ return;
578
+ this._watchers.set(workspacePath, {
579
+ treeWatcher: treeWatcher ?? headWatcher,
580
+ headWatcher: treeWatcher ? headWatcher : undefined,
581
+ refCount: 1,
582
+ });
583
+ }
584
+ unwatch(workspacePath) {
585
+ const entry = this._watchers.get(workspacePath);
586
+ if (!entry)
587
+ return;
588
+ entry.refCount--;
589
+ if (entry.refCount <= 0) {
590
+ try {
591
+ entry.treeWatcher.close();
592
+ }
593
+ catch { }
594
+ if (entry.headWatcher)
595
+ try {
596
+ entry.headWatcher.close();
597
+ }
598
+ catch { }
599
+ this._watchers.delete(workspacePath);
600
+ const timer = this._debounceTimers.get(workspacePath);
601
+ if (timer) {
602
+ clearTimeout(timer);
603
+ this._debounceTimers.delete(workspacePath);
604
+ }
605
+ }
606
+ }
607
+ _debouncedEmit(workspacePath) {
608
+ const existing = this._debounceTimers.get(workspacePath);
609
+ if (existing)
610
+ clearTimeout(existing);
611
+ this._debounceTimers.set(workspacePath, setTimeout(async () => {
612
+ this._debounceTimers.delete(workspacePath);
613
+ // Collect changed file paths via git status for the sidebar's blue-dot tracking
614
+ let changedFiles = [];
615
+ try {
616
+ const { stdout } = await execFileAsync('git', ['status', '--porcelain=v1', '-z'], {
617
+ cwd: workspacePath,
618
+ timeout: 5000,
619
+ });
620
+ const parts = stdout.split('\0').filter(Boolean);
621
+ for (let i = 0; i < parts.length; i++) {
622
+ const entry = parts[i];
623
+ const code = entry.slice(0, 2);
624
+ const filePath = entry.slice(3);
625
+ if (code.startsWith('R')) {
626
+ // Rename: porcelain -z gives [newPath]\0[oldPath]
627
+ // filePath is the new name (the one on disk), next part is the old name
628
+ if (filePath)
629
+ changedFiles.push(filePath);
630
+ i++; // skip old path
631
+ continue;
632
+ }
633
+ if (filePath)
634
+ changedFiles.push(filePath);
635
+ }
636
+ }
637
+ catch {
638
+ // git status failed — emit without file list
639
+ }
640
+ this.emit('files-changed', { workspacePath, changedFiles });
641
+ }, 1000));
642
+ }
643
+ close() {
644
+ for (const entry of this._watchers.values()) {
645
+ try {
646
+ entry.treeWatcher.close();
647
+ }
648
+ catch { }
649
+ if (entry.headWatcher)
650
+ try {
651
+ entry.headWatcher.close();
652
+ }
653
+ catch { }
654
+ }
655
+ this._watchers.clear();
656
+ for (const timer of this._debounceTimers.values()) {
657
+ clearTimeout(timer);
658
+ }
659
+ this._debounceTimers.clear();
660
+ }
661
+ }