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.
- package/bin/vibora +2 -0
- package/bin/vibora.js +1018 -0
- package/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/assets/actionscript-3-CfeIJUat.js +1 -0
- package/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/assets/andromeeda-C-Jbm3Hp.js +1 -0
- package/dist/assets/angular-html-CU67Zn6k.js +1 -0
- package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
- package/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/assets/apex-DDbsPZ6N.js +1 -0
- package/dist/assets/apl-dKokRX4l.js +1 -0
- package/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/assets/asciidoc-Dv7Oe6Be.js +1 -0
- package/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/assets/astro-CbQHKStN.js +1 -0
- package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/assets/awk-DMzUqQB5.js +1 -0
- package/dist/assets/ayu-dark-Cv9koXgw.js +1 -0
- package/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/assets/bat-BkioyH1T.js +1 -0
- package/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/assets/berry-uYugtg8r.js +1 -0
- package/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/assets/blade-DVc8C-J4.js +1 -0
- package/dist/assets/bsl-BO_Y6i37.js +1 -0
- package/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/dist/assets/clarity-D53aC0YG.js +1 -0
- package/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/assets/cobol-nwyudZeR.js +1 -0
- package/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/assets/coq-DkFqJrB1.js +1 -0
- package/dist/assets/cpp-CofmeUqb.js +1 -0
- package/dist/assets/crystal-tKQVLTB8.js +1 -0
- package/dist/assets/csharp-K5feNrxe.js +1 -0
- package/dist/assets/css-DPfMkruS.js +1 -0
- package/dist/assets/csv-fuZLfV_i.js +1 -0
- package/dist/assets/cue-D82EKSYY.js +1 -0
- package/dist/assets/cypher-COkxafJQ.js +1 -0
- package/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/dart-CF10PKvl.js +1 -0
- package/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/assets/diff-D97Zzqfu.js +1 -0
- package/dist/assets/docker-BcOcwvcX.js +1 -0
- package/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/assets/edge-BkV0erSs.js +1 -0
- package/dist/assets/elixir-CDX3lj18.js +1 -0
- package/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/dist/assets/erb-BOJIQeun.js +1 -0
- package/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/assets/fennel-BYunw83y.js +1 -0
- package/dist/assets/fish-BvzEVeQv.js +1 -0
- package/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
- package/dist/assets/fortran-free-form-D22FLkUw.js +1 -0
- package/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/assets/gdresource-B7Tvp0Sc.js +1 -0
- package/dist/assets/gdscript-DTMYz4Jt.js +1 -0
- package/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/assets/genie-D0YGMca9.js +1 -0
- package/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/assets/gleam-BspZqrRM.js +1 -0
- package/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/dist/assets/glimmer-ts-U6CK756n.js +1 -0
- package/dist/assets/glsl-DplSGwfg.js +1 -0
- package/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/assets/go-Dn2_MT6a.js +1 -0
- package/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/assets/hack-CaT9iCJl.js +1 -0
- package/dist/assets/haml-B8DHNrY2.js +1 -0
- package/dist/assets/handlebars-BL8al0AC.js +1 -0
- package/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/assets/haxe-CzTSHFRz.js +1 -0
- package/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/assets/hjson-D5-asLiD.js +1 -0
- package/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/assets/html-GMplVEZG.js +1 -0
- package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
- package/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/assets/index-BNhD6i8B.js +45 -0
- package/dist/assets/index-Co_u49xL.css +1 -0
- package/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
- package/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/assets/json-Cp-IABpG.js +1 -0
- package/dist/assets/json5-C9tS-k6U.js +1 -0
- package/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/assets/julia-C8NyazO9.js +1 -0
- package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/assets/kusto-BvAqAH-y.js +1 -0
- package/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/assets/latex-BdAV_C_H.js +1 -0
- package/dist/assets/lean-Bc6EcWN3.js +1 -0
- package/dist/assets/less-B1dDrJ26.js +1 -0
- package/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/assets/liquid-DYVedYrR.js +1 -0
- package/dist/assets/llvm-BtvRca6l.js +1 -0
- package/dist/assets/log-2UxHyX5q.js +1 -0
- package/dist/assets/logo-BtOb2qkB.js +1 -0
- package/dist/assets/lua-BbnMAYS6.js +1 -0
- package/dist/assets/luau-CXu1NL6O.js +1 -0
- package/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/assets/marko-CPi9NSCl.js +1 -0
- package/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/assets/matlab-D7o27uSR.js +1 -0
- package/dist/assets/mdc-DUICxH0z.js +1 -0
- package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/assets/mermaid-DKYwYmdq.js +1 -0
- package/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/assets/mojo-1DNp92w6.js +1 -0
- package/dist/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/assets/move-Bu9oaDYs.js +1 -0
- package/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/assets/nextflow-BrzmwbiE.js +1 -0
- package/dist/assets/nginx-DknmC5AR.js +1 -0
- package/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/assets/nim-CVrawwO9.js +1 -0
- package/dist/assets/nix-c8nO5XWb.js +1 -0
- package/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/assets/nushell-C-sUppwS.js +1 -0
- package/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/assets/one-light-PoHY5YXO.js +1 -0
- package/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/assets/perl-C0TMdlhV.js +1 -0
- package/dist/assets/php-CDn_0X-4.js +1 -0
- package/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/assets/po-BTJTHyun.js +1 -0
- package/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/assets/polar-C0HS_06l.js +1 -0
- package/dist/assets/postcss-CXtECtnM.js +1 -0
- package/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/assets/powershell-Dpen1YoG.js +1 -0
- package/dist/assets/prisma-Dd19v3D-.js +1 -0
- package/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/assets/proto-DyJlTyXw.js +1 -0
- package/dist/assets/pug-CGlum2m_.js +1 -0
- package/dist/assets/puppet-BMWR74SV.js +1 -0
- package/dist/assets/purescript-CklMAg4u.js +1 -0
- package/dist/assets/python-B6aJPvgy.js +1 -0
- package/dist/assets/qml-3beO22l8.js +1 -0
- package/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/assets/qss-IeuSbFQv.js +1 -0
- package/dist/assets/r-DiinP2Uv.js +1 -0
- package/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/assets/razor-CE9lU5zL.js +1 -0
- package/dist/assets/red-bN70gL4F.js +1 -0
- package/dist/assets/reg-C-SQnVFl.js +1 -0
- package/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/assets/rel-C3B-1QV4.js +1 -0
- package/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
- package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/assets/rst-B0xPkSld.js +1 -0
- package/dist/assets/ruby-BvKwtOVI.js +1 -0
- package/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/assets/sas-cz2c8ADy.js +1 -0
- package/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/assets/scala-C151Ov-r.js +1 -0
- package/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/assets/scss-OYdSNvt2.js +1 -0
- package/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/assets/smalltalk-BERRCDM3.js +1 -0
- package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/assets/solidity-rGO070M0.js +1 -0
- package/dist/assets/soy-Brmx7dQM.js +1 -0
- package/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/assets/sql-BLtJtn59.js +1 -0
- package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/assets/stata-BH5u7GGu.js +1 -0
- package/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/assets/svelte-3Dk4HxPD.js +1 -0
- package/dist/assets/swift-Dg5xB15N.js +1 -0
- package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/assets/system-verilog-CnnmHF94.js +1 -0
- package/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/assets/templ-W15q3VgB.js +1 -0
- package/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/assets/tex-CxkMU7Pf.js +1 -0
- package/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/assets/tsx-COt5Ahok.js +1 -0
- package/dist/assets/turtle-BsS91CYL.js +1 -0
- package/dist/assets/twig-CO9l9SDP.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/typespec-BGHnOYBU.js +1 -0
- package/dist/assets/typst-DHCkPAjA.js +1 -0
- package/dist/assets/v-BcVCzyr7.js +1 -0
- package/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/assets/vb-D17OF-Vu.js +1 -0
- package/dist/assets/verilog-BQ8w6xss.js +1 -0
- package/dist/assets/vesper-DU1UobuO.js +1 -0
- package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/assets/viml-CJc9bBzg.js +1 -0
- package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/assets/vue-DnHKYNfI.js +1 -0
- package/dist/assets/vue-html-CChd_i61.js +1 -0
- package/dist/assets/vue-vine-8moa0y9V.js +1 -0
- package/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/assets/wit-5i3qLPDT.js +1 -0
- package/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/assets/yaml-Buea-lGh.js +1 -0
- package/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/assets/zig-VOosw3JB.js +1 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.html +15 -0
- package/dist/vibora-icon.png +0 -0
- package/dist/vibora-logo.jpeg +0 -0
- package/dist/vite.svg +1 -0
- package/drizzle/0000_jittery_bloodaxe.sql +46 -0
- package/drizzle/0001_soft_amphibian.sql +2 -0
- package/drizzle/0002_burly_tyrannus.sql +12 -0
- package/drizzle/meta/0000_snapshot.json +310 -0
- package/drizzle/meta/0001_snapshot.json +324 -0
- package/drizzle/meta/0002_snapshot.json +398 -0
- package/drizzle/meta/_journal.json +27 -0
- package/lib/librust_pty.so +0 -0
- package/package.json +25 -0
- 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();
|