comprehende 0.5.5 → 0.6.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/README.md +1 -1
- package/dist/api/agent-md.js +9 -9
- package/dist/api/live.js +16 -2
- package/dist/api/shiki-langs.js +138 -0
- package/dist/api/snapshot.js +2 -0
- package/dist/cli/args.js +3 -2
- package/dist/cli/commands.js +5 -2
- package/dist/cli/main.js +5 -6
- package/dist/review/pins.js +30 -0
- package/dist/schema/parse.js +203 -19
- package/dist/schema/source.js +89 -0
- package/dist/schema/types.js +8 -0
- package/dist/ui/assets/index-C1EoRByu.css +1 -0
- package/dist/ui/assets/index-DczIQS6v.js +1713 -0
- package/dist/ui/assets/shiki-lang-angular-expression-18AzvmH7.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-html-QylgGsSV.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-inline-style-CLIjy_D1.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-inline-template-DivDh-Md.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-let-declaration-D-jsw5PQ.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-template-D8JUSKlm.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-template-blocks-CGYc0CHO.js +1 -0
- package/dist/ui/assets/shiki-lang-angular-ts-DYfXTSRq.js +1 -0
- package/dist/ui/assets/shiki-lang-apl-D33AMsfu.js +1 -0
- package/dist/ui/assets/shiki-lang-astro-C5iwtRWy.js +1 -0
- package/dist/ui/assets/shiki-lang-blade-BjANHr7J.js +1 -0
- package/dist/ui/assets/shiki-lang-bsl-yjnClznE.js +1 -0
- package/dist/ui/assets/shiki-lang-cairo-D0VTnW3Z.js +1 -0
- package/dist/ui/assets/shiki-lang-chapel-DEeFbMvd.js +1 -0
- package/dist/ui/assets/shiki-lang-cobol-CEHSLdUX.js +1 -0
- package/dist/ui/assets/shiki-lang-coffee-C2SFSLa5.js +1 -0
- package/dist/ui/assets/shiki-lang-cpp-Czz1CvUk.js +1 -0
- package/dist/ui/assets/shiki-lang-cpp-macro-BytbzYqp.js +1 -0
- package/dist/ui/assets/shiki-lang-crystal-CBpenpwC.js +1 -0
- package/dist/ui/assets/shiki-lang-edge-B6n-4du_.js +1 -0
- package/dist/ui/assets/shiki-lang-elixir-B_21P7TP.js +1 -0
- package/dist/ui/assets/shiki-lang-elm-BhwBe0Iz.js +1 -0
- package/dist/ui/assets/shiki-lang-erb-g6AWwbWh.js +1 -0
- package/dist/ui/assets/shiki-lang-erlang-C1-mE0_u.js +1 -0
- package/dist/ui/assets/shiki-lang-es-tag-css-BEhnidaP.js +1 -0
- package/dist/ui/assets/shiki-lang-es-tag-glsl-BPGpRRbJ.js +1 -0
- package/dist/ui/assets/shiki-lang-es-tag-html-BAAjMQoR.js +1 -0
- package/dist/ui/assets/shiki-lang-es-tag-sql--3_Ne07y.js +1 -0
- package/dist/ui/assets/shiki-lang-es-tag-xml-CbEfPPAn.js +1 -0
- package/dist/ui/assets/shiki-lang-fortran-fixed-form-BtBWY50U.js +1 -0
- package/dist/ui/assets/shiki-lang-fsharp-6SDwCc8t.js +1 -0
- package/dist/ui/assets/shiki-lang-gdresource-CPMZsTRs.js +1 -0
- package/dist/ui/assets/shiki-lang-git-commit-DavtHCRV.js +1 -0
- package/dist/ui/assets/shiki-lang-git-rebase-Db7FSZFE.js +1 -0
- package/dist/ui/assets/shiki-lang-glimmer-js-D_dejnIT.js +1 -0
- package/dist/ui/assets/shiki-lang-glimmer-ts-CK9oT1LX.js +1 -0
- package/dist/ui/assets/shiki-lang-glsl-DgYrmVye.js +1 -0
- package/dist/ui/assets/shiki-lang-graphql-B2fodP1i.js +1 -0
- package/dist/ui/assets/shiki-lang-hack-3fAmn-Yd.js +1 -0
- package/dist/ui/assets/shiki-lang-haml-B5ggipOx.js +1 -0
- package/dist/ui/assets/shiki-lang-handlebars-CwuYkNpJ.js +1 -0
- package/dist/ui/assets/shiki-lang-html-D3aYWL0H.js +1 -0
- package/dist/ui/assets/shiki-lang-html-derivative-ByDQxhtI.js +1 -0
- package/dist/ui/assets/shiki-lang-http-wB1irth9.js +1 -0
- package/dist/ui/assets/shiki-lang-hurl-C98mAZkp.js +1 -0
- package/dist/ui/assets/shiki-lang-hxml-O4x38oJr.js +1 -0
- package/dist/ui/assets/shiki-lang-jinja-COi1gYwR.js +1 -0
- package/dist/ui/assets/shiki-lang-jinja-html-DzJAgftF.js +1 -0
- package/dist/ui/assets/shiki-lang-jison-CA27ooGo.js +1 -0
- package/dist/ui/assets/shiki-lang-julia-DWcJqbPK.js +1 -0
- package/dist/ui/assets/shiki-lang-just-DgUC6AYi.js +1 -0
- package/dist/ui/assets/shiki-lang-latex-CmUXxkfy.js +1 -0
- package/dist/ui/assets/shiki-lang-liquid-BIWOSLXI.js +1 -0
- package/dist/ui/assets/shiki-lang-lua-xHKCfFex.js +1 -0
- package/dist/ui/assets/shiki-lang-markdown-nix-BdjWkH0i.js +1 -0
- package/dist/ui/assets/shiki-lang-markdown-vue-B2VTuFQ6.js +1 -0
- package/dist/ui/assets/shiki-lang-marko-CCwpwraH.js +1 -0
- package/dist/ui/assets/shiki-lang-mdc-RXLzxXpy.js +1 -0
- package/dist/ui/assets/shiki-lang-nextflow-D1Hwl2jV.js +1 -0
- package/dist/ui/assets/shiki-lang-nginx-CdnGOWaL.js +1 -0
- package/dist/ui/assets/shiki-lang-nim-OOUtdPk6.js +1 -0
- package/dist/ui/assets/shiki-lang-nix-CB_6xJbh.js +1 -0
- package/dist/ui/assets/shiki-lang-org-B2im9gxM.js +1 -0
- package/dist/ui/assets/shiki-lang-perl-BOHVV1HZ.js +1 -0
- package/dist/ui/assets/shiki-lang-php-DjAfRze_.js +1 -0
- package/dist/ui/assets/shiki-lang-pug-a5xe6YCG.js +1 -0
- package/dist/ui/assets/shiki-lang-qml-4gxL1odC.js +1 -0
- package/dist/ui/assets/shiki-lang-razor-BBzT8ybq.js +1 -0
- package/dist/ui/assets/shiki-lang-rst-Bp6ncCmx.js +1 -0
- package/dist/ui/assets/shiki-lang-ruby-BZIZ_1-x.js +1 -0
- package/dist/ui/assets/shiki-lang-sas-yO3CuBDk.js +1 -0
- package/dist/ui/assets/shiki-lang-scss-m-4-QPZd.js +1 -0
- package/dist/ui/assets/shiki-lang-shaderlab-BoRclLx6.js +1 -0
- package/dist/ui/assets/shiki-lang-shellsession-BhTOBsOq.js +1 -0
- package/dist/ui/assets/shiki-lang-soy-D1j1eck3.js +1 -0
- package/dist/ui/assets/shiki-lang-sparql-D_PtPRod.js +1 -0
- package/dist/ui/assets/shiki-lang-stata-CqWsybMy.js +1 -0
- package/dist/ui/assets/shiki-lang-surrealql-B6Ue5sS6.js +1 -0
- package/dist/ui/assets/shiki-lang-svelte-rGUXZTmH.js +1 -0
- package/dist/ui/assets/shiki-lang-templ-B4iNmN4w.js +1 -0
- package/dist/ui/assets/shiki-lang-tex-DWfsXeKl.js +1 -0
- package/dist/ui/assets/shiki-lang-ts-tags-CLbH6hBz.js +1 -0
- package/dist/ui/assets/shiki-lang-twig-WIwJS9fZ.js +1 -0
- package/dist/ui/assets/shiki-lang-typst-CZjGHRRw.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-VBLKUFTw.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-directives-DB6IPGVS.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-html-BZSTfcAd.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-interpolations-B333VrTX.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-sfc-style-variable-injection-COC9CTDq.js +1 -0
- package/dist/ui/assets/shiki-lang-vue-vine-Dwbf9xGA.js +1 -0
- package/dist/ui/assets/shiki-lang-xml-CahHRakx.js +1 -0
- package/dist/ui/assets/shiki-lang-xsl-C0UDC1a3.js +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/shiki-langs.json +1 -0
- package/package.json +1 -1
- package/skills/comprehende/SKILL.md +34 -14
- package/skills/comprehende/references/example.md +9 -5
- package/skills/comprehende/references/review.schema.json +88 -26
- package/dist/ui/assets/angular-html-DA-rfuFy.js +0 -1
- package/dist/ui/assets/angular-ts-BrjP3tb8.js +0 -1
- package/dist/ui/assets/apl-CORt7UWP.js +0 -1
- package/dist/ui/assets/astro-HNnZUWAn.js +0 -1
- package/dist/ui/assets/blade-2xfisSek.js +0 -1
- package/dist/ui/assets/bsl-DlhNcFeZ.js +0 -1
- package/dist/ui/assets/cairo-KRGpt6FW.js +0 -1
- package/dist/ui/assets/chapel-DTp_pixX.js +0 -1
- package/dist/ui/assets/cobol-nBiQ_Alo.js +0 -1
- package/dist/ui/assets/coffee-Ch7k5sss.js +0 -1
- package/dist/ui/assets/cpp-BMRokrvK.js +0 -1
- package/dist/ui/assets/crystal-DGywbUpC.js +0 -1
- package/dist/ui/assets/edge-FbVlp4U3.js +0 -1
- package/dist/ui/assets/elixir-CkH2-t6x.js +0 -1
- package/dist/ui/assets/elm-DbKCFpqz.js +0 -1
- package/dist/ui/assets/erb-DXfck5VN.js +0 -1
- package/dist/ui/assets/erlang-DsQrWhSR.js +0 -1
- package/dist/ui/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
- package/dist/ui/assets/fsharp-CXgrBDvD.js +0 -1
- package/dist/ui/assets/gdresource-TyuKm33G.js +0 -1
- package/dist/ui/assets/git-commit-F4YmCXRG.js +0 -1
- package/dist/ui/assets/git-rebase-r7XF79zn.js +0 -1
- package/dist/ui/assets/glimmer-js-ByusRIyA.js +0 -1
- package/dist/ui/assets/glimmer-ts-BfAWNZQY.js +0 -1
- package/dist/ui/assets/glsl-DplSGwfg.js +0 -1
- package/dist/ui/assets/graphql-ChdNCCLP.js +0 -1
- package/dist/ui/assets/hack-BWmVpMyf.js +0 -1
- package/dist/ui/assets/haml-D5jkg6IW.js +0 -1
- package/dist/ui/assets/handlebars-BpdQsYii.js +0 -1
- package/dist/ui/assets/html-derivative-DlHx6ybY.js +0 -1
- package/dist/ui/assets/html-pp8916En.js +0 -1
- package/dist/ui/assets/http-jrhK8wxY.js +0 -1
- package/dist/ui/assets/hurl-irOxFIW8.js +0 -1
- package/dist/ui/assets/hxml-2-FPmUDs.js +0 -1
- package/dist/ui/assets/index-CYJX5eTF.css +0 -1
- package/dist/ui/assets/index-L_tbMz50.js +0 -1713
- package/dist/ui/assets/jinja-f2NsQr07.js +0 -1
- package/dist/ui/assets/jison-wvAkD_A8.js +0 -1
- package/dist/ui/assets/julia-5Bft2YPA.js +0 -1
- package/dist/ui/assets/just-Cwhn7H3k.js +0 -1
- package/dist/ui/assets/latex-D5pSuvFb.js +0 -1
- package/dist/ui/assets/liquid-C0sCDyMI.js +0 -1
- package/dist/ui/assets/lua-BaeVxFsk.js +0 -1
- package/dist/ui/assets/marko-DjSrsDqO.js +0 -1
- package/dist/ui/assets/mdc-D1_yUvq7.js +0 -1
- package/dist/ui/assets/nextflow-C-mBbutL.js +0 -1
- package/dist/ui/assets/nginx-BpAMiNFr.js +0 -1
- package/dist/ui/assets/nim-BIad80T-.js +0 -1
- package/dist/ui/assets/nix-CwoSXNpI.js +0 -1
- package/dist/ui/assets/org-DM6o9KBp.js +0 -1
- package/dist/ui/assets/perl-B9cMNwum.js +0 -1
- package/dist/ui/assets/php-Csjmro_R.js +0 -1
- package/dist/ui/assets/pug-DKIMFp6K.js +0 -1
- package/dist/ui/assets/qml-3beO22l8.js +0 -1
- package/dist/ui/assets/razor-BjBPvh-w.js +0 -1
- package/dist/ui/assets/rst-bs7f0vWN.js +0 -1
- package/dist/ui/assets/ruby-C0TQ7zu5.js +0 -1
- package/dist/ui/assets/sas-DEy46yEz.js +0 -1
- package/dist/ui/assets/scss-D5BDwBP9.js +0 -1
- package/dist/ui/assets/shaderlab-Dg9Lc6iA.js +0 -1
- package/dist/ui/assets/shellsession-BADoaaVG.js +0 -1
- package/dist/ui/assets/soy-8wufbnw4.js +0 -1
- package/dist/ui/assets/sparql-rVzFXLq3.js +0 -1
- package/dist/ui/assets/stata-DI20mbqo.js +0 -1
- package/dist/ui/assets/surrealql-Cjom0U5J.js +0 -1
- package/dist/ui/assets/svelte-Cy7k_4gC.js +0 -1
- package/dist/ui/assets/templ-DhtptRzy.js +0 -1
- package/dist/ui/assets/tex-D96PA37w.js +0 -1
- package/dist/ui/assets/ts-tags-D351s5mN.js +0 -1
- package/dist/ui/assets/twig-27uCiNez.js +0 -1
- package/dist/ui/assets/typst-BUadGCkm.js +0 -1
- package/dist/ui/assets/vue-BqiEGhQt.js +0 -1
- package/dist/ui/assets/vue-html-AaS7Mt5G.js +0 -1
- package/dist/ui/assets/vue-vine-BoDAl6tE.js +0 -1
- package/dist/ui/assets/xml-sdJ4AIDG.js +0 -1
- package/dist/ui/assets/xsl-CtQFsRM5.js +0 -1
- /package/dist/ui/assets/{abap-BdImnpbu.js → shiki-lang-abap-BdImnpbu.js} +0 -0
- /package/dist/ui/assets/{actionscript-3-B3316cI-.js → shiki-lang-actionscript-3-B3316cI-.js} +0 -0
- /package/dist/ui/assets/{ada-bCR0ucgS.js → shiki-lang-ada-bCR0ucgS.js} +0 -0
- /package/dist/ui/assets/{ahk-CsyLZFj1.js → shiki-lang-ahk-CsyLZFj1.js} +0 -0
- /package/dist/ui/assets/{ahk2-8Zs4aa1G.js → shiki-lang-ahk2-8Zs4aa1G.js} +0 -0
- /package/dist/ui/assets/{apache-Pmp26Uib.js → shiki-lang-apache-Pmp26Uib.js} +0 -0
- /package/dist/ui/assets/{apex-DhZFqWV2.js → shiki-lang-apex-DhZFqWV2.js} +0 -0
- /package/dist/ui/assets/{applescript-Co6uUVPk.js → shiki-lang-applescript-Co6uUVPk.js} +0 -0
- /package/dist/ui/assets/{ara-BRHolxvo.js → shiki-lang-ara-BRHolxvo.js} +0 -0
- /package/dist/ui/assets/{asciidoc-CSVQ5wI8.js → shiki-lang-asciidoc-CSVQ5wI8.js} +0 -0
- /package/dist/ui/assets/{asm-D_Q5rh1f.js → shiki-lang-asm-D_Q5rh1f.js} +0 -0
- /package/dist/ui/assets/{awk-DMzUqQB5.js → shiki-lang-awk-DMzUqQB5.js} +0 -0
- /package/dist/ui/assets/{ballerina-BFfxhgS-.js → shiki-lang-ballerina-BFfxhgS-.js} +0 -0
- /package/dist/ui/assets/{bat-CickPsom.js → shiki-lang-bat-CickPsom.js} +0 -0
- /package/dist/ui/assets/{beancount-k_qm7-4y.js → shiki-lang-beancount-k_qm7-4y.js} +0 -0
- /package/dist/ui/assets/{berry-uYugtg8r.js → shiki-lang-berry-uYugtg8r.js} +0 -0
- /package/dist/ui/assets/{bibtex-CHM0blh-.js → shiki-lang-bibtex-CHM0blh-.js} +0 -0
- /package/dist/ui/assets/{bicep-Bmn6On1c.js → shiki-lang-bicep-Bmn6On1c.js} +0 -0
- /package/dist/ui/assets/{bird2-Bx8U0n9b.js → shiki-lang-bird2-Bx8U0n9b.js} +0 -0
- /package/dist/ui/assets/{c-BIGW1oBm.js → shiki-lang-c-BIGW1oBm.js} +0 -0
- /package/dist/ui/assets/{c3-Dp5svz6Z.js → shiki-lang-c3-Dp5svz6Z.js} +0 -0
- /package/dist/ui/assets/{cadence-Bv_4Rxtq.js → shiki-lang-cadence-Bv_4Rxtq.js} +0 -0
- /package/dist/ui/assets/{clarity-Dn5IMItf.js → shiki-lang-clarity-Dn5IMItf.js} +0 -0
- /package/dist/ui/assets/{clojure-P80f7IUj.js → shiki-lang-clojure-P80f7IUj.js} +0 -0
- /package/dist/ui/assets/{cmake-D1j8_8rp.js → shiki-lang-cmake-D1j8_8rp.js} +0 -0
- /package/dist/ui/assets/{codeowners-Bp6g37R7.js → shiki-lang-codeowners-Bp6g37R7.js} +0 -0
- /package/dist/ui/assets/{codeql-DsOJ9woJ.js → shiki-lang-codeql-DsOJ9woJ.js} +0 -0
- /package/dist/ui/assets/{common-lisp-Cg-RD9OK.js → shiki-lang-common-lisp-Cg-RD9OK.js} +0 -0
- /package/dist/ui/assets/{coq-C7JzOVbR.js → shiki-lang-coq-C7JzOVbR.js} +0 -0
- /package/dist/ui/assets/{csharp-DSvCPggb.js → shiki-lang-csharp-DSvCPggb.js} +0 -0
- /package/dist/ui/assets/{css-CLj8gQPS.js → shiki-lang-css-CLj8gQPS.js} +0 -0
- /package/dist/ui/assets/{csv-fuZLfV_i.js → shiki-lang-csv-fuZLfV_i.js} +0 -0
- /package/dist/ui/assets/{cue-D82EKSYY.js → shiki-lang-cue-D82EKSYY.js} +0 -0
- /package/dist/ui/assets/{cypher-COkxafJQ.js → shiki-lang-cypher-COkxafJQ.js} +0 -0
- /package/dist/ui/assets/{d-85-TOEBH.js → shiki-lang-d-85-TOEBH.js} +0 -0
- /package/dist/ui/assets/{dart-bE4Kk8sk.js → shiki-lang-dart-bE4Kk8sk.js} +0 -0
- /package/dist/ui/assets/{dax-CEL-wOlO.js → shiki-lang-dax-CEL-wOlO.js} +0 -0
- /package/dist/ui/assets/{desktop-BmXAJ9_W.js → shiki-lang-desktop-BmXAJ9_W.js} +0 -0
- /package/dist/ui/assets/{diff-D97Zzqfu.js → shiki-lang-diff-D97Zzqfu.js} +0 -0
- /package/dist/ui/assets/{docker-BcOcwvcX.js → shiki-lang-docker-BcOcwvcX.js} +0 -0
- /package/dist/ui/assets/{dotenv-Da5cRb03.js → shiki-lang-dotenv-Da5cRb03.js} +0 -0
- /package/dist/ui/assets/{dream-maker-BtqSS_iP.js → shiki-lang-dream-maker-BtqSS_iP.js} +0 -0
- /package/dist/ui/assets/{emacs-lisp-C_m_b--Z.js → shiki-lang-emacs-lisp-C_m_b--Z.js} +0 -0
- /package/dist/ui/assets/{fennel-BYunw83y.js → shiki-lang-fennel-BYunw83y.js} +0 -0
- /package/dist/ui/assets/{fish-BvzEVeQv.js → shiki-lang-fish-BvzEVeQv.js} +0 -0
- /package/dist/ui/assets/{fluent-C4IJs8-o.js → shiki-lang-fluent-C4IJs8-o.js} +0 -0
- /package/dist/ui/assets/{fortran-free-form-BxgE0vQu.js → shiki-lang-fortran-free-form-BxgE0vQu.js} +0 -0
- /package/dist/ui/assets/{gdscript-DqcFQ5yU.js → shiki-lang-gdscript-DqcFQ5yU.js} +0 -0
- /package/dist/ui/assets/{gdshader-DkwncUOv.js → shiki-lang-gdshader-DkwncUOv.js} +0 -0
- /package/dist/ui/assets/{genie-D0YGMca9.js → shiki-lang-genie-D0YGMca9.js} +0 -0
- /package/dist/ui/assets/{gherkin-DyxjwDmM.js → shiki-lang-gherkin-DyxjwDmM.js} +0 -0
- /package/dist/ui/assets/{gleam-BspZqrRM.js → shiki-lang-gleam-BspZqrRM.js} +0 -0
- /package/dist/ui/assets/{gn-n2N0HUVH.js → shiki-lang-gn-n2N0HUVH.js} +0 -0
- /package/dist/ui/assets/{gnuplot-DdkO51Og.js → shiki-lang-gnuplot-DdkO51Og.js} +0 -0
- /package/dist/ui/assets/{go-C27-OAKa.js → shiki-lang-go-C27-OAKa.js} +0 -0
- /package/dist/ui/assets/{groovy-gcz8RCvz.js → shiki-lang-groovy-gcz8RCvz.js} +0 -0
- /package/dist/ui/assets/{haskell-Df6bDoY_.js → shiki-lang-haskell-Df6bDoY_.js} +0 -0
- /package/dist/ui/assets/{haxe-CfZj7gIn.js → shiki-lang-haxe-CfZj7gIn.js} +0 -0
- /package/dist/ui/assets/{hcl-BWvSN4gD.js → shiki-lang-hcl-BWvSN4gD.js} +0 -0
- /package/dist/ui/assets/{hjson-D5-asLiD.js → shiki-lang-hjson-D5-asLiD.js} +0 -0
- /package/dist/ui/assets/{hlsl-D3lLCCz7.js → shiki-lang-hlsl-D3lLCCz7.js} +0 -0
- /package/dist/ui/assets/{hy-DFXneXwc.js → shiki-lang-hy-DFXneXwc.js} +0 -0
- /package/dist/ui/assets/{imba-DGztddWO.js → shiki-lang-imba-DGztddWO.js} +0 -0
- /package/dist/ui/assets/{ini-BEwlwnbL.js → shiki-lang-ini-BEwlwnbL.js} +0 -0
- /package/dist/ui/assets/{java-CylS5w8V.js → shiki-lang-java-CylS5w8V.js} +0 -0
- /package/dist/ui/assets/{javascript-wDzz0qaB.js → shiki-lang-javascript-wDzz0qaB.js} +0 -0
- /package/dist/ui/assets/{json-Cp-IABpG.js → shiki-lang-json-Cp-IABpG.js} +0 -0
- /package/dist/ui/assets/{json5-C9tS-k6U.js → shiki-lang-json5-C9tS-k6U.js} +0 -0
- /package/dist/ui/assets/{jsonc-Des-eS-w.js → shiki-lang-jsonc-Des-eS-w.js} +0 -0
- /package/dist/ui/assets/{jsonl-DcaNXYhu.js → shiki-lang-jsonl-DcaNXYhu.js} +0 -0
- /package/dist/ui/assets/{jsonnet-DFQXde-d.js → shiki-lang-jsonnet-DFQXde-d.js} +0 -0
- /package/dist/ui/assets/{jssm-C2t-YnRu.js → shiki-lang-jssm-C2t-YnRu.js} +0 -0
- /package/dist/ui/assets/{jsx-g9-lgVsj.js → shiki-lang-jsx-g9-lgVsj.js} +0 -0
- /package/dist/ui/assets/{kdl-DV7GczEv.js → shiki-lang-kdl-DV7GczEv.js} +0 -0
- /package/dist/ui/assets/{kotlin-BdnUsdx6.js → shiki-lang-kotlin-BdnUsdx6.js} +0 -0
- /package/dist/ui/assets/{kusto-wEQ09or8.js → shiki-lang-kusto-wEQ09or8.js} +0 -0
- /package/dist/ui/assets/{lean-BZvkOJ9d.js → shiki-lang-lean-BZvkOJ9d.js} +0 -0
- /package/dist/ui/assets/{less-B1dDrJ26.js → shiki-lang-less-B1dDrJ26.js} +0 -0
- /package/dist/ui/assets/{llvm-BZoOZj88.js → shiki-lang-llvm-BZoOZj88.js} +0 -0
- /package/dist/ui/assets/{log-2UxHyX5q.js → shiki-lang-log-2UxHyX5q.js} +0 -0
- /package/dist/ui/assets/{logo-BtOb2qkB.js → shiki-lang-logo-BtOb2qkB.js} +0 -0
- /package/dist/ui/assets/{luau-BnpPk5vE.js → shiki-lang-luau-BnpPk5vE.js} +0 -0
- /package/dist/ui/assets/{make-CHLpvVh8.js → shiki-lang-make-CHLpvVh8.js} +0 -0
- /package/dist/ui/assets/{markdown-Cvjx9yec.js → shiki-lang-markdown-Cvjx9yec.js} +0 -0
- /package/dist/ui/assets/{matlab-D7o27uSR.js → shiki-lang-matlab-D7o27uSR.js} +0 -0
- /package/dist/ui/assets/{mdx-Cmh6b_Ma.js → shiki-lang-mdx-Cmh6b_Ma.js} +0 -0
- /package/dist/ui/assets/{mermaid-CQcHuHx7.js → shiki-lang-mermaid-CQcHuHx7.js} +0 -0
- /package/dist/ui/assets/{mipsasm-CKIfxQSi.js → shiki-lang-mipsasm-CKIfxQSi.js} +0 -0
- /package/dist/ui/assets/{mojo-DJz3ZmWd.js → shiki-lang-mojo-DJz3ZmWd.js} +0 -0
- /package/dist/ui/assets/{moonbit-CHtswR0a.js → shiki-lang-moonbit-CHtswR0a.js} +0 -0
- /package/dist/ui/assets/{move-el3G9tDJ.js → shiki-lang-move-el3G9tDJ.js} +0 -0
- /package/dist/ui/assets/{narrat-DRg8JJMk.js → shiki-lang-narrat-DRg8JJMk.js} +0 -0
- /package/dist/ui/assets/{nextflow-groovy-vE_lwT2v.js → shiki-lang-nextflow-groovy-vE_lwT2v.js} +0 -0
- /package/dist/ui/assets/{nsis-BlV79W_Q.js → shiki-lang-nsis-BlV79W_Q.js} +0 -0
- /package/dist/ui/assets/{nushell-D3jzshHO.js → shiki-lang-nushell-D3jzshHO.js} +0 -0
- /package/dist/ui/assets/{objective-c-DXmwc3jG.js → shiki-lang-objective-c-DXmwc3jG.js} +0 -0
- /package/dist/ui/assets/{objective-cpp-CLxacb5B.js → shiki-lang-objective-cpp-CLxacb5B.js} +0 -0
- /package/dist/ui/assets/{ocaml-C0hk2d4L.js → shiki-lang-ocaml-C0hk2d4L.js} +0 -0
- /package/dist/ui/assets/{odin-BBf5iR-q.js → shiki-lang-odin-BBf5iR-q.js} +0 -0
- /package/dist/ui/assets/{openscad-C4EeE6gA.js → shiki-lang-openscad-C4EeE6gA.js} +0 -0
- /package/dist/ui/assets/{pascal-D93ZcfNL.js → shiki-lang-pascal-D93ZcfNL.js} +0 -0
- /package/dist/ui/assets/{pkl-u5AG7uiY.js → shiki-lang-pkl-u5AG7uiY.js} +0 -0
- /package/dist/ui/assets/{plsql-ChMvpjG-.js → shiki-lang-plsql-ChMvpjG-.js} +0 -0
- /package/dist/ui/assets/{po-BTJTHyun.js → shiki-lang-po-BTJTHyun.js} +0 -0
- /package/dist/ui/assets/{polar-C0HS_06l.js → shiki-lang-polar-C0HS_06l.js} +0 -0
- /package/dist/ui/assets/{postcss-CXtECtnM.js → shiki-lang-postcss-CXtECtnM.js} +0 -0
- /package/dist/ui/assets/{powerquery-CEu0bR-o.js → shiki-lang-powerquery-CEu0bR-o.js} +0 -0
- /package/dist/ui/assets/{powershell-BmBUJMz7.js → shiki-lang-powershell-BmBUJMz7.js} +0 -0
- /package/dist/ui/assets/{prisma-Vru482bI.js → shiki-lang-prisma-Vru482bI.js} +0 -0
- /package/dist/ui/assets/{prolog-CbFg5uaA.js → shiki-lang-prolog-CbFg5uaA.js} +0 -0
- /package/dist/ui/assets/{proto-C7zT0LnQ.js → shiki-lang-proto-C7zT0LnQ.js} +0 -0
- /package/dist/ui/assets/{puppet-BMWR74SV.js → shiki-lang-puppet-BMWR74SV.js} +0 -0
- /package/dist/ui/assets/{purescript-CklMAg4u.js → shiki-lang-purescript-CklMAg4u.js} +0 -0
- /package/dist/ui/assets/{python-B6aJPvgy.js → shiki-lang-python-B6aJPvgy.js} +0 -0
- /package/dist/ui/assets/{qmldir-C8lEn-DE.js → shiki-lang-qmldir-C8lEn-DE.js} +0 -0
- /package/dist/ui/assets/{qss-IeuSbFQv.js → shiki-lang-qss-IeuSbFQv.js} +0 -0
- /package/dist/ui/assets/{r-Cf5RLm7j.js → shiki-lang-r-Cf5RLm7j.js} +0 -0
- /package/dist/ui/assets/{racket-BqYA7rlc.js → shiki-lang-racket-BqYA7rlc.js} +0 -0
- /package/dist/ui/assets/{raku-DXvB9xmW.js → shiki-lang-raku-DXvB9xmW.js} +0 -0
- /package/dist/ui/assets/{rbs-CpoqiR4B.js → shiki-lang-rbs-CpoqiR4B.js} +0 -0
- /package/dist/ui/assets/{reg-C-SQnVFl.js → shiki-lang-reg-C-SQnVFl.js} +0 -0
- /package/dist/ui/assets/{regexp-CDVJQ6XC.js → shiki-lang-regexp-CDVJQ6XC.js} +0 -0
- /package/dist/ui/assets/{rel-C3B-1QV4.js → shiki-lang-rel-C3B-1QV4.js} +0 -0
- /package/dist/ui/assets/{riscv-BM1_JUlF.js → shiki-lang-riscv-BM1_JUlF.js} +0 -0
- /package/dist/ui/assets/{ron-D8l8udqQ.js → shiki-lang-ron-D8l8udqQ.js} +0 -0
- /package/dist/ui/assets/{rosmsg-BJDFO7_C.js → shiki-lang-rosmsg-BJDFO7_C.js} +0 -0
- /package/dist/ui/assets/{rust-B1yitclQ.js → shiki-lang-rust-B1yitclQ.js} +0 -0
- /package/dist/ui/assets/{sass-Cj5Yp3dK.js → shiki-lang-sass-Cj5Yp3dK.js} +0 -0
- /package/dist/ui/assets/{scala-CqE71os6.js → shiki-lang-scala-CqE71os6.js} +0 -0
- /package/dist/ui/assets/{scheme-C98Dy4si.js → shiki-lang-scheme-C98Dy4si.js} +0 -0
- /package/dist/ui/assets/{sdbl-DVxCFoDh.js → shiki-lang-sdbl-DVxCFoDh.js} +0 -0
- /package/dist/ui/assets/{shellscript-Yzrsuije.js → shiki-lang-shellscript-Yzrsuije.js} +0 -0
- /package/dist/ui/assets/{smalltalk-BOQMe2GC.js → shiki-lang-smalltalk-BOQMe2GC.js} +0 -0
- /package/dist/ui/assets/{smithy-cds9vsN8.js → shiki-lang-smithy-cds9vsN8.js} +0 -0
- /package/dist/ui/assets/{solidity-DijEV5ha.js → shiki-lang-solidity-DijEV5ha.js} +0 -0
- /package/dist/ui/assets/{splunk-BtCnVYZw.js → shiki-lang-splunk-BtCnVYZw.js} +0 -0
- /package/dist/ui/assets/{sql-CRqJ_cUM.js → shiki-lang-sql-CRqJ_cUM.js} +0 -0
- /package/dist/ui/assets/{ssh-config-_ykCGR6B.js → shiki-lang-ssh-config-_ykCGR6B.js} +0 -0
- /package/dist/ui/assets/{stylus-BEDo0Tqx.js → shiki-lang-stylus-BEDo0Tqx.js} +0 -0
- /package/dist/ui/assets/{swift-C2oV4EkX.js → shiki-lang-swift-C2oV4EkX.js} +0 -0
- /package/dist/ui/assets/{system-verilog-0hqHdDBg.js → shiki-lang-system-verilog-0hqHdDBg.js} +0 -0
- /package/dist/ui/assets/{systemd-4A_iFExJ.js → shiki-lang-systemd-4A_iFExJ.js} +0 -0
- /package/dist/ui/assets/{talonscript-CkByrt1z.js → shiki-lang-talonscript-CkByrt1z.js} +0 -0
- /package/dist/ui/assets/{tasl-QIJgUcNo.js → shiki-lang-tasl-QIJgUcNo.js} +0 -0
- /package/dist/ui/assets/{tcl-dwOrl1Do.js → shiki-lang-tcl-dwOrl1Do.js} +0 -0
- /package/dist/ui/assets/{terraform-BETggiCN.js → shiki-lang-terraform-BETggiCN.js} +0 -0
- /package/dist/ui/assets/{toml-vGWfd6FD.js → shiki-lang-toml-vGWfd6FD.js} +0 -0
- /package/dist/ui/assets/{tsv-B_m7g4N7.js → shiki-lang-tsv-B_m7g4N7.js} +0 -0
- /package/dist/ui/assets/{tsx-COt5Ahok.js → shiki-lang-tsx-COt5Ahok.js} +0 -0
- /package/dist/ui/assets/{turtle-BsS91CYL.js → shiki-lang-turtle-BsS91CYL.js} +0 -0
- /package/dist/ui/assets/{typescript-BPQ3VLAy.js → shiki-lang-typescript-BPQ3VLAy.js} +0 -0
- /package/dist/ui/assets/{typespec-CAFt9gP4.js → shiki-lang-typespec-CAFt9gP4.js} +0 -0
- /package/dist/ui/assets/{v-BGw2Nkan.js → shiki-lang-v-BGw2Nkan.js} +0 -0
- /package/dist/ui/assets/{vala-CsfeWuGM.js → shiki-lang-vala-CsfeWuGM.js} +0 -0
- /package/dist/ui/assets/{vb-Cu-pLBUe.js → shiki-lang-vb-Cu-pLBUe.js} +0 -0
- /package/dist/ui/assets/{verilog-nZwndyjY.js → shiki-lang-verilog-nZwndyjY.js} +0 -0
- /package/dist/ui/assets/{vhdl-CeAyd5Ju.js → shiki-lang-vhdl-CeAyd5Ju.js} +0 -0
- /package/dist/ui/assets/{viml-CJc9bBzg.js → shiki-lang-viml-CJc9bBzg.js} +0 -0
- /package/dist/ui/assets/{vyper-CDx5xZoG.js → shiki-lang-vyper-CDx5xZoG.js} +0 -0
- /package/dist/ui/assets/{wasm-MzD3tlZU.js → shiki-lang-wasm-MzD3tlZU.js} +0 -0
- /package/dist/ui/assets/{wenyan-BV7otONQ.js → shiki-lang-wenyan-BV7otONQ.js} +0 -0
- /package/dist/ui/assets/{wgsl-Dx-B1_4e.js → shiki-lang-wgsl-Dx-B1_4e.js} +0 -0
- /package/dist/ui/assets/{wikitext-BhOHFoWU.js → shiki-lang-wikitext-BhOHFoWU.js} +0 -0
- /package/dist/ui/assets/{wit-5i3qLPDT.js → shiki-lang-wit-5i3qLPDT.js} +0 -0
- /package/dist/ui/assets/{wolfram-lXgVvXCa.js → shiki-lang-wolfram-lXgVvXCa.js} +0 -0
- /package/dist/ui/assets/{yaml-Buea-lGh.js → shiki-lang-yaml-Buea-lGh.js} +0 -0
- /package/dist/ui/assets/{zenscript-DVFEvuxE.js → shiki-lang-zenscript-DVFEvuxE.js} +0 -0
- /package/dist/ui/assets/{zig-VOosw3JB.js → shiki-lang-zig-VOosw3JB.js} +0 -0
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ This tool is for preventing this cognitive surrender while trying to maintain mo
|
|
|
42
42
|
|
|
43
43
|
`comprehende serve` and `comprehende export` share one UI and one git payload. Serve resolves refs to commit SHAs when it starts, then computes those payloads from the objects on each request. Export writes the same JSON (and image bytes) next to the UI so any static file server can host the review.
|
|
44
44
|
|
|
45
|
-
`pnpm dev` and `pnpm exec` run with this package as cwd, so they only make sense when _this_ repo is the one under review. To review a different project from a checkout, `cd` into it and run `npx comprehende@0.
|
|
45
|
+
`pnpm dev` and `pnpm exec` run with this package as cwd, so they only make sense when _this_ repo is the one under review. To review a different project from a checkout, `cd` into it and run `npx comprehende@0.6.1` (or `node /path/to/comprehende/dist/cli/main.js` after `pnpm build`).
|
|
46
46
|
|
|
47
47
|
## Release
|
|
48
48
|
|
package/dist/api/agent-md.js
CHANGED
|
@@ -23,7 +23,7 @@ function overviewAgentMd(review) {
|
|
|
23
23
|
"Answer questions about this git change.",
|
|
24
24
|
overviewSteps(review),
|
|
25
25
|
pinBlock(review, { commits: true }),
|
|
26
|
-
|
|
26
|
+
sourcesBlock(review),
|
|
27
27
|
coverageBlock(review),
|
|
28
28
|
joinBlocks(["The title:", review.document.title]),
|
|
29
29
|
review.document.why !== undefined ? joinBlocks(["The why:", review.document.why]) : null,
|
|
@@ -79,17 +79,17 @@ function pinBlock(review, options) {
|
|
|
79
79
|
commits,
|
|
80
80
|
]);
|
|
81
81
|
}
|
|
82
|
-
function
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
82
|
+
function sourcesBlock(review) {
|
|
83
|
+
const sources = review.document.sources ?? [];
|
|
84
|
+
if (sources.length === 0) {
|
|
85
85
|
return null;
|
|
86
86
|
}
|
|
87
|
-
const lines =
|
|
88
|
-
const
|
|
89
|
-
const url =
|
|
90
|
-
return `- ${
|
|
87
|
+
const lines = sources.map((source) => {
|
|
88
|
+
const gist = source.gist !== undefined ? ` ${source.gist}` : "";
|
|
89
|
+
const url = source.url !== undefined ? `\n ${source.url}` : "";
|
|
90
|
+
return `- ${source.kind} ${source.label}${gist}${url}`;
|
|
91
91
|
});
|
|
92
|
-
return ["
|
|
92
|
+
return ["Sources:", ...lines].join("\n");
|
|
93
93
|
}
|
|
94
94
|
function coverageBlock(review) {
|
|
95
95
|
const lines = [];
|
package/dist/api/live.js
CHANGED
|
@@ -6,8 +6,10 @@ import { GitError } from "../git/exec.js";
|
|
|
6
6
|
import { listCommits } from "../git/log.js";
|
|
7
7
|
import { pinRange, readRepoIdentity } from "../git/repo.js";
|
|
8
8
|
import { showFile } from "../git/show.js";
|
|
9
|
-
import { coverReview } from "../review/coverage.js";
|
|
9
|
+
import { coverReview, coverageErrors } from "../review/coverage.js";
|
|
10
|
+
import { commentPinErrors, staleCommentPins } from "../review/pins.js";
|
|
10
11
|
import { isLockfilePath } from "../schema/lockfile.js";
|
|
12
|
+
import { sourceCitationErrors } from "../schema/source.js";
|
|
11
13
|
import { AGENT_MD_MEDIA_TYPE, agentMd } from "./agent-md.js";
|
|
12
14
|
import { ApiError } from "./error.js";
|
|
13
15
|
export async function pinReviewSource(cwd, dataPath) {
|
|
@@ -18,6 +20,7 @@ export async function openReview(cwd, dataPath, pin) {
|
|
|
18
20
|
const document = await loadDocument(dataPath);
|
|
19
21
|
const range = pin ?? (await pinRange(cwd, document.source.baseRef, document.source.headRef));
|
|
20
22
|
const { files, coverage } = await coverReview(cwd, document, range);
|
|
23
|
+
const pins = await staleCommentPins(cwd, document, range);
|
|
21
24
|
const commits = await listCommits(cwd, range.baseSha, range.headSha);
|
|
22
25
|
const repo = await readRepoIdentity(cwd);
|
|
23
26
|
return {
|
|
@@ -33,12 +36,13 @@ export async function openReview(cwd, dataPath, pin) {
|
|
|
33
36
|
},
|
|
34
37
|
files,
|
|
35
38
|
coverage,
|
|
39
|
+
staleCommentPins: pins,
|
|
36
40
|
mergeBaseSha: range.mergeBaseSha,
|
|
37
41
|
commits,
|
|
38
42
|
};
|
|
39
43
|
}
|
|
40
44
|
export function reviewPayload(ctx) {
|
|
41
|
-
const { document, repo, resolved, files, coverage, commits } = ctx;
|
|
45
|
+
const { document, repo, resolved, files, coverage, staleCommentPins, commits } = ctx;
|
|
42
46
|
const lockfiles = lockfileFiles(files);
|
|
43
47
|
return {
|
|
44
48
|
document,
|
|
@@ -49,6 +53,7 @@ export function reviewPayload(ctx) {
|
|
|
49
53
|
assignedHunks: coverage.assignedHunks,
|
|
50
54
|
unassignedCount: coverage.unassigned.length,
|
|
51
55
|
staleCount: coverage.stale.length,
|
|
56
|
+
staleSourceCount: staleCommentPins.length,
|
|
52
57
|
},
|
|
53
58
|
groups: coverage.groups
|
|
54
59
|
.slice()
|
|
@@ -61,6 +66,7 @@ export function reviewPayload(ctx) {
|
|
|
61
66
|
lookFor: group.group.lookFor ?? [],
|
|
62
67
|
dependsOn: group.group.dependsOn ?? [],
|
|
63
68
|
part: group.group.part,
|
|
69
|
+
sources: group.group.sources ?? [],
|
|
64
70
|
suggestedOrder: group.group.suggestedOrder,
|
|
65
71
|
hunkCount: group.hunks.length,
|
|
66
72
|
staleCount: group.stale.length,
|
|
@@ -75,6 +81,7 @@ export function reviewPayload(ctx) {
|
|
|
75
81
|
files: lockfiles.map((file) => file.path),
|
|
76
82
|
},
|
|
77
83
|
stale: coverage.stale,
|
|
84
|
+
staleSources: staleCommentPins,
|
|
78
85
|
files: files.map((file) => {
|
|
79
86
|
const entry = {
|
|
80
87
|
path: file.path,
|
|
@@ -92,6 +99,13 @@ export function reviewPayload(ctx) {
|
|
|
92
99
|
commits,
|
|
93
100
|
};
|
|
94
101
|
}
|
|
102
|
+
export function reviewProblems(ctx) {
|
|
103
|
+
return [
|
|
104
|
+
...coverageErrors(ctx.coverage),
|
|
105
|
+
...sourceCitationErrors(ctx.document),
|
|
106
|
+
...commentPinErrors(ctx.staleCommentPins),
|
|
107
|
+
];
|
|
108
|
+
}
|
|
95
109
|
export function hunksPayload(ctx, groupId) {
|
|
96
110
|
if (groupId === "") {
|
|
97
111
|
throw new ApiError(400, "missing group");
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const SHIKI_LANGS_FILE = "shiki-langs.json";
|
|
4
|
+
const LANG_MODULE = /[/\\]@shikijs[/\\]langs[/\\]dist[/\\]([^/\\]+)\.m?js(?:\?|$)/;
|
|
5
|
+
const SKIP_LANGS = new Set(["text", "ansi"]);
|
|
6
|
+
export function shikiLangId(id) {
|
|
7
|
+
return id.match(LANG_MODULE)?.[1];
|
|
8
|
+
}
|
|
9
|
+
export function parseShikiLangsManifest(input) {
|
|
10
|
+
if (!isRecord(input) || input.version !== 1) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const chunks = stringList(input.chunks);
|
|
14
|
+
const files = stringListMap(input.files);
|
|
15
|
+
const lookup = stringMap(input.lookup);
|
|
16
|
+
if (chunks === undefined || files === undefined || lookup === undefined) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return { version: 1, chunks, files, lookup };
|
|
20
|
+
}
|
|
21
|
+
export function highlightLangFromPath(path, lookup) {
|
|
22
|
+
const fileName = path.split(/[/\\]/).pop() ?? path;
|
|
23
|
+
if (lookup[fileName] !== undefined) {
|
|
24
|
+
return lookup[fileName];
|
|
25
|
+
}
|
|
26
|
+
const compound = fileName.match(/\.([^/\\]+\.[^/\\]+)$/)?.[1];
|
|
27
|
+
if (compound !== undefined && lookup[compound] !== undefined) {
|
|
28
|
+
return lookup[compound];
|
|
29
|
+
}
|
|
30
|
+
const simple = fileName.match(/\.([^.]+)$/)?.[1] ?? "";
|
|
31
|
+
return lookup[simple];
|
|
32
|
+
}
|
|
33
|
+
export function highlightLangsForPaths(paths, lookup) {
|
|
34
|
+
const langs = new Set();
|
|
35
|
+
for (const path of paths) {
|
|
36
|
+
const lang = highlightLangFromPath(path, lookup);
|
|
37
|
+
if (lang !== undefined && !SKIP_LANGS.has(lang)) {
|
|
38
|
+
langs.add(lang);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return [...langs];
|
|
42
|
+
}
|
|
43
|
+
export function highlightPathsFromFiles(files) {
|
|
44
|
+
const paths = [];
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
paths.push(file.path);
|
|
47
|
+
if (file.oldPath !== undefined) {
|
|
48
|
+
paths.push(file.oldPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return paths;
|
|
52
|
+
}
|
|
53
|
+
export function chunksToKeep(manifest, langs) {
|
|
54
|
+
const keep = new Set();
|
|
55
|
+
for (const lang of langs) {
|
|
56
|
+
const files = manifest.files[lang];
|
|
57
|
+
if (files === undefined) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
keep.add(file);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [...keep];
|
|
65
|
+
}
|
|
66
|
+
export async function pruneUnusedHighlighterChunks(outDir, paths) {
|
|
67
|
+
const manifest = await loadManifest(join(outDir, SHIKI_LANGS_FILE));
|
|
68
|
+
if (manifest === undefined) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
const langs = highlightLangsForPaths(paths, manifest.lookup);
|
|
72
|
+
const keep = chunksToKeep(manifest, langs);
|
|
73
|
+
if (keep === undefined) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const keepSet = new Set(keep);
|
|
77
|
+
const removed = [];
|
|
78
|
+
for (const file of manifest.chunks) {
|
|
79
|
+
if (keepSet.has(file)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
await rm(join(outDir, file), { force: true });
|
|
83
|
+
await rm(join(outDir, `${file}.map`), { force: true });
|
|
84
|
+
removed.push(file);
|
|
85
|
+
}
|
|
86
|
+
return removed;
|
|
87
|
+
}
|
|
88
|
+
async function loadManifest(path) {
|
|
89
|
+
let text;
|
|
90
|
+
try {
|
|
91
|
+
text = await readFile(path, "utf8");
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return parseShikiLangsManifest(JSON.parse(text));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function isRecord(value) {
|
|
104
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
105
|
+
}
|
|
106
|
+
function stringList(value) {
|
|
107
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
function stringMap(value) {
|
|
113
|
+
if (!isRecord(value)) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const out = {};
|
|
117
|
+
for (const [key, item] of Object.entries(value)) {
|
|
118
|
+
if (typeof item !== "string") {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
out[key] = item;
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function stringListMap(value) {
|
|
126
|
+
if (!isRecord(value)) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const out = {};
|
|
130
|
+
for (const [key, item] of Object.entries(value)) {
|
|
131
|
+
const list = stringList(item);
|
|
132
|
+
if (list === undefined) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
out[key] = list;
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
package/dist/api/snapshot.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
4
4
|
import { findPackageRoot } from "../package-root.js";
|
|
5
5
|
import { isUnavailableSnapshot, listResources, openReview, renderResource, snapshotJson, } from "./live.js";
|
|
6
6
|
import { apiFsRel } from "./paths.js";
|
|
7
|
+
import { highlightPathsFromFiles, pruneUnusedHighlighterChunks } from "./shiki-langs.js";
|
|
7
8
|
export async function exportStaticSite(opts) {
|
|
8
9
|
const outDir = resolve(opts.outDir);
|
|
9
10
|
const uiRoot = opts.uiRoot ?? join(findPackageRoot(), "dist/ui");
|
|
@@ -41,5 +42,6 @@ export async function exportStaticSite(opts) {
|
|
|
41
42
|
}
|
|
42
43
|
apiFiles.push(rel);
|
|
43
44
|
}
|
|
45
|
+
await pruneUnusedHighlighterChunks(outDir, highlightPathsFromFiles(ctx.files));
|
|
44
46
|
return { outDir, apiFiles };
|
|
45
47
|
}
|
package/dist/cli/args.js
CHANGED
|
@@ -8,13 +8,14 @@ Commands:
|
|
|
8
8
|
List hunk refs from live git (no patch text)
|
|
9
9
|
|
|
10
10
|
validate --data <review.json>
|
|
11
|
-
Check schema, ref resolution, and
|
|
11
|
+
Check schema, ref resolution, hunk coverage, and source citations
|
|
12
12
|
|
|
13
13
|
serve --data <review.json> [--port <n>] [--open]
|
|
14
14
|
Serve the local UI on 127.0.0.1 (pins commit SHAs at start)
|
|
15
15
|
|
|
16
16
|
export --data <review.json> --out <dir>
|
|
17
|
-
Write a static site (same UI + frozen git payloads).
|
|
17
|
+
Write a static site (same UI + frozen git payloads). Highlighters only
|
|
18
|
+
for languages in the review. No server after that.
|
|
18
19
|
|
|
19
20
|
Options:
|
|
20
21
|
--base <ref> Base ref (default: origin/HEAD or main/master)
|
package/dist/cli/commands.js
CHANGED
|
@@ -3,7 +3,9 @@ import { isAbsolute, resolve } from "node:path";
|
|
|
3
3
|
import { readHunkIndex, resolveSource } from "../git/diff.js";
|
|
4
4
|
import { defaultBaseRef } from "../git/repo.js";
|
|
5
5
|
import { coverReview, coverageErrors } from "../review/coverage.js";
|
|
6
|
+
import { commentPinErrors, staleCommentPins } from "../review/pins.js";
|
|
6
7
|
import { parseReviewJson } from "../schema/parse.js";
|
|
8
|
+
import { sourceCitationErrors } from "../schema/source.js";
|
|
7
9
|
export async function resolveRange(cwd, base, head) {
|
|
8
10
|
const baseRef = base ?? (await defaultBaseRef(cwd));
|
|
9
11
|
const headRef = head ?? "HEAD";
|
|
@@ -36,9 +38,10 @@ export function resolveOutPath(out, cwd) {
|
|
|
36
38
|
}
|
|
37
39
|
export async function cmdValidate(cwd, dataPath) {
|
|
38
40
|
const document = await loadDocument(dataPath);
|
|
39
|
-
await resolveSource(cwd, document.source.baseRef, document.source.headRef);
|
|
41
|
+
const resolved = await resolveSource(cwd, document.source.baseRef, document.source.headRef);
|
|
40
42
|
const { coverage } = await coverReview(cwd, document);
|
|
41
|
-
const
|
|
43
|
+
const pins = await staleCommentPins(cwd, document, resolved);
|
|
44
|
+
const errors = [...coverageErrors(coverage), ...sourceCitationErrors(document), ...commentPinErrors(pins)];
|
|
42
45
|
if (errors.length > 0) {
|
|
43
46
|
throw new Error(errors.join("\n\n"));
|
|
44
47
|
}
|
package/dist/cli/main.js
CHANGED
|
@@ -6,8 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { parseArgv, USAGE } from "./args.js";
|
|
7
7
|
import { cmdIndex, cmdValidate, resolveDataPath, resolveOutPath } from "./commands.js";
|
|
8
8
|
import { exportStaticSite } from "../api/snapshot.js";
|
|
9
|
-
import { openReview, pinReviewSource } from "../api/live.js";
|
|
10
|
-
import { coverageErrors } from "../review/coverage.js";
|
|
9
|
+
import { openReview, pinReviewSource, reviewProblems } from "../api/live.js";
|
|
11
10
|
import { assertWorkTree } from "../git/repo.js";
|
|
12
11
|
import { readPackageVersion } from "../package-root.js";
|
|
13
12
|
import { startServer } from "../server/http.js";
|
|
@@ -44,7 +43,7 @@ export async function run(argv) {
|
|
|
44
43
|
const dataPath = resolveDataPath(request.data, request.cwd);
|
|
45
44
|
const pin = await pinReviewSource(request.cwd, dataPath);
|
|
46
45
|
const ctx = await openReview(request.cwd, dataPath, pin);
|
|
47
|
-
|
|
46
|
+
warnReview(ctx, "serve continues; git wins, unassigned/stale are visible");
|
|
48
47
|
const running = await startServer({ cwd: request.cwd, dataPath, port: request.port, pin });
|
|
49
48
|
console.log(running.url);
|
|
50
49
|
console.error(`serving ${dataPath} cwd=${request.cwd} localhost only`);
|
|
@@ -58,7 +57,7 @@ export async function run(argv) {
|
|
|
58
57
|
const dataPath = resolveDataPath(request.data, request.cwd);
|
|
59
58
|
const outDir = resolveOutPath(request.out, request.cwd);
|
|
60
59
|
const ctx = await openReview(request.cwd, dataPath);
|
|
61
|
-
|
|
60
|
+
warnReview(ctx, "export continues; git wins, unassigned/stale are visible");
|
|
62
61
|
const result = await exportStaticSite({ cwd: request.cwd, dataPath, outDir, ctx });
|
|
63
62
|
console.log(result.outDir);
|
|
64
63
|
console.error(`exported ${dataPath} ${result.apiFiles.length} api files no git in the folder`);
|
|
@@ -71,8 +70,8 @@ export async function run(argv) {
|
|
|
71
70
|
return 1;
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
|
-
function
|
|
75
|
-
const problems =
|
|
73
|
+
function warnReview(ctx, note) {
|
|
74
|
+
const problems = reviewProblems(ctx);
|
|
76
75
|
if (problems.length > 0) {
|
|
77
76
|
console.error(`coverage issues (${note}):\n${problems.join("\n\n")}`);
|
|
78
77
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fileExistsAt, showFile } from "../git/show.js";
|
|
2
|
+
import { isLinePinned, textLineCount } from "../schema/source.js";
|
|
3
|
+
export async function staleCommentPins(cwd, document, range) {
|
|
4
|
+
const stale = [];
|
|
5
|
+
for (const source of document.sources ?? []) {
|
|
6
|
+
if (!isLinePinned(source)) {
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
if (!(await commentPinMatches(cwd, source, range))) {
|
|
10
|
+
stale.push({ id: source.id, path: source.path, side: source.side, line: source.line });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return stale;
|
|
14
|
+
}
|
|
15
|
+
export function commentPinErrors(pins) {
|
|
16
|
+
if (pins.length === 0) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const lines = pins.map((pin) => ` ${pin.id} ${pin.path} ${pin.side}:${pin.line}`);
|
|
20
|
+
return [`stale: ${pins.length} comment pin(s) do not match live git:\n${lines.join("\n")}`];
|
|
21
|
+
}
|
|
22
|
+
async function commentPinMatches(cwd, source, range) {
|
|
23
|
+
const sha = source.side === "old" ? range.baseSha : range.headSha;
|
|
24
|
+
if (!(await fileExistsAt(cwd, sha, source.path))) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const content = await showFile(cwd, sha, source.path);
|
|
28
|
+
const count = textLineCount(content);
|
|
29
|
+
return source.line >= 1 && source.line <= count;
|
|
30
|
+
}
|
package/dist/schema/parse.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
|
-
import { isReviewSize, REVIEW_SIZES } from "./types.js";
|
|
2
|
-
const DOCUMENT_KEYS = new Set(["version", "source", "size", "title", "summary", "why", "tickets", "groups"]);
|
|
1
|
+
import { isReviewSize, isSourceKind, isSourceSide, REVIEW_SIZES, SOURCE_KINDS, } from "./types.js";
|
|
2
|
+
const DOCUMENT_KEYS = new Set(["version", "source", "size", "title", "summary", "why", "tickets", "sources", "groups"]);
|
|
3
3
|
const SOURCE_KEYS = new Set(["baseRef", "headRef", "range"]);
|
|
4
4
|
const TICKET_KEYS = new Set(["id", "url", "title", "part"]);
|
|
5
|
-
const
|
|
5
|
+
const REVIEW_SOURCE_KEYS = new Set([
|
|
6
|
+
"id",
|
|
7
|
+
"kind",
|
|
8
|
+
"label",
|
|
9
|
+
"url",
|
|
10
|
+
"title",
|
|
11
|
+
"gist",
|
|
12
|
+
"part",
|
|
13
|
+
"author",
|
|
14
|
+
"body",
|
|
15
|
+
"path",
|
|
16
|
+
"side",
|
|
17
|
+
"line",
|
|
18
|
+
]);
|
|
19
|
+
const COMMENT_ONLY_KEYS = ["author", "body", "path", "side", "line"];
|
|
20
|
+
const GROUP_KEYS = new Set([
|
|
21
|
+
"id",
|
|
22
|
+
"title",
|
|
23
|
+
"why",
|
|
24
|
+
"summary",
|
|
25
|
+
"lookFor",
|
|
26
|
+
"dependsOn",
|
|
27
|
+
"part",
|
|
28
|
+
"sources",
|
|
29
|
+
"suggestedOrder",
|
|
30
|
+
"hunkRefs",
|
|
31
|
+
]);
|
|
6
32
|
const HUNK_KEYS = new Set(["path", "oldPath", "oldStart", "oldLines", "newStart", "newLines"]);
|
|
7
33
|
export function parseReviewDocument(input) {
|
|
8
34
|
const errors = [];
|
|
@@ -17,8 +43,8 @@ export function parseReviewDocument(input) {
|
|
|
17
43
|
const size = parseSize(input.size, errors);
|
|
18
44
|
const title = requiredString(input.title, "title", errors);
|
|
19
45
|
const summary = requiredString(input.summary, "summary", errors);
|
|
20
|
-
const
|
|
21
|
-
const groups = parseGroups(input.groups, errors);
|
|
46
|
+
const sources = parseDocumentSources(input.sources, input.tickets, errors);
|
|
47
|
+
const groups = parseGroups(input.groups, sources, errors);
|
|
22
48
|
const why = input.why === undefined ? undefined : requiredString(input.why, "why", errors);
|
|
23
49
|
if (errors.length > 0 || source === undefined || size === undefined || title === undefined || summary === undefined) {
|
|
24
50
|
return { ok: false, errors };
|
|
@@ -34,8 +60,8 @@ export function parseReviewDocument(input) {
|
|
|
34
60
|
if (why !== undefined) {
|
|
35
61
|
document.why = why;
|
|
36
62
|
}
|
|
37
|
-
if (
|
|
38
|
-
document.
|
|
63
|
+
if (sources !== undefined) {
|
|
64
|
+
document.sources = sources;
|
|
39
65
|
}
|
|
40
66
|
return { ok: true, document };
|
|
41
67
|
}
|
|
@@ -77,15 +103,154 @@ function parseSource(value, errors) {
|
|
|
77
103
|
}
|
|
78
104
|
return source;
|
|
79
105
|
}
|
|
80
|
-
function
|
|
81
|
-
if (
|
|
106
|
+
function parseDocumentSources(sourcesValue, ticketsValue, errors) {
|
|
107
|
+
if (sourcesValue !== undefined && ticketsValue !== undefined) {
|
|
108
|
+
errors.push("document has both sources and tickets; use sources");
|
|
109
|
+
}
|
|
110
|
+
if (sourcesValue !== undefined) {
|
|
111
|
+
return parseSources(sourcesValue, errors);
|
|
112
|
+
}
|
|
113
|
+
if (ticketsValue !== undefined) {
|
|
114
|
+
return parseLegacyTickets(ticketsValue, errors);
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
function parseSources(value, errors) {
|
|
119
|
+
if (!Array.isArray(value)) {
|
|
120
|
+
errors.push("sources must be an array");
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const sources = [];
|
|
124
|
+
const ids = new Set();
|
|
125
|
+
value.forEach((item, i) => {
|
|
126
|
+
const source = parseSourceItem(item, `sources[${i}]`, errors);
|
|
127
|
+
if (source === undefined) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (ids.has(source.id)) {
|
|
131
|
+
errors.push(`duplicate source id "${source.id}"`);
|
|
132
|
+
}
|
|
133
|
+
ids.add(source.id);
|
|
134
|
+
sources.push(source);
|
|
135
|
+
});
|
|
136
|
+
return sources;
|
|
137
|
+
}
|
|
138
|
+
function parseSourceItem(value, label, errors) {
|
|
139
|
+
if (!isRecord(value)) {
|
|
140
|
+
errors.push(`${label} must be an object`);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
extraKeys(value, REVIEW_SOURCE_KEYS, label, errors);
|
|
144
|
+
const id = requiredString(value.id, `${label}.id`, errors);
|
|
145
|
+
const kind = parseKind(value.kind, `${label}.kind`, errors);
|
|
146
|
+
const sourceLabel = requiredString(value.label, `${label}.label`, errors);
|
|
147
|
+
if (id === undefined || kind === undefined || sourceLabel === undefined) {
|
|
82
148
|
return undefined;
|
|
83
149
|
}
|
|
150
|
+
const source = { id, kind, label: sourceLabel };
|
|
151
|
+
if (value.url !== undefined) {
|
|
152
|
+
const url = requiredString(value.url, `${label}.url`, errors);
|
|
153
|
+
if (url !== undefined) {
|
|
154
|
+
source.url = url;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (value.title !== undefined) {
|
|
158
|
+
const title = requiredString(value.title, `${label}.title`, errors);
|
|
159
|
+
if (title !== undefined) {
|
|
160
|
+
source.title = title;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (value.gist !== undefined) {
|
|
164
|
+
const gist = requiredString(value.gist, `${label}.gist`, errors);
|
|
165
|
+
if (gist !== undefined) {
|
|
166
|
+
source.gist = gist;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (value.part !== undefined) {
|
|
170
|
+
const part = requiredString(value.part, `${label}.part`, errors);
|
|
171
|
+
if (part !== undefined) {
|
|
172
|
+
source.part = part;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (kind === "transcript" && source.url !== undefined) {
|
|
176
|
+
errors.push(`${label}.url must be omitted for transcripts`);
|
|
177
|
+
}
|
|
178
|
+
assignCommentFields(value, source, kind, label, errors);
|
|
179
|
+
return source;
|
|
180
|
+
}
|
|
181
|
+
function assignCommentFields(value, source, kind, label, errors) {
|
|
182
|
+
const present = COMMENT_ONLY_KEYS.filter((key) => value[key] !== undefined);
|
|
183
|
+
if (kind !== "pr-comment") {
|
|
184
|
+
for (const key of present) {
|
|
185
|
+
errors.push(`${label}.${key} is only valid on pr-comment sources`);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (value.author !== undefined) {
|
|
190
|
+
const author = requiredString(value.author, `${label}.author`, errors);
|
|
191
|
+
if (author !== undefined) {
|
|
192
|
+
source.author = author;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (value.body !== undefined) {
|
|
196
|
+
const body = requiredString(value.body, `${label}.body`, errors, { allowEmpty: true });
|
|
197
|
+
if (body !== undefined) {
|
|
198
|
+
source.body = body;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (source.author === undefined) {
|
|
202
|
+
errors.push(`${label}.author is required on pr-comment sources`);
|
|
203
|
+
}
|
|
204
|
+
if (source.body === undefined) {
|
|
205
|
+
errors.push(`${label}.body is required on pr-comment sources`);
|
|
206
|
+
}
|
|
207
|
+
const pinCount = [value.path, value.side, value.line].filter((item) => item !== undefined).length;
|
|
208
|
+
if (pinCount === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (pinCount !== 3) {
|
|
212
|
+
errors.push(`${label} line pin needs path, side, and line together`);
|
|
213
|
+
}
|
|
214
|
+
if (value.path !== undefined) {
|
|
215
|
+
const path = requiredString(value.path, `${label}.path`, errors);
|
|
216
|
+
if (path !== undefined) {
|
|
217
|
+
source.path = path;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (value.side !== undefined) {
|
|
221
|
+
if (!isSourceSide(value.side)) {
|
|
222
|
+
errors.push(`${label}.side must be old or new`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
source.side = value.side;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (value.line !== undefined) {
|
|
229
|
+
const line = requiredInt(value.line, `${label}.line`, errors);
|
|
230
|
+
if (line !== undefined) {
|
|
231
|
+
if (line < 1) {
|
|
232
|
+
errors.push(`${label}.line must be a positive integer`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
source.line = line;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function parseKind(value, label, errors) {
|
|
241
|
+
if (isSourceKind(value)) {
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
errors.push(`${label} must be one of ${SOURCE_KINDS.join(", ")}`);
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
function parseLegacyTickets(value, errors) {
|
|
84
248
|
if (!Array.isArray(value)) {
|
|
85
249
|
errors.push("tickets must be an array");
|
|
86
250
|
return undefined;
|
|
87
251
|
}
|
|
88
|
-
const
|
|
252
|
+
const sources = [];
|
|
253
|
+
const ids = new Set();
|
|
89
254
|
value.forEach((item, i) => {
|
|
90
255
|
if (!isRecord(item)) {
|
|
91
256
|
errors.push(`tickets[${i}] must be an object`);
|
|
@@ -96,30 +261,34 @@ function parseTickets(value, errors) {
|
|
|
96
261
|
if (id === undefined) {
|
|
97
262
|
return;
|
|
98
263
|
}
|
|
99
|
-
|
|
264
|
+
if (ids.has(id)) {
|
|
265
|
+
errors.push(`duplicate source id "${id}"`);
|
|
266
|
+
}
|
|
267
|
+
ids.add(id);
|
|
268
|
+
const source = { id, kind: "ticket", label: id };
|
|
100
269
|
if (item.url !== undefined) {
|
|
101
270
|
const url = requiredString(item.url, `tickets[${i}].url`, errors);
|
|
102
271
|
if (url !== undefined) {
|
|
103
|
-
|
|
272
|
+
source.url = url;
|
|
104
273
|
}
|
|
105
274
|
}
|
|
106
275
|
if (item.title !== undefined) {
|
|
107
276
|
const title = requiredString(item.title, `tickets[${i}].title`, errors);
|
|
108
277
|
if (title !== undefined) {
|
|
109
|
-
|
|
278
|
+
source.title = title;
|
|
110
279
|
}
|
|
111
280
|
}
|
|
112
281
|
if (item.part !== undefined) {
|
|
113
282
|
const part = requiredString(item.part, `tickets[${i}].part`, errors);
|
|
114
283
|
if (part !== undefined) {
|
|
115
|
-
|
|
284
|
+
source.part = part;
|
|
116
285
|
}
|
|
117
286
|
}
|
|
118
|
-
|
|
287
|
+
sources.push(source);
|
|
119
288
|
});
|
|
120
|
-
return
|
|
289
|
+
return sources;
|
|
121
290
|
}
|
|
122
|
-
function parseGroups(value, errors) {
|
|
291
|
+
function parseGroups(value, sources, errors) {
|
|
123
292
|
if (!Array.isArray(value)) {
|
|
124
293
|
errors.push("groups must be an array");
|
|
125
294
|
return [];
|
|
@@ -140,6 +309,7 @@ function parseGroups(value, errors) {
|
|
|
140
309
|
const hunkRefs = parseHunkRefs(item.hunkRefs, `groups[${i}].hunkRefs`, errors);
|
|
141
310
|
const lookFor = parseStringList(item.lookFor, `groups[${i}].lookFor`, errors);
|
|
142
311
|
const dependsOn = parseStringList(item.dependsOn, `groups[${i}].dependsOn`, errors);
|
|
312
|
+
const groupSources = parseStringList(item.sources, `groups[${i}].sources`, errors);
|
|
143
313
|
const part = item.part === undefined ? undefined : requiredString(item.part, `groups[${i}].part`, errors);
|
|
144
314
|
if (id === undefined || title === undefined || why === undefined || summary === undefined || suggestedOrder === undefined) {
|
|
145
315
|
return;
|
|
@@ -158,18 +328,32 @@ function parseGroups(value, errors) {
|
|
|
158
328
|
if (part !== undefined) {
|
|
159
329
|
group.part = part;
|
|
160
330
|
}
|
|
331
|
+
if (groupSources !== undefined) {
|
|
332
|
+
group.sources = groupSources;
|
|
333
|
+
}
|
|
161
334
|
groups.push(group);
|
|
162
335
|
});
|
|
163
|
-
const
|
|
336
|
+
const knownGroups = new Set(groups.map((group) => group.id));
|
|
337
|
+
const knownSources = new Set((sources ?? []).map((source) => source.id));
|
|
164
338
|
for (const group of groups) {
|
|
165
339
|
for (const dep of group.dependsOn ?? []) {
|
|
166
340
|
if (dep === group.id) {
|
|
167
341
|
errors.push(`groups id "${group.id}" depends on itself`);
|
|
168
342
|
}
|
|
169
|
-
else if (!
|
|
343
|
+
else if (!knownGroups.has(dep)) {
|
|
170
344
|
errors.push(`groups id "${group.id}" dependsOn unknown group "${dep}"`);
|
|
171
345
|
}
|
|
172
346
|
}
|
|
347
|
+
const seen = new Set();
|
|
348
|
+
for (const sourceId of group.sources ?? []) {
|
|
349
|
+
if (seen.has(sourceId)) {
|
|
350
|
+
errors.push(`groups id "${group.id}" lists source "${sourceId}" twice`);
|
|
351
|
+
}
|
|
352
|
+
seen.add(sourceId);
|
|
353
|
+
if (!knownSources.has(sourceId)) {
|
|
354
|
+
errors.push(`groups id "${group.id}" sources unknown id "${sourceId}"`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
173
357
|
}
|
|
174
358
|
return groups;
|
|
175
359
|
}
|