diffsplain 0.4.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -12
- package/dist/assets/abap-CLvhMVsD.js +1 -0
- package/dist/assets/actionscript-3--17pq3dv.js +1 -0
- package/dist/assets/ada-C5qYipkI.js +1 -0
- package/dist/assets/andromeeda-vGVdxbeo.js +1 -0
- package/dist/assets/angular-html-vBvPnd6a.js +1 -0
- package/dist/assets/angular-ts-CDzF1Z6a.js +1 -0
- package/dist/assets/apache-U0d_L8uA.js +1 -0
- package/dist/assets/apex-VAyPSnFM.js +1 -0
- package/dist/assets/apl-Bm9bxvP7.js +1 -0
- package/dist/assets/applescript-CCn79oCD.js +1 -0
- package/dist/assets/ara-4CJ0cIlV.js +1 -0
- package/dist/assets/asciidoc-DE70LPWp.js +1 -0
- package/dist/assets/asm-Cmm7eHzH.js +1 -0
- package/dist/assets/astro-RNrNWCYO.js +1 -0
- package/dist/assets/aurora-x-CDeNXAV0.js +1 -0
- package/dist/assets/awk-BWXHIvNe.js +1 -0
- package/dist/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/dist/assets/ayu-light-C3h-C4tm.js +1 -0
- package/dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/dist/assets/ballerina-B7ZEbQpA.js +1 -0
- package/dist/assets/bat-Bo4NYOV-.js +1 -0
- package/dist/assets/beancount-D-usSTwE.js +1 -0
- package/dist/assets/berry-DKpUyyne.js +1 -0
- package/dist/assets/bibtex-Ci_nEsc7.js +1 -0
- package/dist/assets/bicep-CUHmPFLl.js +1 -0
- package/dist/assets/bird2-C6vDhewU.js +1 -0
- package/dist/assets/blade-On-NBYUv.js +1 -0
- package/dist/assets/bsl-BkkzgIyY.js +1 -0
- package/dist/assets/c-hRmhFgD0.js +1 -0
- package/dist/assets/c3-BFHwR3_K.js +1 -0
- package/dist/assets/cadence-CQ2zXKGN.js +1 -0
- package/dist/assets/cairo-DLTphjLi.js +1 -0
- package/dist/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
- package/dist/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
- package/dist/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
- package/dist/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
- package/dist/assets/clarity-SemFz856.js +1 -0
- package/dist/assets/clojure-DqKBuwfJ.js +1 -0
- package/dist/assets/cmake-Bj61d0ZC.js +1 -0
- package/dist/assets/cobol-CIqc3k2t.js +1 -0
- package/dist/assets/codeowners-C8r90Shi.js +1 -0
- package/dist/assets/codeql-oeQT6MSM.js +1 -0
- package/dist/assets/coffee-Cd5EyvvS.js +1 -0
- package/dist/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/dist/assets/coq-BrsZFFmf.js +1 -0
- package/dist/assets/cpp-zXFAAp_S.js +1 -0
- package/dist/assets/crystal-DRYY4cSw.js +1 -0
- package/dist/assets/csharp-oqKa8noW.js +1 -0
- package/dist/assets/css-C4op_MsD.js +1 -0
- package/dist/assets/csv-Dx-8-gkx.js +1 -0
- package/dist/assets/cue-CE9AQfxI.js +1 -0
- package/dist/assets/cypher-ClKdZ_lG.js +1 -0
- package/dist/assets/d-qD-0Kul2.js +1 -0
- package/dist/assets/dark-plus-Cs2F2srj.js +1 -0
- package/dist/assets/dart-CnvKMtbv.js +1 -0
- package/dist/assets/dax-BkyTk9wS.js +1 -0
- package/dist/assets/desktop-Dlh5hvp9.js +1 -0
- package/dist/assets/diff-woXpYk--.js +1 -0
- package/dist/assets/docker-IyjqRm3v.js +1 -0
- package/dist/assets/dotenv-_5a1GRtc.js +1 -0
- package/dist/assets/dracula-BHWKrbxM.js +1 -0
- package/dist/assets/dracula-soft-5eyTD99u.js +1 -0
- package/dist/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/dist/assets/edge-d3rJkErf.js +1 -0
- package/dist/assets/elixir-DPqlJhYM.js +1 -0
- package/dist/assets/elm-DgU37I8a.js +1 -0
- package/dist/assets/emacs-lisp-B4R74twV.js +1 -0
- package/dist/assets/erb-CZVB_ntC.js +1 -0
- package/dist/assets/erlang-Cphh6RMH.js +1 -0
- package/dist/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/dist/assets/everforest-light-Df2xbC6M.js +1 -0
- package/dist/assets/fennel-DQxkIbk2.js +1 -0
- package/dist/assets/fish-BJitypiv.js +1 -0
- package/dist/assets/fluent-C03EYrpw.js +1 -0
- package/dist/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/dist/assets/fsharp-D13ZGOAj.js +1 -0
- package/dist/assets/gdresource-C0sCabJj.js +1 -0
- package/dist/assets/gdscript-Cp2uCuqX.js +1 -0
- package/dist/assets/gdshader-CBce3t8t.js +1 -0
- package/dist/assets/genie-CV2tkWYe.js +1 -0
- package/dist/assets/gherkin-DExj1W_8.js +1 -0
- package/dist/assets/git-commit-BSykSTBG.js +1 -0
- package/dist/assets/git-rebase-hUkltHZG.js +1 -0
- package/dist/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/dist/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/assets/github-light-default-BXViO-2h.js +1 -0
- package/dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/dist/assets/gleam-CSRkHgEL.js +1 -0
- package/dist/assets/glimmer-js-5XOT9GQ8.js +1 -0
- package/dist/assets/glimmer-ts-DT7D0Kfq.js +1 -0
- package/dist/assets/glsl-Br4_Eh5O.js +1 -0
- package/dist/assets/gn-ilITqXS6.js +1 -0
- package/dist/assets/gnuplot-7GGW24-e.js +1 -0
- package/dist/assets/go-rLFTqkRN.js +1 -0
- package/dist/assets/graphql--BmZ_AYP.js +1 -0
- package/dist/assets/groovy-CacY0gHj.js +1 -0
- package/dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/dist/assets/hack-CMdqGDoA.js +1 -0
- package/dist/assets/haml-DLEOHGmg.js +1 -0
- package/dist/assets/handlebars-BfUftoV0.js +1 -0
- package/dist/assets/haskell-D8IpX4py.js +1 -0
- package/dist/assets/haxe-OTjmBuCE.js +1 -0
- package/dist/assets/hcl-Dh228itO.js +1 -0
- package/dist/assets/hjson-CxZEssPk.js +1 -0
- package/dist/assets/hlsl-Cvrh5tZx.js +1 -0
- package/dist/assets/horizon-CE9ld1lL.js +1 -0
- package/dist/assets/horizon-bright-DSNQnXHK.js +1 -0
- package/dist/assets/houston-CsvMBhTu.js +1 -0
- package/dist/assets/html-derivative-D5cZE6C4.js +1 -0
- package/dist/assets/html-uOgx2iYC.js +1 -0
- package/dist/assets/http-cW9c66J_.js +1 -0
- package/dist/assets/hurl-BztURO2n.js +1 -0
- package/dist/assets/hxml-B0Qn7Nwc.js +1 -0
- package/dist/assets/hy-CZbG8q4J.js +1 -0
- package/dist/assets/imba-DsUTQ-LC.js +1 -0
- package/dist/assets/{index-BB6eHlnY.css → index-C2p_1iAk.css} +1 -1
- package/dist/assets/index-DZFM4-3L.js +1568 -0
- package/dist/assets/ini-B5eOa1yu.js +1 -0
- package/dist/assets/java-Cy4mnKaj.js +1 -0
- package/dist/assets/javascript-_Z02t_9Q.js +1 -0
- package/dist/assets/jinja-BppBm5gt.js +1 -0
- package/dist/assets/jison-Dzns436M.js +1 -0
- package/dist/assets/json-BYkfLOry.js +1 -0
- package/dist/assets/json5-BR5RXkoi.js +1 -0
- package/dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/dist/assets/jsonl-CmCQp5Yx.js +1 -0
- package/dist/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/dist/assets/jssm-DXw9l8Rf.js +1 -0
- package/dist/assets/jsx-DtAm8NUb.js +1 -0
- package/dist/assets/julia-CLIMRuh0.js +1 -0
- package/dist/assets/just-DMg47an6.js +1 -0
- package/dist/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
- package/dist/assets/kanagawa-lotus-BN08jTvb.js +1 -0
- package/dist/assets/kanagawa-wave-CTweb8Dz.js +1 -0
- package/dist/assets/kdl-CsD5j6eV.js +1 -0
- package/dist/assets/kotlin-DhhofPvG.js +1 -0
- package/dist/assets/kusto-C7mF5XQf.js +1 -0
- package/dist/assets/laserwave-C_8bwKvT.js +1 -0
- package/dist/assets/latex-D05ztCOx.js +1 -0
- package/dist/assets/lean-CewbzKMR.js +1 -0
- package/dist/assets/less-DVTAwKKz.js +1 -0
- package/dist/assets/light-plus-DVQuIRkW.js +1 -0
- package/dist/assets/liquid-DpImQR4t.js +1 -0
- package/dist/assets/llvm-Cm23YOpf.js +1 -0
- package/dist/assets/log-BNLmms1o.js +1 -0
- package/dist/assets/logo-Cluzi2Zq.js +1 -0
- package/dist/assets/lua--ZHUYQUU.js +1 -0
- package/dist/assets/luau-FMPmPwt6.js +1 -0
- package/dist/assets/make-Dixweg8N.js +1 -0
- package/dist/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/assets/marko-Dh3Xi08i.js +1 -0
- package/dist/assets/material-theme-Bm3Qr25_.js +1 -0
- package/dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/dist/assets/matlab-D7qyCx1q.js +1 -0
- package/dist/assets/mdc-Br6Pt78u.js +1 -0
- package/dist/assets/mdx-DQZ5AkYe.js +1 -0
- package/dist/assets/mermaid-Bk4SNUv9.js +1 -0
- package/dist/assets/min-dark-BSWPekZh.js +1 -0
- package/dist/assets/min-light-DDpmG2fV.js +1 -0
- package/dist/assets/mipsasm-BMqwQI7S.js +1 -0
- package/dist/assets/mojo-BgCJLMeH.js +1 -0
- package/dist/assets/monokai-CdkpiU2Y.js +1 -0
- package/dist/assets/moonbit-CaWjb8XO.js +1 -0
- package/dist/assets/move-B1IS1UjX.js +1 -0
- package/dist/assets/narrat-_X_XdTYD.js +1 -0
- package/dist/assets/nextflow-BJtWHP5T.js +1 -0
- package/dist/assets/nextflow-groovy-DJMQeKeT.js +1 -0
- package/dist/assets/nginx-CwSU6U6d.js +1 -0
- package/dist/assets/night-owl-DhmEMT88.js +1 -0
- package/dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/dist/assets/nim-CBbTwyeq.js +1 -0
- package/dist/assets/nix-IvuFDN5E.js +1 -0
- package/dist/assets/nord-Cb4Vim4T.js +1 -0
- package/dist/assets/nushell-DcLAeLz5.js +1 -0
- package/dist/assets/objective-c-D1A_Heim.js +1 -0
- package/dist/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/dist/assets/ocaml-O90oeIOV.js +1 -0
- package/dist/assets/odin-B1RWQWA5.js +1 -0
- package/dist/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/dist/assets/one-light-D7Lr4KcI.js +1 -0
- package/dist/assets/openscad-BUDT5pXO.js +1 -0
- package/dist/assets/pascal-4ZHwLPI5.js +1 -0
- package/dist/assets/perl-D3jQu3LN.js +1 -0
- package/dist/assets/php-BMeXBzlU.js +1 -0
- package/dist/assets/pierre-dark-CpLgRqie.js +1 -0
- package/dist/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
- package/dist/assets/pierre-dark-soft-kZQmAZld.js +1 -0
- package/dist/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
- package/dist/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
- package/dist/assets/pierre-light-CoaEpmwp.js +1 -0
- package/dist/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
- package/dist/assets/pierre-light-soft-lWLdNTOI.js +1 -0
- package/dist/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
- package/dist/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
- package/dist/assets/pkl-ot-7Btpt.js +1 -0
- package/dist/assets/plastic-DQwYfKfQ.js +1 -0
- package/dist/assets/plsql-DGHpHOYJ.js +1 -0
- package/dist/assets/po-BiJDBrnU.js +1 -0
- package/dist/assets/poimandres-DRFjx7u4.js +1 -0
- package/dist/assets/polar-C7UOKdEL.js +1 -0
- package/dist/assets/postcss-BXeXVLqQ.js +1 -0
- package/dist/assets/powerquery-DNMTfnFr.js +1 -0
- package/dist/assets/powershell-DshXNtvi.js +1 -0
- package/dist/assets/prisma-BsRQq5mF.js +1 -0
- package/dist/assets/prolog-iXnhIJG7.js +1 -0
- package/dist/assets/proto-DB4EqR-F.js +1 -0
- package/dist/assets/pug-BGdY1g5E.js +1 -0
- package/dist/assets/puppet-CDv2pdJW.js +1 -0
- package/dist/assets/purescript-9MfHhQsQ.js +1 -0
- package/dist/assets/python-gzcpVVnB.js +1 -0
- package/dist/assets/qml-DsT5biza.js +1 -0
- package/dist/assets/qmldir-DCQb3MpD.js +1 -0
- package/dist/assets/qss-Fe1Jh2GI.js +1 -0
- package/dist/assets/r-CbS9PfN5.js +1 -0
- package/dist/assets/racket-DcIDlBhZ.js +1 -0
- package/dist/assets/raku-B3gFvitq.js +1 -0
- package/dist/assets/razor-Bwm8roHi.js +1 -0
- package/dist/assets/red-CJ3rzSJv.js +1 -0
- package/dist/assets/reg-CRGYupPL.js +1 -0
- package/dist/assets/regexp-Bc_9DyAu.js +1 -0
- package/dist/assets/rel-BtDbiS_P.js +1 -0
- package/dist/assets/riscv-Ckw8ddFX.js +1 -0
- package/dist/assets/ron-VUp2lXgN.js +1 -0
- package/dist/assets/rose-pine-BthvhNj6.js +1 -0
- package/dist/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
- package/dist/assets/rose-pine-moon-hon4tzzS.js +1 -0
- package/dist/assets/rosmsg-CAekHB0j.js +1 -0
- package/dist/assets/rst-Cj1Aii91.js +1 -0
- package/dist/assets/ruby-BPMtFZnc.js +1 -0
- package/dist/assets/rust-Cfkwpbl8.js +1 -0
- package/dist/assets/sas-CoOau1t2.js +1 -0
- package/dist/assets/sass-DXrisJhu.js +1 -0
- package/dist/assets/scala-DKOlJaKm.js +1 -0
- package/dist/assets/scheme-DQCgrYNe.js +1 -0
- package/dist/assets/scss-CvfaI808.js +1 -0
- package/dist/assets/sdbl-bTVj8UrX.js +1 -0
- package/dist/assets/shaderlab-TOUzSsQk.js +1 -0
- package/dist/assets/shellscript-B0C-TdLE.js +1 -0
- package/dist/assets/shellsession-UhmLiqcA.js +1 -0
- package/dist/assets/slack-dark-DnToyrRv.js +1 -0
- package/dist/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/dist/assets/smalltalk-B16xEiuN.js +1 -0
- package/dist/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/dist/assets/solarized-dark-DV17i1UV.js +1 -0
- package/dist/assets/solarized-light-DSh2HLQt.js +1 -0
- package/dist/assets/solidity-CKzVLygQ.js +1 -0
- package/dist/assets/soy-D419u0RL.js +1 -0
- package/dist/assets/sparql-D_iOobhT.js +1 -0
- package/dist/assets/splunk-BC2Px7Mm.js +1 -0
- package/dist/assets/sql-Do9k0rRn.js +1 -0
- package/dist/assets/ssh-config-BgfXC-Er.js +1 -0
- package/dist/assets/stata-x47J6BC4.js +1 -0
- package/dist/assets/stylus-B6D30XZt.js +1 -0
- package/dist/assets/surrealql-BERQ6pHu.js +1 -0
- package/dist/assets/svelte-dRWkyqKW.js +1 -0
- package/dist/assets/swift-DonLKvLd.js +1 -0
- package/dist/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/dist/assets/system-verilog-DJ5XKQeo.js +1 -0
- package/dist/assets/systemd-BxMlprV5.js +1 -0
- package/dist/assets/talonscript-CohzipZa.js +1 -0
- package/dist/assets/tasl-DMoTqEGO.js +1 -0
- package/dist/assets/tcl-CZd0xW_V.js +1 -0
- package/dist/assets/templ-CTq-KoWN.js +1 -0
- package/dist/assets/terraform-DswuEJGm.js +1 -0
- package/dist/assets/tex-CdRGjyYI.js +1 -0
- package/dist/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/dist/assets/toml-CcmNWLt0.js +1 -0
- package/dist/assets/ts-tags-CgaMqyVb.js +1 -0
- package/dist/assets/tsv-sltzmVWM.js +1 -0
- package/dist/assets/tsx-cpBQKcPG.js +1 -0
- package/dist/assets/turtle-ByJddavk.js +1 -0
- package/dist/assets/twig-vbs_PM4-.js +1 -0
- package/dist/assets/typescript-Cc--nwIo.js +1 -0
- package/dist/assets/typespec-BRdr0IET.js +1 -0
- package/dist/assets/typst-DI99ib-x.js +1 -0
- package/dist/assets/v-DETTlOr0.js +1 -0
- package/dist/assets/vala-zf12oZj6.js +1 -0
- package/dist/assets/vb-Djn5o6TS.js +1 -0
- package/dist/assets/verilog-CiiDBU1e.js +1 -0
- package/dist/assets/vesper-DdrHHSXu.js +1 -0
- package/dist/assets/vhdl-BroJfC0k.js +1 -0
- package/dist/assets/viml-DvXPmvsu.js +1 -0
- package/dist/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/dist/assets/vitesse-light-VbXTXTou.js +1 -0
- package/dist/assets/vue-CgHwdBmB.js +1 -0
- package/dist/assets/vue-html-BHfoMw47.js +1 -0
- package/dist/assets/vue-vine-_lVRp_NK.js +1 -0
- package/dist/assets/vyper-CgoNMtux.js +1 -0
- package/dist/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/assets/wasm-ByWQv1Qj.js +1 -0
- package/dist/assets/wenyan-C8pVoKbM.js +1 -0
- package/dist/assets/wgsl-BsKzXJz4.js +1 -0
- package/dist/assets/wikitext-ClFFjSW2.js +1 -0
- package/dist/assets/wit-DdvCle-K.js +1 -0
- package/dist/assets/wolfram-DLL8P-h_.js +1 -0
- package/dist/assets/xml-G4NVmTde.js +1 -0
- package/dist/assets/xsl-s0so2M1i.js +1 -0
- package/dist/assets/yaml-CpsmW05t.js +1 -0
- package/dist/assets/zenscript-BnlCZFoB.js +1 -0
- package/dist/assets/zig-CMLA9XwU.js +1 -0
- package/dist/index.html +2 -2
- package/package.json +52 -23
- package/scripts/access-token.mjs +21 -0
- package/scripts/build-diff-data.mjs +297 -62
- package/scripts/cache.mjs +301 -0
- package/scripts/cli-args.mjs +206 -57
- package/scripts/coding-agents.mjs +279 -162
- package/scripts/dev.mjs +218 -0
- package/scripts/doctor.mjs +265 -46
- package/scripts/generate-summaries.mjs +760 -260
- package/scripts/mock-agent.mjs +165 -0
- package/scripts/present.mjs +394 -85
- package/scripts/presenter-runtime.mjs +148 -0
- package/scripts/serve-built.mjs +343 -71
- package/scripts/summary-path.mjs +24 -7
- package/scripts/support-record.mjs +242 -0
- package/dist/assets/index-D5201POe.js +0 -10
|
@@ -3,14 +3,20 @@
|
|
|
3
3
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import {
|
|
6
|
+
closeSync,
|
|
7
|
+
constants,
|
|
6
8
|
existsSync,
|
|
9
|
+
fstatSync,
|
|
10
|
+
lstatSync,
|
|
7
11
|
mkdirSync,
|
|
12
|
+
openSync,
|
|
8
13
|
readFileSync,
|
|
14
|
+
readlinkSync,
|
|
9
15
|
renameSync,
|
|
10
16
|
statSync,
|
|
11
17
|
writeFileSync,
|
|
12
18
|
} from 'node:fs';
|
|
13
|
-
import { dirname, relative, resolve } from 'node:path';
|
|
19
|
+
import { basename, dirname, relative, resolve } from 'node:path';
|
|
14
20
|
import { fileURLToPath } from 'node:url';
|
|
15
21
|
import { summaryPath } from './summary-path.mjs';
|
|
16
22
|
|
|
@@ -26,6 +32,10 @@ const option = (name) => {
|
|
|
26
32
|
if (!value || value.startsWith('--')) fail(`${name} needs a value`);
|
|
27
33
|
return value;
|
|
28
34
|
};
|
|
35
|
+
const options = (name) =>
|
|
36
|
+
args.flatMap((argument, index) =>
|
|
37
|
+
argument === name && args[index + 1] ? [args[index + 1]] : [],
|
|
38
|
+
);
|
|
29
39
|
const has = (name) => args.includes(name);
|
|
30
40
|
|
|
31
41
|
if (has('--help')) {
|
|
@@ -52,7 +62,7 @@ Options:
|
|
|
52
62
|
|
|
53
63
|
const repo = resolve(option('--repo') || process.cwd());
|
|
54
64
|
const output = resolve(option('--output') || '.cache/diff-data.json');
|
|
55
|
-
const
|
|
65
|
+
const excludedOutputs = options('--exclude-output');
|
|
56
66
|
const baseOption = option('--base');
|
|
57
67
|
const headOption = option('--head');
|
|
58
68
|
const prOption = option('--pr');
|
|
@@ -79,7 +89,18 @@ const cacheRoot = cacheOption
|
|
|
79
89
|
: resolve(projectRoot, '.cache/git');
|
|
80
90
|
const remoteMode = Boolean(prOption || branchOption);
|
|
81
91
|
const watching = has('--watch');
|
|
92
|
+
const watchContent = has('--watch-content');
|
|
82
93
|
const ignoreSummaryWatch = has('--ignore-summary-watch');
|
|
94
|
+
const noSummaries = has('--no-summaries');
|
|
95
|
+
const interval = (name, fallback) => {
|
|
96
|
+
const value = Number(process.env[name]);
|
|
97
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
98
|
+
};
|
|
99
|
+
const watchInterval = interval('DIFFSPLAIN_WATCH_INTERVAL_MS', 2_000);
|
|
100
|
+
const remoteRefreshInterval = interval(
|
|
101
|
+
'DIFFSPLAIN_REMOTE_REFRESH_INTERVAL_MS',
|
|
102
|
+
30_000,
|
|
103
|
+
);
|
|
83
104
|
|
|
84
105
|
if (prOption && branchOption) fail('--pr and --branch cannot be used together');
|
|
85
106
|
if (prOption && (baseOption || headOption)) fail('--pr cannot be used with --base or --head');
|
|
@@ -109,11 +130,13 @@ const repoPath = (file) => {
|
|
|
109
130
|
const path = relative(repo, file).replaceAll('\\', '/');
|
|
110
131
|
return path && path !== '..' && !path.startsWith('../') ? path : undefined;
|
|
111
132
|
};
|
|
133
|
+
const summariesRepoPath = repoPath(summariesPath);
|
|
112
134
|
const excludedPaths = new Set(
|
|
113
135
|
[
|
|
114
|
-
|
|
136
|
+
summariesRepoPath,
|
|
137
|
+
summariesRepoPath ? `${summariesRepoPath}.lock` : undefined,
|
|
115
138
|
repoPath(output),
|
|
116
|
-
|
|
139
|
+
...excludedOutputs.map((path) => repoPath(resolve(path))),
|
|
117
140
|
].filter(Boolean),
|
|
118
141
|
);
|
|
119
142
|
|
|
@@ -176,6 +199,22 @@ const completeChangeSummary = (value) =>
|
|
|
176
199
|
completeText(value.why) &&
|
|
177
200
|
completeList(value.highlights) &&
|
|
178
201
|
completeList(value.risks);
|
|
202
|
+
const failedFileRecords = (value) =>
|
|
203
|
+
Array.isArray(value)
|
|
204
|
+
? value
|
|
205
|
+
.filter(
|
|
206
|
+
(item) =>
|
|
207
|
+
item &&
|
|
208
|
+
typeof item.path === 'string' &&
|
|
209
|
+
item.path.trim() &&
|
|
210
|
+
typeof item.reason === 'string' &&
|
|
211
|
+
item.reason.trim(),
|
|
212
|
+
)
|
|
213
|
+
.map((item) => ({
|
|
214
|
+
path: item.path.trim(),
|
|
215
|
+
reason: item.reason.trim(),
|
|
216
|
+
}))
|
|
217
|
+
: [];
|
|
179
218
|
|
|
180
219
|
function fileSummary(path, value) {
|
|
181
220
|
return {
|
|
@@ -255,31 +294,45 @@ function parseNumstat(raw) {
|
|
|
255
294
|
function compactSnippet(patch, limit = 180) {
|
|
256
295
|
const lines = patch.split('\n');
|
|
257
296
|
if (lines.length <= limit) return patch;
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
297
|
+
const firstHunk = lines.findIndex((line) => line.startsWith('@@'));
|
|
298
|
+
if (firstHunk < 0) return patch;
|
|
299
|
+
|
|
300
|
+
const output = lines.slice(0, firstHunk);
|
|
301
|
+
let cursor = firstHunk;
|
|
302
|
+
while (cursor < lines.length && output.length < limit) {
|
|
303
|
+
const nextHunk = lines.findIndex(
|
|
304
|
+
(line, index) => index > cursor && line.startsWith('@@'),
|
|
305
|
+
);
|
|
306
|
+
const end = nextHunk < 0 ? lines.length : nextHunk;
|
|
307
|
+
const hunk = lines.slice(cursor, end);
|
|
308
|
+
const remaining = limit - output.length;
|
|
309
|
+
|
|
310
|
+
if (hunk.length <= remaining) {
|
|
311
|
+
output.push(...hunk);
|
|
312
|
+
} else {
|
|
313
|
+
const match = hunk[0].match(
|
|
314
|
+
/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/,
|
|
315
|
+
);
|
|
316
|
+
if (!match || remaining < 2) break;
|
|
317
|
+
|
|
318
|
+
const body = hunk.slice(1, remaining);
|
|
319
|
+
let oldCount = 0;
|
|
320
|
+
let newCount = 0;
|
|
321
|
+
for (const line of body) {
|
|
322
|
+
if (line === '\') continue;
|
|
323
|
+
if (!line.startsWith('+')) oldCount += 1;
|
|
324
|
+
if (!line.startsWith('-')) newCount += 1;
|
|
325
|
+
}
|
|
326
|
+
output.push(
|
|
327
|
+
`@@ -${match[1]},${oldCount} +${match[2]},${newCount} @@${match[3]}`,
|
|
328
|
+
...body,
|
|
329
|
+
);
|
|
275
330
|
}
|
|
276
|
-
|
|
331
|
+
|
|
332
|
+
if (nextHunk < 0 || output.length >= limit) break;
|
|
333
|
+
cursor = nextHunk;
|
|
277
334
|
}
|
|
278
|
-
return
|
|
279
|
-
...header,
|
|
280
|
-
...hunks,
|
|
281
|
-
'... diff truncated; see patch for full content ...',
|
|
282
|
-
].join('\n');
|
|
335
|
+
return output.join('\n');
|
|
283
336
|
}
|
|
284
337
|
|
|
285
338
|
function normalizeBranch(value, remoteName) {
|
|
@@ -297,12 +350,24 @@ function normalizeBranch(value, remoteName) {
|
|
|
297
350
|
}
|
|
298
351
|
|
|
299
352
|
function resolveRemote() {
|
|
300
|
-
const configured = tryRepo([
|
|
301
|
-
|
|
353
|
+
const configured = tryRepo([
|
|
354
|
+
'config',
|
|
355
|
+
'--get-all',
|
|
356
|
+
`remote.${remoteOption}.url`,
|
|
357
|
+
])
|
|
358
|
+
.split('\n')
|
|
359
|
+
.find(Boolean);
|
|
360
|
+
if (configured) {
|
|
361
|
+
return {
|
|
362
|
+
name: remoteOption,
|
|
363
|
+
url: configured,
|
|
364
|
+
fetchUrl: tryRepo(['remote', 'get-url', remoteOption]) || configured,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
302
367
|
if (remoteOption === 'origin') {
|
|
303
368
|
throw new Error(`Git remote "origin" was not found in ${repo}`);
|
|
304
369
|
}
|
|
305
|
-
return { name: remoteOption, url: remoteOption };
|
|
370
|
+
return { name: remoteOption, url: remoteOption, fetchUrl: remoteOption };
|
|
306
371
|
}
|
|
307
372
|
|
|
308
373
|
function bareCache(remoteUrl) {
|
|
@@ -373,6 +438,34 @@ function remoteDefaultBranch(remoteUrl) {
|
|
|
373
438
|
return remoteDefaultBranchInfo(remoteUrl).name;
|
|
374
439
|
}
|
|
375
440
|
|
|
441
|
+
function remoteContainsCommits(remoteUrl, commits) {
|
|
442
|
+
let raw;
|
|
443
|
+
try {
|
|
444
|
+
raw = runRepo(['ls-remote', remoteUrl]);
|
|
445
|
+
} catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
const tips = [
|
|
449
|
+
...new Set(
|
|
450
|
+
raw
|
|
451
|
+
.split('\n')
|
|
452
|
+
.map((line) => line.trim().split(/\s+/, 1)[0])
|
|
453
|
+
.filter(Boolean),
|
|
454
|
+
),
|
|
455
|
+
];
|
|
456
|
+
return commits.every((commit) =>
|
|
457
|
+
tips.some((tip) => {
|
|
458
|
+
if (tip === commit) return true;
|
|
459
|
+
const result = spawnSync(
|
|
460
|
+
'git',
|
|
461
|
+
['-C', repo, 'merge-base', '--is-ancestor', commit, tip],
|
|
462
|
+
{ stdio: 'ignore' },
|
|
463
|
+
);
|
|
464
|
+
return result.status === 0;
|
|
465
|
+
}),
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
376
469
|
function localDefaultBranch(remote) {
|
|
377
470
|
if (baseOption) return { name: baseOption };
|
|
378
471
|
|
|
@@ -492,7 +585,7 @@ function resolveBranchTarget() {
|
|
|
492
585
|
const remote = resolveRemote();
|
|
493
586
|
const branch = normalizeBranch(branchOption, remote.name);
|
|
494
587
|
const baseBranch = normalizeBranch(
|
|
495
|
-
baseOption || remoteDefaultBranch(remote.
|
|
588
|
+
baseOption || remoteDefaultBranch(remote.fetchUrl),
|
|
496
589
|
remote.name,
|
|
497
590
|
);
|
|
498
591
|
const cache = bareCache(remote.url);
|
|
@@ -502,7 +595,7 @@ function resolveBranchTarget() {
|
|
|
502
595
|
.slice(0, 16);
|
|
503
596
|
const baseRef = `refs/diffsplain/branch/${key}/base`;
|
|
504
597
|
const headRef = `refs/diffsplain/branch/${key}/head`;
|
|
505
|
-
fetchInto(cache, remote.
|
|
598
|
+
fetchInto(cache, remote.fetchUrl, [
|
|
506
599
|
`+refs/heads/${baseBranch}:${baseRef}`,
|
|
507
600
|
`+refs/heads/${branch}:${headRef}`,
|
|
508
601
|
]);
|
|
@@ -521,6 +614,7 @@ function resolveBranchTarget() {
|
|
|
521
614
|
remote,
|
|
522
615
|
sourceRepositoryUrl: repository?.webUrl,
|
|
523
616
|
baseRepositoryUrl: repository?.webUrl,
|
|
617
|
+
comparisonCommitsOnRemote: true,
|
|
524
618
|
target: {
|
|
525
619
|
kind: 'branch',
|
|
526
620
|
remote: remote.name,
|
|
@@ -548,7 +642,7 @@ function resolvePullRequestTarget() {
|
|
|
548
642
|
.slice(0, 16);
|
|
549
643
|
const baseRef = `refs/diffsplain/pr/${key}/base`;
|
|
550
644
|
const headRef = `refs/diffsplain/pr/${key}/head`;
|
|
551
|
-
fetchInto(cache, remote.
|
|
645
|
+
fetchInto(cache, remote.fetchUrl, [
|
|
552
646
|
`+refs/heads/${pr.baseRefName}:${baseRef}`,
|
|
553
647
|
`+refs/pull/${pr.number}/head:${headRef}`,
|
|
554
648
|
]);
|
|
@@ -586,6 +680,7 @@ function resolvePullRequestTarget() {
|
|
|
586
680
|
pr.url.replace(/\/pull\/\d+(?:\/.*)?$/, ''),
|
|
587
681
|
baseRepositoryUrl:
|
|
588
682
|
repository?.webUrl || pr.url.replace(/\/pull\/\d+(?:\/.*)?$/, ''),
|
|
683
|
+
comparisonCommitsOnRemote: true,
|
|
589
684
|
target: {
|
|
590
685
|
kind: 'pull-request',
|
|
591
686
|
remote: remote.name,
|
|
@@ -632,6 +727,9 @@ function resolveCheckoutTarget() {
|
|
|
632
727
|
const repository = githubRepository(remoteUrl);
|
|
633
728
|
const headLabel = branch || currentHead;
|
|
634
729
|
const hasCommittedChanges = mergeBaseOid !== currentHead;
|
|
730
|
+
const hasUncommittedChanges = Boolean(
|
|
731
|
+
tryRepo(['status', '--porcelain=v1', '-z']),
|
|
732
|
+
);
|
|
635
733
|
const isDefaultBranchCheckout = branch === defaultBranch.name;
|
|
636
734
|
|
|
637
735
|
return {
|
|
@@ -645,6 +743,12 @@ function resolveCheckoutTarget() {
|
|
|
645
743
|
remote,
|
|
646
744
|
sourceRepositoryUrl: repository?.webUrl,
|
|
647
745
|
baseRepositoryUrl: repository?.webUrl,
|
|
746
|
+
comparisonCommitsOnRemote:
|
|
747
|
+
!hasUncommittedChanges &&
|
|
748
|
+
Boolean(
|
|
749
|
+
remote?.url &&
|
|
750
|
+
remoteContainsCommits(remote.url, [mergeBaseOid, currentHead]),
|
|
751
|
+
),
|
|
648
752
|
target: {
|
|
649
753
|
kind: 'checkout',
|
|
650
754
|
...(remote ? { remote: remote.name } : {}),
|
|
@@ -698,6 +802,12 @@ function resolveLocalTarget() {
|
|
|
698
802
|
baseRepositoryUrl: worktree
|
|
699
803
|
? undefined
|
|
700
804
|
: githubRepository(remoteUrl)?.webUrl,
|
|
805
|
+
comparisonCommitsOnRemote:
|
|
806
|
+
!worktree &&
|
|
807
|
+
Boolean(
|
|
808
|
+
remoteUrl &&
|
|
809
|
+
remoteContainsCommits(remoteUrl, [resolvedBase, resolvedHead]),
|
|
810
|
+
),
|
|
701
811
|
target: worktree
|
|
702
812
|
? { kind: 'worktree', base: { ref: 'HEAD', oid: currentHead || null } }
|
|
703
813
|
: {
|
|
@@ -798,6 +908,13 @@ function githubFileUrl(repositoryUrl, ref, path) {
|
|
|
798
908
|
return `${repositoryUrl}/blob/${encodeURIComponent(ref)}/${filePath}`;
|
|
799
909
|
}
|
|
800
910
|
|
|
911
|
+
function githubComparisonUrl(repositoryUrl, base, head) {
|
|
912
|
+
if (!repositoryUrl || !base || !head || head === 'WORKTREE') {
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|
|
915
|
+
return `${repositoryUrl}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}`;
|
|
916
|
+
}
|
|
917
|
+
|
|
801
918
|
function build() {
|
|
802
919
|
const localWorkspace =
|
|
803
920
|
tryRepo(['rev-parse', '--is-inside-work-tree']) === 'true';
|
|
@@ -806,7 +923,7 @@ function build() {
|
|
|
806
923
|
}
|
|
807
924
|
const target = resolveTarget();
|
|
808
925
|
const remoteRepository = githubRepository(target.remote?.url);
|
|
809
|
-
const summaryDoc = readJson(summariesPath, {}) || {};
|
|
926
|
+
const summaryDoc = noSummaries ? {} : readJson(summariesPath, {}) || {};
|
|
810
927
|
const allTrackedFiles = parseNameStatus(
|
|
811
928
|
target.runGit([
|
|
812
929
|
'diff',
|
|
@@ -873,6 +990,13 @@ function build() {
|
|
|
873
990
|
file.status === 'deleted' ? target.base : target.head,
|
|
874
991
|
file.path,
|
|
875
992
|
);
|
|
993
|
+
const comparisonUrl = githubComparisonUrl(
|
|
994
|
+
target.comparisonCommitsOnRemote
|
|
995
|
+
? target.sourceRepositoryUrl
|
|
996
|
+
: undefined,
|
|
997
|
+
target.base,
|
|
998
|
+
target.head,
|
|
999
|
+
);
|
|
876
1000
|
return {
|
|
877
1001
|
path: file.path,
|
|
878
1002
|
...(file.oldPath ? { oldPath: file.oldPath } : {}),
|
|
@@ -885,6 +1009,7 @@ function build() {
|
|
|
885
1009
|
patch: textPatch,
|
|
886
1010
|
snippet: binary ? '' : compactSnippet(textPatch),
|
|
887
1011
|
...(sourceUrl ? { sourceUrl } : {}),
|
|
1012
|
+
...(comparisonUrl ? { comparisonUrl } : {}),
|
|
888
1013
|
};
|
|
889
1014
|
});
|
|
890
1015
|
|
|
@@ -912,39 +1037,64 @@ function build() {
|
|
|
912
1037
|
)
|
|
913
1038
|
.digest('hex');
|
|
914
1039
|
const generatedFor =
|
|
1040
|
+
!noSummaries &&
|
|
915
1041
|
typeof summaryDoc.meta?.reviewFingerprint === 'string'
|
|
916
1042
|
? summaryDoc.meta.reviewFingerprint
|
|
917
1043
|
: undefined;
|
|
918
|
-
const summariesAreFresh =
|
|
1044
|
+
const summariesAreFresh =
|
|
1045
|
+
!noSummaries && (!generatedFor || generatedFor === reviewFingerprint);
|
|
919
1046
|
const sourceSummaries = summariesAreFresh ? summaryDoc : {};
|
|
1047
|
+
const failedFiles = summariesAreFresh
|
|
1048
|
+
? failedFileRecords(summaryDoc.meta?.failedFiles)
|
|
1049
|
+
: [];
|
|
1050
|
+
const summaryErrors = summariesAreFresh
|
|
1051
|
+
? cleanList(summaryDoc.meta?.errors)
|
|
1052
|
+
: [];
|
|
1053
|
+
const failureByPath = new Map(
|
|
1054
|
+
failedFiles.map((failure) => [failure.path, failure.reason]),
|
|
1055
|
+
);
|
|
1056
|
+
const emptyReviewComplete =
|
|
1057
|
+
filesWithoutSummaries.length === 0 &&
|
|
1058
|
+
generatedFor === reviewFingerprint &&
|
|
1059
|
+
sourceSummaries.meta?.status === 'complete';
|
|
920
1060
|
const summariesAreComplete =
|
|
921
1061
|
summariesAreFresh &&
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1062
|
+
failedFiles.length === 0 &&
|
|
1063
|
+
summaryErrors.length === 0 &&
|
|
1064
|
+
(emptyReviewComplete ||
|
|
1065
|
+
(completeChangeSummary(sourceSummaries.change) &&
|
|
1066
|
+
filesWithoutSummaries.every((file) =>
|
|
1067
|
+
completeFileSummary(sourceSummaries.files?.[file.path]),
|
|
1068
|
+
)));
|
|
1069
|
+
const completedFiles = noSummaries
|
|
1070
|
+
? 0
|
|
1071
|
+
: filesWithoutSummaries.filter((file) =>
|
|
1072
|
+
completeFileSummary(sourceSummaries.files?.[file.path]),
|
|
1073
|
+
).length;
|
|
929
1074
|
const storedStatus = summaryDoc.meta?.status;
|
|
930
|
-
const noteStatus =
|
|
931
|
-
? '
|
|
932
|
-
:
|
|
933
|
-
? '
|
|
934
|
-
:
|
|
935
|
-
?
|
|
936
|
-
: '
|
|
1075
|
+
const noteStatus = noSummaries
|
|
1076
|
+
? 'idle'
|
|
1077
|
+
: summariesAreComplete
|
|
1078
|
+
? 'complete'
|
|
1079
|
+
: !summariesAreFresh
|
|
1080
|
+
? 'stale'
|
|
1081
|
+
: ['generating', 'failed'].includes(storedStatus)
|
|
1082
|
+
? storedStatus
|
|
1083
|
+
: 'idle';
|
|
937
1084
|
const files = filesWithoutSummaries.map((file) => ({
|
|
938
1085
|
...file,
|
|
939
1086
|
summary: fileSummary(file.path, sourceSummaries.files?.[file.path]),
|
|
940
1087
|
noteReady: Boolean(
|
|
941
1088
|
completeFileSummary(sourceSummaries.files?.[file.path]),
|
|
942
1089
|
),
|
|
1090
|
+
...(failureByPath.has(file.path)
|
|
1091
|
+
? { noteFailure: failureByPath.get(file.path) }
|
|
1092
|
+
: {}),
|
|
943
1093
|
}));
|
|
944
1094
|
const change = changeSummary(sourceSummaries.change, target.changeDefaults);
|
|
945
1095
|
const content = {
|
|
946
1096
|
repo: {
|
|
947
|
-
name: remoteRepository?.name || repo
|
|
1097
|
+
name: remoteRepository?.name || basename(repo),
|
|
948
1098
|
root: localWorkspace ? repo : target.remote?.url || repo,
|
|
949
1099
|
base: target.base,
|
|
950
1100
|
head: target.head,
|
|
@@ -965,9 +1115,14 @@ function build() {
|
|
|
965
1115
|
status: noteStatus,
|
|
966
1116
|
completedFiles,
|
|
967
1117
|
totalFiles: filesWithoutSummaries.length,
|
|
1118
|
+
...(failedFiles.length ? { failedFiles } : {}),
|
|
1119
|
+
...(summaryErrors.length ? { errors: summaryErrors } : {}),
|
|
968
1120
|
...(typeof summaryDoc.meta?.model === 'string'
|
|
969
1121
|
? { model: summaryDoc.meta.model }
|
|
970
1122
|
: {}),
|
|
1123
|
+
...(typeof summaryDoc.meta?.agent === 'string'
|
|
1124
|
+
? { agent: summaryDoc.meta.agent }
|
|
1125
|
+
: {}),
|
|
971
1126
|
...(typeof summaryDoc.meta?.reasoning === 'string'
|
|
972
1127
|
? { reasoning: summaryDoc.meta.reasoning }
|
|
973
1128
|
: {}),
|
|
@@ -997,9 +1152,52 @@ function build() {
|
|
|
997
1152
|
return true;
|
|
998
1153
|
}
|
|
999
1154
|
|
|
1155
|
+
function untrackedFileKind(stat) {
|
|
1156
|
+
if (stat.isFile()) return 'file';
|
|
1157
|
+
if (stat.isSymbolicLink()) return 'symlink';
|
|
1158
|
+
if (stat.isDirectory()) return 'directory';
|
|
1159
|
+
return 'other';
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function fingerprintUntrackedPath(content, path) {
|
|
1163
|
+
const absolutePath = resolve(repo, path);
|
|
1164
|
+
const stat = lstatSync(absolutePath, { bigint: true });
|
|
1165
|
+
content.update(path);
|
|
1166
|
+
content.update('\0');
|
|
1167
|
+
content.update(untrackedFileKind(stat));
|
|
1168
|
+
content.update('\0');
|
|
1169
|
+
content.update(String(stat.size));
|
|
1170
|
+
content.update('\0');
|
|
1171
|
+
content.update(String(stat.mtimeNs));
|
|
1172
|
+
content.update('\0');
|
|
1173
|
+
if (stat.isFile()) {
|
|
1174
|
+
const descriptor = openSync(
|
|
1175
|
+
absolutePath,
|
|
1176
|
+
constants.O_RDONLY | constants.O_NOFOLLOW,
|
|
1177
|
+
);
|
|
1178
|
+
try {
|
|
1179
|
+
const opened = fstatSync(descriptor, { bigint: true });
|
|
1180
|
+
if (
|
|
1181
|
+
!opened.isFile()
|
|
1182
|
+
|| opened.dev !== stat.dev
|
|
1183
|
+
|| opened.ino !== stat.ino
|
|
1184
|
+
) {
|
|
1185
|
+
throw new Error(`${path} changed while its fingerprint was read`);
|
|
1186
|
+
}
|
|
1187
|
+
content.update(readFileSync(descriptor));
|
|
1188
|
+
} finally {
|
|
1189
|
+
closeSync(descriptor);
|
|
1190
|
+
}
|
|
1191
|
+
content.update('\0');
|
|
1192
|
+
} else if (stat.isSymbolicLink()) {
|
|
1193
|
+
content.update(readlinkSync(absolutePath));
|
|
1194
|
+
content.update('\0');
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1000
1198
|
function fingerprint() {
|
|
1001
1199
|
let summariesTime = '';
|
|
1002
|
-
if (!ignoreSummaryWatch) {
|
|
1200
|
+
if (!ignoreSummaryWatch && !noSummaries) {
|
|
1003
1201
|
try {
|
|
1004
1202
|
summariesTime = String(statSync(summariesPath).mtimeMs);
|
|
1005
1203
|
} catch {}
|
|
@@ -1012,9 +1210,26 @@ function fingerprint() {
|
|
|
1012
1210
|
summariesTime,
|
|
1013
1211
|
].join('|');
|
|
1014
1212
|
}
|
|
1213
|
+
const content = createHash('sha256');
|
|
1214
|
+
content.update(
|
|
1215
|
+
tryRepo(['diff', '--no-ext-diff', '--no-textconv', '--binary', 'HEAD', '--']),
|
|
1216
|
+
);
|
|
1217
|
+
const untracked = tryRepo([
|
|
1218
|
+
'ls-files',
|
|
1219
|
+
'--others',
|
|
1220
|
+
'--exclude-standard',
|
|
1221
|
+
'-z',
|
|
1222
|
+
])
|
|
1223
|
+
.split('\0')
|
|
1224
|
+
.filter((path) => path && !excludedPaths.has(path))
|
|
1225
|
+
.sort();
|
|
1226
|
+
for (const path of untracked) {
|
|
1227
|
+
fingerprintUntrackedPath(content, path);
|
|
1228
|
+
}
|
|
1015
1229
|
return [
|
|
1016
1230
|
tryRepo(['rev-parse', 'HEAD']),
|
|
1017
1231
|
tryRepo(['status', '--porcelain=v1', '--untracked-files=all']),
|
|
1232
|
+
content.digest('hex'),
|
|
1018
1233
|
summariesTime,
|
|
1019
1234
|
].join('|');
|
|
1020
1235
|
}
|
|
@@ -1026,25 +1241,45 @@ const refresh = () => {
|
|
|
1026
1241
|
return true;
|
|
1027
1242
|
} catch (error) {
|
|
1028
1243
|
console.error(error.message);
|
|
1029
|
-
|
|
1244
|
+
process.exitCode = 1;
|
|
1030
1245
|
return false;
|
|
1031
1246
|
}
|
|
1032
1247
|
};
|
|
1033
1248
|
|
|
1034
1249
|
const started = refresh();
|
|
1035
1250
|
if (watching && started) {
|
|
1036
|
-
let last
|
|
1251
|
+
let last;
|
|
1037
1252
|
let remoteWait = 0;
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
if (
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1253
|
+
let watcher;
|
|
1254
|
+
const stopWatching = (error) => {
|
|
1255
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
1256
|
+
process.exitCode = 1;
|
|
1257
|
+
if (watcher) clearInterval(watcher);
|
|
1258
|
+
};
|
|
1259
|
+
const poll = () => {
|
|
1260
|
+
try {
|
|
1261
|
+
const next = fingerprint();
|
|
1262
|
+
if (last === undefined) {
|
|
1263
|
+
last = next;
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1266
|
+
remoteWait += watchInterval;
|
|
1267
|
+
const remoteDue = remoteMode && remoteWait >= remoteRefreshInterval;
|
|
1268
|
+
if (next !== last || remoteDue || watchContent) {
|
|
1269
|
+
last = next;
|
|
1270
|
+
remoteWait = 0;
|
|
1271
|
+
if (!refresh()) {
|
|
1272
|
+
clearInterval(watcher);
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return true;
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
stopWatching(error);
|
|
1279
|
+
return false;
|
|
1046
1280
|
}
|
|
1047
|
-
}
|
|
1281
|
+
};
|
|
1282
|
+
if (poll()) watcher = setInterval(poll, watchInterval);
|
|
1048
1283
|
} else if (watching) {
|
|
1049
1284
|
process.exitCode = 1;
|
|
1050
1285
|
}
|