@sanurb/ringi 0.2.1 → 0.3.1
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/LICENSE +21 -0
- package/dist/cli.mjs +505 -114
- package/dist/cli.mjs.map +1 -1
- package/dist/mcp.mjs +35 -39
- package/dist/mcp.mjs.map +1 -1
- package/dist/runtime.mjs +569 -428
- package/dist/runtime.mjs.map +1 -1
- package/package.json +13 -13
- package/server/nitro.json +17 -0
- package/server/public/assets/ClientOnly-QdfAxyFs.js +1 -0
- package/server/public/assets/_reviewId-CmXHvWLn.js +1 -0
- package/server/public/assets/_reviewId-DdOpDx4U.js +1 -0
- package/server/public/assets/abap-B1dkBSPn.js +1 -0
- package/server/public/assets/action-bar-DLRNvLjj.js +45 -0
- package/server/public/assets/actionscript-3-BT4ibYlP.js +1 -0
- package/server/public/assets/ada-CD92zeps.js +1 -0
- package/server/public/assets/andromeeda-DqSmgxi0.js +1 -0
- package/server/public/assets/angular-html-BDC0PfKr.js +1 -0
- package/server/public/assets/angular-ts-B9yoQMtj.js +1 -0
- package/server/public/assets/apache-D5suuoa_.js +1 -0
- package/server/public/assets/apex-BL-m4VHy.js +1 -0
- package/server/public/assets/apl-CldhY0Pn.js +1 -0
- package/server/public/assets/applescript-CLiBqvKT.js +1 -0
- package/server/public/assets/ara-LdDF8cmv.js +1 -0
- package/server/public/assets/asciidoc-2DZ9hC2N.js +1 -0
- package/server/public/assets/asm-0ZPGRSUy.js +1 -0
- package/server/public/assets/astro-DR6labZJ.js +1 -0
- package/server/public/assets/aurora-x-Da7Zfvbg.js +1 -0
- package/server/public/assets/awk-Bn0gn_B_.js +1 -0
- package/server/public/assets/ayu-dark-bqYKoqpM.js +1 -0
- package/server/public/assets/ayu-light-C45jTIzZ.js +1 -0
- package/server/public/assets/ayu-mirage-BV_FCTQi.js +1 -0
- package/server/public/assets/ballerina-D2nw_w8Q.js +1 -0
- package/server/public/assets/bat-ByUBN5gS.js +1 -0
- package/server/public/assets/beancount-BTb2W6Mp.js +1 -0
- package/server/public/assets/berry-5SO2uITG.js +1 -0
- package/server/public/assets/bibtex-CDBTNfUI.js +1 -0
- package/server/public/assets/bicep-fgxG_4rP.js +1 -0
- package/server/public/assets/bird2-BCwzDhwX.js +1 -0
- package/server/public/assets/blade-BBTRu2-g.js +1 -0
- package/server/public/assets/bsl-CG-fq4sc.js +1 -0
- package/server/public/assets/c-CEvNj7xl.js +1 -0
- package/server/public/assets/c3-Dlaci63_.js +1 -0
- package/server/public/assets/cadence-DHbRuEmm.js +1 -0
- package/server/public/assets/cairo-Ds0kTeYT.js +1 -0
- package/server/public/assets/catppuccin-frappe-DrL1fUuH.js +1 -0
- package/server/public/assets/catppuccin-latte-C0REgVjl.js +1 -0
- package/server/public/assets/catppuccin-macchiato-ChQpylWO.js +1 -0
- package/server/public/assets/catppuccin-mocha-Dd0JU1T0.js +1 -0
- package/server/public/assets/clarity-DMoTOm4G.js +1 -0
- package/server/public/assets/clojure-DBhE3PpS.js +1 -0
- package/server/public/assets/cmake-DwMc40Or.js +1 -0
- package/server/public/assets/cobol-BdNJSMRt.js +1 -0
- package/server/public/assets/codeowners-1lVr8wqV.js +1 -0
- package/server/public/assets/codeql-DWflolvo.js +1 -0
- package/server/public/assets/coffee-RA4xA24H.js +1 -0
- package/server/public/assets/common-lisp-CrVQ5xT-.js +1 -0
- package/server/public/assets/compiler-runtime-DZXZ41-q.js +1 -0
- package/server/public/assets/coq-CjfoyYSh.js +1 -0
- package/server/public/assets/cpp-BoW7e2Ow.js +1 -0
- package/server/public/assets/createServerFn-DTk395iP.js +9 -0
- package/server/public/assets/crystal-CYKRo3F9.js +1 -0
- package/server/public/assets/csharp-C7bIWP5y.js +1 -0
- package/server/public/assets/css-Ck2tii2d.js +1 -0
- package/server/public/assets/csv-DsAkDVtA.js +1 -0
- package/server/public/assets/cue-BWmQgbOB.js +1 -0
- package/server/public/assets/cypher-D-jVC50Q.js +1 -0
- package/server/public/assets/d-CaviyOrm.js +1 -0
- package/server/public/assets/dark-plus-DIrnwZt9.js +1 -0
- package/server/public/assets/dart-CZEi7JgC.js +1 -0
- package/server/public/assets/dax-BK-8zffy.js +1 -0
- package/server/public/assets/desktop-D3cjbL4D.js +1 -0
- package/server/public/assets/diff-sHAzLvlp.js +1 -0
- package/server/public/assets/docker--xs2Ng3w.js +1 -0
- package/server/public/assets/dotenv-Cm4nwcJ7.js +1 -0
- package/server/public/assets/dracula-CAUSusef.js +1 -0
- package/server/public/assets/dracula-soft-cjNkMFza.js +1 -0
- package/server/public/assets/dream-maker-fjmWTFCO.js +1 -0
- package/server/public/assets/edge-DxycC9wl.js +1 -0
- package/server/public/assets/elixir-B-50Er3p.js +1 -0
- package/server/public/assets/elm-B4-ygIVo.js +1 -0
- package/server/public/assets/emacs-lisp-CJzqStIa.js +1 -0
- package/server/public/assets/erb-DJvYE1L1.js +1 -0
- package/server/public/assets/erlang-C-m_88FN.js +1 -0
- package/server/public/assets/everforest-dark-DBpaSMx1.js +1 -0
- package/server/public/assets/everforest-light-CiGrXwia.js +1 -0
- package/server/public/assets/fennel-DRaXF7k8.js +1 -0
- package/server/public/assets/file-tree-CI3Xwwid.js +1907 -0
- package/server/public/assets/fish-Bn-Yh3Jj.js +1 -0
- package/server/public/assets/fluent-DF5F8Ks_.js +1 -0
- package/server/public/assets/fortran-fixed-form-Cx1lv7HN.js +1 -0
- package/server/public/assets/fortran-free-form-hCQHRqew.js +1 -0
- package/server/public/assets/fsharp-DC5k9sy2.js +1 -0
- package/server/public/assets/gdresource-D0EsKdgH.js +1 -0
- package/server/public/assets/gdscript-_C9_Hi_w.js +1 -0
- package/server/public/assets/gdshader-BW7b1X1Y.js +1 -0
- package/server/public/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/server/public/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/server/public/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/server/public/assets/genie-Ch_6TCHd.js +1 -0
- package/server/public/assets/gherkin-CaNUsmTq.js +1 -0
- package/server/public/assets/git-commit-BcFsuO5E.js +1 -0
- package/server/public/assets/git-rebase-ChGA-z50.js +1 -0
- package/server/public/assets/github-dark-B9ygjgg6.js +1 -0
- package/server/public/assets/github-dark-default-Br2bgYSx.js +1 -0
- package/server/public/assets/github-dark-dimmed-CmtqpPJ-.js +1 -0
- package/server/public/assets/github-dark-high-contrast-fSfmrZcC.js +1 -0
- package/server/public/assets/github-light-BZjUqfZl.js +1 -0
- package/server/public/assets/github-light-default-lIytXXhR.js +1 -0
- package/server/public/assets/github-light-high-contrast-BRrjFb7n.js +1 -0
- package/server/public/assets/gleam-DALMDpNs.js +1 -0
- package/server/public/assets/glimmer-js-maLb6ysA.js +1 -0
- package/server/public/assets/glimmer-ts-DGNr-OBA.js +1 -0
- package/server/public/assets/glsl-CmplqyQ1.js +1 -0
- package/server/public/assets/gn-DGjqrYN9.js +1 -0
- package/server/public/assets/gnuplot-BYckvgQI.js +1 -0
- package/server/public/assets/go-JycvP538.js +1 -0
- package/server/public/assets/graphql-VhP7n4--.js +1 -0
- package/server/public/assets/groovy-D5qMRONT.js +1 -0
- package/server/public/assets/gruvbox-dark-hard-M1dj1e6V.js +1 -0
- package/server/public/assets/gruvbox-dark-medium-cqq_ncQu.js +1 -0
- package/server/public/assets/gruvbox-dark-soft-B4QwL2a9.js +1 -0
- package/server/public/assets/gruvbox-light-hard-DLayMKOQ.js +1 -0
- package/server/public/assets/gruvbox-light-medium-D52XgPKf.js +1 -0
- package/server/public/assets/gruvbox-light-soft-Dola3KdD.js +1 -0
- package/server/public/assets/hack-BVSQ2bxM.js +1 -0
- package/server/public/assets/haml-CwTtRHoj.js +1 -0
- package/server/public/assets/handlebars-CcO01SVo.js +1 -0
- package/server/public/assets/haskell-ys7wPPEd.js +1 -0
- package/server/public/assets/haxe-94kiChn7.js +1 -0
- package/server/public/assets/hcl-DmHt_-wq.js +1 -0
- package/server/public/assets/hjson-xMmoJ0Gx.js +1 -0
- package/server/public/assets/hlsl-b-Pskdze.js +1 -0
- package/server/public/assets/horizon-BKMqttiR.js +1 -0
- package/server/public/assets/horizon-bright-HNkBlnm5.js +1 -0
- package/server/public/assets/houston-BkBSBSOQ.js +1 -0
- package/server/public/assets/html-derivative-Cz-cKMi2.js +1 -0
- package/server/public/assets/html-zQbUS8Is.js +1 -0
- package/server/public/assets/http-CaGQ9BgA.js +1 -0
- package/server/public/assets/hurl-BBoki9bg.js +1 -0
- package/server/public/assets/hxml-iQTOTWpM.js +1 -0
- package/server/public/assets/hy-DKl1XhBq.js +1 -0
- package/server/public/assets/imba-DPxkOTAg.js +1 -0
- package/server/public/assets/ini-lkLGq_1x.js +1 -0
- package/server/public/assets/java-LAx6oszV.js +1 -0
- package/server/public/assets/javascript-COqx-gKX.js +1 -0
- package/server/public/assets/jinja-x-G_qSCP.js +1 -0
- package/server/public/assets/jison-7oSeVkKJ.js +1 -0
- package/server/public/assets/json-sTLOVXhc.js +1 -0
- package/server/public/assets/json5-Cy6ypJuJ.js +1 -0
- package/server/public/assets/jsonc-Cw2ugYAK.js +1 -0
- package/server/public/assets/jsonl-Dp5_qBVH.js +1 -0
- package/server/public/assets/jsonnet-BTbmg_-u.js +1 -0
- package/server/public/assets/jssm-CnT7nPea.js +1 -0
- package/server/public/assets/jsx-zXeIBQLI.js +1 -0
- package/server/public/assets/julia-E-6Xm9nd.js +1 -0
- package/server/public/assets/just-D9n74gZy.js +1 -0
- package/server/public/assets/kanagawa-dragon-CxsBnuhV.js +1 -0
- package/server/public/assets/kanagawa-lotus-vHdxDDOS.js +1 -0
- package/server/public/assets/kanagawa-wave-CIkfTKWk.js +1 -0
- package/server/public/assets/kdl-BwK60g80.js +1 -0
- package/server/public/assets/kotlin-Dbd9Vi-v.js +1 -0
- package/server/public/assets/kusto-BuTk9usc.js +1 -0
- package/server/public/assets/laserwave-C0wf_d3o.js +1 -0
- package/server/public/assets/latex-D0t4RtEU.js +1 -0
- package/server/public/assets/lean-CYAW8bRN.js +1 -0
- package/server/public/assets/less-D4uen21c.js +1 -0
- package/server/public/assets/light-plus-oqYyWKEE.js +1 -0
- package/server/public/assets/liquid-BzXN12F6.js +1 -0
- package/server/public/assets/llvm-Z1xJzteV.js +1 -0
- package/server/public/assets/log-BGUxlsk3.js +1 -0
- package/server/public/assets/logo-wVUhvQ1b.js +1 -0
- package/server/public/assets/lua-B0Cg8RP4.js +1 -0
- package/server/public/assets/luau-rPFZzCmq.js +1 -0
- package/server/public/assets/main-FvxVz-kD.js +15 -0
- package/server/public/assets/make-BmPf6m0P.js +1 -0
- package/server/public/assets/markdown-AseU6zcW.js +1 -0
- package/server/public/assets/marko-BlRPXWOe.js +1 -0
- package/server/public/assets/material-theme-6_W6rQhR.js +1 -0
- package/server/public/assets/material-theme-darker-VPEo3Sem.js +1 -0
- package/server/public/assets/material-theme-lighter-CUhzCcZ9.js +1 -0
- package/server/public/assets/material-theme-ocean-B2JdsaGb.js +1 -0
- package/server/public/assets/material-theme-palenight-DhY-sklA.js +1 -0
- package/server/public/assets/matlab-BOj_BDQv.js +1 -0
- package/server/public/assets/mdc-FiVDZSZ4.js +1 -0
- package/server/public/assets/mdx-Cm6cDkDI.js +1 -0
- package/server/public/assets/mermaid-DLO-R4hv.js +1 -0
- package/server/public/assets/min-dark-D34a_pX7.js +1 -0
- package/server/public/assets/min-light-Cdd4KORE.js +1 -0
- package/server/public/assets/mipsasm-DYpHF-GA.js +1 -0
- package/server/public/assets/mojo-DqYVFv_G.js +1 -0
- package/server/public/assets/monokai-CDR4sQ2n.js +1 -0
- package/server/public/assets/moonbit-DRKee9wk.js +1 -0
- package/server/public/assets/move-DbRk6Vn9.js +1 -0
- package/server/public/assets/narrat-IOfmaXfb.js +1 -0
- package/server/public/assets/new-DOyplRwM.js +1 -0
- package/server/public/assets/nextflow-D-Ec_bsY.js +1 -0
- package/server/public/assets/nextflow-groovy-EYl0c2BQ.js +1 -0
- package/server/public/assets/nginx-3JLAqmJa.js +1 -0
- package/server/public/assets/night-owl-light-Bedht9b4.js +1 -0
- package/server/public/assets/night-owl-yQJ3-I0I.js +1 -0
- package/server/public/assets/nim-DyjFVMzT.js +1 -0
- package/server/public/assets/nix-C2IovEl2.js +1 -0
- package/server/public/assets/nord-BjZ63GNL.js +1 -0
- package/server/public/assets/nushell-BflTrRB5.js +1 -0
- package/server/public/assets/objective-c-GRClK1S7.js +1 -0
- package/server/public/assets/objective-cpp-l3qYw-v5.js +1 -0
- package/server/public/assets/ocaml-BBDyhyMH.js +1 -0
- package/server/public/assets/odin-jCJ7Js99.js +1 -0
- package/server/public/assets/one-dark-pro-PIx2Diul.js +1 -0
- package/server/public/assets/one-light-BFMEz49S.js +1 -0
- package/server/public/assets/openscad-Drf0LgCX.js +1 -0
- package/server/public/assets/pascal-BT2XAUTl.js +1 -0
- package/server/public/assets/perl-Dr47G_2Q.js +1 -0
- package/server/public/assets/php-BhBDWTJe.js +1 -0
- package/server/public/assets/pierre-dark-CTXzTLfO.js +1 -0
- package/server/public/assets/pierre-light-C_5rlJRo.js +1 -0
- package/server/public/assets/pkl-ML-dWShO.js +1 -0
- package/server/public/assets/plastic-BFI-Z5Z2.js +1 -0
- package/server/public/assets/plsql-0vd5cLro.js +1 -0
- package/server/public/assets/po-CbZ_uqQA.js +1 -0
- package/server/public/assets/poimandres-Cayhd01L.js +1 -0
- package/server/public/assets/polar-C4hfV8Nc.js +1 -0
- package/server/public/assets/postcss-osFUbTLw.js +1 -0
- package/server/public/assets/powerquery-CTlGUQPj.js +1 -0
- package/server/public/assets/powershell-DyZsOmuq.js +1 -0
- package/server/public/assets/preload-helper-D7oT-Xwl.js +20 -0
- package/server/public/assets/prisma-SS92PO_I.js +1 -0
- package/server/public/assets/prolog-B1O1NNVC.js +1 -0
- package/server/public/assets/proto-BWu3eZTs.js +1 -0
- package/server/public/assets/pug-Dij_IK5w.js +1 -0
- package/server/public/assets/puppet-tvtRVdr6.js +1 -0
- package/server/public/assets/purescript-Dtbpb7D-.js +1 -0
- package/server/public/assets/python-Dlk0Acio.js +1 -0
- package/server/public/assets/qml-qUwk3nhh.js +1 -0
- package/server/public/assets/qmldir-B-iEOngH.js +1 -0
- package/server/public/assets/qss-Ba0p-aHw.js +1 -0
- package/server/public/assets/r-WmtNicKM.js +1 -0
- package/server/public/assets/racket-BXDsxf2U.js +1 -0
- package/server/public/assets/raku-Dw1WWFXK.js +1 -0
- package/server/public/assets/razor-DaqiVx3Q.js +1 -0
- package/server/public/assets/red-BC3Ds49b.js +1 -0
- package/server/public/assets/reg-DXFHGaM4.js +1 -0
- package/server/public/assets/regexp-CiSWN5Ne.js +1 -0
- package/server/public/assets/rel-Dc5_Ytx2.js +1 -0
- package/server/public/assets/reviews-CJvVXRLH.js +1 -0
- package/server/public/assets/reviews-CfbuF6ib.js +1 -0
- package/server/public/assets/riscv-ZgswiWij.js +1 -0
- package/server/public/assets/ron-YghabWAH.js +1 -0
- package/server/public/assets/rose-pine-ByWLnVr3.js +1 -0
- package/server/public/assets/rose-pine-dawn-DBmeySrz.js +1 -0
- package/server/public/assets/rose-pine-moon-B9J-N3nK.js +1 -0
- package/server/public/assets/rosmsg-DTKmAsVH.js +1 -0
- package/server/public/assets/routes-DNxq1Fba.js +1 -0
- package/server/public/assets/routes-Dp0ODZ55.js +2 -0
- package/server/public/assets/rst-CP6xOYlY.js +1 -0
- package/server/public/assets/ruby-BXYLc1CM.js +1 -0
- package/server/public/assets/rust-nfXwuE6F.js +1 -0
- package/server/public/assets/sas-CFdtZutF.js +1 -0
- package/server/public/assets/sass-CbRjkld3.js +1 -0
- package/server/public/assets/scala-BzD3eypx.js +1 -0
- package/server/public/assets/scheme-D4d1PV1y.js +1 -0
- package/server/public/assets/scss-D8KhdObH.js +1 -0
- package/server/public/assets/sdbl-Cf-Ydnvx.js +1 -0
- package/server/public/assets/shaderlab-DGohHMiF.js +1 -0
- package/server/public/assets/shellscript-BHdEbumI.js +1 -0
- package/server/public/assets/shellsession-Dh-bxrap.js +1 -0
- package/server/public/assets/slack-dark-MszIyPZ2.js +1 -0
- package/server/public/assets/slack-ochin-tQ3Q0gE9.js +1 -0
- package/server/public/assets/smalltalk-_uWoArwn.js +1 -0
- package/server/public/assets/snazzy-light-9sniMEk5.js +1 -0
- package/server/public/assets/solarized-dark-CdD0Hxzv.js +1 -0
- package/server/public/assets/solarized-light-C-nsEdqF.js +1 -0
- package/server/public/assets/solidity-D6uC-xwP.js +1 -0
- package/server/public/assets/soy-cDuODfbT.js +1 -0
- package/server/public/assets/sparql-BgU2QITA.js +1 -0
- package/server/public/assets/splunk-LQYHRu14.js +1 -0
- package/server/public/assets/sql-CKZpK620.js +1 -0
- package/server/public/assets/ssh-config-B7BUl8Rd.js +1 -0
- package/server/public/assets/stata-BLJTbKOO.js +1 -0
- package/server/public/assets/styles-UDowwF7S.css +2 -0
- package/server/public/assets/stylus-Byjxdx_q.js +1 -0
- package/server/public/assets/surrealql-C96KvYaj.js +1 -0
- package/server/public/assets/svelte-Qnbj2GWx.js +1 -0
- package/server/public/assets/swift-BexLlMrU.js +1 -0
- package/server/public/assets/synthwave-84-BxMBwQMS.js +1 -0
- package/server/public/assets/system-verilog-DVGwm0mw.js +1 -0
- package/server/public/assets/systemd-H2IT3-p5.js +1 -0
- package/server/public/assets/talonscript-mKZIGM8n.js +1 -0
- package/server/public/assets/tasl-B7he_Ugr.js +1 -0
- package/server/public/assets/tcl-5mT3RxHH.js +1 -0
- package/server/public/assets/templ-CQPDll3D.js +1 -0
- package/server/public/assets/terraform-BZP0GLsT.js +1 -0
- package/server/public/assets/test-D7JRfog1.js +1 -0
- package/server/public/assets/tex-97QNLoBJ.js +1 -0
- package/server/public/assets/tokyo-night-CTPVdZt9.js +1 -0
- package/server/public/assets/toml-CTFA98he.js +1 -0
- package/server/public/assets/ts-tags-B8zlXe2n.js +1 -0
- package/server/public/assets/tsv-BayJtYdY.js +1 -0
- package/server/public/assets/tsx-VqRU8NCz.js +1 -0
- package/server/public/assets/turtle-TVCBh_kY.js +1 -0
- package/server/public/assets/twig-FTTF8rVk.js +1 -0
- package/server/public/assets/typescript-CuX0hIVY.js +1 -0
- package/server/public/assets/typespec-BUvaJDLF.js +1 -0
- package/server/public/assets/typst-8NBaY7Ec.js +1 -0
- package/server/public/assets/useStore-M3H8PB1v.js +1 -0
- package/server/public/assets/utils-DElCu2hq.js +1 -0
- package/server/public/assets/v-VihyTigi.js +1 -0
- package/server/public/assets/vala-DyFAPyX6.js +1 -0
- package/server/public/assets/vb-Dg1Iqi4J.js +1 -0
- package/server/public/assets/verilog-D2Xc-vhD.js +1 -0
- package/server/public/assets/vesper-DJbtqYNr.js +1 -0
- package/server/public/assets/vhdl-CU3BVeE7.js +1 -0
- package/server/public/assets/viml-hG2shuOW.js +1 -0
- package/server/public/assets/vitesse-black-DbG2gsc0.js +1 -0
- package/server/public/assets/vitesse-dark-B6WV4xXH.js +1 -0
- package/server/public/assets/vitesse-light-DC1pdD02.js +1 -0
- package/server/public/assets/vue-DXwaEU0U.js +1 -0
- package/server/public/assets/vue-html-QD7AJ6JJ.js +1 -0
- package/server/public/assets/vue-vine-Bh2m1D2Z.js +1 -0
- package/server/public/assets/vyper-C1wojIuk.js +1 -0
- package/server/public/assets/wasm-C6Y0s02M.js +1 -0
- package/server/public/assets/wasm-qTvCOSHz.js +1 -0
- package/server/public/assets/wenyan-BG5vPQF0.js +1 -0
- package/server/public/assets/wgsl-DrVb-Cub.js +1 -0
- package/server/public/assets/wikitext-PRC4s8sH.js +1 -0
- package/server/public/assets/wit-ChW5qvg_.js +1 -0
- package/server/public/assets/wolfram-B8mKuZSQ.js +1 -0
- package/server/public/assets/xml-BK-rcb5a.js +1 -0
- package/server/public/assets/xsl-dt-d2R7p.js +1 -0
- package/server/public/assets/yaml-UiXU3hGj.js +1 -0
- package/server/public/assets/zenscript-C-jEPC9j.js +1 -0
- package/server/public/assets/zig-EbnRGjcz.js +1 -0
- package/server/public/favicon.ico +0 -0
- package/server/public/logo192.png +0 -0
- package/server/public/logo512.png +0 -0
- package/server/public/manifest.json +25 -0
- package/server/public/robots.txt +3 -0
- package/server/public/tanstack-circle-logo.png +0 -0
- package/server/public/tanstack-word-logo-white.svg +1 -0
- package/server/server/_chunks/ssr-renderer.mjs +15 -0
- package/server/server/_libs/@floating-ui/core+[...].mjs +698 -0
- package/server/server/_libs/@floating-ui/dom+[...].mjs +644 -0
- package/server/server/_libs/@floating-ui/react-dom+[...].mjs +839 -0
- package/server/server/_libs/@pierre/diffs+[...].mjs +18578 -0
- package/server/server/_libs/@radix-ui/react-arrow+[...].mjs +174 -0
- package/server/server/_libs/@radix-ui/react-collection+[...].mjs +162 -0
- package/server/server/_libs/@radix-ui/react-dialog+[...].mjs +1666 -0
- package/server/server/_libs/@radix-ui/react-popper+[...].mjs +289 -0
- package/server/server/_libs/@radix-ui/react-radio-group+[...].mjs +420 -0
- package/server/server/_libs/@radix-ui/react-select+[...].mjs +990 -0
- package/server/server/_libs/@tanstack/react-router+[...].mjs +14113 -0
- package/server/server/_libs/_.mjs +2 -0
- package/server/server/_libs/chokidar+readdirp.mjs +1599 -0
- package/server/server/_libs/class-variance-authority+clsx.mjs +69 -0
- package/server/server/_libs/effect+[...].mjs +34047 -0
- package/server/server/_libs/h3+rou3+srvx.mjs +1195 -0
- package/server/server/_libs/hookable.mjs +41 -0
- package/server/server/_libs/lucide-react.mjs +298 -0
- package/server/server/_libs/pierre__theme.mjs +2668 -0
- package/server/server/_libs/radix-ui__number.mjs +6 -0
- package/server/server/_libs/radix-ui__primitive.mjs +9 -0
- package/server/server/_libs/radix-ui__react-direction.mjs +11 -0
- package/server/server/_libs/shiki.mjs +16 -0
- package/server/server/_libs/shikijs__langs.mjs +1355 -0
- package/server/server/_libs/shikijs__themes.mjs +262 -0
- package/server/server/_libs/tailwind-merge.mjs +1962 -0
- package/server/server/_libs/tanstack__history.mjs +342 -0
- package/server/server/_libs/tanstack__router-core.mjs +6 -0
- package/server/server/_libs/ufo.mjs +64 -0
- package/server/server/_reviewId-AWnOGz5k.mjs +33 -0
- package/server/server/_reviewId-Com4yOlc.mjs +29 -0
- package/server/server/_reviewId-DAhmekJ2.mjs +277 -0
- package/server/server/_reviewId-p9mhYVwa.mjs +18 -0
- package/server/server/_runtime.mjs +35 -0
- package/server/server/_ssr/action-bar-C68xGnWW.mjs +592 -0
- package/server/server/_ssr/api-handler-CstW2n82.mjs +189 -0
- package/server/server/_ssr/client-runtime-BoPuAEoA.mjs +245 -0
- package/server/server/_ssr/createServerRpc--0mcGlWK.mjs +12 -0
- package/server/server/_ssr/createSsrRpc-AwdiLXmF.mjs +16 -0
- package/server/server/_ssr/domain-rpc-3Ds9DPr0.mjs +287 -0
- package/server/server/_ssr/file-tree-CQ5w2GHh.mjs +1951 -0
- package/server/server/_ssr/load-scoped-diff-NL2XAcdz.mjs +45 -0
- package/server/server/_ssr/new-BKl_G2Ks.mjs +37 -0
- package/server/server/_ssr/new-BREdMFAM.mjs +12 -0
- package/server/server/_ssr/new-DCz5eHkb.mjs +137 -0
- package/server/server/_ssr/reviews-BL5Nsgst.mjs +7 -0
- package/server/server/_ssr/reviews-BoaEgGKs.mjs +100 -0
- package/server/server/_ssr/reviews-C7_NIhY8.mjs +19 -0
- package/server/server/_ssr/reviews-Dd69YBDa.mjs +12 -0
- package/server/server/_ssr/router-DLxN8FOm.mjs +415 -0
- package/server/server/_ssr/routes-D25G8OuS.mjs +80 -0
- package/server/server/_ssr/routes-lz0AN75A.mjs +929 -0
- package/server/server/_ssr/runtime-D9IbnMlF.mjs +1401 -0
- package/server/server/_ssr/server-runtime-D99qpmma.mjs +12 -0
- package/server/server/_ssr/ssr.mjs +5318 -0
- package/server/server/_ssr/start-BIQfOZtj.mjs +4 -0
- package/server/server/_ssr/test-CQdMYlqa.mjs +6 -0
- package/server/server/_ssr/todo-m_uUvxca.mjs +88 -0
- package/server/server/_ssr/use-keyboard-shortcuts-D5b1Mxpq.mjs +25 -0
- package/server/server/_ssr/utils-BuOt9_LA.mjs +8 -0
- package/server/server/_tanstack-start-manifest_v-CnL10NRH.mjs +71 -0
- package/server/server/index.mjs +2615 -0
- package/server/server/node_modules/detect-libc/lib/detect-libc.js +313 -0
- package/server/server/node_modules/detect-libc/lib/elf.js +39 -0
- package/server/server/node_modules/detect-libc/lib/filesystem.js +51 -0
- package/server/server/node_modules/detect-libc/lib/process.js +24 -0
- package/server/server/node_modules/detect-libc/package.json +44 -0
- package/server/server/node_modules/msgpackr-extract/index.js +1 -0
- package/server/server/node_modules/msgpackr-extract/package.json +50 -0
- package/server/server/node_modules/node-gyp-build-optional-packages/index.js +6 -0
- package/server/server/node_modules/node-gyp-build-optional-packages/node-gyp-build.js +236 -0
- package/server/server/node_modules/node-gyp-build-optional-packages/package.json +32 -0
- package/server/server/node_modules/tslib/modules/index.js +70 -0
- package/server/server/node_modules/tslib/modules/package.json +3 -0
- package/server/server/node_modules/tslib/package.json +47 -0
- package/server/server/node_modules/tslib/tslib.js +484 -0
- package/server/server/package.json +12 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,23 +1,302 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as ReviewService, c as ReviewFileRepo, d as parseDiff, f as CommentService, g as ReviewNotFound, i as TodoService, l as serializeHunks, m as TodoNotFound, n as GhService, o as GitService, r as ExportService, s as ReviewRepo, t as CoreLive, u as getDiffSummary } from "./runtime.mjs";
|
|
3
3
|
import { exec, execFileSync, fork } from "node:child_process";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import * as Schema from "effect/Schema";
|
|
7
|
-
import * as
|
|
7
|
+
import * as Result from "effect/Result";
|
|
8
8
|
import { writeFile } from "node:fs/promises";
|
|
9
|
+
import { ServiceMap } from "effect";
|
|
9
10
|
import * as Effect from "effect/Effect";
|
|
10
|
-
import * as Option from "effect/Option";
|
|
11
|
-
import * as Context from "effect/Context";
|
|
12
11
|
import * as Layer from "effect/Layer";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
13
|
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
14
14
|
import * as ConfigProvider from "effect/ConfigProvider";
|
|
15
|
+
//#region ../../packages/core/src/services/pr-preflight.ts
|
|
16
|
+
var PreflightFailure = class extends Schema.TaggedErrorClass()("PreflightFailure", {
|
|
17
|
+
exitCode: Schema.Number,
|
|
18
|
+
message: Schema.String,
|
|
19
|
+
phase: Schema.String
|
|
20
|
+
}) {};
|
|
21
|
+
/**
|
|
22
|
+
* Extract repository name from a git remote URL.
|
|
23
|
+
*
|
|
24
|
+
* Handles:
|
|
25
|
+
* - SSH: `git@github.com:owner/repo.git`
|
|
26
|
+
* - HTTPS: `https://github.com/owner/repo.git`
|
|
27
|
+
* - HTTPS: `https://github.com/owner/repo`
|
|
28
|
+
*/
|
|
29
|
+
const extractRepoNameFromRemote = (remote) => {
|
|
30
|
+
return remote.match(/[/:]([^/]+?)(?:\.git)?$/)?.[1] ?? null;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Runs the strict fail-fast preflight sequence for a PR review:
|
|
34
|
+
*
|
|
35
|
+
* 1. Verify `gh` installed
|
|
36
|
+
* 2. Verify `gh` auth for target host
|
|
37
|
+
* 3. Verify local git repository
|
|
38
|
+
* 4. Check repository affinity (warn if mismatch, fail if no repo)
|
|
39
|
+
* 5. Fetch PR metadata and validate it has changed files
|
|
40
|
+
* 6. Fetch PR diff
|
|
41
|
+
*/
|
|
42
|
+
const runPreflight = Effect.fn("PrPreflight.run")(function* (target) {
|
|
43
|
+
const gh = yield* GhService;
|
|
44
|
+
const git = yield* GitService;
|
|
45
|
+
yield* gh.ensureInstalled.pipe(Effect.mapError((e) => new PreflightFailure({
|
|
46
|
+
exitCode: 1,
|
|
47
|
+
message: e.message,
|
|
48
|
+
phase: "gh_install"
|
|
49
|
+
})));
|
|
50
|
+
yield* gh.ensureAuthenticated(target.host).pipe(Effect.mapError((e) => new PreflightFailure({
|
|
51
|
+
exitCode: 5,
|
|
52
|
+
message: e.message,
|
|
53
|
+
phase: "gh_auth"
|
|
54
|
+
})));
|
|
55
|
+
const localRepoPath = yield* git.getRepositoryPath.pipe(Effect.mapError(() => new PreflightFailure({
|
|
56
|
+
exitCode: 4,
|
|
57
|
+
message: "Not inside a git repository. Navigate to a repo or use --repo.",
|
|
58
|
+
phase: "repo_discovery"
|
|
59
|
+
})));
|
|
60
|
+
const repoInfo = yield* git.getRepositoryInfo.pipe(Effect.mapError(() => new PreflightFailure({
|
|
61
|
+
exitCode: 1,
|
|
62
|
+
message: "Could not read repository info.",
|
|
63
|
+
phase: "repo_info"
|
|
64
|
+
})));
|
|
65
|
+
let affinityMatch = false;
|
|
66
|
+
let affinityWarning = null;
|
|
67
|
+
if (repoInfo.remote) if (extractRepoNameFromRemote(repoInfo.remote)?.toLowerCase() === target.repo.toLowerCase()) affinityMatch = true;
|
|
68
|
+
else affinityWarning = `PR is from ${target.nwoRef} but local remote points to ${repoInfo.remote}. Review will be stored in ${localRepoPath}/.ringi/`;
|
|
69
|
+
else affinityWarning = `Local repository has no remote configured. Review will be stored in ${localRepoPath}/.ringi/`;
|
|
70
|
+
const metadata = yield* gh.fetchPrMetadata(target).pipe(Effect.mapError((e) => new PreflightFailure({
|
|
71
|
+
exitCode: 1,
|
|
72
|
+
message: `PR not accessible: ${e.message}`,
|
|
73
|
+
phase: "pr_fetch"
|
|
74
|
+
})));
|
|
75
|
+
if (metadata.changedFiles === 0) return yield* new PreflightFailure({
|
|
76
|
+
exitCode: 1,
|
|
77
|
+
message: `PR #${target.prNumber} has no changed files.`,
|
|
78
|
+
phase: "pr_validation"
|
|
79
|
+
});
|
|
80
|
+
const diff = yield* gh.fetchPrDiff(target).pipe(Effect.mapError((e) => new PreflightFailure({
|
|
81
|
+
exitCode: 1,
|
|
82
|
+
message: `Failed to fetch PR diff: ${e.message}`,
|
|
83
|
+
phase: "diff_fetch"
|
|
84
|
+
})));
|
|
85
|
+
if (!diff.trim()) return yield* new PreflightFailure({
|
|
86
|
+
exitCode: 1,
|
|
87
|
+
message: `PR #${target.prNumber} returned an empty diff.`,
|
|
88
|
+
phase: "diff_fetch"
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
affinityMatch,
|
|
92
|
+
affinityWarning,
|
|
93
|
+
diff,
|
|
94
|
+
localRepoPath,
|
|
95
|
+
metadata,
|
|
96
|
+
target
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region ../../packages/core/src/services/pr-session.ts
|
|
101
|
+
var PrSessionError = class extends Schema.TaggedErrorClass()("PrSessionError", {
|
|
102
|
+
code: Schema.String,
|
|
103
|
+
message: Schema.String
|
|
104
|
+
}) {};
|
|
105
|
+
/** Canonical source_ref for a PR: `host/owner/repo#number`. */
|
|
106
|
+
const prSourceRef = (target) => `${target.host}/${target.owner}/${target.repo}#${target.prNumber}`;
|
|
107
|
+
const parseStoredManifest = (raw) => {
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(raw);
|
|
110
|
+
} catch {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Creates a new PR review session or resumes an existing non-terminal one.
|
|
116
|
+
*
|
|
117
|
+
* Resume logic:
|
|
118
|
+
* - Looks for an existing review with `source_type = "pull_request"` and
|
|
119
|
+
* matching `source_ref`.
|
|
120
|
+
* - If found and non-terminal (`in_progress` or `changes_requested`):
|
|
121
|
+
* resumes, checking for upstream drift via head OID comparison.
|
|
122
|
+
* - If found but terminal (`approved`): creates a new session.
|
|
123
|
+
* - If not found: creates a new session.
|
|
124
|
+
*
|
|
125
|
+
* New sessions persist the full diff as hunks in `review_files`, making
|
|
126
|
+
* the review fully offline-resumable after initial fetch.
|
|
127
|
+
*/
|
|
128
|
+
const createOrResumePrSession = Effect.fn("PrSession.createOrResume")(function* (preflight) {
|
|
129
|
+
const gh = yield* GhService;
|
|
130
|
+
const repo = yield* ReviewRepo;
|
|
131
|
+
const fileRepo = yield* ReviewFileRepo;
|
|
132
|
+
const { target, metadata, diff } = preflight;
|
|
133
|
+
const sourceRef = prSourceRef(target);
|
|
134
|
+
const resumable = (yield* repo.findAll({
|
|
135
|
+
repositoryPath: preflight.localRepoPath,
|
|
136
|
+
sourceType: "pull_request",
|
|
137
|
+
pageSize: 100
|
|
138
|
+
})).data.find((r) => r.sourceRef === sourceRef && r.status !== "approved");
|
|
139
|
+
if (resumable) {
|
|
140
|
+
const currentHeadOid = yield* gh.fetchPrHeadOid(target).pipe(Effect.catch((e) => Effect.logDebug(`Could not fetch current head OID for drift check: ${e.message}`).pipe(Effect.as(metadata.headRefOid))));
|
|
141
|
+
const storedHeadOid = parseStoredManifest(resumable.snapshotData).headOidAtFetch ?? "";
|
|
142
|
+
const isStale = storedHeadOid !== "" && storedHeadOid !== currentHeadOid;
|
|
143
|
+
return {
|
|
144
|
+
isResumed: true,
|
|
145
|
+
isStale,
|
|
146
|
+
reviewId: resumable.id,
|
|
147
|
+
staleWarning: isStale ? `PR head has changed (${storedHeadOid.slice(0, 7)} → ${currentHeadOid.slice(0, 7)}). Review data reflects the previous version. Use --force-refresh to re-fetch.` : null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const files = parseDiff(diff);
|
|
151
|
+
if (files.length === 0) return yield* new PrSessionError({
|
|
152
|
+
code: "NO_CHANGES",
|
|
153
|
+
message: `PR #${target.prNumber} diff parsed to zero files.`
|
|
154
|
+
});
|
|
155
|
+
const reviewId = crypto.randomUUID();
|
|
156
|
+
const snapshotData = JSON.stringify({
|
|
157
|
+
diffByteSize: Buffer.byteLength(diff, "utf8"),
|
|
158
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
159
|
+
headOidAtFetch: metadata.headRefOid,
|
|
160
|
+
metadata,
|
|
161
|
+
source: "pull_request",
|
|
162
|
+
target,
|
|
163
|
+
version: 1
|
|
164
|
+
});
|
|
165
|
+
const fileInputs = files.map((f) => ({
|
|
166
|
+
additions: f.additions,
|
|
167
|
+
deletions: f.deletions,
|
|
168
|
+
filePath: f.newPath,
|
|
169
|
+
hunksData: serializeHunks(f.hunks),
|
|
170
|
+
oldPath: f.oldPath !== f.newPath ? f.oldPath : null,
|
|
171
|
+
reviewId,
|
|
172
|
+
status: f.status
|
|
173
|
+
}));
|
|
174
|
+
yield* repo.create({
|
|
175
|
+
baseRef: metadata.baseRefOid,
|
|
176
|
+
id: reviewId,
|
|
177
|
+
repositoryPath: preflight.localRepoPath,
|
|
178
|
+
snapshotData,
|
|
179
|
+
sourceRef,
|
|
180
|
+
sourceType: "pull_request",
|
|
181
|
+
status: "in_progress"
|
|
182
|
+
});
|
|
183
|
+
yield* fileRepo.createBulk(fileInputs);
|
|
184
|
+
return {
|
|
185
|
+
isResumed: false,
|
|
186
|
+
isStale: false,
|
|
187
|
+
reviewId,
|
|
188
|
+
staleWarning: null
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
/**
|
|
192
|
+
* Re-fetches PR data for an existing session and updates stored diff/metadata.
|
|
193
|
+
* Existing annotations are preserved (re-anchoring is a v1.1 feature).
|
|
194
|
+
*/
|
|
195
|
+
const forceRefreshPrSession = Effect.fn("PrSession.forceRefresh")(function* (reviewId, target) {
|
|
196
|
+
const gh = yield* GhService;
|
|
197
|
+
const repo = yield* ReviewRepo;
|
|
198
|
+
const fileRepo = yield* ReviewFileRepo;
|
|
199
|
+
if (!(yield* repo.findById(reviewId))) return yield* new PrSessionError({
|
|
200
|
+
code: "NOT_FOUND",
|
|
201
|
+
message: `Review session ${reviewId} not found.`
|
|
202
|
+
});
|
|
203
|
+
const metadata = yield* gh.fetchPrMetadata(target);
|
|
204
|
+
const diff = yield* gh.fetchPrDiff(target);
|
|
205
|
+
const files = parseDiff(diff);
|
|
206
|
+
const snapshotData = JSON.stringify({
|
|
207
|
+
diffByteSize: Buffer.byteLength(diff, "utf8"),
|
|
208
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
209
|
+
headOidAtFetch: metadata.headRefOid,
|
|
210
|
+
metadata,
|
|
211
|
+
source: "pull_request",
|
|
212
|
+
target,
|
|
213
|
+
version: 1
|
|
214
|
+
});
|
|
215
|
+
yield* repo.updateSnapshotData(reviewId, snapshotData);
|
|
216
|
+
yield* fileRepo.deleteByReview(reviewId);
|
|
217
|
+
const fileInputs = files.map((f) => ({
|
|
218
|
+
additions: f.additions,
|
|
219
|
+
deletions: f.deletions,
|
|
220
|
+
filePath: f.newPath,
|
|
221
|
+
hunksData: serializeHunks(f.hunks),
|
|
222
|
+
oldPath: f.oldPath !== f.newPath ? f.oldPath : null,
|
|
223
|
+
reviewId,
|
|
224
|
+
status: f.status
|
|
225
|
+
}));
|
|
226
|
+
yield* fileRepo.createBulk(fileInputs);
|
|
227
|
+
return {
|
|
228
|
+
filesUpdated: files.length,
|
|
229
|
+
headOid: metadata.headRefOid,
|
|
230
|
+
reviewId,
|
|
231
|
+
summary: getDiffSummary(files)
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region ../../packages/core/src/services/pr-url.ts
|
|
236
|
+
var InvalidPrUrl = class extends Schema.TaggedErrorClass()("InvalidPrUrl", {
|
|
237
|
+
message: Schema.String,
|
|
238
|
+
url: Schema.String
|
|
239
|
+
}) {};
|
|
240
|
+
/**
|
|
241
|
+
* Quick heuristic: does this string look like a PR URL?
|
|
242
|
+
*
|
|
243
|
+
* Used by the CLI parser to distinguish `review <url>` from `review <verb>`.
|
|
244
|
+
* No ambiguity: no review verb starts with `http`.
|
|
245
|
+
*/
|
|
246
|
+
const looksLikePrUrl = (s) => /^https?:\/\/[^/]+\/[^/]+\/[^/]+\/pull\/\d+/.test(s);
|
|
247
|
+
/**
|
|
248
|
+
* Parses a GitHub PR URL into structured components.
|
|
249
|
+
*
|
|
250
|
+
* Supports:
|
|
251
|
+
* - `https://github.com/owner/repo/pull/42`
|
|
252
|
+
* - `https://github.com/owner/repo/pull/42/files`
|
|
253
|
+
* - `https://github.com/owner/repo/pull/42/commits`
|
|
254
|
+
* - `https://ghe.corp.com/owner/repo/pull/42`
|
|
255
|
+
* - `http://...` (for GHE behind VPN)
|
|
256
|
+
*
|
|
257
|
+
* Does NOT support:
|
|
258
|
+
* - SSH URLs, API URLs, short references like `owner/repo#42`
|
|
259
|
+
*/
|
|
260
|
+
const parsePrUrl = Effect.fn("parsePrUrl")(function* (raw) {
|
|
261
|
+
const url = yield* Effect.try({
|
|
262
|
+
catch: () => new InvalidPrUrl({
|
|
263
|
+
message: "Not a valid URL. Expected: https://github.com/<owner>/<repo>/pull/<number>",
|
|
264
|
+
url: raw
|
|
265
|
+
}),
|
|
266
|
+
try: () => new URL(raw)
|
|
267
|
+
});
|
|
268
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return yield* new InvalidPrUrl({
|
|
269
|
+
message: `Unsupported protocol: ${url.protocol}. Expected https:// or http://`,
|
|
270
|
+
url: raw
|
|
271
|
+
});
|
|
272
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
273
|
+
if (segments.length < 4 || segments[2] !== "pull") return yield* new InvalidPrUrl({
|
|
274
|
+
message: "URL path must match /<owner>/<repo>/pull/<number>",
|
|
275
|
+
url: raw
|
|
276
|
+
});
|
|
277
|
+
const owner = segments[0];
|
|
278
|
+
const repo = segments[1];
|
|
279
|
+
const prNumber = Number.parseInt(segments[3], 10);
|
|
280
|
+
if (!Number.isFinite(prNumber) || prNumber <= 0) return yield* new InvalidPrUrl({
|
|
281
|
+
message: `Invalid PR number: ${segments[3]}`,
|
|
282
|
+
url: raw
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
host: url.host,
|
|
286
|
+
nwoRef: `${owner}/${repo}`,
|
|
287
|
+
owner,
|
|
288
|
+
prNumber,
|
|
289
|
+
repo,
|
|
290
|
+
url: `${url.protocol}//${url.host}/${owner}/${repo}/pull/${prNumber}`
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
//#endregion
|
|
15
294
|
//#region src/cli/config.ts
|
|
16
|
-
var CliConfig = class extends
|
|
295
|
+
var CliConfig = class extends ServiceMap.Service()("@ringi/CliConfig") {};
|
|
17
296
|
/**
|
|
18
297
|
* Wraps a concrete {@link CliConfigShape} in a layer for the Effect runtime.
|
|
19
298
|
*/
|
|
20
|
-
const CliConfigLive = (config) => Layer.succeed(CliConfig, config);
|
|
299
|
+
const CliConfigLive = (config) => Layer.succeed(CliConfig, CliConfig.of(config));
|
|
21
300
|
//#endregion
|
|
22
301
|
//#region src/cli/contracts.ts
|
|
23
302
|
const ExitCode = {
|
|
@@ -45,8 +324,8 @@ const failure = (command, error, fix, nextActions = []) => ({
|
|
|
45
324
|
* Carries an exit code and optional operator-facing details so callers can
|
|
46
325
|
* present a short message without losing the underlying reason.
|
|
47
326
|
*/
|
|
48
|
-
var CliFailure = class extends Schema.
|
|
49
|
-
details: Schema.
|
|
327
|
+
var CliFailure = class extends Schema.TaggedErrorClass()("CliFailure", {
|
|
328
|
+
details: Schema.String.pipe(Schema.optionalKey),
|
|
50
329
|
exitCode: Schema.Number,
|
|
51
330
|
message: Schema.String
|
|
52
331
|
}) {};
|
|
@@ -476,6 +755,86 @@ const runTodoList = Effect.fn("CLI.todoList")(function* runTodoList(command) {
|
|
|
476
755
|
nextActions
|
|
477
756
|
};
|
|
478
757
|
});
|
|
758
|
+
const runReviewPr = Effect.fn("CLI.reviewPr")(function* runReviewPr(command) {
|
|
759
|
+
const target = yield* parsePrUrl(command.prUrl).pipe(Effect.mapError((e) => new CliFailure({
|
|
760
|
+
exitCode: ExitCode.UsageError,
|
|
761
|
+
message: e.message
|
|
762
|
+
})));
|
|
763
|
+
const preflight = yield* runPreflight(target).pipe(Effect.mapError((e) => new CliFailure({
|
|
764
|
+
exitCode: e.exitCode,
|
|
765
|
+
message: e.message
|
|
766
|
+
})));
|
|
767
|
+
if (preflight.affinityWarning) yield* Effect.logWarning(preflight.affinityWarning);
|
|
768
|
+
let session;
|
|
769
|
+
if (command.forceRefresh) {
|
|
770
|
+
const reviewService = yield* ReviewService;
|
|
771
|
+
const sourceRef = prSourceRef(target);
|
|
772
|
+
const cliConfig = yield* CliConfig;
|
|
773
|
+
const resumable = (yield* reviewService.list({
|
|
774
|
+
repositoryPath: cliConfig.repoRoot,
|
|
775
|
+
sourceType: "pull_request",
|
|
776
|
+
pageSize: 100
|
|
777
|
+
})).reviews.find((r) => r.sourceRef === sourceRef && r.status !== "approved");
|
|
778
|
+
if (resumable) {
|
|
779
|
+
yield* forceRefreshPrSession(resumable.id, target).pipe(Effect.mapError((e) => new CliFailure({
|
|
780
|
+
exitCode: ExitCode.RuntimeFailure,
|
|
781
|
+
message: e.message
|
|
782
|
+
})));
|
|
783
|
+
session = {
|
|
784
|
+
isResumed: true,
|
|
785
|
+
isStale: false,
|
|
786
|
+
reviewId: resumable.id,
|
|
787
|
+
staleWarning: null
|
|
788
|
+
};
|
|
789
|
+
} else session = yield* createOrResumePrSession(preflight).pipe(Effect.mapError((e) => new CliFailure({
|
|
790
|
+
exitCode: ExitCode.RuntimeFailure,
|
|
791
|
+
message: e.message
|
|
792
|
+
})));
|
|
793
|
+
} else session = yield* createOrResumePrSession(preflight).pipe(Effect.mapError((e) => new CliFailure({
|
|
794
|
+
exitCode: ExitCode.RuntimeFailure,
|
|
795
|
+
message: e.message
|
|
796
|
+
})));
|
|
797
|
+
if (session.staleWarning) yield* Effect.logWarning(session.staleWarning);
|
|
798
|
+
const serverUrl = `http://localhost:${command.port}`;
|
|
799
|
+
const reviewUrl = `${serverUrl}/review/${session.reviewId}`;
|
|
800
|
+
const data = {
|
|
801
|
+
isResumed: session.isResumed,
|
|
802
|
+
isStale: session.isStale,
|
|
803
|
+
prNumber: target.prNumber,
|
|
804
|
+
prUrl: target.url,
|
|
805
|
+
reviewId: session.reviewId,
|
|
806
|
+
reviewUrl
|
|
807
|
+
};
|
|
808
|
+
const statusLabel = session.isResumed ? command.forceRefresh ? "(refreshed)" : "(resumed)" : "(new)";
|
|
809
|
+
const humanLines = [
|
|
810
|
+
`PR #${target.prNumber}: ${preflight.metadata.title}`,
|
|
811
|
+
`Review: ${session.reviewId} ${statusLabel}`,
|
|
812
|
+
`Author: ${preflight.metadata.author.login}`,
|
|
813
|
+
`Branch: ${preflight.metadata.headRefName} → ${preflight.metadata.baseRefName}`,
|
|
814
|
+
`Files: ${preflight.metadata.changedFiles} (+${preflight.metadata.additions} -${preflight.metadata.deletions})`,
|
|
815
|
+
"",
|
|
816
|
+
`Server: ${serverUrl}`,
|
|
817
|
+
`Review: ${reviewUrl}`
|
|
818
|
+
];
|
|
819
|
+
if (preflight.metadata.isDraft) humanLines.splice(1, 0, "⚠ Draft PR");
|
|
820
|
+
if (preflight.metadata.state === "CLOSED" || preflight.metadata.state === "MERGED") humanLines.splice(1, 0, `⚠ This PR is ${preflight.metadata.state}`);
|
|
821
|
+
const nextActions = [{
|
|
822
|
+
command: `ringi review show ${session.reviewId} --comments --todos`,
|
|
823
|
+
description: "Inspect review details"
|
|
824
|
+
}, {
|
|
825
|
+
command: `ringi review export ${session.reviewId}`,
|
|
826
|
+
description: "Export review as markdown"
|
|
827
|
+
}];
|
|
828
|
+
if (session.isStale) nextActions.unshift({
|
|
829
|
+
command: `ringi review ${command.prUrl} --force-refresh`,
|
|
830
|
+
description: "Re-fetch PR data with latest changes"
|
|
831
|
+
});
|
|
832
|
+
return {
|
|
833
|
+
data,
|
|
834
|
+
human: humanLines.join("\n"),
|
|
835
|
+
nextActions
|
|
836
|
+
};
|
|
837
|
+
});
|
|
479
838
|
/**
|
|
480
839
|
* Data-driven command registry. Each command kind maps to its handler.
|
|
481
840
|
* Adding a new command means adding one entry — no switch duplication.
|
|
@@ -499,6 +858,7 @@ const COMMAND_HANDLERS = {
|
|
|
499
858
|
"review-create": () => requireServerMode("ringi review create"),
|
|
500
859
|
"review-export": (c) => runReviewExport(c),
|
|
501
860
|
"review-list": (c) => runReviewList(c),
|
|
861
|
+
"review-pr": (c) => runReviewPr(c),
|
|
502
862
|
"review-resolve": () => requireServerMode("ringi review resolve"),
|
|
503
863
|
"review-show": (c) => runReviewShow(c),
|
|
504
864
|
"review-status": (c) => runReviewStatus(c),
|
|
@@ -526,6 +886,7 @@ const COMMAND_LABELS = {
|
|
|
526
886
|
"review-create": "ringi review create",
|
|
527
887
|
"review-export": "ringi review export",
|
|
528
888
|
"review-list": "ringi review list",
|
|
889
|
+
"review-pr": "ringi review <pr-url>",
|
|
529
890
|
"review-resolve": "ringi review resolve",
|
|
530
891
|
"review-show": "ringi review show",
|
|
531
892
|
"review-status": "ringi review status",
|
|
@@ -554,6 +915,7 @@ const runCommand = (command) => {
|
|
|
554
915
|
const REVIEW_SOURCES = new Set([
|
|
555
916
|
"branch",
|
|
556
917
|
"commits",
|
|
918
|
+
"pull_request",
|
|
557
919
|
"staged"
|
|
558
920
|
]);
|
|
559
921
|
const REVIEW_STATUSES = new Set([
|
|
@@ -584,12 +946,12 @@ const requireValue = (state, flag) => {
|
|
|
584
946
|
const peekValue = (state) => state.tokens[state.index + 1] ?? "";
|
|
585
947
|
const decodePositiveInt = (raw, flag) => {
|
|
586
948
|
const value = Number.parseInt(raw, 10);
|
|
587
|
-
if (!Number.isInteger(value) || value < 0) return
|
|
588
|
-
return
|
|
949
|
+
if (!Number.isInteger(value) || value < 0) return Result.fail(usageError(`${flag} must be a non-negative integer.`));
|
|
950
|
+
return Result.succeed(value);
|
|
589
951
|
};
|
|
590
952
|
const decodeEnum = (raw, valid, label) => {
|
|
591
|
-
if (!valid.has(raw)) return
|
|
592
|
-
return
|
|
953
|
+
if (!valid.has(raw)) return Result.fail(usageError(`Invalid ${label}: ${raw}.`));
|
|
954
|
+
return Result.succeed(raw);
|
|
593
955
|
};
|
|
594
956
|
/** Boolean flag: sets a key to `true`, advances cursor by 1. */
|
|
595
957
|
const boolFlag = (key) => (state, acc) => {
|
|
@@ -613,9 +975,9 @@ const positiveIntFlag = (key, opts) => (state, acc) => {
|
|
|
613
975
|
const error = requireValue(state, flag);
|
|
614
976
|
if (Option.isSome(error)) return error;
|
|
615
977
|
const decoded = decodePositiveInt(raw, flag);
|
|
616
|
-
if (
|
|
617
|
-
if (opts?.min !== void 0 && decoded.
|
|
618
|
-
acc[key] = decoded.
|
|
978
|
+
if (Result.isFailure(decoded)) return Option.some(decoded.failure);
|
|
979
|
+
if (opts?.min !== void 0 && decoded.success <= opts.min) return Option.some(usageError(`${flag} must be greater than ${opts.min}.`));
|
|
980
|
+
acc[key] = decoded.success;
|
|
619
981
|
return Option.none();
|
|
620
982
|
};
|
|
621
983
|
/** Enum flag: consumes next token, validates membership, assigns to key. */
|
|
@@ -625,8 +987,8 @@ const enumFlag = (key, valid, label) => (state, acc) => {
|
|
|
625
987
|
const error = requireValue(state, flag);
|
|
626
988
|
if (Option.isSome(error)) return error;
|
|
627
989
|
const decoded = decodeEnum(raw, valid, label);
|
|
628
|
-
if (
|
|
629
|
-
acc[key] = decoded.
|
|
990
|
+
if (Result.isFailure(decoded)) return Option.some(decoded.failure);
|
|
991
|
+
acc[key] = decoded.success;
|
|
630
992
|
return Option.none();
|
|
631
993
|
};
|
|
632
994
|
const createDefaultOptions = () => ({
|
|
@@ -695,8 +1057,8 @@ const parseReviewList = (state) => {
|
|
|
695
1057
|
status: void 0
|
|
696
1058
|
};
|
|
697
1059
|
const error = runFlagLoop(state, acc, REVIEW_LIST_FLAGS, "review list");
|
|
698
|
-
if (Option.isSome(error)) return
|
|
699
|
-
return
|
|
1060
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1061
|
+
return Result.succeed({
|
|
700
1062
|
kind: "review-list",
|
|
701
1063
|
...acc
|
|
702
1064
|
});
|
|
@@ -707,15 +1069,15 @@ const REVIEW_SHOW_FLAGS = {
|
|
|
707
1069
|
};
|
|
708
1070
|
const parseReviewShow = (state) => {
|
|
709
1071
|
const id = state.tokens[state.index];
|
|
710
|
-
if (!id) return
|
|
1072
|
+
if (!id) return Result.fail(usageError("review show requires <id|last>."));
|
|
711
1073
|
state.index += 1;
|
|
712
1074
|
const acc = {
|
|
713
1075
|
comments: false,
|
|
714
1076
|
todos: false
|
|
715
1077
|
};
|
|
716
1078
|
const error = runFlagLoop(state, acc, REVIEW_SHOW_FLAGS, "review show");
|
|
717
|
-
if (Option.isSome(error)) return
|
|
718
|
-
return
|
|
1079
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1080
|
+
return Result.succeed({
|
|
719
1081
|
id,
|
|
720
1082
|
kind: "review-show",
|
|
721
1083
|
...acc
|
|
@@ -729,7 +1091,7 @@ const REVIEW_EXPORT_FLAGS = {
|
|
|
729
1091
|
};
|
|
730
1092
|
const parseReviewExport = (state) => {
|
|
731
1093
|
const id = state.tokens[state.index];
|
|
732
|
-
if (!id) return
|
|
1094
|
+
if (!id) return Result.fail(usageError("review export requires <id|last>."));
|
|
733
1095
|
state.index += 1;
|
|
734
1096
|
const acc = {
|
|
735
1097
|
noResolved: false,
|
|
@@ -738,8 +1100,8 @@ const parseReviewExport = (state) => {
|
|
|
738
1100
|
stdout: false
|
|
739
1101
|
};
|
|
740
1102
|
const error = runFlagLoop(state, acc, REVIEW_EXPORT_FLAGS, "review export");
|
|
741
|
-
if (Option.isSome(error)) return
|
|
742
|
-
return
|
|
1103
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1104
|
+
return Result.succeed({
|
|
743
1105
|
id,
|
|
744
1106
|
kind: "review-export",
|
|
745
1107
|
...acc
|
|
@@ -765,10 +1127,10 @@ const parseReviewCreate = (state) => {
|
|
|
765
1127
|
title: void 0
|
|
766
1128
|
};
|
|
767
1129
|
const error = runFlagLoop(state, acc, REVIEW_CREATE_FLAGS, "review create");
|
|
768
|
-
if (Option.isSome(error)) return
|
|
1130
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
769
1131
|
const validationError = validateReviewCreate(acc);
|
|
770
|
-
if (Option.isSome(validationError)) return
|
|
771
|
-
return
|
|
1132
|
+
if (Option.isSome(validationError)) return Result.fail(validationError.value);
|
|
1133
|
+
return Result.succeed({
|
|
772
1134
|
kind: "review-create",
|
|
773
1135
|
...acc
|
|
774
1136
|
});
|
|
@@ -785,7 +1147,7 @@ const validateSourceDiff = (source, acc) => {
|
|
|
785
1147
|
};
|
|
786
1148
|
const parseSourceDiff = (state) => {
|
|
787
1149
|
const source = state.tokens[state.index];
|
|
788
|
-
if (!source || !REVIEW_SOURCES.has(source)) return
|
|
1150
|
+
if (!source || !REVIEW_SOURCES.has(source)) return Result.fail(usageError("source diff requires <staged|branch|commits>."));
|
|
789
1151
|
state.index += 1;
|
|
790
1152
|
const acc = {
|
|
791
1153
|
branch: void 0,
|
|
@@ -793,10 +1155,10 @@ const parseSourceDiff = (state) => {
|
|
|
793
1155
|
stat: false
|
|
794
1156
|
};
|
|
795
1157
|
const error = runFlagLoop(state, acc, SOURCE_DIFF_FLAGS, "source diff");
|
|
796
|
-
if (Option.isSome(error)) return
|
|
1158
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
797
1159
|
const validationError = validateSourceDiff(source, acc);
|
|
798
|
-
if (Option.isSome(validationError)) return
|
|
799
|
-
return
|
|
1160
|
+
if (Option.isSome(validationError)) return Result.fail(validationError.value);
|
|
1161
|
+
return Result.succeed({
|
|
800
1162
|
kind: "source-diff",
|
|
801
1163
|
source,
|
|
802
1164
|
...acc
|
|
@@ -816,8 +1178,8 @@ const parseTodoList = (state) => {
|
|
|
816
1178
|
status: "pending"
|
|
817
1179
|
};
|
|
818
1180
|
const error = runFlagLoop(state, acc, TODO_LIST_FLAGS, "todo list");
|
|
819
|
-
if (Option.isSome(error)) return
|
|
820
|
-
return
|
|
1181
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1182
|
+
return Result.succeed({
|
|
821
1183
|
kind: "todo-list",
|
|
822
1184
|
...acc
|
|
823
1185
|
});
|
|
@@ -828,10 +1190,10 @@ const parseTodoList = (state) => {
|
|
|
828
1190
|
*/
|
|
829
1191
|
const positionalIdParser = (kind, label) => (state) => {
|
|
830
1192
|
const id = state.tokens[state.index];
|
|
831
|
-
if (!id) return
|
|
1193
|
+
if (!id) return Result.fail(usageError(`${label} requires <id>.`));
|
|
832
1194
|
state.index += 1;
|
|
833
|
-
while (state.index < state.tokens.length) if (!maybeParseGlobalFlag(state)) return
|
|
834
|
-
return
|
|
1195
|
+
while (state.index < state.tokens.length) if (!maybeParseGlobalFlag(state)) return Result.fail(usageError(`Unknown flag for ${label}: ${state.tokens[state.index]}.`));
|
|
1196
|
+
return Result.succeed({
|
|
835
1197
|
id,
|
|
836
1198
|
kind
|
|
837
1199
|
});
|
|
@@ -841,13 +1203,13 @@ const parseTodoUndone = positionalIdParser("todo-undone", "todo undone");
|
|
|
841
1203
|
const TODO_MOVE_FLAGS = { "--position": positiveIntFlag("position") };
|
|
842
1204
|
const parseTodoMove = (state) => {
|
|
843
1205
|
const id = state.tokens[state.index];
|
|
844
|
-
if (!id) return
|
|
1206
|
+
if (!id) return Result.fail(usageError("todo move requires <id>."));
|
|
845
1207
|
state.index += 1;
|
|
846
1208
|
const acc = { position: void 0 };
|
|
847
1209
|
const error = runFlagLoop(state, acc, TODO_MOVE_FLAGS, "todo move");
|
|
848
|
-
if (Option.isSome(error)) return
|
|
849
|
-
if (acc.position === void 0) return
|
|
850
|
-
return
|
|
1210
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1211
|
+
if (acc.position === void 0) return Result.fail(usageError("todo move requires --position."));
|
|
1212
|
+
return Result.succeed({
|
|
851
1213
|
id,
|
|
852
1214
|
kind: "todo-move",
|
|
853
1215
|
position: acc.position
|
|
@@ -856,12 +1218,12 @@ const parseTodoMove = (state) => {
|
|
|
856
1218
|
const TODO_REMOVE_FLAGS = { "--yes": boolFlag("yes") };
|
|
857
1219
|
const parseTodoRemove = (state) => {
|
|
858
1220
|
const id = state.tokens[state.index];
|
|
859
|
-
if (!id) return
|
|
1221
|
+
if (!id) return Result.fail(usageError("todo remove requires <id>."));
|
|
860
1222
|
state.index += 1;
|
|
861
1223
|
const acc = { yes: false };
|
|
862
1224
|
const error = runFlagLoop(state, acc, TODO_REMOVE_FLAGS, "todo remove");
|
|
863
|
-
if (Option.isSome(error)) return
|
|
864
|
-
return
|
|
1225
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1226
|
+
return Result.succeed({
|
|
865
1227
|
id,
|
|
866
1228
|
kind: "todo-remove",
|
|
867
1229
|
...acc
|
|
@@ -881,8 +1243,8 @@ const parseTodoClear = (state) => {
|
|
|
881
1243
|
yes: false
|
|
882
1244
|
};
|
|
883
1245
|
const error = runFlagLoop(state, acc, TODO_CLEAR_FLAGS, "todo clear");
|
|
884
|
-
if (Option.isSome(error)) return
|
|
885
|
-
return
|
|
1246
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1247
|
+
return Result.succeed({
|
|
886
1248
|
kind: "todo-clear",
|
|
887
1249
|
...acc
|
|
888
1250
|
});
|
|
@@ -897,8 +1259,8 @@ const parseReviewStatus = (state) => {
|
|
|
897
1259
|
source: void 0
|
|
898
1260
|
};
|
|
899
1261
|
const error = runFlagLoop(state, acc, REVIEW_STATUS_FLAGS, "review status");
|
|
900
|
-
if (Option.isSome(error)) return
|
|
901
|
-
return
|
|
1262
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1263
|
+
return Result.succeed({
|
|
902
1264
|
kind: "review-status",
|
|
903
1265
|
...acc
|
|
904
1266
|
});
|
|
@@ -909,15 +1271,15 @@ const REVIEW_RESOLVE_FLAGS = {
|
|
|
909
1271
|
};
|
|
910
1272
|
const parseReviewResolve = (state) => {
|
|
911
1273
|
const id = state.tokens[state.index];
|
|
912
|
-
if (!id) return
|
|
1274
|
+
if (!id) return Result.fail(usageError("review resolve requires <id|last>."));
|
|
913
1275
|
state.index += 1;
|
|
914
1276
|
const acc = {
|
|
915
1277
|
allComments: true,
|
|
916
1278
|
yes: false
|
|
917
1279
|
};
|
|
918
1280
|
const error = runFlagLoop(state, acc, REVIEW_RESOLVE_FLAGS, "review resolve");
|
|
919
|
-
if (Option.isSome(error)) return
|
|
920
|
-
return
|
|
1281
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1282
|
+
return Result.succeed({
|
|
921
1283
|
id,
|
|
922
1284
|
kind: "review-resolve",
|
|
923
1285
|
...acc
|
|
@@ -935,9 +1297,9 @@ const parseTodoAdd = (state) => {
|
|
|
935
1297
|
text: ""
|
|
936
1298
|
};
|
|
937
1299
|
const error = runFlagLoop(state, acc, TODO_ADD_FLAGS, "todo add");
|
|
938
|
-
if (Option.isSome(error)) return
|
|
939
|
-
if (!acc.text.trim()) return
|
|
940
|
-
return
|
|
1300
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1301
|
+
if (!acc.text.trim()) return Result.fail(usageError("todo add requires --text."));
|
|
1302
|
+
return Result.succeed({
|
|
941
1303
|
kind: "todo-add",
|
|
942
1304
|
...acc
|
|
943
1305
|
});
|
|
@@ -966,8 +1328,8 @@ const parseServe = (state) => {
|
|
|
966
1328
|
username: void 0
|
|
967
1329
|
};
|
|
968
1330
|
const error = runFlagLoop(state, acc, SERVE_FLAGS, "serve");
|
|
969
|
-
if (Option.isSome(error)) return
|
|
970
|
-
return
|
|
1331
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1332
|
+
return Result.succeed({
|
|
971
1333
|
kind: "serve",
|
|
972
1334
|
...acc
|
|
973
1335
|
});
|
|
@@ -988,8 +1350,8 @@ const parseMcp = (state) => {
|
|
|
988
1350
|
readonly: false
|
|
989
1351
|
};
|
|
990
1352
|
const error = runFlagLoop(state, acc, MCP_FLAGS, "mcp");
|
|
991
|
-
if (Option.isSome(error)) return
|
|
992
|
-
return
|
|
1353
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1354
|
+
return Result.succeed({
|
|
993
1355
|
kind: "mcp",
|
|
994
1356
|
...acc
|
|
995
1357
|
});
|
|
@@ -1010,8 +1372,8 @@ const parseEvents = (state) => {
|
|
|
1010
1372
|
type: void 0
|
|
1011
1373
|
};
|
|
1012
1374
|
const error = runFlagLoop(state, acc, EVENTS_FLAGS, "events");
|
|
1013
|
-
if (Option.isSome(error)) return
|
|
1014
|
-
return
|
|
1375
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1376
|
+
return Result.succeed({
|
|
1015
1377
|
kind: "events",
|
|
1016
1378
|
...acc
|
|
1017
1379
|
});
|
|
@@ -1026,8 +1388,8 @@ const parseDataReset = (state) => {
|
|
|
1026
1388
|
yes: false
|
|
1027
1389
|
};
|
|
1028
1390
|
const error = runFlagLoop(state, acc, DATA_RESET_FLAGS, "data reset");
|
|
1029
|
-
if (Option.isSome(error)) return
|
|
1030
|
-
return
|
|
1391
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1392
|
+
return Result.succeed({
|
|
1031
1393
|
kind: "data-reset",
|
|
1032
1394
|
...acc
|
|
1033
1395
|
});
|
|
@@ -1055,13 +1417,32 @@ const TODO_VERB_PARSERS = {
|
|
|
1055
1417
|
remove: parseTodoRemove,
|
|
1056
1418
|
undone: parseTodoUndone
|
|
1057
1419
|
};
|
|
1420
|
+
const REVIEW_PR_FLAGS = {
|
|
1421
|
+
"--force-refresh": boolFlag("forceRefresh"),
|
|
1422
|
+
"--no-open": boolFlag("noOpen"),
|
|
1423
|
+
"--port": positiveIntFlag("port", { min: 0 })
|
|
1424
|
+
};
|
|
1425
|
+
const parseReviewPr = (state, prUrl) => {
|
|
1426
|
+
const acc = {
|
|
1427
|
+
forceRefresh: false,
|
|
1428
|
+
noOpen: false,
|
|
1429
|
+
port: 3e3
|
|
1430
|
+
};
|
|
1431
|
+
const error = runFlagLoop(state, acc, REVIEW_PR_FLAGS, "review <pr-url>");
|
|
1432
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1433
|
+
return Result.succeed({
|
|
1434
|
+
kind: "review-pr",
|
|
1435
|
+
prUrl,
|
|
1436
|
+
...acc
|
|
1437
|
+
});
|
|
1438
|
+
};
|
|
1058
1439
|
/** Subcommand family parsers keyed by family name. */
|
|
1059
1440
|
/** Data verb parsers keyed by verb name. */
|
|
1060
1441
|
const DATA_VERB_PARSERS = {
|
|
1061
1442
|
migrate: (state) => {
|
|
1062
1443
|
const error = ensureNoExtraArgs(state, "data migrate");
|
|
1063
|
-
if (Option.isSome(error)) return
|
|
1064
|
-
return
|
|
1444
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1445
|
+
return Result.succeed({ kind: "data-migrate" });
|
|
1065
1446
|
},
|
|
1066
1447
|
reset: parseDataReset
|
|
1067
1448
|
};
|
|
@@ -1069,59 +1450,63 @@ const DATA_VERB_PARSERS = {
|
|
|
1069
1450
|
const FAMILY_PARSERS = {
|
|
1070
1451
|
data: (state) => {
|
|
1071
1452
|
const verb = state.tokens[state.index];
|
|
1072
|
-
if (!verb) return
|
|
1453
|
+
if (!verb) return Result.succeed({
|
|
1073
1454
|
kind: "help",
|
|
1074
1455
|
topic: ["data"]
|
|
1075
1456
|
});
|
|
1076
1457
|
state.index += 1;
|
|
1077
1458
|
const parser = DATA_VERB_PARSERS[verb];
|
|
1078
|
-
if (!parser) return
|
|
1459
|
+
if (!parser) return Result.fail(usageError(`Unknown data command: ${verb}.`));
|
|
1079
1460
|
return parser(state);
|
|
1080
1461
|
},
|
|
1081
1462
|
doctor: (state) => {
|
|
1082
1463
|
const error = ensureNoExtraArgs(state, "doctor");
|
|
1083
|
-
if (Option.isSome(error)) return
|
|
1084
|
-
return
|
|
1464
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1465
|
+
return Result.succeed({ kind: "doctor" });
|
|
1085
1466
|
},
|
|
1086
1467
|
events: parseEvents,
|
|
1087
1468
|
export: parseReviewExport,
|
|
1088
1469
|
mcp: parseMcp,
|
|
1089
1470
|
review: (state) => {
|
|
1090
1471
|
const verb = state.tokens[state.index];
|
|
1091
|
-
if (!verb) return
|
|
1472
|
+
if (!verb) return Result.succeed({
|
|
1092
1473
|
kind: "help",
|
|
1093
1474
|
topic: ["review"]
|
|
1094
1475
|
});
|
|
1476
|
+
if (looksLikePrUrl(verb)) {
|
|
1477
|
+
state.index += 1;
|
|
1478
|
+
return parseReviewPr(state, verb);
|
|
1479
|
+
}
|
|
1095
1480
|
state.index += 1;
|
|
1096
1481
|
const parser = REVIEW_VERB_PARSERS[verb];
|
|
1097
|
-
if (!parser) return
|
|
1482
|
+
if (!parser) return Result.fail(usageError(`Unknown review command: ${verb}.`));
|
|
1098
1483
|
return parser(state);
|
|
1099
1484
|
},
|
|
1100
1485
|
serve: parseServe,
|
|
1101
1486
|
source: (state) => {
|
|
1102
1487
|
const verb = state.tokens[state.index];
|
|
1103
|
-
if (!verb) return
|
|
1488
|
+
if (!verb) return Result.succeed({
|
|
1104
1489
|
kind: "help",
|
|
1105
1490
|
topic: ["source"]
|
|
1106
1491
|
});
|
|
1107
1492
|
state.index += 1;
|
|
1108
1493
|
if (verb === "list") {
|
|
1109
1494
|
const error = ensureNoExtraArgs(state, "source list");
|
|
1110
|
-
if (Option.isSome(error)) return
|
|
1111
|
-
return
|
|
1495
|
+
if (Option.isSome(error)) return Result.fail(error.value);
|
|
1496
|
+
return Result.succeed({ kind: "source-list" });
|
|
1112
1497
|
}
|
|
1113
1498
|
if (verb === "diff") return parseSourceDiff(state);
|
|
1114
|
-
return
|
|
1499
|
+
return Result.fail(usageError(`Unknown source command: ${verb}.`));
|
|
1115
1500
|
},
|
|
1116
1501
|
todo: (state) => {
|
|
1117
1502
|
const verb = state.tokens[state.index];
|
|
1118
|
-
if (!verb) return
|
|
1503
|
+
if (!verb) return Result.succeed({
|
|
1119
1504
|
kind: "help",
|
|
1120
1505
|
topic: ["todo"]
|
|
1121
1506
|
});
|
|
1122
1507
|
state.index += 1;
|
|
1123
1508
|
const parser = TODO_VERB_PARSERS[verb];
|
|
1124
|
-
if (!parser) return
|
|
1509
|
+
if (!parser) return Result.fail(usageError(`Unknown todo command: ${verb}.`));
|
|
1125
1510
|
return parser(state);
|
|
1126
1511
|
}
|
|
1127
1512
|
};
|
|
@@ -1131,31 +1516,31 @@ const FAMILY_PARSERS = {
|
|
|
1131
1516
|
*/
|
|
1132
1517
|
const parseWithState = (state) => {
|
|
1133
1518
|
while (state.index < state.tokens.length && maybeParseGlobalFlag(state));
|
|
1134
|
-
if (state.options.version) return
|
|
1135
|
-
if (state.index >= state.tokens.length) return
|
|
1519
|
+
if (state.options.version) return Result.succeed({ kind: "version" });
|
|
1520
|
+
if (state.index >= state.tokens.length) return Result.succeed({
|
|
1136
1521
|
kind: "help",
|
|
1137
1522
|
topic: []
|
|
1138
1523
|
});
|
|
1139
1524
|
const first = state.tokens[state.index];
|
|
1140
|
-
if (!first) return
|
|
1525
|
+
if (!first) return Result.succeed({
|
|
1141
1526
|
kind: "help",
|
|
1142
1527
|
topic: []
|
|
1143
1528
|
});
|
|
1144
1529
|
if (first === "help") {
|
|
1145
1530
|
const topic = state.tokens.slice(state.index + 1);
|
|
1146
1531
|
state.index = state.tokens.length;
|
|
1147
|
-
return
|
|
1532
|
+
return Result.succeed({
|
|
1148
1533
|
kind: "help",
|
|
1149
1534
|
topic
|
|
1150
1535
|
});
|
|
1151
1536
|
}
|
|
1152
|
-
if (state.options.help) return
|
|
1537
|
+
if (state.options.help) return Result.succeed({
|
|
1153
1538
|
kind: "help",
|
|
1154
1539
|
topic: state.tokens.slice(state.index)
|
|
1155
1540
|
});
|
|
1156
1541
|
state.index += 1;
|
|
1157
1542
|
const familyParser = FAMILY_PARSERS[first];
|
|
1158
|
-
if (!familyParser) return
|
|
1543
|
+
if (!familyParser) return Result.fail(usageError(`Unknown command: ${first}.`));
|
|
1159
1544
|
return familyParser(state);
|
|
1160
1545
|
};
|
|
1161
1546
|
const parseCliArgs = (argv) => {
|
|
@@ -1165,23 +1550,14 @@ const parseCliArgs = (argv) => {
|
|
|
1165
1550
|
options,
|
|
1166
1551
|
tokens: argv
|
|
1167
1552
|
});
|
|
1168
|
-
if (
|
|
1169
|
-
return
|
|
1170
|
-
command: result.
|
|
1553
|
+
if (Result.isFailure(result)) return Result.fail(result.failure);
|
|
1554
|
+
return Result.succeed({
|
|
1555
|
+
command: result.success,
|
|
1171
1556
|
options
|
|
1172
1557
|
});
|
|
1173
1558
|
};
|
|
1174
1559
|
//#endregion
|
|
1175
1560
|
//#region src/cli/runtime.ts
|
|
1176
|
-
/**
|
|
1177
|
-
* Resolves the repository root up front so every later git and database path is
|
|
1178
|
-
* anchored to the same directory, even when the user invoked the CLI elsewhere.
|
|
1179
|
-
*
|
|
1180
|
-
* NOTE: This uses execFileSync intentionally — it runs once at CLI startup
|
|
1181
|
-
* before the Effect runtime is constructed, so there is no fiber to block.
|
|
1182
|
-
* Moving this into the Effect runtime would create a circular dependency
|
|
1183
|
-
* (config → git → config).
|
|
1184
|
-
*/
|
|
1185
1561
|
const resolveRepositoryRoot = (repoOverride) => {
|
|
1186
1562
|
const cwd = repoOverride ? resolve(repoOverride) : process.cwd();
|
|
1187
1563
|
try {
|
|
@@ -1198,8 +1574,12 @@ const resolveRepositoryRoot = (repoOverride) => {
|
|
|
1198
1574
|
};
|
|
1199
1575
|
const resolveDbPath = (repoRoot, dbPathOverride) => dbPathOverride ? resolve(dbPathOverride) : resolve(repoRoot, ".ringi/reviews.db");
|
|
1200
1576
|
const commandNeedsRepository = (command) => command.kind !== "help" && command.kind !== "version" && command.kind !== "mcp" && command.kind !== "serve";
|
|
1577
|
+
/**
|
|
1578
|
+
* Commands that need the database to already exist. `review-pr` is NOT here
|
|
1579
|
+
* because it auto-initializes `.ringi/` (like `serve` does).
|
|
1580
|
+
*/
|
|
1201
1581
|
const commandNeedsDatabase = (command) => command.kind === "review-list" || command.kind === "review-show" || command.kind === "review-export" || command.kind === "review-status" || command.kind === "todo-list" || command.kind === "doctor";
|
|
1202
|
-
const commandUsesCoreRuntime = (command) => command.kind === "review-list" || command.kind === "review-show" || command.kind === "review-export" || command.kind === "review-status" || command.kind === "todo-list" || command.kind === "review-create" || command.kind === "todo-add" || command.kind === "doctor";
|
|
1582
|
+
const commandUsesCoreRuntime = (command) => command.kind === "review-list" || command.kind === "review-show" || command.kind === "review-export" || command.kind === "review-pr" || command.kind === "review-status" || command.kind === "todo-list" || command.kind === "review-create" || command.kind === "todo-add" || command.kind === "doctor";
|
|
1203
1583
|
const resolveCliConfig = (args) => {
|
|
1204
1584
|
const repoRootResult = resolveRepositoryRoot(args.repo);
|
|
1205
1585
|
if (repoRootResult instanceof CliFailure) return repoRootResult;
|
|
@@ -1213,23 +1593,18 @@ const resolveCliConfig = (args) => {
|
|
|
1213
1593
|
verbose: args.verbose
|
|
1214
1594
|
};
|
|
1215
1595
|
};
|
|
1216
|
-
/**
|
|
1217
|
-
* Read-only commands still depend on initialized local state because the shared
|
|
1218
|
-
* services use the same database-backed storage as the other clients.
|
|
1219
|
-
*/
|
|
1220
1596
|
const ensureLocalStateAvailable = (config) => {
|
|
1221
1597
|
if (!existsSync(config.dbPath)) return new CliFailure({
|
|
1222
1598
|
exitCode: ExitCode.StateUnavailable,
|
|
1223
1599
|
message: `Local state is missing at ${config.dbPath}. Run 'ringi data migrate' or start 'ringi serve' once to initialize local state.`
|
|
1224
1600
|
});
|
|
1225
1601
|
};
|
|
1226
|
-
const makeConfigLayer = (config) =>
|
|
1602
|
+
const makeConfigLayer = (config) => ConfigProvider.layer(ConfigProvider.fromUnknown({
|
|
1603
|
+
DB_PATH: config.dbPath,
|
|
1604
|
+
REPOSITORY_PATH: config.repoRoot
|
|
1605
|
+
}));
|
|
1227
1606
|
const createCoreCliRuntime = (config) => ManagedRuntime.make(Layer.mergeAll(CoreLive, CliConfigLive(config)).pipe(Layer.provideMerge(makeConfigLayer(config))));
|
|
1228
1607
|
const createGitCliRuntime = (config) => ManagedRuntime.make(Layer.mergeAll(GitService.Default, CliConfigLive(config)).pipe(Layer.provideMerge(makeConfigLayer(config))));
|
|
1229
|
-
/**
|
|
1230
|
-
* Centralizes runtime selection. Returns either valid resources or a CliFailure.
|
|
1231
|
-
* Returns null for help/version commands that don't need a runtime.
|
|
1232
|
-
*/
|
|
1233
1608
|
const createCliRuntimeResources = (command, args) => {
|
|
1234
1609
|
if (!commandNeedsRepository(command)) return null;
|
|
1235
1610
|
const configResult = resolveCliConfig(args);
|
|
@@ -1245,7 +1620,7 @@ const createCliRuntimeResources = (command, args) => {
|
|
|
1245
1620
|
};
|
|
1246
1621
|
//#endregion
|
|
1247
1622
|
//#region src/cli/main.ts
|
|
1248
|
-
const CLI_VERSION = "0.
|
|
1623
|
+
const CLI_VERSION = "0.3.1";
|
|
1249
1624
|
const COMMAND_TREE = {
|
|
1250
1625
|
commands: [
|
|
1251
1626
|
{
|
|
@@ -1611,19 +1986,35 @@ const installSignalHandlers = (dispose) => {
|
|
|
1611
1986
|
};
|
|
1612
1987
|
};
|
|
1613
1988
|
/**
|
|
1614
|
-
* Resolves the built Nitro server entry point.
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1989
|
+
* Resolves the built Nitro server entry point.
|
|
1990
|
+
*
|
|
1991
|
+
* Lookup order (first match wins):
|
|
1992
|
+
* 1. **Packaged location** — `<pkg-root>/server/server/index.mjs`
|
|
1993
|
+
* The published npm package includes `server/` at the package root, which
|
|
1994
|
+
* contains `server/index.mjs` (the Nitro entry) and `public/` (static
|
|
1995
|
+
* assets). From the bundled CLI at `dist/cli.mjs`, the package root is
|
|
1996
|
+
* `import.meta.dirname/..`.
|
|
1997
|
+
* 2. **Monorepo development** — `apps/web/.output/server/index.mjs`
|
|
1998
|
+
* During local development the web build output lives inside the web
|
|
1999
|
+
* workspace. We resolve it relative to the CLI package root.
|
|
2000
|
+
* 3. **CWD fallback** — `.output/server/index.mjs` relative to the current
|
|
2001
|
+
* working directory, for running the server in a standalone checkout.
|
|
1617
2002
|
*/
|
|
1618
2003
|
const resolveServerEntry = () => {
|
|
1619
|
-
const candidates = [
|
|
1620
|
-
if (import.meta.dirname)
|
|
2004
|
+
const candidates = [];
|
|
2005
|
+
if (import.meta.dirname) {
|
|
2006
|
+
const pkgRoot = resolve(import.meta.dirname, "..");
|
|
2007
|
+
candidates.push(resolve(pkgRoot, "server", "server", "index.mjs"));
|
|
2008
|
+
candidates.push(resolve(pkgRoot, "..", "web", ".output", "server", "index.mjs"));
|
|
2009
|
+
}
|
|
2010
|
+
candidates.push(resolve(process.cwd(), ".output", "server", "index.mjs"));
|
|
1621
2011
|
return candidates.find((candidate) => existsSync(candidate));
|
|
1622
2012
|
};
|
|
1623
2013
|
const runServe = (command) => {
|
|
1624
2014
|
const serverEntry = resolveServerEntry();
|
|
1625
2015
|
if (!serverEntry) {
|
|
1626
|
-
|
|
2016
|
+
const hint = import.meta.dirname && !import.meta.dirname.includes("apps/cli/") ? "The installed package is missing its server assets. Try reinstalling: npm install -g @sanurb/ringi" : "Run 'pnpm build' at the monorepo root, then 'pnpm --filter @sanurb/ringi build:server' to copy the server assets.";
|
|
2017
|
+
process.stderr.write(`No built server found.\n${hint}\n`);
|
|
1627
2018
|
process.exit(ExitCode.RuntimeFailure);
|
|
1628
2019
|
}
|
|
1629
2020
|
const env = {
|
|
@@ -1670,13 +2061,13 @@ const failAndExit = (opts) => {
|
|
|
1670
2061
|
const main = async () => {
|
|
1671
2062
|
const argv = process.argv.slice(2);
|
|
1672
2063
|
const parseResult = parseCliArgs(argv);
|
|
1673
|
-
if (
|
|
2064
|
+
if (Result.isFailure(parseResult)) return failAndExit({
|
|
1674
2065
|
cmdStr: "ringi",
|
|
1675
|
-
error: parseResult.
|
|
2066
|
+
error: parseResult.failure,
|
|
1676
2067
|
json: argv.includes("--json"),
|
|
1677
2068
|
verbose: false
|
|
1678
2069
|
});
|
|
1679
|
-
const { command, options } = parseResult.
|
|
2070
|
+
const { command, options } = parseResult.success;
|
|
1680
2071
|
if (command.kind === "help") {
|
|
1681
2072
|
if (options.json) writeJson(success("ringi", COMMAND_TREE, ROOT_NEXT_ACTIONS));
|
|
1682
2073
|
else writeHuman(renderHelp(command));
|