@herbertgao/pi-extensions 2026.10.1 → 2026.10.2
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 +13 -9
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
- package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
- package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
- package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
- package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
- package/node_modules/@herbertgao/pi-agency/LICENSE +21 -0
- package/node_modules/@herbertgao/pi-agency/README.md +40 -0
- package/node_modules/@herbertgao/pi-agency/package.json +53 -0
- package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
- package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
- package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
- package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
- package/node_modules/@tifan/pi-recap/package.json +1 -1
- package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
- package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
- package/node_modules/pi-antigravity/README.md +23 -2
- package/node_modules/pi-antigravity/package.json +5 -3
- package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
- package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
- package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
- package/node_modules/pi-antigravity/src/image/image.ts +86 -47
- package/node_modules/pi-antigravity/src/image/native.ts +69 -0
- package/node_modules/pi-antigravity/src/index.ts +52 -137
- package/node_modules/pi-antigravity/src/search/search.ts +130 -31
- package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
- package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
- package/node_modules/pi-antigravity/src/types/types.ts +1 -1
- package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
- package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
- package/node_modules/pi-lens/CHANGELOG.md +1064 -0
- package/node_modules/pi-lens/README.md +115 -25
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
- package/node_modules/pi-lens/dist/chunk-22AVIGXS.js +560 -0
- package/node_modules/pi-lens/dist/chunk-27BBHSZC.js +106 -0
- package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
- package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
- package/node_modules/pi-lens/dist/chunk-34U7PLGE.js +23 -0
- package/node_modules/pi-lens/dist/chunk-3I5QFT3N.js +181 -0
- package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
- package/node_modules/pi-lens/dist/chunk-47D2BO4S.js +87 -0
- package/node_modules/pi-lens/dist/chunk-4RERDEXR.js +348 -0
- package/node_modules/pi-lens/dist/chunk-4W2PLC4J.js +27 -0
- package/node_modules/pi-lens/dist/chunk-52H5B5TC.js +202 -0
- package/node_modules/pi-lens/dist/chunk-54B42YRX.js +27 -0
- package/node_modules/pi-lens/dist/chunk-5RXBNSW2.js +266 -0
- package/node_modules/pi-lens/dist/chunk-5THXD6X5.js +201 -0
- package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
- package/node_modules/pi-lens/dist/chunk-5ZEJHV35.js +341 -0
- package/node_modules/pi-lens/dist/chunk-6P62QVEA.js +118 -0
- package/node_modules/pi-lens/dist/chunk-6QIUX4EP.js +459 -0
- package/node_modules/pi-lens/dist/chunk-6VCD4VKZ.js +78 -0
- package/node_modules/pi-lens/dist/chunk-7F72PXY2.js +326 -0
- package/node_modules/pi-lens/dist/chunk-7FBF7NPR.js +998 -0
- package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
- package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
- package/node_modules/pi-lens/dist/chunk-AHDW3GPE.js +50 -0
- package/node_modules/pi-lens/dist/chunk-AJ3QXTRQ.js +756 -0
- package/node_modules/pi-lens/dist/chunk-AJVLWNFN.js +1610 -0
- package/node_modules/pi-lens/dist/chunk-AJXODZY6.js +112 -0
- package/node_modules/pi-lens/dist/chunk-ARFCXG7S.js +84 -0
- package/node_modules/pi-lens/dist/chunk-AS44CFQ4.js +708 -0
- package/node_modules/pi-lens/dist/chunk-AWE5TMPO.js +38 -0
- package/node_modules/pi-lens/dist/chunk-B3I2UUT4.js +4692 -0
- package/node_modules/pi-lens/dist/chunk-BRJKOBZP.js +331 -0
- package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
- package/node_modules/pi-lens/dist/chunk-C2AUYXQH.js +149 -0
- package/node_modules/pi-lens/dist/chunk-CGGAIP7J.js +29 -0
- package/node_modules/pi-lens/dist/chunk-D6QS3U6M.js +30 -0
- package/node_modules/pi-lens/dist/chunk-DFVMIKMQ.js +349 -0
- package/node_modules/pi-lens/dist/chunk-DIULIWKU.js +169 -0
- package/node_modules/pi-lens/dist/chunk-DQ2NZ7C4.js +2317 -0
- package/node_modules/pi-lens/dist/chunk-E2WFBRHM.js +213 -0
- package/node_modules/pi-lens/dist/chunk-E4MAEDPM.js +50 -0
- package/node_modules/pi-lens/dist/chunk-EOKRQ3SA.js +30 -0
- package/node_modules/pi-lens/dist/chunk-F3AU2TMM.js +2626 -0
- package/node_modules/pi-lens/dist/chunk-F3P7VQ7H.js +170 -0
- package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
- package/node_modules/pi-lens/dist/chunk-FJNKANOS.js +32 -0
- package/node_modules/pi-lens/dist/chunk-GUOGVTCQ.js +146 -0
- package/node_modules/pi-lens/dist/chunk-GVXA6CCE.js +18 -0
- package/node_modules/pi-lens/dist/chunk-HHYAELLJ.js +2334 -0
- package/node_modules/pi-lens/dist/chunk-HMFXILPV.js +83 -0
- package/node_modules/pi-lens/dist/chunk-HVUZXSWK.js +421 -0
- package/node_modules/pi-lens/dist/chunk-HWBZIGMJ.js +62 -0
- package/node_modules/pi-lens/dist/chunk-I3R7GKDR.js +17 -0
- package/node_modules/pi-lens/dist/chunk-I7SPGOWI.js +55 -0
- package/node_modules/pi-lens/dist/chunk-IIXOAGHR.js +38 -0
- package/node_modules/pi-lens/dist/chunk-IVP5KAB7.js +2250 -0
- package/node_modules/pi-lens/dist/chunk-IXEBJSUE.js +22 -0
- package/node_modules/pi-lens/dist/chunk-JOLITGCC.js +27 -0
- package/node_modules/pi-lens/dist/chunk-K6VYLB2U.js +145 -0
- package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
- package/node_modules/pi-lens/dist/chunk-LNAKEO63.js +78 -0
- package/node_modules/pi-lens/dist/chunk-LSMJ6KBE.js +4139 -0
- package/node_modules/pi-lens/dist/chunk-LW6XKDZQ.js +5565 -0
- package/node_modules/pi-lens/dist/chunk-N3YQJI6O.js +899 -0
- package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
- package/node_modules/pi-lens/dist/chunk-NKOUFPQF.js +659 -0
- package/node_modules/pi-lens/dist/chunk-NL5GLMFI.js +211 -0
- package/node_modules/pi-lens/dist/chunk-NMABWBZN.js +327 -0
- package/node_modules/pi-lens/dist/chunk-NTHH2J74.js +27 -0
- package/node_modules/pi-lens/dist/chunk-NTUJEGDZ.js +59 -0
- package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
- package/node_modules/pi-lens/dist/chunk-NXPS2L6U.js +466 -0
- package/node_modules/pi-lens/dist/chunk-O5WILGMI.js +42 -0
- package/node_modules/pi-lens/dist/chunk-O6TQT6RI.js +48 -0
- package/node_modules/pi-lens/dist/chunk-OAXNDZJ7.js +323 -0
- package/node_modules/pi-lens/dist/chunk-ODEEOXPH.js +44 -0
- package/node_modules/pi-lens/dist/chunk-OQGFCZJ4.js +4533 -0
- package/node_modules/pi-lens/dist/chunk-ORTMY4Q4.js +282 -0
- package/node_modules/pi-lens/dist/chunk-OS32RNR2.js +442 -0
- package/node_modules/pi-lens/dist/chunk-PAK6IDME.js +189 -0
- package/node_modules/pi-lens/dist/chunk-PWFHBTSF.js +406 -0
- package/node_modules/pi-lens/dist/chunk-Q5U6FMDI.js +2352 -0
- package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
- package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
- package/node_modules/pi-lens/dist/chunk-RIISMHKN.js +36 -0
- package/node_modules/pi-lens/dist/chunk-RPD4ZSB7.js +25 -0
- package/node_modules/pi-lens/dist/chunk-RPD5K5NC.js +79 -0
- package/node_modules/pi-lens/dist/chunk-RUOG5D2C.js +262 -0
- package/node_modules/pi-lens/dist/chunk-T4ERT73Y.js +599 -0
- package/node_modules/pi-lens/dist/chunk-T6D4Q4AQ.js +54 -0
- package/node_modules/pi-lens/dist/chunk-TGPXLY2J.js +391 -0
- package/node_modules/pi-lens/dist/chunk-TLGFBM3F.js +12116 -0
- package/node_modules/pi-lens/dist/chunk-TO6WL3GM.js +88 -0
- package/node_modules/pi-lens/dist/chunk-TT22N74J.js +517 -0
- package/node_modules/pi-lens/dist/chunk-TVN5N6GY.js +21216 -0
- package/node_modules/pi-lens/dist/chunk-U3VGR4VU.js +308 -0
- package/node_modules/pi-lens/dist/chunk-UK3CMEAL.js +1250 -0
- package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
- package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
- package/node_modules/pi-lens/dist/chunk-UY752QSN.js +45 -0
- package/node_modules/pi-lens/dist/chunk-V5PXFSZK.js +188 -0
- package/node_modules/pi-lens/dist/chunk-V7R425XG.js +398 -0
- package/node_modules/pi-lens/dist/chunk-VCSRSYLF.js +81 -0
- package/node_modules/pi-lens/dist/chunk-VD4JQCQC.js +162 -0
- package/node_modules/pi-lens/dist/chunk-VDBPVIKD.js +62 -0
- package/node_modules/pi-lens/dist/chunk-VHQLPIRJ.js +114 -0
- package/node_modules/pi-lens/dist/chunk-VN2AXLEX.js +5414 -0
- package/node_modules/pi-lens/dist/chunk-VNMT7C64.js +478 -0
- package/node_modules/pi-lens/dist/chunk-WONERJTP.js +309 -0
- package/node_modules/pi-lens/dist/chunk-XECLWQZN.js +85 -0
- package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
- package/node_modules/pi-lens/dist/chunk-XK4CIILI.js +144 -0
- package/node_modules/pi-lens/dist/chunk-Y4T27COV.js +562 -0
- package/node_modules/pi-lens/dist/chunk-YGVYKNSN.js +1860 -0
- package/node_modules/pi-lens/dist/chunk-YLF3PPT5.js +308 -0
- package/node_modules/pi-lens/dist/chunk-YSNFMJKN.js +14 -0
- package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
- package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
- package/node_modules/pi-lens/dist/chunk-ZAWW3AF6.js +97 -0
- package/node_modules/pi-lens/dist/chunk-ZMOL6RQC.js +167 -0
- package/node_modules/pi-lens/dist/index.js +16456 -8335
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
- package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
- package/node_modules/pi-lens/dist/mcp/server.js +22589 -1573
- package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
- package/node_modules/pi-lens/dist/probes/installer.js +135 -0
- package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
- package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
- package/node_modules/pi-lens/docs/agent-guide.md +13 -4
- package/node_modules/pi-lens/docs/agent-tools.md +42 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +3 -2
- package/node_modules/pi-lens/docs/configuration.md +20 -1
- package/node_modules/pi-lens/docs/environment-variables.md +52 -2
- package/node_modules/pi-lens/docs/features.md +8 -4
- package/node_modules/pi-lens/docs/globalconfig.md +1 -0
- package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +36 -4
- package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
- package/node_modules/pi-lens/docs/mcp.md +21 -5
- package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +156 -88
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
- package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +694 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +73 -7
- package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +188 -48
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
- package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
- package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
- package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
- package/node_modules/pi-lens/docs/servercapabilities.md +53 -49
- package/node_modules/pi-lens/docs/settings.md +1 -0
- package/node_modules/pi-lens/docs/usage.md +5 -1
- package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
- package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
- package/node_modules/pi-lens/package.json +18 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
- package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
- package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
- package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
- package/package.json +15 -10
- package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
- package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
- package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
- package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
- package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
- package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
- package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
- package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
- package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
- package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
- package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
- package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
- package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
- package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
- package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
- package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
- package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
- package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
- package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
- package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
- package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
- package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
- package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
- package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
- package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
- package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
- package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
- package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
- package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
- package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
- package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
- package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
- package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
- package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
- package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
- package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
- package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
- package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
- package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
- package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
- package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
- package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
- package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
- package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
- package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
- package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
- package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
- package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
- package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
- package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
- package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
- package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
- package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
- package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
- package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
- package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
- package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
- package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
- package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
- package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
- package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
- package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
- package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
- package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
- package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
- package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
- package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
- package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
- package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
- package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
- package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
- package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
- package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
- package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
- package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
- package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
- package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
- package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
- package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
- package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
- package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
- package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
- package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
- package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
- package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
- package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
- package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
- package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
- package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
- package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
- package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
- package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
- package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
- package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
- package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
- package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
- package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
- package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
- package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
- package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
- package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
- package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
- package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
- package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
- package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
- package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
- package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
- package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
- package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
- package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
- package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
- package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
- package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
- package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
- package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
- package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
- package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
- package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
- package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
- package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
- package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
- package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
- package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
- package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
- package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
- package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
- package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
- package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
- package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
- package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
- package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
- package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
- package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
- package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
- package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
- package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
- package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
- package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
- package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
- package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
- package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
- package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
- package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
- package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
- package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
- package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
- package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
- package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
- package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
- package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
- package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
- package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
- package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
- package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
- package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
- package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
- package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
- package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
- package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
- package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
- package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
- package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
- package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
- package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
- package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
- package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
- package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
- package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
- package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
- package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
- package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
- package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
- package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
- package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
- package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
- package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
- package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
- package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
- package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
- package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
- package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
- package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
- package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
- package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
- package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
- package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
- package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
- package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
- package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
- package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
- package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
- package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
- package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
- package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
- package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
- package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
- package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
- package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
- package/node_modules/pi-lens/dist/i18n.js +0 -69
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
- package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
- package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
- package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
- package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
- package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
- package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
- package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
- package/node_modules/pi-lens/dist/tools/shared.js +0 -29
- package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
|
@@ -1,1244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path utilities for pi-lens
|
|
3
|
-
*
|
|
4
|
-
* Handles cross-platform path normalization, particularly
|
|
5
|
-
* Windows case-insensitivity issues when using paths as Map keys.
|
|
6
|
-
*
|
|
7
|
-
* Approach (inspired by OpenCode's Filesystem.normalizePath):
|
|
8
|
-
* - On Windows: try realpathSync.native() for canonical casing
|
|
9
|
-
* - Falls back to lowercase for files that don't exist yet
|
|
10
|
-
* - On non-Windows: adopt the on-disk casing of an existing path (#3098 — a
|
|
11
|
-
* case-insensitive POSIX filesystem otherwise derives two Map keys for one
|
|
12
|
-
* file); a path that does not exist is returned as-is, case-preserving
|
|
13
|
-
* - Always convert backslashes to forward slashes for Map key consistency
|
|
14
|
-
*/
|
|
15
|
-
import { existsSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
16
|
-
import * as os from "node:os";
|
|
17
|
-
import * as path from "node:path";
|
|
18
|
-
import { win32 } from "node:path";
|
|
19
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
|
-
import { minimatch } from "./deps/minimatch.js";
|
|
21
|
-
/**
|
|
22
|
-
* Detect a positively Windows-shaped path, regardless of the host OS.
|
|
23
|
-
*
|
|
24
|
-
* A backslash anywhere in a path is not enough: it is a legal character in a
|
|
25
|
-
* POSIX filename. Only a drive-letter prefix (`X:`), a UNC root (`\\`), or a
|
|
26
|
-
* rooted backslash at position zero (`\`) selects Windows parsing.
|
|
27
|
-
*/
|
|
28
|
-
export function isWindowsPath(filePath) {
|
|
29
|
-
return /^[A-Za-z]:/.test(filePath) || filePath.startsWith("\\");
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Canonical backslash→forward-slash fold — the single sanctioned form of the
|
|
33
|
-
* `p.replace(/\\/g, "/")` idiom otherwise hand-rolled across the codebase
|
|
34
|
-
* (~138 sites, #1193). PURE separator normalization: it does NOT resolve,
|
|
35
|
-
* canonicalize, lowercase, or collapse repeated slashes — reach for
|
|
36
|
-
* `normalizeFilePath`/`normalizeMapKey`/`normalizeEphemeralMapKey` when a
|
|
37
|
-
* canonical map *key* (case-fold / realpath) is what you need. Consolidating on
|
|
38
|
-
* this funnels the scattered transform and makes a shape-2 lint/ast-grep rule
|
|
39
|
-
* possible for the first time: today a bare inline `.replace(/\\/g, "/")` is
|
|
40
|
-
* byte-identical to the sanctioned use so it can't be ruled (#1158); once
|
|
41
|
-
* everything routes through `toPosix`, an *un-migrated* inline `.replace`
|
|
42
|
-
* becomes detectable.
|
|
43
|
-
*/
|
|
44
|
-
export function toPosix(filePath) {
|
|
45
|
-
return filePath.replace(/\\/g, "/");
|
|
46
|
-
}
|
|
47
|
-
/** Return whether `filePath` is fully qualified under Windows semantics. */
|
|
48
|
-
export function isFullyQualifiedWin32(filePath) {
|
|
49
|
-
return win32.isAbsolute(filePath) && win32.parse(filePath).root.length > 1;
|
|
50
|
-
}
|
|
51
|
-
/** Return whether `filePath` is fully qualified under POSIX semantics. */
|
|
52
|
-
export function isFullyQualifiedPosix(filePath) {
|
|
53
|
-
return path.posix.isAbsolute(filePath) && !isWindowsPath(filePath);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Return whether `filePath` is fully qualified under the host's semantics.
|
|
57
|
-
*
|
|
58
|
-
* In particular, `/foo` is rooted-relative under Win32 (ambient-drive
|
|
59
|
-
* dependent) but fully qualified under POSIX.
|
|
60
|
-
*/
|
|
61
|
-
export function isFullyQualified(filePath) {
|
|
62
|
-
return process.platform === "win32"
|
|
63
|
-
? isFullyQualifiedWin32(filePath)
|
|
64
|
-
: isFullyQualifiedPosix(filePath);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Shape-based absolute check (mirrors `toProjectRelativePath`'s idiom): a
|
|
68
|
-
* Windows-shaped path (drive-letter/UNC) is parsed with `win32.isAbsolute`
|
|
69
|
-
* regardless of host OS, since the host-default `path.isAbsolute` returns
|
|
70
|
-
* FALSE for a Windows-shaped path on POSIX (#1150 class) and would let a
|
|
71
|
-
* cross-platform-persisted relative path slip past this guard on Linux CI.
|
|
72
|
-
* MUST run on the RAW `filePath`, before `normalizeMapKey`: on Windows,
|
|
73
|
-
* `normalizeMapKey`'s nonexistent-path fallback (`resolveNonExisting`) calls
|
|
74
|
-
* `win32.resolve`, which silently makes any relative path absolute against
|
|
75
|
-
* `process.cwd()` — checking the normalized value would defeat this guard
|
|
76
|
-
* entirely on Windows.
|
|
77
|
-
*
|
|
78
|
-
* Promoted from a private copy in `clients/review-graph/service.ts` (#2477)
|
|
79
|
-
* so `clients/dispatch/dispatcher.ts`'s baseline-key guard (#2489) can share
|
|
80
|
-
* the same sanctioned check instead of hand-rolling a second one.
|
|
81
|
-
*/
|
|
82
|
-
export function isAbsoluteFilePath(filePath) {
|
|
83
|
-
const p = isWindowsPath(filePath) ? win32 : path;
|
|
84
|
-
return p.isAbsolute(filePath);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Split a path into its non-empty segments on EITHER separator (`\` or `/`),
|
|
88
|
-
* regardless of the running OS — the shape-safe form of `p.split(path.sep)` /
|
|
89
|
-
* an inline `p.split(/[\\/]+/)`, which #1161/#1163 showed must not assume the
|
|
90
|
-
* host separator for a possibly-cross-shaped path. Drops empty segments
|
|
91
|
-
* (leading slash, drive-root, doubled separators).
|
|
92
|
-
*/
|
|
93
|
-
export function splitPathSegments(filePath) {
|
|
94
|
-
return filePath.split(/[\\/]+/).filter(Boolean);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Adopt `canonical`'s CASING for the trailing segments of `held`, stopping at
|
|
98
|
-
* the first segment that differs by more than case.
|
|
99
|
-
*
|
|
100
|
-
* `realpathSync.native` answers a different question than the one the POSIX
|
|
101
|
-
* arm of `normalizeFilePath` asks: it resolves symlinks AND reports on-disk
|
|
102
|
-
* casing, and we want only the second half. Walking from the tail and halting
|
|
103
|
-
* at the first structural divergence separates them with no extra syscall:
|
|
104
|
-
*
|
|
105
|
-
* held /var/folders/T/x/SUB/a.ts (macOS tmpdir, mis-cased segment)
|
|
106
|
-
* canonical /private/var/folders/T/x/sub/a.ts
|
|
107
|
-
* result /var/folders/T/x/sub/a.ts (case fixed, symlink prefix kept)
|
|
108
|
-
*
|
|
109
|
-
* The symlink prefix matters: macOS's `os.tmpdir()` is `/var/folders/...`,
|
|
110
|
-
* a symlink into `/private/var`, so a whole-string "is this a case variant"
|
|
111
|
-
* test would decline to canonicalize exactly the paths the #1024 regression
|
|
112
|
-
* test (and any macOS temp-dir workflow) runs on. Keeping the prefix is also
|
|
113
|
-
* what bounds this change: on POSIX, `normalizeFilePath` still never resolves
|
|
114
|
-
* a symlink, so a symlinked monorepo package keeps keying under the path the
|
|
115
|
-
* caller held (refs #2490 — a cwd fold for its own sake broke every monorepo).
|
|
116
|
-
*
|
|
117
|
-
* PURE string algebra: this pins what the rewrite DOES, never what the kernel
|
|
118
|
-
* reports — and from two strings alone it cannot tell "the same file, spelled
|
|
119
|
-
* with different case" from "a different file whose name happens to be a case
|
|
120
|
-
* variant". Any symlink whose BASENAME is a case variant of its target's
|
|
121
|
-
* basename (`<root>/MyProject` → `work/myproject`, `node_modules/Foo` →
|
|
122
|
-
* `../pkgs/foo`) has its name replaced while its own parent is kept, so the
|
|
123
|
-
* result names a different place — or no place at all. Measured in #3159
|
|
124
|
-
* review round 2 (F1): two different inodes collapsed onto ONE key (the #1024
|
|
125
|
-
* defect inverted — false suppression), and a symlinked package keyed under a
|
|
126
|
-
* path that does not exist on disk, breaking the #2490 bound above. The caller
|
|
127
|
-
* therefore CONFIRMS every rewrite against the filesystem before adopting it
|
|
128
|
-
* (see `normalizeFilePath`); do not use this function without that step.
|
|
129
|
-
*/
|
|
130
|
-
function adoptCanonicalCasing(held, canonical) {
|
|
131
|
-
const heldParts = held.split("/");
|
|
132
|
-
const realParts = canonical.split("/");
|
|
133
|
-
let i = heldParts.length - 1;
|
|
134
|
-
let j = realParts.length - 1;
|
|
135
|
-
let changed = false;
|
|
136
|
-
while (i >= 0 && j >= 0) {
|
|
137
|
-
const a = heldParts[i];
|
|
138
|
-
const b = realParts[j];
|
|
139
|
-
if (a !== b) {
|
|
140
|
-
if (a.toLowerCase() !== b.toLowerCase())
|
|
141
|
-
break;
|
|
142
|
-
heldParts[i] = b;
|
|
143
|
-
changed = true;
|
|
144
|
-
}
|
|
145
|
-
i--;
|
|
146
|
-
j--;
|
|
147
|
-
}
|
|
148
|
-
// Identity-preserving when nothing moved — this runs on every POSIX map-key
|
|
149
|
-
// derivation and the overwhelmingly common answer is "already canonical".
|
|
150
|
-
// NOT a behavioural branch (both arms produce an equal string): it is the
|
|
151
|
-
// same allocation guard `normalizeEphemeralMapKey` documents below.
|
|
152
|
-
return changed ? heldParts.join("/") : held;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Normalize a file path for consistent Map key usage.
|
|
156
|
-
*
|
|
157
|
-
* On Windows:
|
|
158
|
-
* - If the file exists: uses realpathSync.native() to get the canonical
|
|
159
|
-
* filesystem path (actual casing, resolved symlinks)
|
|
160
|
-
* - If the file doesn't exist: resolves the path and lowercases
|
|
161
|
-
* (needed for new files where we haven't written yet)
|
|
162
|
-
*
|
|
163
|
-
* On POSIX:
|
|
164
|
-
* - Folds `.`, `..` and duplicate separators first (#3184, see below).
|
|
165
|
-
* - If the file exists: adopts the on-disk CASING of the trailing segments
|
|
166
|
-
* (see `adoptCanonicalCasing`) — no lowercasing, no symlink resolution.
|
|
167
|
-
* - If the file doesn't exist: returns the path as-is, case-PRESERVING. On a
|
|
168
|
-
* case-sensitive filesystem `SUB/a.ts` and `sub/a.ts` are two different
|
|
169
|
-
* files, and nothing may fold one into the other; only the filesystem's own
|
|
170
|
-
* answer for a path that EXISTS can tell the two apart, and for a path that
|
|
171
|
-
* does not exist there is no such answer to ask for.
|
|
172
|
-
*
|
|
173
|
-
* Why the POSIX arm folds dot segments (#3184): the casing arm returns the
|
|
174
|
-
* caller's own spelling whenever `adoptCanonicalCasing` changes nothing, and
|
|
175
|
-
* for `<base>/src/../src/a.ts` it always changes nothing — `realpath` answers
|
|
176
|
-
* a string with FEWER segments, which a casing-only rewrite cannot express, so
|
|
177
|
-
* it declines and the caller's un-folded spelling came back as the map key.
|
|
178
|
-
* Every canonical writer keys through `path.resolve` first (`ctx.filePath` =
|
|
179
|
-
* `normalizeMapKey(resolveAgainstAncestors(...))`, `clients/dispatch/
|
|
180
|
-
* runner-context.ts:49`), so a consumer that passes an ALREADY-absolute
|
|
181
|
-
* agent-typed path straight in (`tools/lens-diagnostic-mark.ts`,
|
|
182
|
-
* `clients/mcp/analyze.ts`) derived an orphan key that no reader could reach.
|
|
183
|
-
* Folding here is pure string algebra — no cwd, no filesystem — so a relative
|
|
184
|
-
* path stays relative (`src/../x` → `x`, `../x` → `../x`, never resolved
|
|
185
|
-
* against `process.cwd()`; refs #2490, where a cwd fold broke every monorepo)
|
|
186
|
-
* and a symlinked package still keys under the path the caller held. These
|
|
187
|
-
* are `path.resolve`'s own TEXTUAL `..` semantics, which is exactly what
|
|
188
|
-
* makes a folded reader key equal to the canonical writer's key: where a
|
|
189
|
-
* `..` sits right after a symlinked directory, textual folding and the
|
|
190
|
-
* kernel disagree, and both sides of every comparison take the textual
|
|
191
|
-
* answer because every canonical writer already resolved that way.
|
|
192
|
-
*
|
|
193
|
-
* Why POSIX canonicalizes casing at all (#3098, the #1024 defect's live half):
|
|
194
|
-
* a case-insensitive POSIX filesystem — macOS's default APFS, `nocase` vfat /
|
|
195
|
-
* ntfs3 / cifs mounts — makes `SUB/a.ts` and `sub/a.ts` ONE file, so a raw
|
|
196
|
-
* mis-cased write (`lens_diagnostic_mark` anchors under `path.resolve(cwd,
|
|
197
|
-
* arg)`) and a `normalizeMapKey` read derived two anchors for one file and the
|
|
198
|
-
* agent's own disposition mark silently never applied. A case-preserving POSIX
|
|
199
|
-
* arm cannot close that: the two keys only become one if the normalizer asks
|
|
200
|
-
* the filesystem which name is really on disk.
|
|
201
|
-
*
|
|
202
|
-
* `realpathSync.native` is `realpath(3)`. On Darwin it rebuilds every
|
|
203
|
-
* component from the filesystem's own `ATTR_CMN_NAME` — Libc-1669.0.4
|
|
204
|
-
* `stdlib/FreeBSD/realpath.c:233` (`getattrlist(resolved, &_rp_alist, …,
|
|
205
|
-
* FSOPT_NOFOLLOW)` with `ATTR_CMN_NAME`) and `:348-354` ("attrs already has
|
|
206
|
-
* the real name") — which is why it returns on-disk casing on APFS/HFS+.
|
|
207
|
-
* MEASURED counter-example (#3098): a Linux ext4/tmpfs `chattr +F` casefold
|
|
208
|
-
* directory aliases the two spellings but `realpath(3)` there returns the
|
|
209
|
-
* spelling the caller asked with, so this arm is a no-op on casefolded Linux
|
|
210
|
-
* directories — filed with the transcript as #3154.
|
|
211
|
-
*
|
|
212
|
-
* Always converts backslashes to forward slashes for consistent Map keys.
|
|
213
|
-
*/
|
|
214
|
-
export function normalizeFilePath(filePath) {
|
|
215
|
-
// Convert backslashes to forward slashes first
|
|
216
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
217
|
-
if (process.platform !== "win32" && !isWindowsPath(normalized)) {
|
|
218
|
-
// #3184. `path.posix`, not the host default: this branch is already
|
|
219
|
-
// committed to POSIX parsing of a slash-folded string (shape 2). Only
|
|
220
|
-
// the POSIX arm needs this — the win32 arm below reaches `realpath` or
|
|
221
|
-
// `win32.resolve`/`win32.normalize` on every path, all of which fold
|
|
222
|
-
// dot segments already (measured: `C:\repo\src\..\src\a.ts` →
|
|
223
|
-
// `c:/repo/src/a.ts` on this POSIX host, before this change) — and
|
|
224
|
-
// folding BEFORE the arms would also move which arm a degenerate
|
|
225
|
-
// drive-letter path selects (`path.posix.normalize("C:/repo/../..")`
|
|
226
|
-
// is `"."`, no longer Windows-shaped).
|
|
227
|
-
// Two inputs keep the caller's spelling instead:
|
|
228
|
-
// - "" is a non-path sentinel in this codebase's path-typed fields
|
|
229
|
-
// (see `normalizeLoggedPath`'s doc); `posix.normalize("")` invents
|
|
230
|
-
// ".", the process cwd.
|
|
231
|
-
// - a UNC root (`\\server\share`, slash-folded to `//server/share`)
|
|
232
|
-
// reaches THIS arm on a POSIX host, because `isWindowsPath` tests
|
|
233
|
-
// the already-folded string and sees no backslash; POSIX
|
|
234
|
-
// `normalize` collapses its leading `//` to `/`, renaming the path
|
|
235
|
-
// to an unrelated local one.
|
|
236
|
-
const folded = normalized === "" || normalized.startsWith("//")
|
|
237
|
-
? normalized
|
|
238
|
-
: path.posix.normalize(normalized);
|
|
239
|
-
try {
|
|
240
|
-
const canonical = realpathSync.native(folded);
|
|
241
|
-
// Fast path, not a guard: both arms answer `folded` when the
|
|
242
|
-
// strings match, but skipping the two `split`s there is a measured
|
|
243
|
-
// 1.9 vs 2.3 microseconds per call on the per-edit seam (#3098).
|
|
244
|
-
if (canonical === folded)
|
|
245
|
-
return folded;
|
|
246
|
-
const adopted = adoptCanonicalCasing(folded, canonical);
|
|
247
|
-
if (adopted === folded)
|
|
248
|
-
return folded;
|
|
249
|
-
// The rewrite is string algebra and can land on a DIFFERENT file
|
|
250
|
-
// (#3159 review round 2, F1 — see `adoptCanonicalCasing`). Adopt it
|
|
251
|
-
// only once the filesystem agrees it still names the file the caller
|
|
252
|
-
// held: `canonical` IS `realpath(normalized)`, so this asks exactly
|
|
253
|
-
// "does the rewritten spelling resolve to the same file?". One extra
|
|
254
|
-
// syscall, and only on the rare branch where casing actually moved —
|
|
255
|
-
// never on an already-canonical path. A throw here (the rewritten
|
|
256
|
-
// path does not exist, the #2490 monorepo case) lands in the catch
|
|
257
|
-
// below and keeps the caller's spelling, which is the same answer.
|
|
258
|
-
return realpathSync.native(adopted) === canonical ? adopted : folded;
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
// Does not exist (or is unreadable): case-preserving, as above.
|
|
262
|
-
return folded;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// Windows: try realpathSync.native() for canonical casing
|
|
266
|
-
// This resolves symlinks and returns the actual filesystem casing
|
|
267
|
-
try {
|
|
268
|
-
const canonical = realpathSync.native(filePath);
|
|
269
|
-
return canonical.replace(/\\/g, "/");
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
// File doesn't exist yet (new file) — resolve path and lowercase
|
|
273
|
-
// We need to walk up the directory tree to find the nearest existing
|
|
274
|
-
// parent, resolve its casing, then append the non-existent parts
|
|
275
|
-
try {
|
|
276
|
-
return resolveNonExisting(filePath);
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
// Last resort: just lowercase the resolved path
|
|
280
|
-
const resolved = win32.normalize(win32.resolve(filePath));
|
|
281
|
-
return resolved.replace(/\\/g, "/").toLowerCase();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/** Resolve a filesystem identity once, retaining a usable absolute fallback. */
|
|
286
|
-
export function realpathOrResolve(filePath) {
|
|
287
|
-
const resolved = path.resolve(filePath);
|
|
288
|
-
try {
|
|
289
|
-
return realpathSync(resolved);
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
return resolved;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Normalize a logged `filePath`/`cwd` value, but ONLY when it is already a
|
|
297
|
-
* fully-qualified path (#2219, the #2141 class's sibling loggers). Several
|
|
298
|
-
* NDJSON log-entry types reuse a `filePath`-typed field for non-path
|
|
299
|
-
* sentinels alongside genuine paths — `"<quiet-window>"` in
|
|
300
|
-
* `cascade-logger.ts`, `"<tree-sitter>"` in `tree-sitter-logger.ts`, a shell
|
|
301
|
-
* command or an empty placeholder in `latency-logger.ts`. `normalizeFilePath`
|
|
302
|
-
* resolves a relative-looking string against the CURRENT process cwd (see
|
|
303
|
-
* `resolveNonExisting` above), so running it over one of those sentinels
|
|
304
|
-
* would silently corrupt it into `"<repoRoot>/<quiet-window>"` instead of
|
|
305
|
-
* normalizing it. Only a value that is already fully qualified can be the
|
|
306
|
-
* #2141 mixed-raw/normalized-path defect; anything else is passed through
|
|
307
|
-
* unchanged.
|
|
308
|
-
*
|
|
309
|
-
* #2229 review round 1, F1: this classifier checks BOTH host shapes
|
|
310
|
-
* (`isFullyQualifiedWin32(value) || isFullyQualifiedPosix(value)`), not
|
|
311
|
-
* `isFullyQualified(value)` (host-dispatched on `process.platform`). A
|
|
312
|
-
* Windows-shaped absolute path (`C:\Users\...`) is exactly the log payload
|
|
313
|
-
* this fix exists to normalize, but `isFullyQualified` on Linux CI routes to
|
|
314
|
-
* `isFullyQualifiedPosix`, which rejects it (no leading `/`) — so on Linux
|
|
315
|
-
* the guard silently no-ops for the very inputs the #2141 defect produces,
|
|
316
|
-
* passing the raw backslash form straight through. Checking both shapes
|
|
317
|
-
* makes the classifier's answer for a given STRING independent of which OS
|
|
318
|
-
* is asking; `normalizeFilePath` itself still branches on `process.platform`
|
|
319
|
-
* for how the file is resolved, but whether to normalize at all no longer
|
|
320
|
-
* does (AGENTS.md shape 2, the #1024/#1150 OS-divergence class).
|
|
321
|
-
*/
|
|
322
|
-
export function normalizeLoggedPath(value) {
|
|
323
|
-
return isFullyQualifiedWin32(value) || isFullyQualifiedPosix(value)
|
|
324
|
-
? normalizeFilePath(value)
|
|
325
|
-
: value;
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Resolve a non-existing path by finding the nearest existing parent,
|
|
329
|
-
* getting its canonical casing, then appending the non-existent parts lowercased.
|
|
330
|
-
*
|
|
331
|
-
* Example: C:\Users\Foo\newdir\file.ts
|
|
332
|
-
* - C:\Users\Foo exists → realpathSync gives C:\Users\Foo
|
|
333
|
-
* - newdir\file.ts doesn't exist → lowercased
|
|
334
|
-
* - Result: C:/Users/Foo/newdir/file.ts
|
|
335
|
-
*/
|
|
336
|
-
function resolveNonExisting(filePath) {
|
|
337
|
-
const resolved = win32.resolve(filePath);
|
|
338
|
-
let current = resolved;
|
|
339
|
-
const nonExistentParts = [];
|
|
340
|
-
// Walk up until we find an existing directory
|
|
341
|
-
while (true) {
|
|
342
|
-
if (existsSync(current)) {
|
|
343
|
-
// Found existing ancestor — get its canonical casing
|
|
344
|
-
const canonical = realpathSync.native(current);
|
|
345
|
-
if (nonExistentParts.length === 0) {
|
|
346
|
-
return canonical.replace(/\\/g, "/");
|
|
347
|
-
}
|
|
348
|
-
// Append non-existent parts (lowercased for consistency)
|
|
349
|
-
const tail = nonExistentParts.reverse().join("/").toLowerCase();
|
|
350
|
-
const base = canonical.replace(/\\/g, "/");
|
|
351
|
-
return base.endsWith("/") ? base + tail : `${base}/${tail}`;
|
|
352
|
-
}
|
|
353
|
-
// Use win32.dirname (not the platform-default dirname) so a
|
|
354
|
-
// Windows-shaped path is parsed with win32 semantics regardless of the
|
|
355
|
-
// running OS — consistent with the win32.resolve/win32.normalize this
|
|
356
|
-
// branch already commits to. The platform-default POSIX dirname would
|
|
357
|
-
// find no separator in a win32-resolved "C:\repo\..." path (its only
|
|
358
|
-
// separators are backslashes), collapse to ".", stop the upward walk at
|
|
359
|
-
// cwd, and mangle the key on Linux CI (refs #1150, the #1024
|
|
360
|
-
// OS-divergence class).
|
|
361
|
-
const parent = win32.dirname(current);
|
|
362
|
-
if (parent === current) {
|
|
363
|
-
// Reached filesystem root without finding existing dir
|
|
364
|
-
// Fall back to full lowercase
|
|
365
|
-
throw new Error("No existing parent found");
|
|
366
|
-
}
|
|
367
|
-
nonExistentParts.push(win32.basename(current));
|
|
368
|
-
current = parent;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Convert a file:// URI to a normalized path.
|
|
373
|
-
* Handles URL decoding and Windows drive letter normalization.
|
|
374
|
-
*/
|
|
375
|
-
export function uriToPath(uri) {
|
|
376
|
-
try {
|
|
377
|
-
const filePath = fileURLToPath(uri);
|
|
378
|
-
return normalizeFilePath(filePath);
|
|
379
|
-
}
|
|
380
|
-
catch {
|
|
381
|
-
// Not a valid file:// URI, treat as plain path
|
|
382
|
-
return normalizeFilePath(uri);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Decode a file:// URI to an on-disk path WITHOUT map-key normalization.
|
|
387
|
-
*
|
|
388
|
-
* `uriToPath` runs its result through `normalizeFilePath`, which on win32
|
|
389
|
-
* lowercases the nonexistent tail of a path (see `resolveNonExisting`) and
|
|
390
|
-
* canonicalizes an existing path to its real casing. That is correct for Map
|
|
391
|
-
* keys, but DESTRUCTIVE for a real create/rename target: creating `NewFile.txt`
|
|
392
|
-
* would write `newfile.txt`, and a legitimate case-only rename would collapse
|
|
393
|
-
* to a no-op ("source and destination must differ"). Disk mutations must honor
|
|
394
|
-
* the caller's intended casing, so they resolve their target through this
|
|
395
|
-
* decode-only path while confinement/validation keep using the normalized
|
|
396
|
-
* `uriToPath`. Non-win32 is unaffected either way (normalizeFilePath is a
|
|
397
|
-
* near-identity there).
|
|
398
|
-
*/
|
|
399
|
-
export function uriToDiskPath(uri) {
|
|
400
|
-
try {
|
|
401
|
-
return fileURLToPath(uri);
|
|
402
|
-
}
|
|
403
|
-
catch {
|
|
404
|
-
// Not a valid file:// URI — treat as a plain path (matches uriToPath).
|
|
405
|
-
return uri;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* Convert a path to a file:// URI.
|
|
410
|
-
* Does NOT normalize the path - URIs preserve original casing.
|
|
411
|
-
*/
|
|
412
|
-
export function pathToUri(filePath) {
|
|
413
|
-
return pathToFileURL(filePath).href;
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Normalize a Map key lookup for file paths.
|
|
417
|
-
* Use this when getting/setting values in Maps that use file paths as keys.
|
|
418
|
-
*/
|
|
419
|
-
export function normalizeMapKey(filePath) {
|
|
420
|
-
return normalizeFilePath(filePath);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Human-facing path relative to a project root when the file is inside it.
|
|
424
|
-
*
|
|
425
|
-
* Parses by path SHAPE, not host OS (refs #1150/#1152, shape-2 class #1163):
|
|
426
|
-
* a Windows-shaped `filePath` (drive-letter/UNC — e.g. a persisted call-graph
|
|
427
|
-
* symbol-key path `C:\repo\src\x.ts` rehydrated on a Linux CI run) is split
|
|
428
|
-
* with `win32.*` regardless of `process.platform`. The host-default
|
|
429
|
-
* `isAbsolute`/`relative` find no drive-letter anchor in a win32 path on POSIX:
|
|
430
|
-
* `path.isAbsolute("C:\\repo\\x.ts")` returns FALSE on Linux, short-circuiting
|
|
431
|
-
* to the raw absolute path instead of ever relativizing it — so a file that IS
|
|
432
|
-
* under the project root renders as a full absolute path on Linux but the
|
|
433
|
-
* expected `src/x.ts` on Windows (green-locally / wrong-on-CI, the #1024
|
|
434
|
-
* divergence class). `win32.*` on a native POSIX path (Windows never sees one;
|
|
435
|
-
* Linux native paths aren't Windows-shaped) is never selected, so same-OS
|
|
436
|
-
* native paths are unchanged either way.
|
|
437
|
-
*/
|
|
438
|
-
export function toProjectRelativePath(filePath, projectRoot) {
|
|
439
|
-
const p = isWindowsPath(filePath) ? win32 : path;
|
|
440
|
-
if (!p.isAbsolute(filePath))
|
|
441
|
-
return filePath.replace(/\\/g, "/");
|
|
442
|
-
const relative = p.relative(p.resolve(projectRoot), filePath);
|
|
443
|
-
return relative && !relative.startsWith("..") && !p.isAbsolute(relative)
|
|
444
|
-
? relative.replace(/\\/g, "/")
|
|
445
|
-
: filePath.replace(/\\/g, "/");
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Cheap, syntactic-only Map key normalization: slash-fold + (on Windows)
|
|
449
|
-
* lowercase. No `realpathSync` / filesystem I/O.
|
|
450
|
-
*
|
|
451
|
-
* `normalizeMapKey` (via `normalizeFilePath`) calls `realpathSync.native()` to
|
|
452
|
-
* get canonical on-disk casing — correct for maps that key long-lived state
|
|
453
|
-
* shared across call sites (e.g. LSP/read-guard caches), but expensive when
|
|
454
|
-
* the *point* of the cache is to avoid filesystem calls in the first place:
|
|
455
|
-
* for a candidate path that does NOT exist (the common case for sibling-probe
|
|
456
|
-
* memos), `normalizeFilePath` walks up the directory tree doing its own
|
|
457
|
-
* `existsSync` calls to resolve the nearest existing ancestor — measured at
|
|
458
|
-
* ~11x slower than the single `existsSync` probe such a cache is trying to
|
|
459
|
-
* save (refs #191).
|
|
460
|
-
*
|
|
461
|
-
* Safe to use ONLY for ephemeral, single-process, single-walk caches whose
|
|
462
|
-
* keys are produced by this process's own `path.join`/`path.resolve` calls
|
|
463
|
-
* within the same run (so separators and casing are already consistent
|
|
464
|
-
* modulo simple slash direction) — never for state shared across processes,
|
|
465
|
-
* persisted, or compared against externally-supplied paths where symlink /
|
|
466
|
-
* real-casing resolution actually matters.
|
|
467
|
-
*/
|
|
468
|
-
export function normalizeEphemeralMapKey(filePath) {
|
|
469
|
-
// Most hot-path keys on POSIX are already canonical slash-separated strings.
|
|
470
|
-
// Preserve that identity instead of allocating a replacement string for each
|
|
471
|
-
// file in a large diagnostics reconciliation.
|
|
472
|
-
if (process.platform !== "win32" && !filePath.includes("\\"))
|
|
473
|
-
return filePath;
|
|
474
|
-
const slashed = filePath.replace(/\\/g, "/");
|
|
475
|
-
return process.platform === "win32" ? slashed.toLowerCase() : slashed;
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Compare two file paths for equality, handling Windows case-insensitivity
|
|
479
|
-
* and mixed separators (backslash vs forward slash).
|
|
480
|
-
*/
|
|
481
|
-
export function pathsEqual(a, b) {
|
|
482
|
-
return normalizeFilePath(a) === normalizeFilePath(b);
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Check if `child` is under `parent` directory.
|
|
486
|
-
* Separator-agnostic and case-insensitive on Windows.
|
|
487
|
-
*/
|
|
488
|
-
/**
|
|
489
|
-
* Yield each directory from `startDir` up to (and including) the filesystem
|
|
490
|
-
* root. Terminates when `path.dirname(current) === current` so it works on
|
|
491
|
-
* Windows drive roots and POSIX `/` alike.
|
|
492
|
-
*
|
|
493
|
-
* Single source of truth for the half-dozen "walk up the directory tree
|
|
494
|
-
* looking for X" loops that have accumulated across the codebase. Callers
|
|
495
|
-
* that need an "is there a file named Y anywhere on the way up" check
|
|
496
|
-
* should use `findNearestContaining` instead.
|
|
497
|
-
*/
|
|
498
|
-
export function* walkUpDirs(startDir) {
|
|
499
|
-
let current = path.resolve(startDir);
|
|
500
|
-
while (true) {
|
|
501
|
-
yield current;
|
|
502
|
-
const parent = path.dirname(current);
|
|
503
|
-
if (parent === current)
|
|
504
|
-
return;
|
|
505
|
-
current = parent;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* Walk up from `startDir` and return the first directory that contains any
|
|
510
|
-
* of `candidates` on disk. Returns `undefined` if none match.
|
|
511
|
-
*
|
|
512
|
-
* @example
|
|
513
|
-
* findNearestContaining("/repo/pkg/src", ["package.json", "tsconfig.json"]);
|
|
514
|
-
* // → "/repo/pkg" if pkg/package.json exists, "/repo" if only /repo/package.json
|
|
515
|
-
*/
|
|
516
|
-
export function findNearestContaining(startDir, candidates) {
|
|
517
|
-
for (const dir of walkUpDirs(startDir)) {
|
|
518
|
-
for (const name of candidates) {
|
|
519
|
-
if (existsSync(path.join(dir, name)))
|
|
520
|
-
return dir;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return undefined;
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Walk up from `startDir` and return the first matching FILE path (not just
|
|
527
|
-
* the containing directory) for any of `names`, first-match-wins within each
|
|
528
|
-
* directory in `names` order. Single source of truth for the "walk up
|
|
529
|
-
* looking for one of these config filenames" loop that `opengrep-config.ts`,
|
|
530
|
-
* `typos-config.ts`, `zizmor-config.ts`, and `sgconfig.ts` each hand-rolled
|
|
531
|
-
* independently (refs #680), and that `php-cs-fixer-config.ts` now delegates
|
|
532
|
-
* to as well (refs #2472 review F2).
|
|
533
|
-
*
|
|
534
|
-
* UNCEILINGED by default (refs #2472 review round 3, F1) — `options.homeDir`
|
|
535
|
-
* is opt-in, not default-on. A prior version applied the SAME `$HOME`
|
|
536
|
-
* ceiling as `findNearestMarkerRoot` unconditionally, which broke every one
|
|
537
|
-
* of these tools' actual discovery contract: each of them treats a config
|
|
538
|
-
* living directly at `$HOME` (`~/typos.toml`, `~/sgconfig.yml`, …) as the
|
|
539
|
-
* user's legitimate GLOBAL config, and reads it itself regardless of pi-lens
|
|
540
|
-
* — the ceiling didn't stop pi-lens from seeing an unrelated ancestor
|
|
541
|
-
* config, it stopped pi-lens from seeing the SAME config the tool was about
|
|
542
|
-
* to read on its own, so pi-lens silently fell back to (or, for typos,
|
|
543
|
-
* injected and let its own shipped `_typos.toml` merge over) the user's
|
|
544
|
-
* config where the tool's own resolver would have honored it. `php-cs-fixer`
|
|
545
|
-
* makes the same mismatch concrete: its detection gates
|
|
546
|
-
* (`hasPhpCsFixerConfig` via `findNearestContaining`, `phpCsFixerFormatter
|
|
547
|
-
* .detect` via its own `findUp`) are both unceilinged, so a ceilinged
|
|
548
|
-
* carriage here disagreed with its own gate — "config exists" from the gate,
|
|
549
|
-
* "config not found" from the resolver — and dropped the very `--config`
|
|
550
|
-
* argv #2472 exists to carry. Pass `options.homeDir` only when a caller
|
|
551
|
-
* affirmatively wants the ceiling (a config found at or above THAT directory
|
|
552
|
-
* is never returned); omitting it walks all the way to the filesystem root,
|
|
553
|
-
* matching `findNearestContaining`'s unceilinged behavior and every
|
|
554
|
-
* underlying tool's own discovery.
|
|
555
|
-
*
|
|
556
|
-
* Distinct from `findNearestContaining`, which returns the containing
|
|
557
|
-
* directory rather than the matched file path — use that one when the caller
|
|
558
|
-
* only needs "is one of these present nearby", not which file it is.
|
|
559
|
-
*
|
|
560
|
-
* @example
|
|
561
|
-
* findLocalToolConfig(cwd, ["typos.toml", "_typos.toml", ".typos.toml"]);
|
|
562
|
-
* // → "/repo/typos.toml" if present, else undefined
|
|
563
|
-
*/
|
|
564
|
-
export function findLocalToolConfig(startDir, names, options = {}) {
|
|
565
|
-
const homeDir = options.homeDir !== undefined ? path.resolve(options.homeDir) : undefined;
|
|
566
|
-
for (const dir of walkUpDirs(startDir || process.cwd())) {
|
|
567
|
-
if (homeDir !== undefined && isAtOrAboveHomeDir(dir, homeDir)) {
|
|
568
|
-
return undefined;
|
|
569
|
-
}
|
|
570
|
-
for (const name of names) {
|
|
571
|
-
const candidate = path.join(dir, name);
|
|
572
|
-
if (existsSync(candidate))
|
|
573
|
-
return candidate;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return undefined;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Accept only a real Git repository marker: a directory with HEAD, or a
|
|
580
|
-
* worktree/submodule marker file whose first line starts with `gitdir:`.
|
|
581
|
-
*/
|
|
582
|
-
export function isRealGitMarker(markerPath) {
|
|
583
|
-
try {
|
|
584
|
-
const marker = statSync(markerPath);
|
|
585
|
-
if (marker.isDirectory())
|
|
586
|
-
return existsSync(path.join(markerPath, "HEAD"));
|
|
587
|
-
if (!marker.isFile())
|
|
588
|
-
return false;
|
|
589
|
-
return readFileSync(markerPath, "utf8")
|
|
590
|
-
.split(/\r?\n/, 1)[0]
|
|
591
|
-
.startsWith("gitdir:");
|
|
592
|
-
}
|
|
593
|
-
catch {
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Walk up from `startDir` looking for a directory containing any of
|
|
599
|
-
* `markers`, the same containment-aware climb `knip-client.ts` and
|
|
600
|
-
* `dead-code-client.ts` each used to hand-roll independently (refs #625):
|
|
601
|
-
*
|
|
602
|
-
* - Never resolves at or above `$HOME` (via `isAtOrAboveHomeDir`) — a
|
|
603
|
-
* marker found there has escaped the user's workspace.
|
|
604
|
-
* - If `options.boundaries` is given and one is found before any `marker`,
|
|
605
|
-
* stops and returns `null` rather than continuing past it.
|
|
606
|
-
* - Depth-capped at 64 climbs, matching the callers' existing safety bound
|
|
607
|
-
* (guards a pathological symlink loop; real depths are ~10).
|
|
608
|
-
* - Returns `null` — never `startDir` — when nothing is found. Callers
|
|
609
|
-
* must treat `null` as "no project here", not fall back to the start
|
|
610
|
-
* directory (a `null`-swallowing fallback was the #250/#296 bug class:
|
|
611
|
-
* scanning $HOME wholesale from a bare cwd).
|
|
612
|
-
*
|
|
613
|
-
* For a plain "find nearest containing directory" with no boundary concept,
|
|
614
|
-
* use `findNearestContaining` instead. Distinct from `startup-scan.ts`'s
|
|
615
|
-
* `findNearestProjectRoot` (fixed marker list, no boundaries, no home-check —
|
|
616
|
-
* that caller applies `isAtOrAboveHomeDir` itself afterward); named
|
|
617
|
-
* differently here to avoid confusion between the two.
|
|
618
|
-
*/
|
|
619
|
-
export function findNearestMarkerRoot(startDir, markers, options = {}) {
|
|
620
|
-
const boundaries = options.boundaries ?? [];
|
|
621
|
-
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
622
|
-
const markerPredicate = options.markerPredicate ?? (() => true);
|
|
623
|
-
let current = path.resolve(startDir);
|
|
624
|
-
for (let depth = 0; depth < 64; depth++) {
|
|
625
|
-
if (isAtOrAboveHomeDir(current, homeDir))
|
|
626
|
-
return null;
|
|
627
|
-
if (markers.some((m) => existsSync(path.join(current, m)) &&
|
|
628
|
-
markerPredicate(path.join(current, m))))
|
|
629
|
-
return current;
|
|
630
|
-
if (boundaries.some((m) => existsSync(path.join(current, m))))
|
|
631
|
-
return null;
|
|
632
|
-
const parent = path.dirname(current);
|
|
633
|
-
if (parent === current)
|
|
634
|
-
return null;
|
|
635
|
-
current = parent;
|
|
636
|
-
}
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* True when `dir` is the home directory OR an ancestor of it (`/home`,
|
|
641
|
-
* `C:\Users`, the filesystem root, …). A project-root search that climbs to
|
|
642
|
-
* such a directory has escaped the user's workspace — walking down from it
|
|
643
|
-
* scans unrelated trees (the #250 runaway). Use this as the single shared
|
|
644
|
-
* ceiling on any upward project-root resolution, instead of an exact
|
|
645
|
-
* `=== os.homedir()` check (which a marker found *above* `$HOME` slips past).
|
|
646
|
-
* A normal project *under* home (e.g. `~/code/app`) is NOT at-or-above home,
|
|
647
|
-
* so it still resolves fine. Refs #253.
|
|
648
|
-
*
|
|
649
|
-
* ONE expression, deliberately: `path.relative` alone answers "is home inside
|
|
650
|
-
* `dir`, or the same directory as `dir`", and it answers with the PLATFORM's
|
|
651
|
-
* path-equality semantics — case-folding on win32, case-sensitive on POSIX.
|
|
652
|
-
* The `resolvedDir === resolvedHome` shortcut that used to precede it was
|
|
653
|
-
* case-SENSITIVE on every platform, so `c:\Users\jane` (the lowercase-drive
|
|
654
|
-
* form VS Code URIs produce, and the form 46 records in a real `latency.log`
|
|
655
|
-
* carry) missed the equality test AND was then rejected by a `rel !== ""`
|
|
656
|
-
* clause — reporting "not at home" for the home directory itself and letting
|
|
657
|
-
* every ceilinged walker climb straight past HOME (#2544 review F1).
|
|
658
|
-
*
|
|
659
|
-
* rel === "" ⇢ `dir` IS home
|
|
660
|
-
* rel relative, no leading `..` ⇢ home is INSIDE `dir` (an ancestor)
|
|
661
|
-
* rel starts with `..` ⇢ home is elsewhere — sibling, or the
|
|
662
|
-
* prefix trap `C:\Users\jane2` → home
|
|
663
|
-
* `C:\Users\jane` gives `..\jane`
|
|
664
|
-
* rel absolute ⇢ different Windows drive
|
|
665
|
-
*
|
|
666
|
-
* Same shape as `isUnderDir` above; keep the two in step.
|
|
667
|
-
*
|
|
668
|
-
* `pathImpl` exists so the ubuntu Unit tests lane — the authoritative one —
|
|
669
|
-
* can exercise the WIN32 semantics this helper's whole bug was about. Every
|
|
670
|
-
* assertion that matters here (drive-letter folding, cross-drive `rel`) is
|
|
671
|
-
* win32-only, so with the ambient `path` hardcoded the ceiling was enforced
|
|
672
|
-
* only on a maintainer's dev box and CI's diff was structurally zero (#2544
|
|
673
|
-
* review F2). Production never passes it; `path.win32`/`path.posix` are pure,
|
|
674
|
-
* host-independent implementations, so `tests/clients/path-utils.test.ts` runs
|
|
675
|
-
* the same table under both on every lane. Lane-level fix tracked as #2536.
|
|
676
|
-
*/
|
|
677
|
-
export function isAtOrAboveHomeDir(dir, homeDir = os.homedir(), pathImpl = path) {
|
|
678
|
-
const rel = pathImpl.relative(pathImpl.resolve(dir), pathImpl.resolve(homeDir));
|
|
679
|
-
return !rel.startsWith("..") && !pathImpl.isAbsolute(rel);
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Rewrite a `$HOME`-anchored path to its `~` form for a DIAGNOSTIC surface.
|
|
683
|
-
*
|
|
684
|
-
* `~/.pi-lens/config.json` says everything an operator needs about which file
|
|
685
|
-
* won, and `C:/Users/jane.doe/.pi-lens/config.json` says that plus the
|
|
686
|
-
* account name. The second is the shape a global config path always has, so any
|
|
687
|
-
* projection that names one leaks an identifier by default rather than by
|
|
688
|
-
* accident (#2440 review finding F5).
|
|
689
|
-
*
|
|
690
|
-
* Deliberately NOT built on `normalizeFilePath`/`isUnderDir`: those resolve
|
|
691
|
-
* through `realpathSync`, and a redaction helper on a diagnostic path must be
|
|
692
|
-
* pure, total, and unable to throw for a file that no longer exists. This is a
|
|
693
|
-
* string rewrite and nothing else.
|
|
694
|
-
*
|
|
695
|
-
* A path that is not under home is returned UNCHANGED — separators included.
|
|
696
|
-
* Normalizing unrelated paths on the way past would make the helper's blast
|
|
697
|
-
* radius every path any projection ever carries, for no redaction benefit.
|
|
698
|
-
*/
|
|
699
|
-
export function homeRelativePath(filePath, homeDir = os.homedir()) {
|
|
700
|
-
if (filePath.length === 0)
|
|
701
|
-
return filePath;
|
|
702
|
-
const candidate = toPosix(filePath);
|
|
703
|
-
const home = toPosix(homeDir).replace(/\/+$/, "");
|
|
704
|
-
if (home.length === 0)
|
|
705
|
-
return filePath;
|
|
706
|
-
// Windows paths are case-insensitive, so the COMPARISON folds case there
|
|
707
|
-
// while the returned tail keeps the caller's own casing.
|
|
708
|
-
const fold = (text) => process.platform === "win32" ? text.toLowerCase() : text;
|
|
709
|
-
const foldedHome = fold(home);
|
|
710
|
-
const foldedCandidate = fold(candidate);
|
|
711
|
-
if (foldedCandidate === foldedHome)
|
|
712
|
-
return "~";
|
|
713
|
-
if (!foldedCandidate.startsWith(`${foldedHome}/`))
|
|
714
|
-
return filePath;
|
|
715
|
-
return `~${candidate.slice(home.length)}`;
|
|
716
|
-
}
|
|
717
|
-
export function isUnderDir(child, parent) {
|
|
718
|
-
const normChild = normalizeFilePath(child);
|
|
719
|
-
const normParent = normalizeFilePath(parent);
|
|
720
|
-
// Ensure parent ends with / for prefix matching
|
|
721
|
-
const parentPrefix = normParent.endsWith("/") ? normParent : `${normParent}/`;
|
|
722
|
-
return normChild === normParent || normChild.startsWith(parentPrefix);
|
|
723
|
-
}
|
|
724
|
-
const VENDOR_DIR_NAMES = new Set([
|
|
725
|
-
"node_modules",
|
|
726
|
-
"vendor",
|
|
727
|
-
"vendors",
|
|
728
|
-
"third_party",
|
|
729
|
-
"third-party",
|
|
730
|
-
]);
|
|
731
|
-
/**
|
|
732
|
-
* Returns true when a file should be treated as external/vendor and excluded
|
|
733
|
-
* from pipelines (LSP, diagnostics, complexity, read-guard, etc.).
|
|
734
|
-
*
|
|
735
|
-
* Cases:
|
|
736
|
-
* 1. Outside the project root entirely (e.g. global npm packages, system files)
|
|
737
|
-
* 2. Inside the project but under a vendor directory (node_modules, vendor, third_party, etc.)
|
|
738
|
-
*/
|
|
739
|
-
export function isExternalOrVendorFile(filePath, projectRoot) {
|
|
740
|
-
if (!isUnderDir(filePath, projectRoot))
|
|
741
|
-
return true;
|
|
742
|
-
const normalized = normalizeFilePath(filePath);
|
|
743
|
-
const rootNorm = normalizeFilePath(projectRoot);
|
|
744
|
-
const rel = normalized.startsWith(rootNorm + "/")
|
|
745
|
-
? normalized.slice(rootNorm.length + 1)
|
|
746
|
-
: normalized;
|
|
747
|
-
return rel.split("/").some((seg) => VENDOR_DIR_NAMES.has(seg));
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Shared marker-glob semantics for every "does this directory contain a file
|
|
751
|
-
* matching this glob" probe (#895 review): match against the entry NAME only,
|
|
752
|
-
* `dot: true` so dotfile markers match, `nocase` on win32 to match the
|
|
753
|
-
* filesystem (and the project ignore matcher). The three marker probes —
|
|
754
|
-
* language-profile.ts `hasProjectMarker`, workspace-topology.ts
|
|
755
|
-
* `hasBasenameMarker`, lsp/server.ts `markerExists` — must all route their
|
|
756
|
-
* glob matching through here rather than call minimatch with hand-copied
|
|
757
|
-
* options.
|
|
758
|
-
*/
|
|
759
|
-
export function nameMatchesMarkerGlob(name, pattern) {
|
|
760
|
-
return minimatch(name, pattern, {
|
|
761
|
-
dot: true,
|
|
762
|
-
nocase: process.platform === "win32",
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Files/symlinks-only marker-glob probe over a directory listing — a
|
|
767
|
-
* *directory* named like a marker (e.g. a `Foo.csproj/` dir) is not a project
|
|
768
|
-
* file (#201).
|
|
769
|
-
*/
|
|
770
|
-
export function direntsHaveMarkerGlobMatch(entries, pattern) {
|
|
771
|
-
return entries.some((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
|
|
772
|
-
nameMatchesMarkerGlob(entry.name, pattern));
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Cargo `[workspace] members`/`exclude` entries.
|
|
776
|
-
*
|
|
777
|
-
* KNOWN LIMITATION (#1671 F6, documented rather than implemented, preserved
|
|
778
|
-
* byte-for-byte by #2591's fold): a recursive `**` component is NOT supported
|
|
779
|
-
* and a pattern containing one never matches — cargo workspaces that rely on
|
|
780
|
-
* `**` to pull in an arbitrarily-nested crate tree under-hoist (the crate stays
|
|
781
|
-
* independently rooted instead of joining the workspace). Folding cargo onto
|
|
782
|
-
* uv's `crosses-components` globstar would silently change Rust-LSP root
|
|
783
|
-
* selection, so the divergence is kept as a dialect value, not resolved.
|
|
784
|
-
*
|
|
785
|
-
* `characterClasses: false` is likewise a preserved divergence, not a
|
|
786
|
-
* considered choice: cargo's pre-fold segment compiler escaped `[` and `]`
|
|
787
|
-
* into literals, so `crates/[ab]` names a directory literally called `[ab]`.
|
|
788
|
-
* Real cargo (the `glob` crate) would read it as a class; changing that here
|
|
789
|
-
* would be the same unreviewed Rust-hoisting change.
|
|
790
|
-
*/
|
|
791
|
-
export const CARGO_WORKSPACE_MEMBER_DIALECT = {
|
|
792
|
-
name: "cargo",
|
|
793
|
-
globstar: "pattern-never-matches",
|
|
794
|
-
wildcardCrossesSeparator: false,
|
|
795
|
-
characterClasses: false,
|
|
796
|
-
normalizePattern: (pattern) => pattern.replace(/\/+$/, ""),
|
|
797
|
-
};
|
|
798
|
-
/**
|
|
799
|
-
* uv `[tool.uv.workspace] members`, pinned to
|
|
800
|
-
* `astral-sh/uv@3c979abda4530fe9bf3d92e9bcf5c5575e3b3126`,
|
|
801
|
-
* `crates/uv-workspace/src/workspace.rs` `is_included_in_workspace`: the glob is
|
|
802
|
-
* `normalize_path`d first (so a leading `./` and any interior `.` component are
|
|
803
|
-
* not part of the pattern) and matched with
|
|
804
|
-
* `MatchOptions { require_literal_separator: true, ..MatchOptions::new() }`.
|
|
805
|
-
*/
|
|
806
|
-
export const UV_WORKSPACE_MEMBERS_DIALECT = {
|
|
807
|
-
name: "uv-members",
|
|
808
|
-
globstar: "crosses-components",
|
|
809
|
-
wildcardCrossesSeparator: false,
|
|
810
|
-
characterClasses: true,
|
|
811
|
-
normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
|
|
812
|
-
};
|
|
813
|
-
/**
|
|
814
|
-
* uv `[tool.uv.workspace] exclude`, same upstream file and SHA
|
|
815
|
-
* (`WorkspaceExclusions::matches`): an exclusion is matched with
|
|
816
|
-
* `Pattern::matches_path`, i.e. `MatchOptions::new()` defaults, where
|
|
817
|
-
* `require_literal_separator` is FALSE — so a `*` in an exclusion DOES cross
|
|
818
|
-
* `/` (`exclude = ['packages/a*c']` excludes `packages/a/b/c`). Two different
|
|
819
|
-
* option sets inside one tool, which is why the dialect is an object and the
|
|
820
|
-
* two uv constants are not one.
|
|
821
|
-
*
|
|
822
|
-
* Before #2591 this was a documented limitation: minimatch cannot express a
|
|
823
|
-
* separator-crossing `*`, so pi-lens under-excluded. The dialect object can, so
|
|
824
|
-
* it is now implemented rather than documented.
|
|
825
|
-
*/
|
|
826
|
-
export const UV_WORKSPACE_EXCLUDE_DIALECT = {
|
|
827
|
-
name: "uv-exclude",
|
|
828
|
-
globstar: "crosses-components",
|
|
829
|
-
wildcardCrossesSeparator: true,
|
|
830
|
-
characterClasses: true,
|
|
831
|
-
normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
|
|
832
|
-
};
|
|
833
|
-
/**
|
|
834
|
-
* Index of the `]` closing the character class opened at `open`, or `-1` when
|
|
835
|
-
* the run is unterminated (in which case the `[` is a literal). A leading `!`
|
|
836
|
-
* or `^` negates, and a `]` in first position is a literal class member — the
|
|
837
|
-
* shape both minimatch and rust `glob` accept.
|
|
838
|
-
*/
|
|
839
|
-
function findCharacterClassEnd(segment, open) {
|
|
840
|
-
let i = open + 1;
|
|
841
|
-
if (segment[i] === "!" || segment[i] === "^")
|
|
842
|
-
i += 1;
|
|
843
|
-
if (segment[i] === "]")
|
|
844
|
-
i += 1;
|
|
845
|
-
for (; i < segment.length; i += 1)
|
|
846
|
-
if (segment[i] === "]")
|
|
847
|
-
return i;
|
|
848
|
-
return -1;
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* May a wildcard with this separator policy consume `ch`?
|
|
852
|
-
*
|
|
853
|
-
* A separator-confined wildcard (`[^/]` in the regex this replaced) takes
|
|
854
|
-
* anything but `/`. A separator-CROSSING one — uv `exclude`'s `*`/`?`, and
|
|
855
|
-
* every `**` in either uv dialect — spelled `.` in that regex, which is every
|
|
856
|
-
* code unit EXCEPT the four line terminators. That exclusion is preserved
|
|
857
|
-
* deliberately rather than "fixed": minimatch's globstar is `.`-based too, so
|
|
858
|
-
* a `\n` inside a directory name has never matched across a `**` in either
|
|
859
|
-
* implementation, and the differential oracle would flag a change here as a
|
|
860
|
-
* divergence rather than an improvement (#2603).
|
|
861
|
-
*/
|
|
862
|
-
function stepAcceptsChar(crossesSeparator, ch) {
|
|
863
|
-
return crossesSeparator
|
|
864
|
-
? ch !== "\n" && ch !== "\r" && ch !== "\u2028" && ch !== "\u2029"
|
|
865
|
-
: ch !== "/";
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* Compile a normalized workspace-member pattern into a step list, or
|
|
869
|
-
* `undefined` when it cannot be compiled at all.
|
|
870
|
-
*
|
|
871
|
-
* A `**` component consumes zero or more path components — except as the LAST
|
|
872
|
-
* component, where it requires at least one (`a/**` matches `a/b`, not `a`),
|
|
873
|
-
* reproducing both minimatch's and rust `glob`'s answer. It is emitted as
|
|
874
|
-
* `split → one → star → literal "/"`, i.e. "nothing, or one-or-more characters
|
|
875
|
-
* followed by the `/` that ends them", after the separator that precedes it.
|
|
876
|
-
* That is the same language as the `(?:/.+)?/` group the previous compiler
|
|
877
|
-
* emitted — `X(?:/.+)?/Y` and `X/(?:.+/)?Y` both denote `X/Y` ∪ `X/.+/Y` — in a
|
|
878
|
-
* form with no nested quantifier.
|
|
879
|
-
*/
|
|
880
|
-
function compileWorkspaceMemberPattern(pattern, dialect) {
|
|
881
|
-
const isGlobstar = (component) => component === "**" && dialect.globstar === "crosses-components";
|
|
882
|
-
const components = pattern.split("/");
|
|
883
|
-
const steps = [];
|
|
884
|
-
let needSeparator = false;
|
|
885
|
-
for (let c = 0; c < components.length; c += 1) {
|
|
886
|
-
if (needSeparator)
|
|
887
|
-
steps.push({ kind: "literal", char: "/" });
|
|
888
|
-
needSeparator = false;
|
|
889
|
-
const component = components[c];
|
|
890
|
-
if (isGlobstar(component)) {
|
|
891
|
-
if (c === components.length - 1) {
|
|
892
|
-
// `.+` — a trailing `**` requires at least one character.
|
|
893
|
-
steps.push({ kind: "one", crossesSeparator: true });
|
|
894
|
-
steps.push({ kind: "star", crossesSeparator: true });
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
// `(?:.+/)?` — zero or more whole components. The group already ends
|
|
898
|
-
// at a `/`, so the next component must NOT emit one.
|
|
899
|
-
const split = { kind: "split", alternative: -1 };
|
|
900
|
-
steps.push(split);
|
|
901
|
-
steps.push({ kind: "one", crossesSeparator: true });
|
|
902
|
-
steps.push({ kind: "star", crossesSeparator: true });
|
|
903
|
-
steps.push({ kind: "literal", char: "/" });
|
|
904
|
-
split.alternative = steps.length;
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
for (let i = 0; i < component.length; i += 1) {
|
|
908
|
-
const ch = component[i];
|
|
909
|
-
if (ch === "*") {
|
|
910
|
-
steps.push({
|
|
911
|
-
kind: "star",
|
|
912
|
-
crossesSeparator: dialect.wildcardCrossesSeparator,
|
|
913
|
-
});
|
|
914
|
-
continue;
|
|
915
|
-
}
|
|
916
|
-
if (ch === "?") {
|
|
917
|
-
steps.push({
|
|
918
|
-
kind: "one",
|
|
919
|
-
crossesSeparator: dialect.wildcardCrossesSeparator,
|
|
920
|
-
});
|
|
921
|
-
continue;
|
|
922
|
-
}
|
|
923
|
-
if (ch === "[" && dialect.characterClasses) {
|
|
924
|
-
const close = findCharacterClassEnd(component, i);
|
|
925
|
-
if (close !== -1) {
|
|
926
|
-
// `\` and a first-position `]` are literal class MEMBERS in glob;
|
|
927
|
-
// left alone they would be a regex escape and an empty-class
|
|
928
|
-
// terminator (`[]ab]` is `[]` + `ab]` in JS), so both are escaped.
|
|
929
|
-
const body = component.slice(i + 1, close).replace(/[\\\]]/g, "\\$&");
|
|
930
|
-
const source = body.startsWith("!")
|
|
931
|
-
? `[^${body.slice(1)}]`
|
|
932
|
-
: `[${body}]`;
|
|
933
|
-
let match;
|
|
934
|
-
try {
|
|
935
|
-
match = new RegExp(`^${source}$`);
|
|
936
|
-
}
|
|
937
|
-
catch {
|
|
938
|
-
// A glob character class is not a JS character class: `[z-a]`
|
|
939
|
-
// is a legal glob (matching nothing, since the range is empty)
|
|
940
|
-
// and an illegal RegExp ("Range out of order"). Fail CLOSED —
|
|
941
|
-
// an uncompilable pattern declares no member and excludes
|
|
942
|
-
// nothing — which is also the answer the deleted minimatch call
|
|
943
|
-
// gave (#2591 review round 2, F2).
|
|
944
|
-
return undefined;
|
|
945
|
-
}
|
|
946
|
-
steps.push({ kind: "class", match });
|
|
947
|
-
i = close;
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
steps.push({ kind: "literal", char: ch });
|
|
952
|
-
}
|
|
953
|
-
needSeparator = true;
|
|
954
|
-
}
|
|
955
|
-
return steps;
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Does the whole of `subject` match the whole of `steps`?
|
|
959
|
-
*
|
|
960
|
-
* A memoized (step index, path index) table, filled once, bottom-up:
|
|
961
|
-
* `table[s][p]` is "steps `s…` match `subject[p…]`". Every cell reads only
|
|
962
|
-
* cells with a larger step index or a larger path index, so one backward
|
|
963
|
-
* double loop fills the table with no recursion and no re-entry — the match is
|
|
964
|
-
* O(steps x characters) in time and space, with no path through it that can
|
|
965
|
-
* take exponential time (#2603).
|
|
966
|
-
*
|
|
967
|
-
* The compiled whole-path regex this replaced was correct but backtracking:
|
|
968
|
-
* every `**` emitted its own nullable `.+`, and N of them explored 2^N splits
|
|
969
|
-
* of a non-matching subject. #2591 collapsed CONSECUTIVE `**`s, which is a
|
|
970
|
-
* normalization that cannot fire across a separating component, so
|
|
971
|
-
* `("**\/*" x12)/zzz` against a 40-component path still took 124900 ms (#2603);
|
|
972
|
-
* the same shapes are microseconds here. There is nothing left to collapse for
|
|
973
|
-
* speed, so no collapse is done: `a/**\/**\/b` compiles to two adjacent
|
|
974
|
-
* `(?:.+/)?` groups, which denote the same language as one and cost the same
|
|
975
|
-
* table.
|
|
976
|
-
*/
|
|
977
|
-
function matchesWorkspaceMemberSteps(steps, subject) {
|
|
978
|
-
const stepCount = steps.length;
|
|
979
|
-
const width = subject.length + 1;
|
|
980
|
-
// One byte per (step, position) cell; `1` means "the rest matches from here".
|
|
981
|
-
const table = new Uint8Array((stepCount + 1) * width);
|
|
982
|
-
for (let p = subject.length; p >= 0; p -= 1) {
|
|
983
|
-
const atEnd = p === subject.length;
|
|
984
|
-
// The empty step list matches only the empty remainder.
|
|
985
|
-
table[stepCount * width + p] = atEnd ? 1 : 0;
|
|
986
|
-
for (let s = stepCount - 1; s >= 0; s -= 1) {
|
|
987
|
-
const step = steps[s];
|
|
988
|
-
const next = (s + 1) * width;
|
|
989
|
-
let matched = 0;
|
|
990
|
-
switch (step.kind) {
|
|
991
|
-
case "literal":
|
|
992
|
-
matched =
|
|
993
|
-
!atEnd && subject[p] === step.char ? table[next + p + 1] : 0;
|
|
994
|
-
break;
|
|
995
|
-
case "one":
|
|
996
|
-
matched =
|
|
997
|
-
!atEnd && stepAcceptsChar(step.crossesSeparator, subject[p])
|
|
998
|
-
? table[next + p + 1]
|
|
999
|
-
: 0;
|
|
1000
|
-
break;
|
|
1001
|
-
case "class":
|
|
1002
|
-
matched =
|
|
1003
|
-
!atEnd && step.match.test(subject[p]) ? table[next + p + 1] : 0;
|
|
1004
|
-
break;
|
|
1005
|
-
case "star":
|
|
1006
|
-
// Consume nothing, or one more character and stay on this step.
|
|
1007
|
-
matched =
|
|
1008
|
-
table[next + p] === 1 ||
|
|
1009
|
-
(!atEnd &&
|
|
1010
|
-
stepAcceptsChar(step.crossesSeparator, subject[p]) &&
|
|
1011
|
-
table[s * width + p + 1] === 1)
|
|
1012
|
-
? 1
|
|
1013
|
-
: 0;
|
|
1014
|
-
break;
|
|
1015
|
-
case "split":
|
|
1016
|
-
matched =
|
|
1017
|
-
table[step.alternative * width + p] === 1 || table[next + p] === 1
|
|
1018
|
-
? 1
|
|
1019
|
-
: 0;
|
|
1020
|
-
break;
|
|
1021
|
-
}
|
|
1022
|
-
table[s * width + p] = matched;
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
return table[0] === 1;
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Does `relativePath` — a `/`-separated path relative to the workspace root —
|
|
1029
|
-
* match one declared workspace-member (or workspace-exclude) `pattern` under
|
|
1030
|
-
* `dialect`?
|
|
1031
|
-
*
|
|
1032
|
-
* This is the ONE workspace-member matcher (#2591). `clients/lsp/server.ts`'s
|
|
1033
|
-
* `cargoWorkspaceDeclaresMember` and `clients/python-environment.ts`'s
|
|
1034
|
-
* `isUvWorkspaceMember` are thin callers of it; neither keeps a private segment
|
|
1035
|
-
* compiler or minimatch options block any more.
|
|
1036
|
-
*
|
|
1037
|
-
* `clients/review-graph/workspace-modules.ts`'s `expandWorkspacePattern` is
|
|
1038
|
-
* deliberately NOT a caller: npm/pnpm `workspaces` entries are EXPANDED against
|
|
1039
|
-
* the filesystem (one `readdir` of the directory preceding the first `*`,
|
|
1040
|
-
* yielding the directories that exist and hold a manifest) rather than tested
|
|
1041
|
-
* against a candidate path. It answers "which directories does this pattern
|
|
1042
|
-
* name", not "does this pattern name this directory"; folding it in would mean
|
|
1043
|
-
* giving this pure function a filesystem.
|
|
1044
|
-
*
|
|
1045
|
-
* The supported syntax is the intersection of the two upstream dialects: `*`,
|
|
1046
|
-
* `?`, `**`, `[abc]`/`[!abc]` (dialect-gated), and literals. minimatch-only
|
|
1047
|
-
* extensions the pre-fold uv path inherited by accident — brace expansion,
|
|
1048
|
-
* extglobs, leading-`!` negation, leading-`#` comments — are NOT honored, and
|
|
1049
|
-
* uv's own `glob` crate does not honor them either (same pinned SHA), so
|
|
1050
|
-
* dropping them moves uv toward upstream rather than away from it.
|
|
1051
|
-
*
|
|
1052
|
-
* Matching is NON-BACKTRACKING (#2603): the pattern compiles to a list of
|
|
1053
|
-
* bounded steps and {@link matchesWorkspaceMemberSteps} decides it with one
|
|
1054
|
-
* memoized (step, position) table, O(steps x characters), no matter how many
|
|
1055
|
-
* `**`s the pattern carries. The anchored whole-path regex this replaced was
|
|
1056
|
-
* correct but explored 2^N splits of a non-matching path for N `**`
|
|
1057
|
-
* components — 124900 ms for `("**\/*" x12)/zzz` against a 40-component path,
|
|
1058
|
-
* on a call `detectPythonEnvironment` awaits with no timeout, i.e. a wedged
|
|
1059
|
-
* turn rather than a slow answer. The wall-clock half of that fix lives in
|
|
1060
|
-
* `tests/clients/workspace-glob-nonbacktracking-budget.test.ts`; the answers
|
|
1061
|
-
* are unchanged, pinned by the dialect table and the minimatch differential in
|
|
1062
|
-
* `tests/clients/path-utils.test.ts`.
|
|
1063
|
-
*
|
|
1064
|
-
* A pattern that cannot be compiled at all answers `false` for every path —
|
|
1065
|
-
* it declares no member and excludes nothing (#2591 review round 2, F2). The
|
|
1066
|
-
* only such patterns today carry an empty character-class range (`[z-a]`:
|
|
1067
|
-
* legal glob, illegal JS RegExp). Reachability note, recorded rather than
|
|
1068
|
-
* assumed away: no such pattern can reach here through a manifest right now,
|
|
1069
|
-
* because `parseTomlStringArray` (`clients/cargo-manifest.ts`) captures an
|
|
1070
|
-
* array body non-greedily up to the FIRST `]`, so any entry containing a `]`
|
|
1071
|
-
* loses its closing quote and is dropped before it becomes a pattern. That is
|
|
1072
|
-
* a property of the TOML reader, not of this matcher, and it is not this
|
|
1073
|
-
* function's to rely on — the character-class axis stays because upstream
|
|
1074
|
-
* cargo and uv both support classes, and the reader's gap may be closed later.
|
|
1075
|
-
*/
|
|
1076
|
-
export function matchesWorkspaceMemberPattern(pattern, relativePath, dialect) {
|
|
1077
|
-
const normalized = dialect.normalizePattern(pattern);
|
|
1078
|
-
if (dialect.globstar === "pattern-never-matches" &&
|
|
1079
|
-
normalized.includes("**")) {
|
|
1080
|
-
return false;
|
|
1081
|
-
}
|
|
1082
|
-
const steps = compileWorkspaceMemberPattern(normalized, dialect);
|
|
1083
|
-
return (steps !== undefined && matchesWorkspaceMemberSteps(steps, relativePath));
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* Narrow no-break space, U+202F. macOS writes it before AM/PM in screenshot
|
|
1087
|
-
* file names; users type an ordinary space.
|
|
1088
|
-
*/
|
|
1089
|
-
const NARROW_NO_BREAK_SPACE = "\u202F";
|
|
1090
|
-
/** Right single quotation mark, U+2019. macOS writes it; users type U+0027. */
|
|
1091
|
-
const RIGHT_SINGLE_QUOTE = "\u2019";
|
|
1092
|
-
/**
|
|
1093
|
-
* The unicode space class pi folds to U+0020, copied character-for-character
|
|
1094
|
-
* from `@earendil-works/pi-coding-agent/dist/utils/paths.js:6`
|
|
1095
|
-
* (`UNICODE_SPACES`, source `src/utils/paths.ts`). Widening or narrowing this
|
|
1096
|
-
* set makes pi-lens resolve a different file than pi does.
|
|
1097
|
-
*/
|
|
1098
|
-
const HOST_UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
1099
|
-
/**
|
|
1100
|
-
* Mirror pi's `normalizeWindowsShellPath`
|
|
1101
|
-
* (`@earendil-works/pi-coding-agent/dist/utils/paths.js:47-56`): convert Git
|
|
1102
|
-
* Bash, MSYS, Cygwin, and WSL drive paths to a form native Windows APIs
|
|
1103
|
-
* accept.
|
|
1104
|
-
*/
|
|
1105
|
-
function hostNormalizeWindowsShellPath(filePath) {
|
|
1106
|
-
if (!filePath.startsWith("/") ||
|
|
1107
|
-
filePath.startsWith("//") ||
|
|
1108
|
-
filePath.includes("\\")) {
|
|
1109
|
-
return filePath;
|
|
1110
|
-
}
|
|
1111
|
-
const match = filePath.match(/^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i);
|
|
1112
|
-
if (!match)
|
|
1113
|
-
return filePath;
|
|
1114
|
-
const suffix = match[2]?.replaceAll("/", "\\");
|
|
1115
|
-
return `${match[1].toUpperCase()}:\\${suffix ?? ""}`;
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Mirror pi's `normalizePath`
|
|
1119
|
-
* (`@earendil-works/pi-coding-agent/dist/utils/paths.js:57-79`, source
|
|
1120
|
-
* `src/utils/paths.ts`), in pi's order, which is load-bearing: the unicode
|
|
1121
|
-
* fold runs BEFORE the `@` strip, and the tilde expansion before the
|
|
1122
|
-
* `file://` conversion.
|
|
1123
|
-
*
|
|
1124
|
-
* The `win32` step is gated on `process.platform`, exactly as pi gates it.
|
|
1125
|
-
* This is a deliberate exception to the usual probe-the-filesystem rule
|
|
1126
|
-
* (AGENTS.md shape 2): pi-lens runs in pi's own process, so mirroring the
|
|
1127
|
-
* host's own platform branch is what keeps the two resolvers in agreement.
|
|
1128
|
-
* Shape-based parsing here would DIVERGE from the host, not protect against
|
|
1129
|
-
* it.
|
|
1130
|
-
*/
|
|
1131
|
-
export function normalizeHostToolPath(input, options = {}) {
|
|
1132
|
-
let normalized = input;
|
|
1133
|
-
if (options.normalizeUnicodeSpaces) {
|
|
1134
|
-
normalized = normalized.replace(HOST_UNICODE_SPACES, " ");
|
|
1135
|
-
}
|
|
1136
|
-
if (options.stripAtPrefix && normalized.startsWith("@")) {
|
|
1137
|
-
normalized = normalized.slice(1);
|
|
1138
|
-
}
|
|
1139
|
-
if (process.platform === "win32") {
|
|
1140
|
-
normalized = hostNormalizeWindowsShellPath(normalized);
|
|
1141
|
-
}
|
|
1142
|
-
// `homedir()` is resolved inside the branch, as pi does. This runs on every
|
|
1143
|
-
// tool_call and a `~` path is the rare case.
|
|
1144
|
-
if (normalized === "~")
|
|
1145
|
-
return os.homedir();
|
|
1146
|
-
if (normalized.startsWith("~/") ||
|
|
1147
|
-
(process.platform === "win32" && normalized.startsWith("~\\"))) {
|
|
1148
|
-
return path.join(os.homedir(), normalized.slice(2));
|
|
1149
|
-
}
|
|
1150
|
-
if (normalized.startsWith("file://")) {
|
|
1151
|
-
try {
|
|
1152
|
-
return fileURLToPath(normalized);
|
|
1153
|
-
}
|
|
1154
|
-
catch {
|
|
1155
|
-
// pi lets a malformed file: URL throw out of normalizePath, but pi-lens
|
|
1156
|
-
// is advisory instrumentation on the same event: a URL pi rejects must
|
|
1157
|
-
// degrade to "no path" here, never take down the tool_call handler.
|
|
1158
|
-
return normalized;
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
return normalized;
|
|
1162
|
-
}
|
|
1163
|
-
/**
|
|
1164
|
-
* Mirror pi's `resolveToCwd`
|
|
1165
|
-
* (`@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js:42-44`,
|
|
1166
|
-
* source `src/core/tools/path-utils.ts:~44-46`) \u2014 the BASE resolution every
|
|
1167
|
-
* read/edit/write path goes through before the variant ladder below ever
|
|
1168
|
-
* runs.
|
|
1169
|
-
*
|
|
1170
|
-
* Two details are pi's, not ours, and both matter:
|
|
1171
|
-
* - the input is normalized with `normalizeUnicodeSpaces` + `stripAtPrefix`;
|
|
1172
|
-
* the BASE DIR is normalized with neither (`resolvePath`,
|
|
1173
|
-
* `dist/utils/paths.js:80-84`).
|
|
1174
|
-
* - an already-absolute input is re-resolved on its own, ignoring the cwd.
|
|
1175
|
-
*/
|
|
1176
|
-
export function resolveHostToolPath(input, baseDir) {
|
|
1177
|
-
const normalized = normalizeHostToolPath(input, {
|
|
1178
|
-
normalizeUnicodeSpaces: true,
|
|
1179
|
-
stripAtPrefix: true,
|
|
1180
|
-
});
|
|
1181
|
-
const normalizedBaseDir = normalizeHostToolPath(baseDir);
|
|
1182
|
-
return path.isAbsolute(normalized)
|
|
1183
|
-
? path.resolve(normalized)
|
|
1184
|
-
: path.resolve(normalizedBaseDir, normalized);
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Mirror pi's read-path fallback ladder (#1655 item 5).
|
|
1188
|
-
*
|
|
1189
|
-
* pi does NOT open `resolve(cwd, input.path)`. `resolveReadPath`
|
|
1190
|
-
* (`@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js:45-70`,
|
|
1191
|
-
* source `src/core/tools/path-utils.ts:52-83`) resolves, and when that path
|
|
1192
|
-
* does not exist it silently retries four unicode/spacing variants in this
|
|
1193
|
-
* exact order:
|
|
1194
|
-
*
|
|
1195
|
-
* 1. narrow no-break space before `AM.`/`PM.` (`tryMacOSScreenshotPath`)
|
|
1196
|
-
* 2. NFD normalization (`tryNFDVariant`) — macOS stores names decomposed
|
|
1197
|
-
* 3. U+0027 → U+2019 (`tryCurlyQuoteVariant`)
|
|
1198
|
-
* 4. NFD + curly quote combined
|
|
1199
|
-
*
|
|
1200
|
-
* Each candidate is used only when it DIFFERS from the resolved path and the
|
|
1201
|
-
* file exists; otherwise pi falls back to the resolved path. So the file pi
|
|
1202
|
-
* actually read can differ from what a naive `path.resolve` produces, and
|
|
1203
|
-
* pi-lens keyed its read guard, LSP touch, and dispatch off the naive form —
|
|
1204
|
-
* silently doing nothing for exactly those files.
|
|
1205
|
-
*
|
|
1206
|
-
* Order matters: it is pi's, so pi-lens picks the same file pi did when more
|
|
1207
|
-
* than one variant happens to exist.
|
|
1208
|
-
*
|
|
1209
|
-
* @param resolvedPath an already-resolved absolute path (the naive form)
|
|
1210
|
-
* @param fileExists injectable existence probe; defaults to `existsSync`
|
|
1211
|
-
*/
|
|
1212
|
-
export function resolveHostPathVariants(resolvedPath, fileExists = existsSync) {
|
|
1213
|
-
if (fileExists(resolvedPath)) {
|
|
1214
|
-
return { path: resolvedPath, unresolved: false, triedVariants: [] };
|
|
1215
|
-
}
|
|
1216
|
-
const nfd = resolvedPath.normalize("NFD");
|
|
1217
|
-
const candidates = [
|
|
1218
|
-
{
|
|
1219
|
-
variant: "narrow-nbsp",
|
|
1220
|
-
candidate: resolvedPath.replace(/ (AM|PM)\./gi, `${NARROW_NO_BREAK_SPACE}$1.`),
|
|
1221
|
-
},
|
|
1222
|
-
{ variant: "nfd", candidate: nfd },
|
|
1223
|
-
{
|
|
1224
|
-
variant: "curly-quote",
|
|
1225
|
-
candidate: resolvedPath.replaceAll("'", RIGHT_SINGLE_QUOTE),
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
variant: "nfd-curly-quote",
|
|
1229
|
-
candidate: nfd.replaceAll("'", RIGHT_SINGLE_QUOTE),
|
|
1230
|
-
},
|
|
1231
|
-
];
|
|
1232
|
-
const triedVariants = [];
|
|
1233
|
-
for (const { variant, candidate } of candidates) {
|
|
1234
|
-
// pi skips a candidate identical to the resolved path, so pi-lens does
|
|
1235
|
-
// too — otherwise a no-op "variant" would be reported as a match.
|
|
1236
|
-
if (candidate === resolvedPath)
|
|
1237
|
-
continue;
|
|
1238
|
-
triedVariants.push(variant);
|
|
1239
|
-
if (fileExists(candidate)) {
|
|
1240
|
-
return { path: candidate, variant, unresolved: false, triedVariants };
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
return { path: resolvedPath, unresolved: true, triedVariants };
|
|
1244
|
-
}
|