vibora 0.1.6

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 (311) hide show
  1. package/bin/vibora +2 -0
  2. package/bin/vibora.js +1018 -0
  3. package/dist/assets/abap-BdImnpbu.js +1 -0
  4. package/dist/assets/actionscript-3-CfeIJUat.js +1 -0
  5. package/dist/assets/ada-bCR0ucgS.js +1 -0
  6. package/dist/assets/andromeeda-C-Jbm3Hp.js +1 -0
  7. package/dist/assets/angular-html-CU67Zn6k.js +1 -0
  8. package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
  9. package/dist/assets/apache-Pmp26Uib.js +1 -0
  10. package/dist/assets/apex-DDbsPZ6N.js +1 -0
  11. package/dist/assets/apl-dKokRX4l.js +1 -0
  12. package/dist/assets/applescript-Co6uUVPk.js +1 -0
  13. package/dist/assets/ara-BRHolxvo.js +1 -0
  14. package/dist/assets/asciidoc-Dv7Oe6Be.js +1 -0
  15. package/dist/assets/asm-D_Q5rh1f.js +1 -0
  16. package/dist/assets/astro-CbQHKStN.js +1 -0
  17. package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  18. package/dist/assets/awk-DMzUqQB5.js +1 -0
  19. package/dist/assets/ayu-dark-Cv9koXgw.js +1 -0
  20. package/dist/assets/ballerina-BFfxhgS-.js +1 -0
  21. package/dist/assets/bat-BkioyH1T.js +1 -0
  22. package/dist/assets/beancount-k_qm7-4y.js +1 -0
  23. package/dist/assets/berry-uYugtg8r.js +1 -0
  24. package/dist/assets/bibtex-CHM0blh-.js +1 -0
  25. package/dist/assets/bicep-Bmn6On1c.js +1 -0
  26. package/dist/assets/blade-DVc8C-J4.js +1 -0
  27. package/dist/assets/bsl-BO_Y6i37.js +1 -0
  28. package/dist/assets/c-BIGW1oBm.js +1 -0
  29. package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  30. package/dist/assets/cairo-KRGpt6FW.js +1 -0
  31. package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  32. package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  33. package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  34. package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  35. package/dist/assets/clarity-D53aC0YG.js +1 -0
  36. package/dist/assets/clojure-P80f7IUj.js +1 -0
  37. package/dist/assets/cmake-D1j8_8rp.js +1 -0
  38. package/dist/assets/cobol-nwyudZeR.js +1 -0
  39. package/dist/assets/codeowners-Bp6g37R7.js +1 -0
  40. package/dist/assets/codeql-DsOJ9woJ.js +1 -0
  41. package/dist/assets/coffee-Ch7k5sss.js +1 -0
  42. package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  43. package/dist/assets/coq-DkFqJrB1.js +1 -0
  44. package/dist/assets/cpp-CofmeUqb.js +1 -0
  45. package/dist/assets/crystal-tKQVLTB8.js +1 -0
  46. package/dist/assets/csharp-K5feNrxe.js +1 -0
  47. package/dist/assets/css-DPfMkruS.js +1 -0
  48. package/dist/assets/csv-fuZLfV_i.js +1 -0
  49. package/dist/assets/cue-D82EKSYY.js +1 -0
  50. package/dist/assets/cypher-COkxafJQ.js +1 -0
  51. package/dist/assets/d-85-TOEBH.js +1 -0
  52. package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  53. package/dist/assets/dart-CF10PKvl.js +1 -0
  54. package/dist/assets/dax-CEL-wOlO.js +1 -0
  55. package/dist/assets/desktop-BmXAJ9_W.js +1 -0
  56. package/dist/assets/diff-D97Zzqfu.js +1 -0
  57. package/dist/assets/docker-BcOcwvcX.js +1 -0
  58. package/dist/assets/dotenv-Da5cRb03.js +1 -0
  59. package/dist/assets/dracula-BzJJZx-M.js +1 -0
  60. package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  61. package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  62. package/dist/assets/edge-BkV0erSs.js +1 -0
  63. package/dist/assets/elixir-CDX3lj18.js +1 -0
  64. package/dist/assets/elm-DbKCFpqz.js +1 -0
  65. package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
  66. package/dist/assets/erb-BOJIQeun.js +1 -0
  67. package/dist/assets/erlang-DsQrWhSR.js +1 -0
  68. package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  69. package/dist/assets/everforest-light-C8M2exoo.js +1 -0
  70. package/dist/assets/fennel-BYunw83y.js +1 -0
  71. package/dist/assets/fish-BvzEVeQv.js +1 -0
  72. package/dist/assets/fluent-C4IJs8-o.js +1 -0
  73. package/dist/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
  74. package/dist/assets/fortran-free-form-D22FLkUw.js +1 -0
  75. package/dist/assets/fsharp-CXgrBDvD.js +1 -0
  76. package/dist/assets/gdresource-B7Tvp0Sc.js +1 -0
  77. package/dist/assets/gdscript-DTMYz4Jt.js +1 -0
  78. package/dist/assets/gdshader-DkwncUOv.js +1 -0
  79. package/dist/assets/genie-D0YGMca9.js +1 -0
  80. package/dist/assets/gherkin-DyxjwDmM.js +1 -0
  81. package/dist/assets/git-commit-F4YmCXRG.js +1 -0
  82. package/dist/assets/git-rebase-r7XF79zn.js +1 -0
  83. package/dist/assets/github-dark-DHJKELXO.js +1 -0
  84. package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  85. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  86. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  87. package/dist/assets/github-light-DAi9KRSo.js +1 -0
  88. package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  89. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  90. package/dist/assets/gleam-BspZqrRM.js +1 -0
  91. package/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
  92. package/dist/assets/glimmer-ts-U6CK756n.js +1 -0
  93. package/dist/assets/glsl-DplSGwfg.js +1 -0
  94. package/dist/assets/gnuplot-DdkO51Og.js +1 -0
  95. package/dist/assets/go-Dn2_MT6a.js +1 -0
  96. package/dist/assets/graphql-ChdNCCLP.js +1 -0
  97. package/dist/assets/groovy-gcz8RCvz.js +1 -0
  98. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  99. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  100. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  101. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  102. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  103. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  104. package/dist/assets/hack-CaT9iCJl.js +1 -0
  105. package/dist/assets/haml-B8DHNrY2.js +1 -0
  106. package/dist/assets/handlebars-BL8al0AC.js +1 -0
  107. package/dist/assets/haskell-Df6bDoY_.js +1 -0
  108. package/dist/assets/haxe-CzTSHFRz.js +1 -0
  109. package/dist/assets/hcl-BWvSN4gD.js +1 -0
  110. package/dist/assets/hjson-D5-asLiD.js +1 -0
  111. package/dist/assets/hlsl-D3lLCCz7.js +1 -0
  112. package/dist/assets/houston-DnULxvSX.js +1 -0
  113. package/dist/assets/html-GMplVEZG.js +1 -0
  114. package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
  115. package/dist/assets/http-jrhK8wxY.js +1 -0
  116. package/dist/assets/hurl-irOxFIW8.js +1 -0
  117. package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
  118. package/dist/assets/hy-DFXneXwc.js +1 -0
  119. package/dist/assets/imba-DGztddWO.js +1 -0
  120. package/dist/assets/index-BNhD6i8B.js +45 -0
  121. package/dist/assets/index-Co_u49xL.css +1 -0
  122. package/dist/assets/ini-BEwlwnbL.js +1 -0
  123. package/dist/assets/java-CylS5w8V.js +1 -0
  124. package/dist/assets/javascript-wDzz0qaB.js +1 -0
  125. package/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  126. package/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  127. package/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  128. package/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  129. package/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  130. package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
  131. package/dist/assets/jison-wvAkD_A8.js +1 -0
  132. package/dist/assets/json-Cp-IABpG.js +1 -0
  133. package/dist/assets/json5-C9tS-k6U.js +1 -0
  134. package/dist/assets/jsonc-Des-eS-w.js +1 -0
  135. package/dist/assets/jsonl-DcaNXYhu.js +1 -0
  136. package/dist/assets/jsonnet-DFQXde-d.js +1 -0
  137. package/dist/assets/jssm-C2t-YnRu.js +1 -0
  138. package/dist/assets/jsx-g9-lgVsj.js +1 -0
  139. package/dist/assets/julia-C8NyazO9.js +1 -0
  140. package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  141. package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  142. package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
  143. package/dist/assets/kdl-DV7GczEv.js +1 -0
  144. package/dist/assets/kotlin-BdnUsdx6.js +1 -0
  145. package/dist/assets/kusto-BvAqAH-y.js +1 -0
  146. package/dist/assets/laserwave-DUszq2jm.js +1 -0
  147. package/dist/assets/latex-BdAV_C_H.js +1 -0
  148. package/dist/assets/lean-Bc6EcWN3.js +1 -0
  149. package/dist/assets/less-B1dDrJ26.js +1 -0
  150. package/dist/assets/light-plus-B7mTdjB0.js +1 -0
  151. package/dist/assets/liquid-DYVedYrR.js +1 -0
  152. package/dist/assets/llvm-BtvRca6l.js +1 -0
  153. package/dist/assets/log-2UxHyX5q.js +1 -0
  154. package/dist/assets/logo-BtOb2qkB.js +1 -0
  155. package/dist/assets/lua-BbnMAYS6.js +1 -0
  156. package/dist/assets/luau-CXu1NL6O.js +1 -0
  157. package/dist/assets/make-CHLpvVh8.js +1 -0
  158. package/dist/assets/markdown-Cvjx9yec.js +1 -0
  159. package/dist/assets/marko-CPi9NSCl.js +1 -0
  160. package/dist/assets/material-theme-D5KoaKCx.js +1 -0
  161. package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  162. package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  163. package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  164. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  165. package/dist/assets/matlab-D7o27uSR.js +1 -0
  166. package/dist/assets/mdc-DUICxH0z.js +1 -0
  167. package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  168. package/dist/assets/mermaid-DKYwYmdq.js +1 -0
  169. package/dist/assets/min-dark-CafNBF8u.js +1 -0
  170. package/dist/assets/min-light-CTRr51gU.js +1 -0
  171. package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  172. package/dist/assets/mojo-1DNp92w6.js +1 -0
  173. package/dist/assets/monokai-D4h5O-jR.js +1 -0
  174. package/dist/assets/move-Bu9oaDYs.js +1 -0
  175. package/dist/assets/narrat-DRg8JJMk.js +1 -0
  176. package/dist/assets/nextflow-BrzmwbiE.js +1 -0
  177. package/dist/assets/nginx-DknmC5AR.js +1 -0
  178. package/dist/assets/night-owl-C39BiMTA.js +1 -0
  179. package/dist/assets/nim-CVrawwO9.js +1 -0
  180. package/dist/assets/nix-c8nO5XWb.js +1 -0
  181. package/dist/assets/nord-Ddv68eIx.js +1 -0
  182. package/dist/assets/nushell-C-sUppwS.js +1 -0
  183. package/dist/assets/objective-c-DXmwc3jG.js +1 -0
  184. package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  185. package/dist/assets/ocaml-C0hk2d4L.js +1 -0
  186. package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  187. package/dist/assets/one-light-PoHY5YXO.js +1 -0
  188. package/dist/assets/openscad-C4EeE6gA.js +1 -0
  189. package/dist/assets/pascal-D93ZcfNL.js +1 -0
  190. package/dist/assets/perl-C0TMdlhV.js +1 -0
  191. package/dist/assets/php-CDn_0X-4.js +1 -0
  192. package/dist/assets/pkl-u5AG7uiY.js +1 -0
  193. package/dist/assets/plastic-3e1v2bzS.js +1 -0
  194. package/dist/assets/plsql-ChMvpjG-.js +1 -0
  195. package/dist/assets/po-BTJTHyun.js +1 -0
  196. package/dist/assets/poimandres-CS3Unz2-.js +1 -0
  197. package/dist/assets/polar-C0HS_06l.js +1 -0
  198. package/dist/assets/postcss-CXtECtnM.js +1 -0
  199. package/dist/assets/powerquery-CEu0bR-o.js +1 -0
  200. package/dist/assets/powershell-Dpen1YoG.js +1 -0
  201. package/dist/assets/prisma-Dd19v3D-.js +1 -0
  202. package/dist/assets/prolog-CbFg5uaA.js +1 -0
  203. package/dist/assets/proto-DyJlTyXw.js +1 -0
  204. package/dist/assets/pug-CGlum2m_.js +1 -0
  205. package/dist/assets/puppet-BMWR74SV.js +1 -0
  206. package/dist/assets/purescript-CklMAg4u.js +1 -0
  207. package/dist/assets/python-B6aJPvgy.js +1 -0
  208. package/dist/assets/qml-3beO22l8.js +1 -0
  209. package/dist/assets/qmldir-C8lEn-DE.js +1 -0
  210. package/dist/assets/qss-IeuSbFQv.js +1 -0
  211. package/dist/assets/r-DiinP2Uv.js +1 -0
  212. package/dist/assets/racket-BqYA7rlc.js +1 -0
  213. package/dist/assets/raku-DXvB9xmW.js +1 -0
  214. package/dist/assets/razor-CE9lU5zL.js +1 -0
  215. package/dist/assets/red-bN70gL4F.js +1 -0
  216. package/dist/assets/reg-C-SQnVFl.js +1 -0
  217. package/dist/assets/regexp-CDVJQ6XC.js +1 -0
  218. package/dist/assets/rel-C3B-1QV4.js +1 -0
  219. package/dist/assets/riscv-BM1_JUlF.js +1 -0
  220. package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  221. package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  222. package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
  223. package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  224. package/dist/assets/rst-B0xPkSld.js +1 -0
  225. package/dist/assets/ruby-BvKwtOVI.js +1 -0
  226. package/dist/assets/rust-B1yitclQ.js +1 -0
  227. package/dist/assets/sas-cz2c8ADy.js +1 -0
  228. package/dist/assets/sass-Cj5Yp3dK.js +1 -0
  229. package/dist/assets/scala-C151Ov-r.js +1 -0
  230. package/dist/assets/scheme-C98Dy4si.js +1 -0
  231. package/dist/assets/scss-OYdSNvt2.js +1 -0
  232. package/dist/assets/sdbl-DVxCFoDh.js +1 -0
  233. package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  234. package/dist/assets/shellscript-Yzrsuije.js +1 -0
  235. package/dist/assets/shellsession-BADoaaVG.js +1 -0
  236. package/dist/assets/slack-dark-BthQWCQV.js +1 -0
  237. package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  238. package/dist/assets/smalltalk-BERRCDM3.js +1 -0
  239. package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  240. package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
  241. package/dist/assets/solarized-light-L9t79GZl.js +1 -0
  242. package/dist/assets/solidity-rGO070M0.js +1 -0
  243. package/dist/assets/soy-Brmx7dQM.js +1 -0
  244. package/dist/assets/sparql-rVzFXLq3.js +1 -0
  245. package/dist/assets/splunk-BtCnVYZw.js +1 -0
  246. package/dist/assets/sql-BLtJtn59.js +1 -0
  247. package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  248. package/dist/assets/stata-BH5u7GGu.js +1 -0
  249. package/dist/assets/stylus-BEDo0Tqx.js +1 -0
  250. package/dist/assets/svelte-3Dk4HxPD.js +1 -0
  251. package/dist/assets/swift-Dg5xB15N.js +1 -0
  252. package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  253. package/dist/assets/system-verilog-CnnmHF94.js +1 -0
  254. package/dist/assets/systemd-4A_iFExJ.js +1 -0
  255. package/dist/assets/talonscript-CkByrt1z.js +1 -0
  256. package/dist/assets/tasl-QIJgUcNo.js +1 -0
  257. package/dist/assets/tcl-dwOrl1Do.js +1 -0
  258. package/dist/assets/templ-W15q3VgB.js +1 -0
  259. package/dist/assets/terraform-BETggiCN.js +1 -0
  260. package/dist/assets/tex-CxkMU7Pf.js +1 -0
  261. package/dist/assets/tokyo-night-hegEt444.js +1 -0
  262. package/dist/assets/toml-vGWfd6FD.js +1 -0
  263. package/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
  264. package/dist/assets/tsv-B_m7g4N7.js +1 -0
  265. package/dist/assets/tsx-COt5Ahok.js +1 -0
  266. package/dist/assets/turtle-BsS91CYL.js +1 -0
  267. package/dist/assets/twig-CO9l9SDP.js +1 -0
  268. package/dist/assets/typescript-BPQ3VLAy.js +1 -0
  269. package/dist/assets/typespec-BGHnOYBU.js +1 -0
  270. package/dist/assets/typst-DHCkPAjA.js +1 -0
  271. package/dist/assets/v-BcVCzyr7.js +1 -0
  272. package/dist/assets/vala-CsfeWuGM.js +1 -0
  273. package/dist/assets/vb-D17OF-Vu.js +1 -0
  274. package/dist/assets/verilog-BQ8w6xss.js +1 -0
  275. package/dist/assets/vesper-DU1UobuO.js +1 -0
  276. package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  277. package/dist/assets/viml-CJc9bBzg.js +1 -0
  278. package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  279. package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  280. package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  281. package/dist/assets/vue-DnHKYNfI.js +1 -0
  282. package/dist/assets/vue-html-CChd_i61.js +1 -0
  283. package/dist/assets/vue-vine-8moa0y9V.js +1 -0
  284. package/dist/assets/vyper-CDx5xZoG.js +1 -0
  285. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  286. package/dist/assets/wasm-MzD3tlZU.js +1 -0
  287. package/dist/assets/wenyan-BV7otONQ.js +1 -0
  288. package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  289. package/dist/assets/wikitext-BhOHFoWU.js +1 -0
  290. package/dist/assets/wit-5i3qLPDT.js +1 -0
  291. package/dist/assets/wolfram-lXgVvXCa.js +1 -0
  292. package/dist/assets/xml-sdJ4AIDG.js +1 -0
  293. package/dist/assets/xsl-CtQFsRM5.js +1 -0
  294. package/dist/assets/yaml-Buea-lGh.js +1 -0
  295. package/dist/assets/zenscript-DVFEvuxE.js +1 -0
  296. package/dist/assets/zig-VOosw3JB.js +1 -0
  297. package/dist/favicon.ico +0 -0
  298. package/dist/index.html +15 -0
  299. package/dist/vibora-icon.png +0 -0
  300. package/dist/vibora-logo.jpeg +0 -0
  301. package/dist/vite.svg +1 -0
  302. package/drizzle/0000_jittery_bloodaxe.sql +46 -0
  303. package/drizzle/0001_soft_amphibian.sql +2 -0
  304. package/drizzle/0002_burly_tyrannus.sql +12 -0
  305. package/drizzle/meta/0000_snapshot.json +310 -0
  306. package/drizzle/meta/0001_snapshot.json +324 -0
  307. package/drizzle/meta/0002_snapshot.json +398 -0
  308. package/drizzle/meta/_journal.json +27 -0
  309. package/lib/librust_pty.so +0 -0
  310. package/package.json +25 -0
  311. package/server/index.js +139285 -0
package/bin/vibora.js ADDED
@@ -0,0 +1,1018 @@
1
+ #!/usr/bin/env node
2
+ // @bun
3
+
4
+ // cli/src/utils/server.ts
5
+ import { existsSync, readFileSync } from "fs";
6
+ import { join } from "path";
7
+ import { homedir } from "os";
8
+ function expandPath(p) {
9
+ if (p.startsWith("~/")) {
10
+ return join(homedir(), p.slice(2));
11
+ }
12
+ return p;
13
+ }
14
+ function readSettingsFile(path) {
15
+ try {
16
+ if (existsSync(path)) {
17
+ const content = readFileSync(path, "utf-8");
18
+ return JSON.parse(content);
19
+ }
20
+ } catch {}
21
+ return null;
22
+ }
23
+ function discoverServerUrl(urlOverride, portOverride) {
24
+ if (urlOverride) {
25
+ return urlOverride;
26
+ }
27
+ if (portOverride) {
28
+ return `http://localhost:${portOverride}`;
29
+ }
30
+ if (process.env.VIBORA_URL) {
31
+ return process.env.VIBORA_URL;
32
+ }
33
+ if (process.env.VIBORA_DIR) {
34
+ const viboraDirSettings = join(expandPath(process.env.VIBORA_DIR), "settings.json");
35
+ const settings = readSettingsFile(viboraDirSettings);
36
+ if (settings?.port) {
37
+ return `http://localhost:${settings.port}`;
38
+ }
39
+ }
40
+ const cwdSettings = join(process.cwd(), ".vibora", "settings.json");
41
+ const localSettings = readSettingsFile(cwdSettings);
42
+ if (localSettings?.port) {
43
+ return `http://localhost:${localSettings.port}`;
44
+ }
45
+ const globalSettings = join(homedir(), ".vibora", "settings.json");
46
+ const homeSettings = readSettingsFile(globalSettings);
47
+ if (homeSettings?.port) {
48
+ return `http://localhost:${homeSettings.port}`;
49
+ }
50
+ return "http://localhost:3333";
51
+ }
52
+ function getViboraDir() {
53
+ if (process.env.VIBORA_DIR) {
54
+ return expandPath(process.env.VIBORA_DIR);
55
+ }
56
+ const cwdViboraDir = join(process.cwd(), ".vibora");
57
+ if (existsSync(cwdViboraDir)) {
58
+ return cwdViboraDir;
59
+ }
60
+ return join(homedir(), ".vibora");
61
+ }
62
+
63
+ // cli/src/utils/errors.ts
64
+ var ExitCodes = {
65
+ SUCCESS: 0,
66
+ ERROR: 1,
67
+ INVALID_ARGS: 2,
68
+ SERVER_UNREACHABLE: 3,
69
+ NOT_FOUND: 4,
70
+ VALIDATION_ERROR: 5
71
+ };
72
+
73
+ class CliError extends Error {
74
+ code;
75
+ exitCode;
76
+ constructor(code, message, exitCode = ExitCodes.ERROR) {
77
+ super(message);
78
+ this.code = code;
79
+ this.exitCode = exitCode;
80
+ this.name = "CliError";
81
+ }
82
+ }
83
+
84
+ class ApiError extends CliError {
85
+ statusCode;
86
+ constructor(statusCode, message) {
87
+ const exitCode = statusCode === 0 ? ExitCodes.SERVER_UNREACHABLE : statusCode === 404 ? ExitCodes.NOT_FOUND : statusCode === 400 ? ExitCodes.VALIDATION_ERROR : ExitCodes.ERROR;
88
+ const code = statusCode === 0 ? "SERVER_UNREACHABLE" : statusCode === 404 ? "NOT_FOUND" : statusCode === 400 ? "VALIDATION_ERROR" : "API_ERROR";
89
+ super(code, message, exitCode);
90
+ this.statusCode = statusCode;
91
+ this.name = "ApiError";
92
+ }
93
+ }
94
+
95
+ // cli/src/client.ts
96
+ class ViboraClient {
97
+ baseUrl;
98
+ constructor(urlOverride, portOverride) {
99
+ this.baseUrl = discoverServerUrl(urlOverride, portOverride);
100
+ }
101
+ async fetch(path, options) {
102
+ const url = `${this.baseUrl}${path}`;
103
+ try {
104
+ const res = await fetch(url, {
105
+ ...options,
106
+ headers: {
107
+ "Content-Type": "application/json",
108
+ ...options?.headers
109
+ }
110
+ });
111
+ if (!res.ok) {
112
+ const body = await res.json().catch(() => ({}));
113
+ throw new ApiError(res.status, body.error || body.message || `Request failed: ${res.status}`);
114
+ }
115
+ return res.json();
116
+ } catch (err) {
117
+ if (err instanceof ApiError)
118
+ throw err;
119
+ throw new ApiError(0, `Server unreachable: ${this.baseUrl}`);
120
+ }
121
+ }
122
+ async health() {
123
+ return this.fetch("/health");
124
+ }
125
+ async listTasks() {
126
+ return this.fetch("/api/tasks");
127
+ }
128
+ async getTask(id) {
129
+ return this.fetch(`/api/tasks/${id}`);
130
+ }
131
+ async createTask(data) {
132
+ return this.fetch("/api/tasks", {
133
+ method: "POST",
134
+ body: JSON.stringify(data)
135
+ });
136
+ }
137
+ async updateTask(id, updates) {
138
+ return this.fetch(`/api/tasks/${id}`, {
139
+ method: "PATCH",
140
+ body: JSON.stringify(updates)
141
+ });
142
+ }
143
+ async moveTask(id, status, position) {
144
+ if (position === undefined) {
145
+ const tasks = await this.listTasks();
146
+ const targetTasks = tasks.filter((t) => t.status === status);
147
+ position = targetTasks.length;
148
+ }
149
+ return this.fetch(`/api/tasks/${id}/status`, {
150
+ method: "PATCH",
151
+ body: JSON.stringify({ status, position })
152
+ });
153
+ }
154
+ async deleteTask(id) {
155
+ return this.fetch(`/api/tasks/${id}`, { method: "DELETE" });
156
+ }
157
+ async bulkDeleteTasks(ids) {
158
+ return this.fetch("/api/tasks/bulk", {
159
+ method: "DELETE",
160
+ body: JSON.stringify({ ids })
161
+ });
162
+ }
163
+ async getBranches(repo) {
164
+ return this.fetch(`/api/git/branches?repo=${encodeURIComponent(repo)}`);
165
+ }
166
+ async getDiff(path, options) {
167
+ const params = new URLSearchParams({ path });
168
+ if (options?.staged)
169
+ params.set("staged", "true");
170
+ if (options?.ignoreWhitespace)
171
+ params.set("ignoreWhitespace", "true");
172
+ if (options?.includeUntracked)
173
+ params.set("includeUntracked", "true");
174
+ return this.fetch(`/api/git/diff?${params}`);
175
+ }
176
+ async getStatus(path) {
177
+ return this.fetch(`/api/git/status?path=${encodeURIComponent(path)}`);
178
+ }
179
+ async listWorktrees() {
180
+ return this.fetch("/api/worktrees");
181
+ }
182
+ async deleteWorktree(worktreePath, repoPath) {
183
+ return this.fetch("/api/worktrees", {
184
+ method: "DELETE",
185
+ body: JSON.stringify({ worktreePath, repoPath })
186
+ });
187
+ }
188
+ async getConfig(key) {
189
+ return this.fetch(`/api/config/${key}`);
190
+ }
191
+ async setConfig(key, value) {
192
+ return this.fetch(`/api/config/${key}`, {
193
+ method: "PUT",
194
+ body: JSON.stringify({ value })
195
+ });
196
+ }
197
+ async resetConfig(key) {
198
+ return this.fetch(`/api/config/${key}`, { method: "DELETE" });
199
+ }
200
+ }
201
+
202
+ // cli/src/utils/output.ts
203
+ var prettyOutput = false;
204
+ function setPrettyOutput(value) {
205
+ prettyOutput = value;
206
+ }
207
+ function isPrettyOutput() {
208
+ return prettyOutput;
209
+ }
210
+ function prettyLog(type, message) {
211
+ const prefixes = {
212
+ success: "\u2713",
213
+ info: "\u2192",
214
+ error: "\u2717",
215
+ warning: "\u26A0"
216
+ };
217
+ console.log(`${prefixes[type]} ${message}`);
218
+ }
219
+ function outputSuccess(message) {
220
+ if (prettyOutput) {
221
+ prettyLog("success", message);
222
+ } else {
223
+ output({ message });
224
+ }
225
+ }
226
+ function output(data) {
227
+ const response = {
228
+ success: true,
229
+ data
230
+ };
231
+ console.log(prettyOutput ? JSON.stringify(response, null, 2) : JSON.stringify(response));
232
+ }
233
+ function outputError(error) {
234
+ const response = {
235
+ success: false,
236
+ error: {
237
+ code: error.code,
238
+ message: error.message
239
+ }
240
+ };
241
+ console.log(prettyOutput ? JSON.stringify(response, null, 2) : JSON.stringify(response));
242
+ process.exit(error.exitCode);
243
+ }
244
+
245
+ // cli/src/commands/current-task.ts
246
+ var STATUS_MAP = {
247
+ done: "DONE",
248
+ review: "IN_REVIEW",
249
+ cancel: "CANCELED",
250
+ "in-progress": "IN_PROGRESS"
251
+ };
252
+ async function findCurrentTask(client, pathOverride) {
253
+ const currentPath = pathOverride || process.cwd();
254
+ const tasks = await client.listTasks();
255
+ const task = tasks.find((t) => {
256
+ if (!t.worktreePath)
257
+ return false;
258
+ return currentPath === t.worktreePath || currentPath.startsWith(t.worktreePath + "/");
259
+ });
260
+ if (!task) {
261
+ throw new CliError("NOT_IN_WORKTREE", `No task found for path: ${currentPath}. Are you inside a Vibora task worktree?`, ExitCodes.NOT_FOUND);
262
+ }
263
+ return task;
264
+ }
265
+ async function handleCurrentTaskCommand(action, rest, flags) {
266
+ const client = new ViboraClient(flags.url, flags.port);
267
+ const pathOverride = flags.path;
268
+ if (!action) {
269
+ const task2 = await findCurrentTask(client, pathOverride);
270
+ output(task2);
271
+ return;
272
+ }
273
+ if (action === "pr") {
274
+ const prUrl = rest[0];
275
+ if (!prUrl) {
276
+ throw new CliError("MISSING_PR_URL", "Usage: vibora current-task pr <url>", ExitCodes.INVALID_ARGS);
277
+ }
278
+ const task2 = await findCurrentTask(client, pathOverride);
279
+ const updatedTask2 = await client.updateTask(task2.id, { prUrl });
280
+ output(updatedTask2);
281
+ return;
282
+ }
283
+ if (action === "linear") {
284
+ const linearUrl = rest[0];
285
+ if (!linearUrl) {
286
+ throw new CliError("MISSING_LINEAR_URL", "Usage: vibora current-task linear <url>", ExitCodes.INVALID_ARGS);
287
+ }
288
+ const ticketId = linearUrl.match(/\/issue\/([A-Z]+-\d+)/i)?.[1];
289
+ if (!ticketId) {
290
+ throw new CliError("INVALID_LINEAR_URL", "Invalid Linear URL. Expected format: https://linear.app/team/issue/TEAM-123", ExitCodes.INVALID_ARGS);
291
+ }
292
+ const task2 = await findCurrentTask(client, pathOverride);
293
+ const updatedTask2 = await client.updateTask(task2.id, {
294
+ linearTicketId: ticketId,
295
+ linearTicketUrl: linearUrl
296
+ });
297
+ output(updatedTask2);
298
+ return;
299
+ }
300
+ const newStatus = STATUS_MAP[action];
301
+ if (!newStatus) {
302
+ throw new CliError("INVALID_ACTION", `Unknown action: ${action}. Valid actions: done, review, cancel, in-progress, pr, linear`, ExitCodes.INVALID_ARGS);
303
+ }
304
+ const task = await findCurrentTask(client, pathOverride);
305
+ const updatedTask = await client.moveTask(task.id, newStatus);
306
+ output(updatedTask);
307
+ }
308
+
309
+ // cli/src/commands/tasks.ts
310
+ import { basename } from "path";
311
+ var VALID_STATUSES = ["IN_PROGRESS", "IN_REVIEW", "DONE", "CANCELED"];
312
+ async function handleTasksCommand(action, positional, flags) {
313
+ const client = new ViboraClient(flags.url, flags.port);
314
+ switch (action) {
315
+ case "list": {
316
+ let tasks = await client.listTasks();
317
+ if (flags.status) {
318
+ const status = flags.status.toUpperCase();
319
+ if (!VALID_STATUSES.includes(status)) {
320
+ throw new CliError("INVALID_STATUS", `Invalid status: ${flags.status}. Valid: ${VALID_STATUSES.join(", ")}`, ExitCodes.INVALID_ARGS);
321
+ }
322
+ tasks = tasks.filter((t) => t.status === status);
323
+ }
324
+ if (flags.repo) {
325
+ const repoFilter = flags.repo.toLowerCase();
326
+ tasks = tasks.filter((t) => t.repoName.toLowerCase().includes(repoFilter) || t.repoPath.toLowerCase().includes(repoFilter));
327
+ }
328
+ output(tasks);
329
+ break;
330
+ }
331
+ case "get": {
332
+ const [id] = positional;
333
+ if (!id) {
334
+ throw new CliError("MISSING_ID", "Task ID required", ExitCodes.INVALID_ARGS);
335
+ }
336
+ const task = await client.getTask(id);
337
+ output(task);
338
+ break;
339
+ }
340
+ case "create": {
341
+ const title = flags.title;
342
+ const repoPath = flags.repo || flags["repo-path"];
343
+ const baseBranch = flags["base-branch"] || "main";
344
+ const branch = flags.branch;
345
+ const description = flags.description || "";
346
+ if (!title) {
347
+ throw new CliError("MISSING_TITLE", "--title is required", ExitCodes.INVALID_ARGS);
348
+ }
349
+ if (!repoPath) {
350
+ throw new CliError("MISSING_REPO", "--repo is required", ExitCodes.INVALID_ARGS);
351
+ }
352
+ const repoName = flags["repo-name"] || basename(repoPath);
353
+ const task = await client.createTask({
354
+ title,
355
+ description,
356
+ repoPath,
357
+ repoName,
358
+ baseBranch,
359
+ branch: branch || null,
360
+ worktreePath: flags["worktree-path"] || null,
361
+ status: "IN_PROGRESS"
362
+ });
363
+ output(task);
364
+ break;
365
+ }
366
+ case "update": {
367
+ const [id] = positional;
368
+ if (!id) {
369
+ throw new CliError("MISSING_ID", "Task ID required", ExitCodes.INVALID_ARGS);
370
+ }
371
+ const updates = {};
372
+ if (flags.title !== undefined)
373
+ updates.title = flags.title;
374
+ if (flags.description !== undefined)
375
+ updates.description = flags.description;
376
+ if (Object.keys(updates).length === 0) {
377
+ throw new CliError("NO_UPDATES", "No updates provided. Use --title or --description", ExitCodes.INVALID_ARGS);
378
+ }
379
+ const task = await client.updateTask(id, updates);
380
+ output(task);
381
+ break;
382
+ }
383
+ case "move": {
384
+ const [id] = positional;
385
+ if (!id) {
386
+ throw new CliError("MISSING_ID", "Task ID required", ExitCodes.INVALID_ARGS);
387
+ }
388
+ const status = flags.status?.toUpperCase() || "";
389
+ if (!status || !VALID_STATUSES.includes(status)) {
390
+ throw new CliError("INVALID_STATUS", `--status is required. Valid: ${VALID_STATUSES.join(", ")}`, ExitCodes.INVALID_ARGS);
391
+ }
392
+ const position = flags.position ? parseInt(flags.position, 10) : undefined;
393
+ const task = await client.moveTask(id, status, position);
394
+ output(task);
395
+ break;
396
+ }
397
+ case "delete": {
398
+ const [id] = positional;
399
+ if (!id) {
400
+ throw new CliError("MISSING_ID", "Task ID required", ExitCodes.INVALID_ARGS);
401
+ }
402
+ await client.deleteTask(id);
403
+ output({ deleted: id });
404
+ break;
405
+ }
406
+ default:
407
+ throw new CliError("UNKNOWN_ACTION", `Unknown action: ${action}. Valid: list, get, create, update, move, delete`, ExitCodes.INVALID_ARGS);
408
+ }
409
+ }
410
+
411
+ // cli/src/commands/up.ts
412
+ import { spawn } from "child_process";
413
+ import { dirname as dirname2, join as join3 } from "path";
414
+ import { fileURLToPath } from "url";
415
+
416
+ // cli/src/utils/process.ts
417
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, unlinkSync, mkdirSync } from "fs";
418
+ import { join as join2, dirname } from "path";
419
+ function getPidPath() {
420
+ return join2(getViboraDir(), "vibora.pid");
421
+ }
422
+ function writePid(pid) {
423
+ const pidPath = getPidPath();
424
+ const dir = dirname(pidPath);
425
+ if (!existsSync2(dir)) {
426
+ mkdirSync(dir, { recursive: true });
427
+ }
428
+ writeFileSync(pidPath, pid.toString(), "utf-8");
429
+ }
430
+ function readPid() {
431
+ const pidPath = getPidPath();
432
+ try {
433
+ if (existsSync2(pidPath)) {
434
+ const content = readFileSync2(pidPath, "utf-8").trim();
435
+ const pid = parseInt(content, 10);
436
+ return isNaN(pid) ? null : pid;
437
+ }
438
+ } catch {}
439
+ return null;
440
+ }
441
+ function removePid() {
442
+ const pidPath = getPidPath();
443
+ try {
444
+ if (existsSync2(pidPath)) {
445
+ unlinkSync(pidPath);
446
+ }
447
+ } catch {}
448
+ }
449
+ function isProcessRunning(pid) {
450
+ try {
451
+ process.kill(pid, 0);
452
+ return true;
453
+ } catch {
454
+ return false;
455
+ }
456
+ }
457
+ function getPort(portOverride) {
458
+ if (portOverride) {
459
+ const port = parseInt(portOverride, 10);
460
+ if (!isNaN(port))
461
+ return port;
462
+ }
463
+ if (process.env.PORT) {
464
+ const port = parseInt(process.env.PORT, 10);
465
+ if (!isNaN(port))
466
+ return port;
467
+ }
468
+ return 3333;
469
+ }
470
+
471
+ // cli/src/commands/up.ts
472
+ function getPackageRoot() {
473
+ const currentFile = fileURLToPath(import.meta.url);
474
+ return dirname2(dirname2(dirname2(currentFile)));
475
+ }
476
+ async function handleUpCommand(flags) {
477
+ const existingPid = readPid();
478
+ if (existingPid && isProcessRunning(existingPid)) {
479
+ throw new CliError("ALREADY_RUNNING", `Vibora server is already running (PID: ${existingPid})`, ExitCodes.ERROR);
480
+ }
481
+ const port = getPort(flags.port);
482
+ const packageRoot = getPackageRoot();
483
+ const serverPath = join3(packageRoot, "server", "index.js");
484
+ const ptyLibPath = join3(packageRoot, "lib", "librust_pty.so");
485
+ console.error("Starting Vibora server...");
486
+ const serverProc = spawn("bun", [serverPath], {
487
+ detached: true,
488
+ stdio: "ignore",
489
+ env: {
490
+ ...process.env,
491
+ NODE_ENV: "production",
492
+ PORT: port.toString(),
493
+ VIBORA_PACKAGE_ROOT: packageRoot,
494
+ BUN_PTY_LIB: ptyLibPath
495
+ }
496
+ });
497
+ serverProc.unref();
498
+ const pid = serverProc.pid;
499
+ if (!pid) {
500
+ throw new CliError("START_FAILED", "Failed to start server process", ExitCodes.ERROR);
501
+ }
502
+ writePid(pid);
503
+ await new Promise((resolve) => setTimeout(resolve, 1000));
504
+ if (!isProcessRunning(pid)) {
505
+ throw new CliError("START_FAILED", "Server process died immediately after starting", ExitCodes.ERROR);
506
+ }
507
+ output({
508
+ pid,
509
+ port,
510
+ url: `http://localhost:${port}`
511
+ });
512
+ }
513
+
514
+ // cli/src/commands/down.ts
515
+ async function handleDownCommand() {
516
+ const pid = readPid();
517
+ if (!pid) {
518
+ throw new CliError("NOT_RUNNING", "No PID file found. Vibora server may not be running.", ExitCodes.ERROR);
519
+ }
520
+ if (!isProcessRunning(pid)) {
521
+ removePid();
522
+ output({ stopped: true, pid, wasRunning: false });
523
+ return;
524
+ }
525
+ try {
526
+ process.kill(pid, "SIGTERM");
527
+ } catch (err) {
528
+ throw new CliError("KILL_FAILED", `Failed to stop server (PID: ${pid}): ${err}`, ExitCodes.ERROR);
529
+ }
530
+ let attempts = 0;
531
+ while (attempts < 50 && isProcessRunning(pid)) {
532
+ await new Promise((resolve) => setTimeout(resolve, 100));
533
+ attempts++;
534
+ }
535
+ if (isProcessRunning(pid)) {
536
+ try {
537
+ process.kill(pid, "SIGKILL");
538
+ } catch {}
539
+ }
540
+ removePid();
541
+ output({ stopped: true, pid, wasRunning: true });
542
+ }
543
+
544
+ // cli/src/commands/status.ts
545
+ async function handleStatusCommand(flags) {
546
+ const pid = readPid();
547
+ const port = getPort(flags.port);
548
+ const serverUrl = discoverServerUrl(flags.url, flags.port);
549
+ const pidRunning = pid !== null && isProcessRunning(pid);
550
+ let healthOk = false;
551
+ if (pidRunning) {
552
+ try {
553
+ const res = await fetch(`${serverUrl}/health`, { signal: AbortSignal.timeout(2000) });
554
+ healthOk = res.ok;
555
+ } catch {}
556
+ }
557
+ output({
558
+ running: pidRunning,
559
+ healthy: healthOk,
560
+ pid: pid || null,
561
+ port,
562
+ url: serverUrl
563
+ });
564
+ }
565
+
566
+ // cli/src/commands/git.ts
567
+ async function handleGitCommand(action, flags) {
568
+ const client = new ViboraClient(flags.url, flags.port);
569
+ switch (action) {
570
+ case "status": {
571
+ const path = flags.path || process.cwd();
572
+ const status = await client.getStatus(path);
573
+ output(status);
574
+ break;
575
+ }
576
+ case "diff": {
577
+ const path = flags.path || process.cwd();
578
+ const diff = await client.getDiff(path, {
579
+ staged: flags.staged === "true",
580
+ ignoreWhitespace: flags["ignore-whitespace"] === "true",
581
+ includeUntracked: flags["include-untracked"] === "true"
582
+ });
583
+ output(diff);
584
+ break;
585
+ }
586
+ case "branches": {
587
+ const repo = flags.repo;
588
+ if (!repo) {
589
+ throw new CliError("MISSING_REPO", "--repo is required", ExitCodes.INVALID_ARGS);
590
+ }
591
+ const branches = await client.getBranches(repo);
592
+ output(branches);
593
+ break;
594
+ }
595
+ default:
596
+ throw new CliError("UNKNOWN_ACTION", `Unknown action: ${action}. Valid: status, diff, branches`, ExitCodes.INVALID_ARGS);
597
+ }
598
+ }
599
+
600
+ // cli/src/commands/worktrees.ts
601
+ async function handleWorktreesCommand(action, flags) {
602
+ const client = new ViboraClient(flags.url, flags.port);
603
+ switch (action) {
604
+ case "list": {
605
+ const worktrees = await client.listWorktrees();
606
+ output(worktrees);
607
+ break;
608
+ }
609
+ case "delete": {
610
+ const worktreePath = flags.path;
611
+ if (!worktreePath) {
612
+ throw new CliError("MISSING_PATH", "--path is required", ExitCodes.INVALID_ARGS);
613
+ }
614
+ const result = await client.deleteWorktree(worktreePath, flags.repo);
615
+ output(result);
616
+ break;
617
+ }
618
+ default:
619
+ throw new CliError("UNKNOWN_ACTION", `Unknown action: ${action}. Valid: list, delete`, ExitCodes.INVALID_ARGS);
620
+ }
621
+ }
622
+
623
+ // cli/src/commands/config.ts
624
+ async function handleConfigCommand(action, positional, flags) {
625
+ const client = new ViboraClient(flags.url, flags.port);
626
+ switch (action) {
627
+ case "get": {
628
+ const [key] = positional;
629
+ if (!key) {
630
+ throw new CliError("MISSING_KEY", "Config key is required", ExitCodes.INVALID_ARGS);
631
+ }
632
+ const config = await client.getConfig(key);
633
+ output(config);
634
+ break;
635
+ }
636
+ case "set": {
637
+ const [key, value] = positional;
638
+ if (!key) {
639
+ throw new CliError("MISSING_KEY", "Config key is required", ExitCodes.INVALID_ARGS);
640
+ }
641
+ if (value === undefined) {
642
+ throw new CliError("MISSING_VALUE", "Config value is required", ExitCodes.INVALID_ARGS);
643
+ }
644
+ const parsedValue = /^\d+$/.test(value) ? parseInt(value, 10) : value;
645
+ const config = await client.setConfig(key, parsedValue);
646
+ output(config);
647
+ break;
648
+ }
649
+ case "reset": {
650
+ const [key] = positional;
651
+ if (!key) {
652
+ throw new CliError("MISSING_KEY", "Config key is required", ExitCodes.INVALID_ARGS);
653
+ }
654
+ const config = await client.resetConfig(key);
655
+ output(config);
656
+ break;
657
+ }
658
+ default:
659
+ throw new CliError("UNKNOWN_ACTION", `Unknown action: ${action}. Valid: get, set, reset`, ExitCodes.INVALID_ARGS);
660
+ }
661
+ }
662
+
663
+ // cli/src/commands/health.ts
664
+ async function handleHealthCommand(flags) {
665
+ const client = new ViboraClient(flags.url, flags.port);
666
+ const health = await client.health();
667
+ output(health);
668
+ }
669
+
670
+ // cli/src/commands/hooks.ts
671
+ import * as fs from "fs";
672
+ import * as path from "path";
673
+ import * as os from "os";
674
+ function getClaudeSettingsPath(global) {
675
+ if (global) {
676
+ return path.join(os.homedir(), ".claude", "settings.json");
677
+ }
678
+ return path.join(process.cwd(), ".claude", "settings.json");
679
+ }
680
+ function readClaudeSettings(settingsPath) {
681
+ if (!fs.existsSync(settingsPath)) {
682
+ return {};
683
+ }
684
+ try {
685
+ const content = fs.readFileSync(settingsPath, "utf-8");
686
+ return JSON.parse(content);
687
+ } catch {
688
+ return {};
689
+ }
690
+ }
691
+ function writeClaudeSettings(settingsPath, settings) {
692
+ const dir = path.dirname(settingsPath);
693
+ if (!fs.existsSync(dir)) {
694
+ fs.mkdirSync(dir, { recursive: true });
695
+ }
696
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
697
+ }
698
+ function getViboraHookPath() {
699
+ const scriptDir = path.dirname(Bun.main);
700
+ const possiblePaths = [
701
+ path.join(scriptDir, "..", "scripts", "vibora-plan-complete-hook"),
702
+ path.join(scriptDir, "..", "..", "scripts", "vibora-plan-complete-hook"),
703
+ "vibora-plan-complete-hook"
704
+ ];
705
+ for (const p of possiblePaths) {
706
+ if (p === "vibora-plan-complete-hook") {
707
+ try {
708
+ const result = Bun.spawnSync(["which", "vibora-plan-complete-hook"]);
709
+ if (result.exitCode === 0) {
710
+ return "vibora-plan-complete-hook";
711
+ }
712
+ } catch {
713
+ continue;
714
+ }
715
+ } else if (fs.existsSync(p)) {
716
+ return path.resolve(p);
717
+ }
718
+ }
719
+ return "vibora-plan-complete-hook";
720
+ }
721
+ function installStopHook(global) {
722
+ const settingsPath = getClaudeSettingsPath(global);
723
+ const settings = readClaudeSettings(settingsPath);
724
+ const hookCommand = getViboraHookPath();
725
+ if (!settings.hooks) {
726
+ settings.hooks = {};
727
+ }
728
+ const existingStopHooks = settings.hooks.Stop || [];
729
+ const hasViboraHook = existingStopHooks.some((hook) => hook.hooks.some((h) => h.type === "command" && h.command?.includes("vibora-plan-complete-hook")));
730
+ if (hasViboraHook) {
731
+ return { settingsPath, hookCommand };
732
+ }
733
+ settings.hooks.Stop = [
734
+ ...existingStopHooks,
735
+ {
736
+ hooks: [
737
+ {
738
+ type: "command",
739
+ command: hookCommand
740
+ }
741
+ ]
742
+ }
743
+ ];
744
+ writeClaudeSettings(settingsPath, settings);
745
+ return { settingsPath, hookCommand };
746
+ }
747
+ function uninstallStopHook(global) {
748
+ const settingsPath = getClaudeSettingsPath(global);
749
+ const settings = readClaudeSettings(settingsPath);
750
+ if (!settings.hooks?.Stop) {
751
+ return { settingsPath, removed: false };
752
+ }
753
+ const originalLength = settings.hooks.Stop.length;
754
+ settings.hooks.Stop = settings.hooks.Stop.filter((hook) => !hook.hooks.some((h) => h.type === "command" && h.command?.includes("vibora-plan-complete-hook")));
755
+ const removed = settings.hooks.Stop.length < originalLength;
756
+ if (removed) {
757
+ if (settings.hooks.Stop.length === 0) {
758
+ delete settings.hooks.Stop;
759
+ }
760
+ if (Object.keys(settings.hooks).length === 0) {
761
+ delete settings.hooks;
762
+ }
763
+ writeClaudeSettings(settingsPath, settings);
764
+ }
765
+ return { settingsPath, removed };
766
+ }
767
+ function checkStopHook(global) {
768
+ const settingsPath = getClaudeSettingsPath(global);
769
+ const settings = readClaudeSettings(settingsPath);
770
+ if (!settings.hooks?.Stop) {
771
+ return { installed: false, settingsPath };
772
+ }
773
+ for (const hook of settings.hooks.Stop) {
774
+ for (const h of hook.hooks) {
775
+ if (h.type === "command" && h.command?.includes("vibora-plan-complete-hook")) {
776
+ return { installed: true, settingsPath, hookCommand: h.command };
777
+ }
778
+ }
779
+ }
780
+ return { installed: false, settingsPath };
781
+ }
782
+ async function handleHooksCommand(action, _rest, flags) {
783
+ const global = flags.global === "true" || flags.g === "true";
784
+ switch (action) {
785
+ case "install": {
786
+ const { settingsPath, hookCommand } = installStopHook(global);
787
+ if (isPrettyOutput()) {
788
+ prettyLog("success", `Installed Vibora Stop hook`);
789
+ prettyLog("info", ` Settings: ${settingsPath}`);
790
+ prettyLog("info", ` Command: ${hookCommand}`);
791
+ prettyLog("info", "");
792
+ prettyLog("info", "The hook will automatically transition tasks to IN_REVIEW");
793
+ prettyLog("info", "when Claude Code finishes in a Vibora worktree.");
794
+ } else {
795
+ outputSuccess({
796
+ action: "install",
797
+ settingsPath,
798
+ hookCommand,
799
+ message: "Stop hook installed successfully"
800
+ });
801
+ }
802
+ break;
803
+ }
804
+ case "uninstall": {
805
+ const { settingsPath, removed } = uninstallStopHook(global);
806
+ if (isPrettyOutput()) {
807
+ if (removed) {
808
+ prettyLog("success", `Removed Vibora Stop hook from ${settingsPath}`);
809
+ } else {
810
+ prettyLog("info", "Vibora Stop hook was not installed");
811
+ }
812
+ } else {
813
+ outputSuccess({
814
+ action: "uninstall",
815
+ settingsPath,
816
+ removed
817
+ });
818
+ }
819
+ break;
820
+ }
821
+ case "status": {
822
+ const { installed, settingsPath, hookCommand } = checkStopHook(global);
823
+ if (isPrettyOutput()) {
824
+ if (installed) {
825
+ prettyLog("success", "Vibora Stop hook is installed");
826
+ prettyLog("info", ` Settings: ${settingsPath}`);
827
+ prettyLog("info", ` Command: ${hookCommand}`);
828
+ } else {
829
+ prettyLog("info", "Vibora Stop hook is not installed");
830
+ prettyLog("info", ` Settings: ${settingsPath}`);
831
+ prettyLog("info", "");
832
+ prettyLog("info", 'Run "vibora hooks install" to install it.');
833
+ }
834
+ } else {
835
+ outputSuccess({
836
+ action: "status",
837
+ installed,
838
+ settingsPath,
839
+ hookCommand
840
+ });
841
+ }
842
+ break;
843
+ }
844
+ default:
845
+ if (isPrettyOutput()) {
846
+ console.log(`Usage: vibora hooks <action> [--global]
847
+
848
+ Actions:
849
+ install Install the Stop hook for auto task transitions
850
+ uninstall Remove the Stop hook
851
+ status Check if the Stop hook is installed
852
+
853
+ Options:
854
+ --global Use global Claude settings (~/.claude/settings.json)
855
+ Default is project-local (.claude/settings.json)
856
+
857
+ The Stop hook automatically transitions tasks from IN_PROGRESS to IN_REVIEW
858
+ when Claude Code finishes in a Vibora worktree.`);
859
+ } else {
860
+ throw new CliError("INVALID_ACTION", `Invalid hooks action: ${action}. Use install, uninstall, or status.`, ExitCodes.INVALID_ARGS);
861
+ }
862
+ }
863
+ }
864
+
865
+ // cli/src/index.ts
866
+ var VERSION = "0.1.0";
867
+ function parseArgs(args) {
868
+ const positional = [];
869
+ const flags = {};
870
+ for (let i = 0;i < args.length; i++) {
871
+ const arg = args[i];
872
+ if (arg.startsWith("--")) {
873
+ const eqIndex = arg.indexOf("=");
874
+ if (eqIndex !== -1) {
875
+ const key = arg.slice(2, eqIndex);
876
+ const value = arg.slice(eqIndex + 1);
877
+ flags[key] = value;
878
+ } else {
879
+ const key = arg.slice(2);
880
+ const nextArg = args[i + 1];
881
+ if (nextArg && !nextArg.startsWith("--")) {
882
+ flags[key] = nextArg;
883
+ i++;
884
+ } else {
885
+ flags[key] = "true";
886
+ }
887
+ }
888
+ } else {
889
+ positional.push(arg);
890
+ }
891
+ }
892
+ return { positional, flags };
893
+ }
894
+ async function main() {
895
+ const args = Bun.argv.slice(2);
896
+ const { positional, flags } = parseArgs(args);
897
+ if (flags.pretty) {
898
+ setPrettyOutput(true);
899
+ }
900
+ const [command, ...rest] = positional;
901
+ if (flags.version || command === "--version") {
902
+ console.log(JSON.stringify({ success: true, data: { version: VERSION } }));
903
+ process.exit(0);
904
+ }
905
+ if (flags.help || command === "--help" || !command) {
906
+ console.log(`vibora CLI v${VERSION}
907
+
908
+ Usage: vibora <command> [options]
909
+
910
+ Commands:
911
+ current-task Get task for current worktree
912
+ current-task pr <url> Associate a PR with current task
913
+ current-task in-progress Mark current task as IN_PROGRESS
914
+ current-task review Mark current task as IN_REVIEW
915
+ current-task done Mark current task as DONE
916
+ current-task cancel Mark current task as CANCELED
917
+
918
+ tasks list List all tasks
919
+ tasks get <id> Get a task by ID
920
+ tasks create Create a new task
921
+ tasks update <id> Update a task
922
+ tasks move <id> Move task to different status
923
+ tasks delete <id> Delete a task
924
+
925
+ up Start Vibora server (daemon)
926
+ down Stop Vibora server
927
+ status Check if server is running
928
+
929
+ git status Get git status for worktree
930
+ git diff Get git diff for worktree
931
+ git branches List branches in a repo
932
+
933
+ worktrees list List all worktrees
934
+ worktrees delete Delete a worktree
935
+
936
+ config get <key> Get a config value
937
+ config set <key> <value> Set a config value
938
+
939
+ hooks install Install Claude Code Stop hook
940
+ hooks uninstall Remove Claude Code Stop hook
941
+ hooks status Check if Stop hook is installed
942
+
943
+ health Check server health
944
+
945
+ Global Options:
946
+ --port=<port> Server port (default: 3333)
947
+ --url=<url> Override full server URL
948
+ --pretty Pretty-print JSON output
949
+ --version Show version
950
+ --help Show this help
951
+
952
+ Examples:
953
+ vibora current-task # Get current task info
954
+ vibora current-task review # Mark current task as IN_REVIEW
955
+ vibora tasks list --status=IN_PROGRESS # List in-progress tasks
956
+ vibora tasks create --title="My Task" --repo=/path/to/repo
957
+ `);
958
+ process.exit(0);
959
+ }
960
+ try {
961
+ switch (command) {
962
+ case "current-task": {
963
+ const [action, ...actionRest] = rest;
964
+ await handleCurrentTaskCommand(action, actionRest, flags);
965
+ break;
966
+ }
967
+ case "tasks": {
968
+ const [action, ...taskRest] = rest;
969
+ await handleTasksCommand(action, taskRest, flags);
970
+ break;
971
+ }
972
+ case "up": {
973
+ await handleUpCommand(flags);
974
+ break;
975
+ }
976
+ case "down": {
977
+ await handleDownCommand();
978
+ break;
979
+ }
980
+ case "status": {
981
+ await handleStatusCommand(flags);
982
+ break;
983
+ }
984
+ case "git": {
985
+ const [action] = rest;
986
+ await handleGitCommand(action, flags);
987
+ break;
988
+ }
989
+ case "worktrees": {
990
+ const [action] = rest;
991
+ await handleWorktreesCommand(action, flags);
992
+ break;
993
+ }
994
+ case "config": {
995
+ const [action, ...configRest] = rest;
996
+ await handleConfigCommand(action, configRest, flags);
997
+ break;
998
+ }
999
+ case "health": {
1000
+ await handleHealthCommand(flags);
1001
+ break;
1002
+ }
1003
+ case "hooks": {
1004
+ const [action, ...hooksRest] = rest;
1005
+ await handleHooksCommand(action, hooksRest, flags);
1006
+ break;
1007
+ }
1008
+ default:
1009
+ throw new CliError("UNKNOWN_COMMAND", `Unknown command: ${command}`, ExitCodes.INVALID_ARGS);
1010
+ }
1011
+ } catch (err) {
1012
+ if (err instanceof CliError) {
1013
+ outputError(err);
1014
+ }
1015
+ throw err;
1016
+ }
1017
+ }
1018
+ main();