srcly 0.1.30__tar.gz → 0.1.31__tar.gz
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.
- {srcly-0.1.30 → srcly-0.1.31}/PKG-INFO +1 -1
- {srcly-0.1.30 → srcly-0.1.31}/app/run.py +54 -3
- {srcly-0.1.30 → srcly-0.1.31}/app/services/analysis.py +27 -19
- {srcly-0.1.30 → srcly-0.1.31}/app/services/reporting.py +163 -12
- srcly-0.1.30/app/static/assets/index-Bn6hDYiR.js → srcly-0.1.31/app/static/assets/index--Q3ju03c.js +3 -3
- srcly-0.1.31/app/static/assets/index-DOSG0br7.css +1 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/index.html +2 -2
- {srcly-0.1.30 → srcly-0.1.31}/pyproject.toml +1 -1
- srcly-0.1.30/app/static/assets/index-DK1czI50.css +0 -1
- {srcly-0.1.30 → srcly-0.1.31}/.gitignore +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/README.md +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/.gitignore +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/__init__.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/config.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/main.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/models.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/routers/analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/routers/files.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/analysis_types.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/cache.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/css/css_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/data_flow_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/focus_overlay.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/ipynb/ipynb_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/markdown/markdown_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/python/python_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/services/typescript/typescript_analysis.py +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/abap-BdImnpbu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/actionscript-3-CfeIJUat.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ada-bCR0ucgS.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/andromeeda-C-Jbm3Hp.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/angular-html-CU67Zn6k.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/angular-ts-BwZT4LLn.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/apache-Pmp26Uib.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/apex-DhZLUxFE.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/apl-dKokRX4l.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/applescript-Co6uUVPk.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ara-BRHolxvo.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/asciidoc-Dv7Oe6Be.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/asm-D_Q5rh1f.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/astro-CbQHKStN.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/aurora-x-D-2ljcwZ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/awk-DMzUqQB5.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ayu-dark-Cv9koXgw.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ballerina-BFfxhgS-.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/bat-BkioyH1T.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/beancount-k_qm7-4y.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/berry-D08WgyRC.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/bibtex-CHM0blh-.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/bicep-Bmn6On1c.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/blade-DVc8C-J4.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/bsl-BO_Y6i37.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/c-BIGW1oBm.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cadence-Bv_4Rxtq.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cairo-KRGpt6FW.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/catppuccin-frappe-DFWUc33u.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/catppuccin-latte-C9dUb6Cb.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/catppuccin-macchiato-DQyhUUbL.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/catppuccin-mocha-D87Tk5Gz.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/clarity-D53aC0YG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/clojure-P80f7IUj.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cmake-D1j8_8rp.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cobol-nwyudZeR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/codeowners-Bp6g37R7.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/codeql-DsOJ9woJ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/coffee-Ch7k5sss.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/common-lisp-Cg-RD9OK.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/coq-DkFqJrB1.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cpp-CofmeUqb.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/crystal-tKQVLTB8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/csharp-CX12Zw3r.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/css-DPfMkruS.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/csv-fuZLfV_i.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cue-D82EKSYY.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/cypher-COkxafJQ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/d-85-TOEBH.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dark-plus-eOWES_5F.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dart-CF10PKvl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dax-CEL-wOlO.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/desktop-BmXAJ9_W.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/diff-D97Zzqfu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/docker-BcOcwvcX.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dotenv-Da5cRb03.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dracula-BzJJZx-M.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dracula-soft-BXkSAIEj.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/dream-maker-BtqSS_iP.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/edge-BkV0erSs.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/elixir-CDX3lj18.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/elm-DbKCFpqz.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/emacs-lisp-C9XAeP06.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/erb-BOJIQeun.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/erlang-DsQrWhSR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/everforest-dark-BgDCqdQA.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/everforest-light-C8M2exoo.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fennel-BYunw83y.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fish-BvzEVeQv.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fluent-C4IJs8-o.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fortran-fixed-form-BZjJHVRy.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fortran-free-form-D22FLkUw.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/fsharp-CXgrBDvD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gdresource-B7Tvp0Sc.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gdscript-DTMYz4Jt.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gdshader-DkwncUOv.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/genie-D0YGMca9.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gherkin-DyxjwDmM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/git-commit-F4YmCXRG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/git-rebase-r7XF79zn.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-dark-DHJKELXO.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-dark-default-Cuk6v7N8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-dark-dimmed-DH5Ifo-i.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-light-DAi9KRSo.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-light-default-D7oLnXFd.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/github-light-high-contrast-BfjtVDDH.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gleam-BspZqrRM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/glimmer-js-Rg0-pVw9.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/glimmer-ts-U6CK756n.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/glsl-DplSGwfg.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gnuplot-DdkO51Og.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/go-Dn2_MT6a.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/graphql-ChdNCCLP.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/groovy-gcz8RCvz.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-dark-medium-GsRaNv29.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-dark-soft-CVdnzihN.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-light-hard-CH1njM8p.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-light-medium-DRw_LuNl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/gruvbox-light-soft-hJgmCMqR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hack-CaT9iCJl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/haml-B8DHNrY2.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/handlebars-BL8al0AC.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/haskell-Df6bDoY_.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/haxe-CzTSHFRz.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hcl-BWvSN4gD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hjson-D5-asLiD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hlsl-D3lLCCz7.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/houston-DnULxvSX.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/html-GMplVEZG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/html-derivative-BFtXZ54Q.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/http-jrhK8wxY.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hurl-irOxFIW8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hxml-Bvhsp5Yf.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/hy-DFXneXwc.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/imba-DGztddWO.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ini-BEwlwnbL.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/java-CylS5w8V.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/javascript-wDzz0qaB.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jinja-4LBKfQ-Z.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jison-wvAkD_A8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/json-Cp-IABpG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/json5-C9tS-k6U.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jsonc-Des-eS-w.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jsonl-DcaNXYhu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jsonnet-DFQXde-d.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jssm-C2t-YnRu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/jsx-g9-lgVsj.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/julia-C8NyazO9.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kanagawa-dragon-CkXjmgJE.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kanagawa-lotus-CfQXZHmo.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kanagawa-wave-DWedfzmr.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kdl-DV7GczEv.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kotlin-BdnUsdx6.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/kusto-BvAqAH-y.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/laserwave-DUszq2jm.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/latex-BUKiar2Z.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/lean-DP1Csr6i.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/less-B1dDrJ26.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/light-plus-B7mTdjB0.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/liquid-DYVedYrR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/llvm-BtvRca6l.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/log-2UxHyX5q.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/logo-BtOb2qkB.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/lua-BbnMAYS6.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/luau-CXu1NL6O.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/make-CHLpvVh8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/markdown-Cvjx9yec.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/marko-CPi9NSCl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/material-theme-D5KoaKCx.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/material-theme-darker-BfHTSMKl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/material-theme-lighter-B0m2ddpp.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/material-theme-ocean-CyktbL80.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/material-theme-palenight-Csfq5Kiy.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/matlab-D7o27uSR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/mdc-DUICxH0z.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/mdx-Cmh6b_Ma.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/mermaid-DKYwYmdq.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/min-dark-CafNBF8u.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/min-light-CTRr51gU.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/mipsasm-CKIfxQSi.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/mojo-1DNp92w6.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/monokai-D4h5O-jR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/move-Bu9oaDYs.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/narrat-DRg8JJMk.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nextflow-CUEJCptM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nginx-DknmC5AR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/night-owl-C39BiMTA.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nim-CVrawwO9.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nix-BbRYJGeE.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nord-Ddv68eIx.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/nushell-C-sUppwS.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/objective-c-DXmwc3jG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/objective-cpp-CLxacb5B.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ocaml-C0hk2d4L.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/one-dark-pro-DVMEJ2y_.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/one-light-PoHY5YXO.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/pascal-D93ZcfNL.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/perl-C0TMdlhV.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/php-CDn_0X-4.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/pkl-u5AG7uiY.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/plastic-3e1v2bzS.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/plsql-ChMvpjG-.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/po-BTJTHyun.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/poimandres-CS3Unz2-.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/polar-C0HS_06l.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/postcss-CXtECtnM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/powerquery-CEu0bR-o.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/powershell-Dpen1YoG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/prisma-Dd19v3D-.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/prolog-CbFg5uaA.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/proto-DyJlTyXw.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/pug-CGlum2m_.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/puppet-BMWR74SV.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/purescript-CklMAg4u.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/python-B6aJPvgy.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/qml-3beO22l8.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/qmldir-C8lEn-DE.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/qss-IeuSbFQv.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/r-DiinP2Uv.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/racket-BqYA7rlc.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/raku-DXvB9xmW.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/razor-WgofotgN.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/red-bN70gL4F.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/reg-C-SQnVFl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/regexp-CDVJQ6XC.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rel-C3B-1QV4.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/riscv-BM1_JUlF.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rose-pine-BHrmToEH.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rose-pine-dawn-CnK8MTSM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rose-pine-moon-NleAzG8P.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rosmsg-BJDFO7_C.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rst-B0xPkSld.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ruby-BvKwtOVI.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/rust-B1yitclQ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/sas-cz2c8ADy.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/sass-Cj5Yp3dK.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/scala-C151Ov-r.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/scheme-C98Dy4si.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/scss-OYdSNvt2.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/sdbl-DVxCFoDh.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/shaderlab-Dg9Lc6iA.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/shellscript-Yzrsuije.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/shellsession-BADoaaVG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/slack-dark-BthQWCQV.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/slack-ochin-DqwNpetd.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/smalltalk-BERRCDM3.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/snazzy-light-Bw305WKR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/solarized-dark-DXbdFlpD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/solarized-light-L9t79GZl.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/solidity-BbcW6ACK.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/soy-Brmx7dQM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/sparql-rVzFXLq3.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/splunk-BtCnVYZw.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/sql-BLtJtn59.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ssh-config-_ykCGR6B.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/stata-BH5u7GGu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/stylus-BEDo0Tqx.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/svelte-3Dk4HxPD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/swift-Dg5xB15N.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/synthwave-84-CbfX1IO0.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/system-verilog-CnnmHF94.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/systemd-4A_iFExJ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/talonscript-CkByrt1z.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tasl-QIJgUcNo.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tcl-dwOrl1Do.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/templ-W15q3VgB.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/terraform-BETggiCN.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tex-Cppo0RY3.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tokyo-night-hegEt444.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/toml-vGWfd6FD.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/ts-tags-zn1MmPIZ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tsv-B_m7g4N7.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/tsx-COt5Ahok.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/turtle-BsS91CYL.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/twig-CO9l9SDP.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/typescript-BPQ3VLAy.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/typespec-Df68jz8_.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/typst-DHCkPAjA.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/v-BcVCzyr7.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vala-CsfeWuGM.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vb-D17OF-Vu.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/verilog-BQ8w6xss.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vesper-DU1UobuO.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vhdl-CeAyd5Ju.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/viml-CJc9bBzg.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vitesse-black-Bkuqu6BP.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vitesse-dark-D0r3Knsf.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vitesse-light-CVO1_9PV.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vue-CCoi5OLL.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vue-html-DAAvJJDi.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vue-vine-_Ih-lPRR.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/vyper-CDx5xZoG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wasm-CG6Dc4jp.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wasm-MzD3tlZU.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wenyan-BV7otONQ.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wgsl-Dx-B1_4e.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wikitext-BhOHFoWU.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wit-5i3qLPDT.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/wolfram-lXgVvXCa.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/xml-sdJ4AIDG.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/xsl-CtQFsRM5.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/yaml-Buea-lGh.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/zenscript-DVFEvuxE.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/assets/zig-VOosw3JB.js +0 -0
- {srcly-0.1.30 → srcly-0.1.31}/app/static/vite.svg +0 -0
|
@@ -170,6 +170,33 @@ def _run_headless(argv: list[str]) -> None:
|
|
|
170
170
|
)
|
|
171
171
|
report_parser.add_argument("--refresh", action="store_true", help="Ignore cache when cache support is available.")
|
|
172
172
|
report_parser.add_argument("--limit", type=int, default=50, help="Maximum ranked findings to emit.")
|
|
173
|
+
report_parser.add_argument(
|
|
174
|
+
"--quiet",
|
|
175
|
+
action="store_true",
|
|
176
|
+
help="Suppress per-file scan progress and print only the report summary.",
|
|
177
|
+
)
|
|
178
|
+
report_parser.add_argument(
|
|
179
|
+
"--agent-compact",
|
|
180
|
+
action="store_true",
|
|
181
|
+
help="Expand compact agent artifacts from the default top 10 findings to the top 20.",
|
|
182
|
+
)
|
|
183
|
+
report_parser.add_argument(
|
|
184
|
+
"--focus",
|
|
185
|
+
action="append",
|
|
186
|
+
default=None,
|
|
187
|
+
help="Path prefix to promote in report ranking. May be repeated.",
|
|
188
|
+
)
|
|
189
|
+
report_parser.add_argument(
|
|
190
|
+
"--deprioritize",
|
|
191
|
+
action="append",
|
|
192
|
+
default=None,
|
|
193
|
+
help="Path prefix to lower in report ranking. May be repeated. Defaults include .agents and docs/idea.",
|
|
194
|
+
)
|
|
195
|
+
report_parser.add_argument(
|
|
196
|
+
"--no-default-deprioritize",
|
|
197
|
+
action="store_true",
|
|
198
|
+
help="Disable default report deprioritization for agent docs, vendored, and generated-like paths.",
|
|
199
|
+
)
|
|
173
200
|
report_parser.add_argument(
|
|
174
201
|
"--profile",
|
|
175
202
|
choices=["general", "frontend", "backend", "typescript", "python", "docs"],
|
|
@@ -234,6 +261,11 @@ def _run_headless(argv: list[str]) -> None:
|
|
|
234
261
|
tree_top=args.tree_top,
|
|
235
262
|
include_tree=args.include_tree,
|
|
236
263
|
include_dependencies=args.include_dependencies,
|
|
264
|
+
focus_paths=args.focus,
|
|
265
|
+
deprioritize_paths=_deprioritize_paths(args),
|
|
266
|
+
agent_compact=args.agent_compact,
|
|
267
|
+
verbose=not args.quiet,
|
|
268
|
+
output_format=args.format,
|
|
237
269
|
)
|
|
238
270
|
print(reporting._json_dumps(payload))
|
|
239
271
|
else:
|
|
@@ -250,10 +282,17 @@ def _run_headless(argv: list[str]) -> None:
|
|
|
250
282
|
tree_top=args.tree_top,
|
|
251
283
|
include_tree=args.include_tree,
|
|
252
284
|
include_dependencies=args.include_dependencies,
|
|
285
|
+
focus_paths=args.focus,
|
|
286
|
+
deprioritize_paths=_deprioritize_paths(args),
|
|
287
|
+
agent_compact=args.agent_compact,
|
|
288
|
+
verbose=not args.quiet,
|
|
253
289
|
)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
290
|
+
if args.quiet:
|
|
291
|
+
print(_format_quiet_report_summary(out_dir, written))
|
|
292
|
+
else:
|
|
293
|
+
print(f"Wrote Srcly report artifacts to {out_dir}")
|
|
294
|
+
for path in written.values():
|
|
295
|
+
print(f"- {path}")
|
|
257
296
|
if args.fail_on != "none":
|
|
258
297
|
if args.stdout:
|
|
259
298
|
_enforce_fail_on_payload(payload["findings"], args.fail_on)
|
|
@@ -295,6 +334,18 @@ def _profile_from_metrics(metrics: list[str] | None) -> str:
|
|
|
295
334
|
return "_custom"
|
|
296
335
|
|
|
297
336
|
|
|
337
|
+
def _deprioritize_paths(args) -> tuple[str, ...]:
|
|
338
|
+
if args.no_default_deprioritize:
|
|
339
|
+
return tuple(args.deprioritize or ())
|
|
340
|
+
return tuple(reporting.DEFAULT_DEPRIORITIZED_PATHS) + tuple(args.deprioritize or ())
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _format_quiet_report_summary(out_dir: Path, written: dict[str, Path]) -> str:
|
|
344
|
+
names = ", ".join(path.name for path in written.values())
|
|
345
|
+
read_first = out_dir / "report.md"
|
|
346
|
+
return f"Wrote {len(written)} Srcly artifacts to {out_dir}: {names}\nRead first: {read_first}"
|
|
347
|
+
|
|
348
|
+
|
|
298
349
|
def _enforce_fail_on(findings_path: Path, fail_on: str) -> None:
|
|
299
350
|
import json
|
|
300
351
|
|
|
@@ -481,6 +481,8 @@ def _run_file_analyses_with_hard_timeouts(
|
|
|
481
481
|
files_to_scan: list[str],
|
|
482
482
|
timeout_seconds: float,
|
|
483
483
|
max_workers: int,
|
|
484
|
+
*,
|
|
485
|
+
verbose: bool = True,
|
|
484
486
|
) -> list:
|
|
485
487
|
"""
|
|
486
488
|
Analyze files with a *hard* per-file timeout by running each file in its own
|
|
@@ -508,7 +510,8 @@ def _run_file_analyses_with_hard_timeouts(
|
|
|
508
510
|
|
|
509
511
|
# Log every file *before* it is processed so we can identify the
|
|
510
512
|
# last-started file if analysis hangs or crashes.
|
|
511
|
-
|
|
513
|
+
if verbose:
|
|
514
|
+
print(f"➡️ [{next_index}/{total_count}] Starting analysis: {file_path}", file=sys.stderr, flush=True)
|
|
512
515
|
|
|
513
516
|
recv_conn, send_conn = ctx.Pipe(duplex=False)
|
|
514
517
|
proc = ctx.Process(
|
|
@@ -540,11 +543,12 @@ def _run_file_analyses_with_hard_timeouts(
|
|
|
540
543
|
# Either finished, or timed out.
|
|
541
544
|
if proc.is_alive() and elapsed > timeout_seconds:
|
|
542
545
|
completed_count += 1
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
546
|
+
if verbose:
|
|
547
|
+
print(
|
|
548
|
+
f"❌ [{completed_count}/{total_count}] Timeout analyzing {file_path} after {elapsed:.2f}s (terminated)",
|
|
549
|
+
file=sys.stderr,
|
|
550
|
+
flush=True,
|
|
551
|
+
)
|
|
548
552
|
try:
|
|
549
553
|
proc.terminate()
|
|
550
554
|
except Exception:
|
|
@@ -579,13 +583,14 @@ def _run_file_analyses_with_hard_timeouts(
|
|
|
579
583
|
pass
|
|
580
584
|
|
|
581
585
|
if isinstance(result, dict) and "error" in result:
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
586
|
+
if verbose:
|
|
587
|
+
print(
|
|
588
|
+
f"❌ [{completed_count}/{total_count}] Error analyzing {file_path}: {result.get('error')}",
|
|
589
|
+
file=sys.stderr,
|
|
590
|
+
flush=True,
|
|
591
|
+
)
|
|
587
592
|
else:
|
|
588
|
-
if _should_log_file_progress(completed_count, total_count):
|
|
593
|
+
if verbose and _should_log_file_progress(completed_count, total_count):
|
|
589
594
|
print(f"✅ [{completed_count}/{total_count}] Analyzed {file_path}", file=sys.stderr, flush=True)
|
|
590
595
|
results.append(result)
|
|
591
596
|
|
|
@@ -598,8 +603,9 @@ def _run_file_analyses_with_hard_timeouts(
|
|
|
598
603
|
return results
|
|
599
604
|
|
|
600
605
|
|
|
601
|
-
def scan_codebase(root_path: Path) -> Node:
|
|
602
|
-
|
|
606
|
+
def scan_codebase(root_path: Path, *, verbose: bool = True) -> Node:
|
|
607
|
+
if verbose:
|
|
608
|
+
print(f"🔍 Scanning: {root_path}", file=sys.stderr, flush=True)
|
|
603
609
|
|
|
604
610
|
# Load .gitignore spec (repo-wide, with nested .gitignore support)
|
|
605
611
|
ignore_root, gitignore_spec = _load_gitignore_spec(root_path)
|
|
@@ -641,16 +647,18 @@ def scan_codebase(root_path: Path) -> Node:
|
|
|
641
647
|
if current_ignored_count > 0:
|
|
642
648
|
ignored_counts[str(root_dir_path)] = current_ignored_count
|
|
643
649
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
650
|
+
if verbose:
|
|
651
|
+
print(
|
|
652
|
+
f"📂 Analyzing {len(files_to_scan)} source files... (workers={MAX_ANALYSIS_WORKERS}, timeout={PER_FILE_ANALYSIS_TIMEOUT_SECONDS}s)",
|
|
653
|
+
file=sys.stderr,
|
|
654
|
+
flush=True,
|
|
655
|
+
)
|
|
649
656
|
|
|
650
657
|
analysis_results = _run_file_analyses_with_hard_timeouts(
|
|
651
658
|
files_to_scan=files_to_scan,
|
|
652
659
|
timeout_seconds=PER_FILE_ANALYSIS_TIMEOUT_SECONDS,
|
|
653
660
|
max_workers=MAX_ANALYSIS_WORKERS,
|
|
661
|
+
verbose=verbose,
|
|
654
662
|
)
|
|
655
663
|
|
|
656
664
|
tree_root = create_node("root", "folder", str(root_path))
|
|
@@ -16,6 +16,8 @@ ARTIFACT_VERSION = 1
|
|
|
16
16
|
ProfileName = Literal["general", "frontend", "backend", "typescript", "python", "docs"]
|
|
17
17
|
OutputFormat = Literal["markdown", "json", "both"]
|
|
18
18
|
|
|
19
|
+
DEFAULT_DEPRIORITIZED_PATHS = (".agents/", "docs/idea/", "vendor/", "vendors/", "node_modules/")
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
@dataclass(frozen=True)
|
|
21
23
|
class MetricDefinition:
|
|
@@ -114,8 +116,8 @@ PROFILE_WEIGHTS: dict[str, dict[str, float]] = {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
|
|
117
|
-
def scan_tree(root_path: Path) -> Node:
|
|
118
|
-
return analysis.scan_codebase(root_path)
|
|
119
|
+
def scan_tree(root_path: Path, *, verbose: bool = True) -> Node:
|
|
120
|
+
return analysis.scan_codebase(root_path, verbose=verbose)
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
def write_scan(root_path: Path, out_path: Path, *, refresh: bool = False) -> Path:
|
|
@@ -139,22 +141,31 @@ def build_report_payload(
|
|
|
139
141
|
tree_top: int = 6,
|
|
140
142
|
include_tree: bool = False,
|
|
141
143
|
include_dependencies: bool = False,
|
|
144
|
+
focus_paths: Iterable[str] | None = None,
|
|
145
|
+
deprioritize_paths: Iterable[str] | None = DEFAULT_DEPRIORITIZED_PATHS,
|
|
146
|
+
agent_compact: bool = False,
|
|
147
|
+
verbose: bool = True,
|
|
148
|
+
output_format: OutputFormat = "both",
|
|
142
149
|
) -> dict[str, Any]:
|
|
143
150
|
del refresh # Cache support is currently a no-op in app.services.cache.
|
|
144
151
|
if profile not in PROFILE_WEIGHTS:
|
|
145
152
|
raise ValueError(f"Unknown profile: {profile}")
|
|
146
153
|
|
|
147
|
-
tree = scan_tree(root_path)
|
|
148
|
-
ranked = rank_nodes(tree, profile=profile)
|
|
154
|
+
tree = scan_tree(root_path, verbose=verbose)
|
|
155
|
+
ranked = rank_nodes(tree, profile=profile, root_path=root_path, focus_paths=focus_paths, deprioritize_paths=deprioritize_paths)
|
|
149
156
|
findings = build_findings(ranked, limit=limit)
|
|
150
157
|
summary_tree = summarize_tree(tree, ranked, root_path=root_path, max_depth=tree_depth, top_children=tree_top)
|
|
151
158
|
manifest = build_manifest(root_path=root_path, profile=profile)
|
|
152
159
|
metrics_schema = build_metrics_schema()
|
|
160
|
+
top_findings_limit = min(limit, 20 if agent_compact else 10)
|
|
161
|
+
top_findings = {"findings": findings[:top_findings_limit]}
|
|
162
|
+
action_plan_markdown = render_action_plan(findings=findings, profile=profile)
|
|
153
163
|
payload = {
|
|
154
164
|
"manifest": manifest,
|
|
155
165
|
"tree_summary": summary_tree,
|
|
156
166
|
"hotspots": {"rankings": [r for r in ranked[:limit]]},
|
|
157
167
|
"findings": {"findings": findings},
|
|
168
|
+
"findings_top": top_findings,
|
|
158
169
|
"metrics_schema": metrics_schema,
|
|
159
170
|
"report_markdown": render_markdown_report(
|
|
160
171
|
manifest=manifest,
|
|
@@ -162,13 +173,22 @@ def build_report_payload(
|
|
|
162
173
|
ranked=ranked[:limit],
|
|
163
174
|
summary_tree=summary_tree,
|
|
164
175
|
profile=profile,
|
|
176
|
+
focus_paths=focus_paths,
|
|
177
|
+
deprioritize_paths=deprioritize_paths,
|
|
165
178
|
),
|
|
179
|
+
"action_plan_markdown": action_plan_markdown,
|
|
166
180
|
"agent_skill_markdown": render_agent_skill(),
|
|
167
181
|
}
|
|
168
182
|
if include_tree:
|
|
169
183
|
payload["tree"] = _node_to_json(tree)
|
|
170
184
|
if include_dependencies:
|
|
171
185
|
payload["dependencies"] = build_dependencies(root_path)
|
|
186
|
+
payload["manifest"] = enrich_manifest_with_artifacts(
|
|
187
|
+
payload,
|
|
188
|
+
output_format=output_format,
|
|
189
|
+
include_tree=include_tree,
|
|
190
|
+
include_dependencies=include_dependencies,
|
|
191
|
+
)
|
|
172
192
|
return payload
|
|
173
193
|
|
|
174
194
|
|
|
@@ -184,6 +204,10 @@ def write_report(
|
|
|
184
204
|
tree_top: int = 6,
|
|
185
205
|
include_tree: bool = False,
|
|
186
206
|
include_dependencies: bool = False,
|
|
207
|
+
focus_paths: Iterable[str] | None = None,
|
|
208
|
+
deprioritize_paths: Iterable[str] | None = DEFAULT_DEPRIORITIZED_PATHS,
|
|
209
|
+
agent_compact: bool = False,
|
|
210
|
+
verbose: bool = True,
|
|
187
211
|
) -> dict[str, Path]:
|
|
188
212
|
payload = build_report_payload(
|
|
189
213
|
root_path,
|
|
@@ -194,6 +218,11 @@ def write_report(
|
|
|
194
218
|
tree_top=tree_top,
|
|
195
219
|
include_tree=include_tree,
|
|
196
220
|
include_dependencies=include_dependencies,
|
|
221
|
+
focus_paths=focus_paths,
|
|
222
|
+
deprioritize_paths=deprioritize_paths,
|
|
223
|
+
agent_compact=agent_compact,
|
|
224
|
+
verbose=verbose,
|
|
225
|
+
output_format=output_format,
|
|
197
226
|
)
|
|
198
227
|
|
|
199
228
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -204,6 +233,7 @@ def write_report(
|
|
|
204
233
|
written["tree_summary"] = _write_json(out_dir / "tree.summary.json", payload["tree_summary"])
|
|
205
234
|
written["hotspots"] = _write_json(out_dir / "hotspots.json", payload["hotspots"])
|
|
206
235
|
written["findings"] = _write_json(out_dir / "findings.json", payload["findings"])
|
|
236
|
+
written["findings_top"] = _write_json(out_dir / "findings.top.json", payload["findings_top"])
|
|
207
237
|
written["metrics_schema"] = _write_json(out_dir / "metrics.schema.json", payload["metrics_schema"])
|
|
208
238
|
if include_tree:
|
|
209
239
|
written["tree"] = _write_json(out_dir / "tree.json", payload["tree"])
|
|
@@ -212,15 +242,25 @@ def write_report(
|
|
|
212
242
|
|
|
213
243
|
if output_format in ("markdown", "both"):
|
|
214
244
|
written["report"] = _write_text(out_dir / "report.md", payload["report_markdown"])
|
|
245
|
+
written["action_plan"] = _write_text(out_dir / "action-plan.md", payload["action_plan_markdown"])
|
|
215
246
|
written["agent_skill"] = _write_text(out_dir / "agent-skill.md", payload["agent_skill_markdown"])
|
|
216
247
|
|
|
217
248
|
return written
|
|
218
249
|
|
|
219
250
|
|
|
220
|
-
def rank_nodes(
|
|
251
|
+
def rank_nodes(
|
|
252
|
+
root: Node,
|
|
253
|
+
*,
|
|
254
|
+
profile: str = "general",
|
|
255
|
+
root_path: Path | None = None,
|
|
256
|
+
focus_paths: Iterable[str] | None = None,
|
|
257
|
+
deprioritize_paths: Iterable[str] | None = DEFAULT_DEPRIORITIZED_PATHS,
|
|
258
|
+
) -> list[dict[str, Any]]:
|
|
221
259
|
weights = PROFILE_WEIGHTS.get(profile, PROFILE_WEIGHTS["general"])
|
|
222
260
|
nodes = [node for node in iter_nodes(root) if node.type != "folder" and node.metrics is not None]
|
|
223
261
|
maxima = _metric_maxima(nodes, weights.keys())
|
|
262
|
+
focus_prefixes = tuple(_normalize_path_filter(path) for path in (focus_paths or ()) if path)
|
|
263
|
+
deprioritize_prefixes = tuple(_normalize_path_filter(path) for path in (deprioritize_paths or ()) if path)
|
|
224
264
|
ranked: list[dict[str, Any]] = []
|
|
225
265
|
|
|
226
266
|
for node in nodes:
|
|
@@ -248,6 +288,9 @@ def rank_nodes(root: Node, *, profile: str = "general") -> list[dict[str, Any]]:
|
|
|
248
288
|
continue
|
|
249
289
|
|
|
250
290
|
score = weighted_sum / weight_sum
|
|
291
|
+
display_path = _display_path(node.path, root_path) if root_path is not None else node.path
|
|
292
|
+
path_priority = _path_priority(display_path, focus_prefixes=focus_prefixes, deprioritize_prefixes=deprioritize_prefixes)
|
|
293
|
+
adjusted_score = max(0.0, min(1.0, score * path_priority["multiplier"]))
|
|
251
294
|
ranked.append(
|
|
252
295
|
{
|
|
253
296
|
"path": node.path,
|
|
@@ -255,7 +298,9 @@ def rank_nodes(root: Node, *, profile: str = "general") -> list[dict[str, Any]]:
|
|
|
255
298
|
"node_type": node.type,
|
|
256
299
|
"start_line": node.start_line,
|
|
257
300
|
"end_line": node.end_line,
|
|
258
|
-
"score": round(
|
|
301
|
+
"score": round(adjusted_score, 4),
|
|
302
|
+
"raw_score": round(score, 4),
|
|
303
|
+
"path_priority": path_priority["label"],
|
|
259
304
|
"drivers": sorted(drivers, key=lambda d: d["normalized"] * d["weight"], reverse=True)[:5],
|
|
260
305
|
"metrics": _metrics_subset(node.metrics, weights.keys()),
|
|
261
306
|
"reason": _reason_for_node(node),
|
|
@@ -294,9 +339,11 @@ def build_findings(ranked: list[dict[str, Any]], *, limit: int = 50) -> list[dic
|
|
|
294
339
|
"start_line": item["start_line"],
|
|
295
340
|
"end_line": item["end_line"],
|
|
296
341
|
"score": item["score"],
|
|
342
|
+
"raw_score": item.get("raw_score", item["score"]),
|
|
343
|
+
"path_priority": item.get("path_priority", "neutral"),
|
|
297
344
|
"title": title,
|
|
298
345
|
"evidence": metrics,
|
|
299
|
-
"suggested_action": _suggested_action(category),
|
|
346
|
+
"suggested_action": _suggested_action(category, item),
|
|
300
347
|
"agent_prompt": _agent_prompt(category),
|
|
301
348
|
}
|
|
302
349
|
)
|
|
@@ -357,6 +404,8 @@ def render_markdown_report(
|
|
|
357
404
|
ranked: list[dict[str, Any]],
|
|
358
405
|
summary_tree: dict[str, Any],
|
|
359
406
|
profile: str,
|
|
407
|
+
focus_paths: Iterable[str] | None = None,
|
|
408
|
+
deprioritize_paths: Iterable[str] | None = DEFAULT_DEPRIORITIZED_PATHS,
|
|
360
409
|
) -> str:
|
|
361
410
|
high_count = sum(1 for finding in findings if finding["priority"] == "high")
|
|
362
411
|
medium_count = sum(1 for finding in findings if finding["priority"] == "medium")
|
|
@@ -369,18 +418,22 @@ def render_markdown_report(
|
|
|
369
418
|
f"- Profile: `{profile}`",
|
|
370
419
|
f"- Ranked hotspots: {len(ranked)}",
|
|
371
420
|
f"- Findings: {len(findings)} total, {high_count} high, {medium_count} medium.",
|
|
421
|
+
f"- Focus paths: `{', '.join(focus_paths or []) or 'none'}`",
|
|
422
|
+
f"- Deprioritized paths: `{', '.join(deprioritize_paths or []) or 'none'}`",
|
|
423
|
+
"",
|
|
424
|
+
"Read first: `report.md`, then `findings.top.json`, then `action-plan.md`. Load full JSON artifacts only when you need detail beyond the top findings.",
|
|
372
425
|
"",
|
|
373
426
|
"## Top Targets",
|
|
374
427
|
"",
|
|
375
|
-
"| Rank | Priority | Path | Why | Suggested next action |",
|
|
376
|
-
"| --- | --- | --- | --- | --- |",
|
|
428
|
+
"| Rank | Priority | Path priority | Path | Why | Suggested next action |",
|
|
429
|
+
"| --- | --- | --- | --- | --- | --- |",
|
|
377
430
|
]
|
|
378
431
|
|
|
379
432
|
for finding in findings[:10]:
|
|
380
433
|
location = _format_location(finding)
|
|
381
434
|
evidence = ", ".join(f"{key}={value}" for key, value in finding["evidence"].items() if value) or "metric signal"
|
|
382
435
|
lines.append(
|
|
383
|
-
f"| {finding['id']} | {finding['priority']} | `{location}` | {finding['title']} ({evidence}) | {finding['suggested_action']} |"
|
|
436
|
+
f"| {finding['id']} | {finding['priority']} | {finding.get('path_priority', 'neutral')} | `{location}` | {finding['title']} ({evidence}) | {finding['suggested_action']} |"
|
|
384
437
|
)
|
|
385
438
|
|
|
386
439
|
lines.extend(
|
|
@@ -437,6 +490,33 @@ When reporting back, include:
|
|
|
437
490
|
"""
|
|
438
491
|
|
|
439
492
|
|
|
493
|
+
def render_action_plan(*, findings: list[dict[str, Any]], profile: str) -> str:
|
|
494
|
+
lines = [
|
|
495
|
+
"# Srcly Action Plan",
|
|
496
|
+
"",
|
|
497
|
+
f"Profile: `{profile}`",
|
|
498
|
+
"",
|
|
499
|
+
"## Suggested Work Queue",
|
|
500
|
+
"",
|
|
501
|
+
]
|
|
502
|
+
for index, finding in enumerate(findings[:5], start=1):
|
|
503
|
+
location = _format_location(finding)
|
|
504
|
+
evidence = ", ".join(f"{key}={value}" for key, value in finding["evidence"].items() if value) or "metric signal"
|
|
505
|
+
lines.extend(
|
|
506
|
+
[
|
|
507
|
+
f"{index}. {finding['title']}",
|
|
508
|
+
f" Evidence: `{location}`; {evidence}; priority={finding['priority']}; path_priority={finding.get('path_priority', 'neutral')}.",
|
|
509
|
+
f" Safe first patch: {finding['suggested_action']}",
|
|
510
|
+
f" Verify: inspect nearby tests first, then run the narrowest type-check or test command for the touched package.",
|
|
511
|
+
"",
|
|
512
|
+
]
|
|
513
|
+
)
|
|
514
|
+
if not findings:
|
|
515
|
+
lines.append("No findings were generated for this scan.")
|
|
516
|
+
lines.append("")
|
|
517
|
+
return "\n".join(lines)
|
|
518
|
+
|
|
519
|
+
|
|
440
520
|
def render_tree_lines(summary_tree: dict[str, Any], *, indent: int = 0) -> list[str]:
|
|
441
521
|
prefix = " " * indent
|
|
442
522
|
name = summary_tree.get("path") or summary_tree.get("name") or "<unknown>"
|
|
@@ -465,6 +545,51 @@ def build_manifest(*, root_path: Path, profile: str) -> dict[str, Any]:
|
|
|
465
545
|
}
|
|
466
546
|
|
|
467
547
|
|
|
548
|
+
def enrich_manifest_with_artifacts(
|
|
549
|
+
payload: dict[str, Any],
|
|
550
|
+
*,
|
|
551
|
+
output_format: OutputFormat,
|
|
552
|
+
include_tree: bool,
|
|
553
|
+
include_dependencies: bool,
|
|
554
|
+
) -> dict[str, Any]:
|
|
555
|
+
manifest = dict(payload["manifest"])
|
|
556
|
+
artifact_specs: list[tuple[str, str, Any, int, str]] = []
|
|
557
|
+
if output_format in ("json", "both"):
|
|
558
|
+
artifact_specs.extend(
|
|
559
|
+
[
|
|
560
|
+
("manifest", "manifest.json", manifest, 1, "full"),
|
|
561
|
+
("tree_summary", "tree.summary.json", payload["tree_summary"], 4, "full"),
|
|
562
|
+
("findings_top", "findings.top.json", payload["findings_top"], 2, "full"),
|
|
563
|
+
("findings", "findings.json", payload["findings"], 5, "top_n"),
|
|
564
|
+
("hotspots", "hotspots.json", payload["hotspots"], 6, "top_n"),
|
|
565
|
+
("metrics_schema", "metrics.schema.json", payload["metrics_schema"], 7, "reference"),
|
|
566
|
+
]
|
|
567
|
+
)
|
|
568
|
+
if include_tree:
|
|
569
|
+
artifact_specs.append(("tree", "tree.json", payload["tree"], 9, "avoid_full_load"))
|
|
570
|
+
if include_dependencies:
|
|
571
|
+
artifact_specs.append(("dependencies", "dependencies.json", payload["dependencies"], 8, "top_n"))
|
|
572
|
+
if output_format in ("markdown", "both"):
|
|
573
|
+
artifact_specs.extend(
|
|
574
|
+
[
|
|
575
|
+
("report", "report.md", payload["report_markdown"], 1, "full"),
|
|
576
|
+
("action_plan", "action-plan.md", payload["action_plan_markdown"], 3, "full"),
|
|
577
|
+
("agent_skill", "agent-skill.md", payload["agent_skill_markdown"], 10, "reference"),
|
|
578
|
+
]
|
|
579
|
+
)
|
|
580
|
+
manifest["artifacts"] = [
|
|
581
|
+
{
|
|
582
|
+
"name": name,
|
|
583
|
+
"path": path,
|
|
584
|
+
"bytes": len(_json_dumps(content).encode("utf-8")) if not isinstance(content, str) else len(content.encode("utf-8")),
|
|
585
|
+
"read_order": read_order,
|
|
586
|
+
"agent_load": agent_load,
|
|
587
|
+
}
|
|
588
|
+
for name, path, content, read_order, agent_load in sorted(artifact_specs, key=lambda item: (item[3], item[1]))
|
|
589
|
+
]
|
|
590
|
+
return manifest
|
|
591
|
+
|
|
592
|
+
|
|
468
593
|
def build_metrics_schema() -> dict[str, Any]:
|
|
469
594
|
return {
|
|
470
595
|
"artifact_version": ARTIFACT_VERSION,
|
|
@@ -572,9 +697,16 @@ def _title_for_category(category: str, item: dict[str, Any]) -> str:
|
|
|
572
697
|
return labels.get(category, "Code quality hotspot") + f" in {item['name']}"
|
|
573
698
|
|
|
574
699
|
|
|
575
|
-
def _suggested_action(category: str) -> str:
|
|
700
|
+
def _suggested_action(category: str, item: dict[str, Any] | None = None) -> str:
|
|
701
|
+
path = str((item or {}).get("path", ""))
|
|
702
|
+
is_tsx = path.endswith(".tsx") or "::" in path and ".tsx" in path
|
|
576
703
|
actions = {
|
|
577
|
-
"tsx":
|
|
704
|
+
"tsx": (
|
|
705
|
+
"Look first for render-only child components, modal/dialog subtrees, action hooks, and derived selectors to extract; "
|
|
706
|
+
"verify with type-check and targeted UI coverage."
|
|
707
|
+
if is_tsx
|
|
708
|
+
else "Inspect rendering branches, state ownership, and extraction points; add focused UI tests before changing behavior."
|
|
709
|
+
),
|
|
578
710
|
"typing": "Review whether `any` or ignore directives can be replaced with precise types.",
|
|
579
711
|
"coupling": "Inspect dependency boundaries and look for imports that can be inverted, localized, or simplified.",
|
|
580
712
|
"docs": "Check whether large embedded data URLs should move to external assets.",
|
|
@@ -620,6 +752,25 @@ def _display_path(path: str, root_path: Path) -> str:
|
|
|
620
752
|
return path
|
|
621
753
|
|
|
622
754
|
|
|
755
|
+
def _normalize_path_filter(path: str) -> str:
|
|
756
|
+
normalized = path.strip().replace("\\", "/").strip("/")
|
|
757
|
+
return f"{normalized}/" if normalized and not Path(normalized).suffix else normalized
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _path_priority(
|
|
761
|
+
display_path: str,
|
|
762
|
+
*,
|
|
763
|
+
focus_prefixes: tuple[str, ...],
|
|
764
|
+
deprioritize_prefixes: tuple[str, ...],
|
|
765
|
+
) -> dict[str, Any]:
|
|
766
|
+
normalized = display_path.replace("\\", "/").lstrip("/")
|
|
767
|
+
if focus_prefixes and any(normalized == prefix.rstrip("/") or normalized.startswith(prefix) for prefix in focus_prefixes):
|
|
768
|
+
return {"label": "focused", "multiplier": 1.25}
|
|
769
|
+
if any(normalized == prefix.rstrip("/") or normalized.startswith(prefix) for prefix in deprioritize_prefixes):
|
|
770
|
+
return {"label": "deprioritized", "multiplier": 0.55}
|
|
771
|
+
return {"label": "neutral", "multiplier": 1.0}
|
|
772
|
+
|
|
773
|
+
|
|
623
774
|
def _node_identity(path: str, start_line: int, end_line: int, node_type: str) -> str:
|
|
624
775
|
return f"{path}|{node_type}|{start_line}|{end_line}"
|
|
625
776
|
|