@herbertgao/pi-extensions 2026.9.0 → 2026.9.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.
Files changed (360) hide show
  1. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  2. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@narumitw/pi-btw/README.md +57 -141
  11. package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
  12. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  13. package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
  14. package/node_modules/@narumitw/pi-btw/package.json +2 -1
  15. package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
  16. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
  17. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
  18. package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
  19. package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
  20. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
  21. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
  22. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  23. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  24. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  25. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  26. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  27. package/node_modules/pi-lens/CHANGELOG.md +1072 -0
  28. package/node_modules/pi-lens/README.md +3 -0
  29. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  30. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  31. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -9
  32. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  33. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
  34. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  35. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  36. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  37. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -9
  38. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
  39. package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -3
  40. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  41. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -6
  42. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  43. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  44. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
  45. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -9
  46. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  47. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +3 -11
  48. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  49. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  50. package/node_modules/pi-lens/dist/clients/cache-observability.js +40 -19
  51. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  52. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -9
  53. package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
  54. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  55. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  56. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  57. package/node_modules/pi-lens/dist/clients/config-core/index.js +50 -0
  58. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  59. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  60. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  61. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +164 -0
  62. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  63. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  64. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  65. package/node_modules/pi-lens/dist/clients/config-core/schema.js +165 -0
  66. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  67. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  68. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  69. package/node_modules/pi-lens/dist/clients/config-schema.js +234 -0
  70. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  71. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  72. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -6
  73. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  74. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  75. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  76. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  77. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  78. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  79. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
  80. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  81. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +15 -5
  82. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
  83. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -44
  84. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +27 -8
  86. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
  87. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
  89. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
  90. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +383 -27
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +1 -2
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +14 -18
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +6 -3
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +1 -2
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +1 -2
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -3
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -67
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  146. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
  147. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -10
  148. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  149. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  150. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  151. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  153. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  154. package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
  155. package/node_modules/pi-lens/dist/clients/file-time.js +7 -2
  156. package/node_modules/pi-lens/dist/clients/file-utils.js +117 -10
  157. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  158. package/node_modules/pi-lens/dist/clients/format-service.js +6 -2
  159. package/node_modules/pi-lens/dist/clients/formatters.js +385 -104
  160. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  161. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +116 -22
  162. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  163. package/node_modules/pi-lens/dist/clients/git-guard.js +6 -1
  164. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  165. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  166. package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
  167. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  168. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  169. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  170. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  171. package/node_modules/pi-lens/dist/clients/installer/index.js +289 -59
  172. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
  173. package/node_modules/pi-lens/dist/clients/instance-reaper.js +110 -236
  174. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  175. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
  176. package/node_modules/pi-lens/dist/clients/language-registry.js +599 -0
  177. package/node_modules/pi-lens/dist/clients/latency-logger.js +165 -9
  178. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +34 -1
  179. package/node_modules/pi-lens/dist/clients/lens-config.js +160 -30
  180. package/node_modules/pi-lens/dist/clients/lens-engine.js +33 -41
  181. package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
  182. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +77 -6
  183. package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -38
  185. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  186. package/node_modules/pi-lens/dist/clients/lsp/client.js +172 -33
  187. package/node_modules/pi-lens/dist/clients/lsp/config.js +484 -158
  188. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  189. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
  190. package/node_modules/pi-lens/dist/clients/lsp/edits.js +18 -5
  191. package/node_modules/pi-lens/dist/clients/lsp/index.js +892 -96
  192. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
  193. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -177
  194. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  195. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  196. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -18
  197. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  198. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  199. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  200. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
  201. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +14 -7
  202. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
  203. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
  204. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  205. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
  206. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  207. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  208. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  209. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  210. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  211. package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
  212. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +3 -5
  213. package/node_modules/pi-lens/dist/clients/module-report.js +21 -46
  214. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  215. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  216. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  217. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -24
  218. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  219. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  220. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +70 -28
  221. package/node_modules/pi-lens/dist/clients/opengrep-config.js +5 -1
  222. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  223. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  224. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  225. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  226. package/node_modules/pi-lens/dist/clients/path-utils.js +451 -15
  227. package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
  228. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  229. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  230. package/node_modules/pi-lens/dist/clients/pipeline.js +79 -18
  231. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  232. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  233. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  234. package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
  235. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
  236. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  237. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  238. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  239. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  240. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  241. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +28 -4
  242. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  243. package/node_modules/pi-lens/dist/clients/project-lens-config.js +438 -122
  244. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
  245. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  246. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  247. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  248. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  249. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  250. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  251. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +16 -5
  252. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  253. package/node_modules/pi-lens/dist/clients/read-guard.js +26 -2
  254. package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
  255. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  256. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +155 -114
  257. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
  258. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  259. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  260. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  261. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  262. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +19 -3
  263. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  264. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  265. package/node_modules/pi-lens/dist/clients/runtime-session.js +184 -32
  266. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +179 -36
  267. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  268. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  269. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  270. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  271. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -12
  272. package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
  273. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  274. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  275. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  277. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
  278. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  279. package/node_modules/pi-lens/dist/clients/sgconfig.js +7 -6
  280. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
  281. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  282. package/node_modules/pi-lens/dist/clients/smells-rollup.js +4 -4
  283. package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
  284. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  285. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  286. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  287. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +244 -0
  288. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  289. package/node_modules/pi-lens/dist/clients/tool-policy.js +92 -22
  290. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  291. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +87 -100
  292. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  293. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  294. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  295. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  296. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
  297. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  298. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -6
  299. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  300. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -9
  301. package/node_modules/pi-lens/dist/clients/word-index-store.js +23 -12
  302. package/node_modules/pi-lens/dist/clients/word-index.js +252 -47
  303. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  304. package/node_modules/pi-lens/dist/clients/zizmor-config.js +4 -1
  305. package/node_modules/pi-lens/dist/index.js +65899 -55340
  306. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  307. package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
  308. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  309. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  310. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  311. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  312. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +96 -25
  313. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +104 -86
  314. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  315. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
  316. package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
  317. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  318. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
  319. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  320. package/node_modules/pi-lens/docs/configuration.md +222 -0
  321. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  322. package/node_modules/pi-lens/docs/features.md +44 -5
  323. package/node_modules/pi-lens/docs/globalconfig.md +20 -0
  324. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  325. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
  326. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  327. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  328. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +33 -0
  329. package/node_modules/pi-lens/docs/pi-lens-subagent.md +49 -0
  330. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  331. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  332. package/node_modules/pi-lens/docs/release-qa-baseline.md +190 -0
  333. package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
  334. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  335. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  336. package/node_modules/pi-lens/docs/word-index.md +1 -1
  337. package/node_modules/pi-lens/package.json +18 -35
  338. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  339. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  340. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  341. package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
  342. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  343. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  344. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  345. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
  346. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  347. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
  348. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  349. package/node_modules/pi-web-access/README.md +62 -15
  350. package/node_modules/pi-web-access/curator-page.ts +3 -1
  351. package/node_modules/pi-web-access/curator-server.ts +5 -1
  352. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  353. package/node_modules/pi-web-access/github-extract.ts +242 -1
  354. package/node_modules/pi-web-access/index.ts +118 -64
  355. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  356. package/node_modules/pi-web-access/package.json +2 -2
  357. package/node_modules/pi-web-access/perplexity.ts +14 -1
  358. package/node_modules/pi-web-access/utils.ts +23 -6
  359. package/node_modules/pi-web-access/xai-search.ts +96 -33
  360. package/package.json +6 -6
@@ -0,0 +1,639 @@
1
+ const SUMMARY_BY_ROOT = new WeakMap();
2
+ const TRAVERSAL_VISIT_CAP = 50_000;
3
+ const TRAVERSAL_DEPTH_CAP = 128;
4
+ const EXPRESSION_DEPTH_CAP = 8;
5
+ const BINDING_SCAN_DEPTH_CAP = 32;
6
+ const ANCESTOR_DEPTH_CAP = 64;
7
+ /**
8
+ * Receiver names conventionally bound to a SQLAlchemy session. The pre-existing
9
+ * `session.execute(...)` exemption keys off these; structural provenance below
10
+ * proves the annotated case.
11
+ */
12
+ export const PYTHON_SQLALCHEMY_RECEIVER_NAMES = new Set([
13
+ "session",
14
+ "db_session",
15
+ "async_session",
16
+ "sync_session",
17
+ ]);
18
+ /** Statement constructors whose result is an expression object, never a string. */
19
+ export const PYTHON_SQLALCHEMY_STATEMENT_BUILDERS = new Set(["select", "insert", "update", "delete"]);
20
+ const FROM_IMPORT_PROVENANCE = new Map([
21
+ ["sqlalchemy.orm:Session", "sqlalchemy-session"],
22
+ ["sqlalchemy.ext.asyncio:AsyncSession", "sqlalchemy-session"],
23
+ ["psycopg:sql", "psycopg-sql-module"],
24
+ ["psycopg2:sql", "psycopg-sql-module"],
25
+ ["psycopg.sql:SQL", "psycopg-sql-constructor"],
26
+ ["psycopg2.sql:SQL", "psycopg-sql-constructor"],
27
+ ["psycopg.sql:Identifier", "psycopg-identifier-constructor"],
28
+ ["psycopg2.sql:Identifier", "psycopg-identifier-constructor"],
29
+ ]);
30
+ const PLAIN_PACKAGE_PROVENANCE = new Map([
31
+ ["sqlalchemy", "sqlalchemy-module"],
32
+ ["psycopg", "psycopg-package"],
33
+ ["psycopg2", "psycopg-package"],
34
+ ]);
35
+ function nodeKey(node) {
36
+ return `${node.type}:${node.startIndex}:${node.endIndex}`;
37
+ }
38
+ function namedChildren(node) {
39
+ return (node.children ?? []).filter((child) => child.isNamed);
40
+ }
41
+ function calleeNode(call) {
42
+ if (call.type !== "call")
43
+ return undefined;
44
+ return call.childForFieldName?.("function") ?? namedChildren(call)[0];
45
+ }
46
+ function directNamedChild(node, type) {
47
+ return namedChildren(node).find((child) => child.type === type);
48
+ }
49
+ // Containers hold further binding positions; reference types (`a.b`, `a[b]`,
50
+ // `Mod.Case`) never introduce a name.
51
+ const BINDING_TARGET_CONTAINER_TYPES = new Set([
52
+ "tuple",
53
+ "pattern_list",
54
+ "list",
55
+ "list_pattern",
56
+ "tuple_pattern",
57
+ "starred_expression",
58
+ "list_splat_pattern",
59
+ "dictionary_splat_pattern",
60
+ ]);
61
+ const BINDING_TARGET_REFERENCE_TYPES = new Set(["attribute", "subscript"]);
62
+ const BINDING_PATTERN_CONTAINER_TYPES = new Set([
63
+ "case_pattern",
64
+ "as_pattern",
65
+ "union_pattern",
66
+ "list_pattern",
67
+ "tuple_pattern",
68
+ "list_splat_pattern",
69
+ "dictionary_splat_pattern",
70
+ "class_pattern",
71
+ ]);
72
+ const BINDING_PATTERN_REFERENCE_TYPES = new Set([
73
+ "attribute",
74
+ "qualified_pattern",
75
+ ]);
76
+ function scanBindingNames(node, mode, scan, depth = 0) {
77
+ if (!node || depth > BINDING_SCAN_DEPTH_CAP) {
78
+ scan.unknown = true;
79
+ return;
80
+ }
81
+ const descend = (child, next) => scanBindingNames(child, next, scan, depth + 1);
82
+ if (node.type === "as_pattern_target") {
83
+ descend(namedChildren(node)[0], "target");
84
+ return;
85
+ }
86
+ if (mode === "as") {
87
+ // `with ctx() as name` / `except E as name`: only the as-target binds.
88
+ for (const child of namedChildren(node))
89
+ descend(child, "as");
90
+ return;
91
+ }
92
+ if (node.type === "identifier") {
93
+ if (node.text !== "_")
94
+ scan.names.push(node.text);
95
+ return;
96
+ }
97
+ if (mode === "pattern") {
98
+ if (node.type === "dotted_name") {
99
+ // A bare `case name` captures; `case Mod.CONST` is a value reference.
100
+ if (!node.text.includes(".") && node.text !== "_") {
101
+ scan.names.push(node.text);
102
+ }
103
+ return;
104
+ }
105
+ if (BINDING_PATTERN_REFERENCE_TYPES.has(node.type))
106
+ return;
107
+ if (node.type === "keyword_pattern") {
108
+ // `Wrapper(field=capture)`: the keyword itself is not a binder.
109
+ const value = namedChildren(node).slice(1);
110
+ if (value.length !== 1)
111
+ scan.unknown = true;
112
+ else
113
+ descend(value[0], "pattern");
114
+ return;
115
+ }
116
+ if (BINDING_PATTERN_CONTAINER_TYPES.has(node.type)) {
117
+ for (const child of namedChildren(node))
118
+ descend(child, "pattern");
119
+ return;
120
+ }
121
+ scan.unknown = true;
122
+ return;
123
+ }
124
+ if (BINDING_TARGET_REFERENCE_TYPES.has(node.type))
125
+ return;
126
+ if (BINDING_TARGET_CONTAINER_TYPES.has(node.type)) {
127
+ for (const child of namedChildren(node))
128
+ descend(child, "target");
129
+ return;
130
+ }
131
+ scan.unknown = true;
132
+ }
133
+ /** Extract only identifiers in Python binding positions. */
134
+ function collectBindingNames(node, mode) {
135
+ const scan = { names: [], unknown: false };
136
+ scanBindingNames(node, mode, scan);
137
+ return scan;
138
+ }
139
+ /**
140
+ * One import clause. `source` is the name as written in the module
141
+ * (`sql` in `from psycopg import sql`, `psycopg.sql` in `import psycopg.sql`);
142
+ * `local` is the name it binds.
143
+ */
144
+ function parseImportBinding(node, isFrom) {
145
+ if (node.type === "aliased_import") {
146
+ const children = namedChildren(node);
147
+ const source = children[0]?.text;
148
+ const local = children.at(-1);
149
+ return source && local?.type === "identifier"
150
+ ? { source, local: local.text }
151
+ : undefined;
152
+ }
153
+ if (node.type !== "dotted_name" && node.type !== "identifier") {
154
+ return undefined;
155
+ }
156
+ if (isFrom)
157
+ return { source: node.text, local: node.text };
158
+ const local = node.text.split(".")[0];
159
+ return local ? { source: node.text, local } : undefined;
160
+ }
161
+ function directAnnotationName(parameter) {
162
+ // `typed_default_parameter` is FastAPI's `db: Session = Depends(get_db)`.
163
+ if (parameter.type !== "typed_parameter" &&
164
+ parameter.type !== "typed_default_parameter") {
165
+ return undefined;
166
+ }
167
+ const type = directNamedChild(parameter, "type");
168
+ const children = type ? namedChildren(type) : [];
169
+ return children.length === 1 && children[0]?.type === "identifier"
170
+ ? children[0].text
171
+ : undefined;
172
+ }
173
+ function parameterName(parameter) {
174
+ if (parameter.type === "identifier")
175
+ return parameter.text;
176
+ return directNamedChild(parameter, "identifier")?.text;
177
+ }
178
+ function markInvalid(state) {
179
+ state.invalid = true;
180
+ }
181
+ function addBinding(state, name, functionChain) {
182
+ state.bindingCounts.set(name, (state.bindingCounts.get(name) ?? 0) + 1);
183
+ for (const fn of functionChain) {
184
+ const key = nodeKey(fn);
185
+ const bindings = state.functionBindings.get(key) ?? new Map();
186
+ bindings.set(name, (bindings.get(name) ?? 0) + 1);
187
+ state.functionBindings.set(key, bindings);
188
+ }
189
+ }
190
+ function addTarget(state, target, functionChain) {
191
+ const extracted = collectBindingNames(target, "target");
192
+ if (extracted.unknown)
193
+ markInvalid(state);
194
+ for (const name of extracted.names)
195
+ addBinding(state, name, functionChain);
196
+ }
197
+ function recordFunctionParameters(node, state) {
198
+ const annotations = new Map();
199
+ const parameters = directNamedChild(node, "parameters");
200
+ for (const parameter of namedChildren(parameters ?? node)) {
201
+ const name = parameterName(parameter);
202
+ if (!name) {
203
+ if (parameter.type !== "list_splat_pattern")
204
+ markInvalid(state);
205
+ continue;
206
+ }
207
+ addBinding(state, name, state.functionChain);
208
+ const annotation = directAnnotationName(parameter);
209
+ if (annotation)
210
+ annotations.set(name, annotation);
211
+ }
212
+ state.functionAnnotations.set(nodeKey(node), annotations);
213
+ }
214
+ function recordFunctionDefinition(node, state) {
215
+ recordFunctionParameters(node, state);
216
+ recordDefinitionBinding(node, state);
217
+ }
218
+ function recordLambdaParameters(node, state) {
219
+ const parameters = directNamedChild(node, "lambda_parameters");
220
+ for (const parameter of namedChildren(parameters ?? node)) {
221
+ const name = parameterName(parameter);
222
+ if (name)
223
+ addBinding(state, name, state.functionChain);
224
+ else
225
+ markInvalid(state);
226
+ }
227
+ }
228
+ function recordImportBindings(node, state) {
229
+ const isFrom = node.type === "import_from_statement";
230
+ const children = namedChildren(node);
231
+ const moduleName = isFrom ? children[0]?.text : undefined;
232
+ // A star import can introduce anything: give up on the file.
233
+ if (node.text.includes("*") || (isFrom && !moduleName))
234
+ markInvalid(state);
235
+ for (const part of isFrom ? children.slice(1) : children) {
236
+ const binding = parseImportBinding(part, isFrom);
237
+ if (!binding) {
238
+ markInvalid(state);
239
+ continue;
240
+ }
241
+ addBinding(state, binding.local, state.functionChain);
242
+ // Only imports at module top level prove provenance: a class-body or
243
+ // function-body import is not in scope for the sink's namespace.
244
+ if (!state.moduleDirect)
245
+ continue;
246
+ const provenance = isFrom
247
+ ? FROM_IMPORT_PROVENANCE.get(`${moduleName}:${binding.source}`)
248
+ : PLAIN_PACKAGE_PROVENANCE.get(binding.source);
249
+ if (provenance) {
250
+ state.imports.set(binding.local, {
251
+ name: binding.local,
252
+ provenance,
253
+ endIndex: node.endIndex,
254
+ });
255
+ }
256
+ }
257
+ }
258
+ function recordTargetBinding(node, state) {
259
+ addTarget(state, node.childForFieldName?.("left") ?? namedChildren(node)[0], state.functionChain);
260
+ }
261
+ /**
262
+ * `stmt = select(User)` — the assigned value, indexed by name. Only consulted
263
+ * when the name has exactly one binding in the file, so a rebound or shadowed
264
+ * name never proves anything.
265
+ */
266
+ function recordAssignmentValue(node, state) {
267
+ recordTargetBinding(node, state);
268
+ const target = node.childForFieldName?.("left") ?? namedChildren(node)[0];
269
+ const value = node.childForFieldName?.("right") ?? namedChildren(node).at(-1);
270
+ if (target?.type !== "identifier" || !value || value === target)
271
+ return;
272
+ state.assignments.set(target.text, value);
273
+ }
274
+ function recordAsPatternBinding(node, state) {
275
+ const extracted = collectBindingNames(node, "as");
276
+ if (extracted.unknown)
277
+ markInvalid(state);
278
+ for (const name of extracted.names)
279
+ addBinding(state, name, state.functionChain);
280
+ }
281
+ function recordDeleteTargets(node, state) {
282
+ for (const child of namedChildren(node)) {
283
+ addTarget(state, child, state.functionChain);
284
+ }
285
+ }
286
+ function recordCaseBindings(node, state) {
287
+ const extracted = collectBindingNames(directNamedChild(node, "case_pattern"), "pattern");
288
+ if (extracted.unknown)
289
+ markInvalid(state);
290
+ for (const name of extracted.names)
291
+ addBinding(state, name, state.functionChain);
292
+ }
293
+ /**
294
+ * `global sql` / `nonlocal sql` — the declaration alone is enough to give up on
295
+ * the name (covered by the "global" case of the fail-closed runner regression).
296
+ */
297
+ function recordDeclarationBindings(node, state) {
298
+ for (const child of namedChildren(node)) {
299
+ addBinding(state, child.text, state.functionChain);
300
+ }
301
+ }
302
+ function recordDefinitionBinding(node, state) {
303
+ const name = directNamedChild(node, "identifier")?.text;
304
+ if (name)
305
+ addBinding(state, name, state.parentFunctionChain);
306
+ else
307
+ markInvalid(state);
308
+ }
309
+ function recordTypeAliasBinding(node, state) {
310
+ const name = directNamedChild(node, "type")?.children?.find((child) => child.type === "identifier")?.text;
311
+ if (name)
312
+ addBinding(state, name, state.functionChain);
313
+ else
314
+ markInvalid(state);
315
+ }
316
+ const DYNAMIC_NAMESPACE_BUILTINS = new Set([
317
+ "exec",
318
+ "eval",
319
+ "globals",
320
+ "locals",
321
+ "vars",
322
+ ]);
323
+ /** `exec`/`eval`/`globals()` can rebind anything: give up on the whole file. */
324
+ function recordDynamicNamespaceHazard(node, state) {
325
+ const callee = calleeNode(node);
326
+ if (callee?.type === "identifier" &&
327
+ DYNAMIC_NAMESPACE_BUILTINS.has(callee.text)) {
328
+ markInvalid(state);
329
+ }
330
+ }
331
+ const SUMMARY_NODE_RECORDERS = Object.freeze({
332
+ function_definition: recordFunctionDefinition,
333
+ class_definition: recordDefinitionBinding,
334
+ lambda: recordLambdaParameters,
335
+ import_from_statement: recordImportBindings,
336
+ import_statement: recordImportBindings,
337
+ assignment: recordAssignmentValue,
338
+ augmented_assignment: recordTargetBinding,
339
+ named_expression: recordTargetBinding,
340
+ for_statement: recordTargetBinding,
341
+ for_in_clause: recordTargetBinding,
342
+ with_item: recordAsPatternBinding,
343
+ except_clause: recordAsPatternBinding,
344
+ except_group_clause: recordAsPatternBinding,
345
+ delete_statement: recordDeleteTargets,
346
+ case_clause: recordCaseBindings,
347
+ type_alias_statement: recordTypeAliasBinding,
348
+ global_statement: recordDeclarationBindings,
349
+ nonlocal_statement: recordDeclarationBindings,
350
+ call: recordDynamicNamespaceHazard,
351
+ });
352
+ class Summary {
353
+ invalid;
354
+ imports;
355
+ bindingCounts;
356
+ assignments;
357
+ functions;
358
+ constructor(root) {
359
+ const state = {
360
+ imports: new Map(),
361
+ bindingCounts: new Map(),
362
+ assignments: new Map(),
363
+ functionBindings: new Map(),
364
+ functionAnnotations: new Map(),
365
+ invalid: false,
366
+ visits: 0,
367
+ functionChain: [],
368
+ parentFunctionChain: [],
369
+ moduleDirect: false,
370
+ };
371
+ const visit = (node, depth, moduleDirect, activeFunctions) => {
372
+ if (++state.visits > TRAVERSAL_VISIT_CAP || depth > TRAVERSAL_DEPTH_CAP) {
373
+ markInvalid(state);
374
+ return;
375
+ }
376
+ if (node.hasError || node.type === "ERROR" || node.type === "MISSING") {
377
+ markInvalid(state);
378
+ return;
379
+ }
380
+ const functionChain = node.type === "function_definition"
381
+ ? [...activeFunctions, node]
382
+ : activeFunctions;
383
+ state.functionChain = functionChain;
384
+ state.parentFunctionChain = activeFunctions;
385
+ state.moduleDirect = moduleDirect;
386
+ const recorder = SUMMARY_NODE_RECORDERS[node.type];
387
+ if (recorder)
388
+ recorder(node, state);
389
+ for (const child of node.children ?? []) {
390
+ visit(child, depth + 1, node === root, functionChain);
391
+ }
392
+ };
393
+ visit(root, 0, false, []);
394
+ this.invalid = state.invalid;
395
+ this.imports = state.imports;
396
+ this.bindingCounts = state.bindingCounts;
397
+ this.assignments = state.assignments;
398
+ this.functions = new Map([...state.functionAnnotations.entries()].map(([key, annotations]) => [
399
+ key,
400
+ {
401
+ parameterAnnotations: annotations,
402
+ bindingCounts: state.functionBindings.get(key) ?? new Map(),
403
+ },
404
+ ]));
405
+ }
406
+ provenanceFor(name, reference) {
407
+ // Exactly one binding in the file, and it is the import itself: any
408
+ // shadow, rebind or `del` anywhere makes the name unusable as proof.
409
+ const candidate = this.boundOnce(name) ? this.imports.get(name) : undefined;
410
+ return candidate && candidate.endIndex <= reference.startIndex
411
+ ? candidate.provenance
412
+ : null;
413
+ }
414
+ boundOnce(name) {
415
+ return !this.invalid && (this.bindingCounts.get(name) ?? 0) === 1;
416
+ }
417
+ singleAssignmentValue(name, reference) {
418
+ if (!this.boundOnce(name))
419
+ return null;
420
+ const value = this.assignments.get(name);
421
+ return value && value.endIndex <= reference.startIndex ? value : null;
422
+ }
423
+ isFunctionLocalName(reference) {
424
+ if (this.invalid || reference.type !== "identifier")
425
+ return true;
426
+ let current = reference.parent;
427
+ for (let depth = 0; current && depth < ANCESTOR_DEPTH_CAP; depth++) {
428
+ if (current.type === "function_definition") {
429
+ const summary = this.functions.get(nodeKey(current));
430
+ if (!summary)
431
+ return true;
432
+ if ((summary.bindingCounts.get(reference.text) ?? 0) > 0)
433
+ return true;
434
+ }
435
+ current = current.parent;
436
+ }
437
+ return false;
438
+ }
439
+ isSqlAlchemySessionReceiver(receiver) {
440
+ if (this.invalid || receiver.type !== "identifier")
441
+ return false;
442
+ let current = receiver.parent;
443
+ for (let depth = 0; current && depth < ANCESTOR_DEPTH_CAP; depth++) {
444
+ if (current.type === "function_definition") {
445
+ const summary = this.functions.get(nodeKey(current));
446
+ if (!summary)
447
+ return false;
448
+ const annotation = summary.parameterAnnotations.get(receiver.text);
449
+ return (annotation !== undefined &&
450
+ (summary.bindingCounts.get(receiver.text) ?? 0) === 1 &&
451
+ this.provenanceFor(annotation, receiver) === "sqlalchemy-session");
452
+ }
453
+ current = current.parent;
454
+ }
455
+ return false;
456
+ }
457
+ }
458
+ function getPythonProvenanceSummary(root) {
459
+ const cached = SUMMARY_BY_ROOT.get(root);
460
+ if (cached)
461
+ return cached;
462
+ const summary = new Summary(root);
463
+ SUMMARY_BY_ROOT.set(root, summary);
464
+ return summary;
465
+ }
466
+ function expressionProvenance(node, summary, depth = 0) {
467
+ if (!node || depth > EXPRESSION_DEPTH_CAP)
468
+ return null;
469
+ if (node.type === "identifier")
470
+ return summary.provenanceFor(node.text, node);
471
+ if (node.type !== "attribute")
472
+ return null;
473
+ const object = node.childForFieldName?.("object") ?? namedChildren(node)[0];
474
+ const attribute = node.childForFieldName?.("attribute") ?? namedChildren(node).at(-1);
475
+ const provenance = expressionProvenance(object, summary, depth + 1);
476
+ if (provenance === "psycopg-package" && attribute?.text === "sql") {
477
+ return "psycopg-sql-module";
478
+ }
479
+ if (provenance === "psycopg-sql-module") {
480
+ if (attribute?.text === "SQL")
481
+ return "psycopg-sql-constructor";
482
+ if (attribute?.text === "Identifier")
483
+ return "psycopg-identifier-constructor";
484
+ }
485
+ return null;
486
+ }
487
+ /** Exact static SQL(...).format(Identifier(...), ...) proof. */
488
+ export function isSafePsycopgIdentifierComposition(node, root) {
489
+ if (node?.type !== "call" || !root)
490
+ return false;
491
+ const summary = getPythonProvenanceSummary(root);
492
+ if (summary.invalid)
493
+ return false;
494
+ const formatCallee = node.childForFieldName?.("function") ?? namedChildren(node)[0];
495
+ if (formatCallee?.type !== "attribute")
496
+ return false;
497
+ const formatChildren = namedChildren(formatCallee);
498
+ const hasFormatMethod = formatChildren.some((child) => child.text === "format");
499
+ const sqlConstructor = formatChildren.find((child) => child.type === "call");
500
+ if (!hasFormatMethod || !sqlConstructor)
501
+ return false;
502
+ const constructorCallee = sqlConstructor.childForFieldName?.("function") ??
503
+ namedChildren(sqlConstructor)[0];
504
+ if (expressionProvenance(constructorCallee, summary) !==
505
+ "psycopg-sql-constructor")
506
+ return false;
507
+ const templateArgs = namedChildren(directNamedChild(sqlConstructor, "argument_list") ?? sqlConstructor).filter((child) => child.type !== "comment");
508
+ if (templateArgs.length !== 1 ||
509
+ templateArgs[0]?.type !== "string" ||
510
+ namedChildren(templateArgs[0]).some((child) => child.type === "interpolation"))
511
+ return false;
512
+ const formatArgs = namedChildren(directNamedChild(node, "argument_list") ?? node).filter((child) => child.type !== "comment");
513
+ if (formatArgs.length === 0)
514
+ return false;
515
+ return formatArgs.every((argument) => {
516
+ if (argument.type !== "call")
517
+ return false;
518
+ const callee = argument.childForFieldName?.("function") ?? namedChildren(argument)[0];
519
+ return (expressionProvenance(callee, summary) === "psycopg-identifier-constructor");
520
+ });
521
+ }
522
+ export function isProvenSqlAlchemySessionReceiver(receiver, root) {
523
+ return (!!receiver &&
524
+ !!root &&
525
+ getPythonProvenanceSummary(root).isSqlAlchemySessionReceiver(receiver));
526
+ }
527
+ function isStaticStringLiteral(node) {
528
+ return (node?.type === "string" &&
529
+ !namedChildren(node).some((child) => child.type === "interpolation"));
530
+ }
531
+ function callArguments(call) {
532
+ return namedChildren(directNamedChild(call, "argument_list") ?? call).filter((child) => child.type !== "comment");
533
+ }
534
+ // Node types that ARE SQL text, or that compose it (`+`, `%`).
535
+ const COMPOSED_SQL_NODE_TYPES = new Set([
536
+ "string",
537
+ "concatenated_string",
538
+ "binary_operator",
539
+ ]);
540
+ /**
541
+ * True when an expression carries SQL text a caller composed — a literal, a
542
+ * concatenation, a `%` format, an f-string, `"...".format(...)`, or any call
543
+ * that wraps one. Fails closed at the depth cap: unknown means composed.
544
+ */
545
+ function carriesComposedSql(node, depth = 0) {
546
+ if (!node)
547
+ return false;
548
+ if (depth > EXPRESSION_DEPTH_CAP)
549
+ return true;
550
+ if (COMPOSED_SQL_NODE_TYPES.has(node.type))
551
+ return true;
552
+ if (node.type !== "call")
553
+ return false;
554
+ const callee = calleeNode(node);
555
+ if (callee?.type === "attribute") {
556
+ // `"SELECT {}".format(uid)` — the template hangs off the callee.
557
+ const object = callee.childForFieldName?.("object") ?? namedChildren(callee)[0];
558
+ if (carriesComposedSql(object, depth + 1))
559
+ return true;
560
+ }
561
+ return callArguments(node).some((argument) => carriesComposedSql(argument, depth + 1));
562
+ }
563
+ /**
564
+ * The builder name a call invokes: a bare `select(...)`, or `sa.select(...)`
565
+ * where `sa` is a PROVEN sqlalchemy import. Without that proof any object's
566
+ * `.update(...)` would read as a statement builder (#2577 review F2).
567
+ */
568
+ function builderCalleeName(call, summary) {
569
+ const callee = calleeNode(call);
570
+ if (callee?.type === "identifier")
571
+ return callee.text;
572
+ if (callee?.type !== "attribute")
573
+ return undefined;
574
+ const object = callee.childForFieldName?.("object") ?? namedChildren(callee)[0];
575
+ if (expressionProvenance(object, summary) !== "sqlalchemy-module") {
576
+ return undefined;
577
+ }
578
+ const attribute = callee.childForFieldName?.("attribute") ?? namedChildren(callee).at(-1);
579
+ return attribute?.type === "identifier" ? attribute.text : undefined;
580
+ }
581
+ /**
582
+ * `select(User)`, `sa.update(User)`, `text("SELECT 1")` — a statement object,
583
+ * not a SQL string. `text()` is the one builder that carries raw SQL, so only a
584
+ * literal template counts; and no builder may wrap composed SQL, or
585
+ * `update("UPDATE t SET x=" + uid)` would launder it (#2577 review F2).
586
+ */
587
+ function isStatementBuilderCall(node, summary) {
588
+ if (node?.type !== "call")
589
+ return false;
590
+ const name = builderCalleeName(node, summary);
591
+ if (!name)
592
+ return false;
593
+ const args = callArguments(node);
594
+ if (name === "text") {
595
+ return args.length === 1 && isStaticStringLiteral(args[0]);
596
+ }
597
+ if (!PYTHON_SQLALCHEMY_STATEMENT_BUILDERS.has(name))
598
+ return false;
599
+ return !args.some((argument) => carriesComposedSql(argument));
600
+ }
601
+ /**
602
+ * True when a statement-executing argument is a builder call, or a name bound
603
+ * exactly once in the file to one (`stmt = select(User); db.execute(stmt)`).
604
+ */
605
+ export function isSqlAlchemyStatementArgument(node, root) {
606
+ if (!node || !root)
607
+ return false;
608
+ const summary = getPythonProvenanceSummary(root);
609
+ if (isStatementBuilderCall(node, summary))
610
+ return true;
611
+ if (node.type !== "identifier")
612
+ return false;
613
+ const bound = summary.singleAssignmentValue(node.text, node);
614
+ return isStatementBuilderCall(bound ?? undefined, summary);
615
+ }
616
+ /**
617
+ * `Session.query` takes mapped classes, so it must NOT require a builder
618
+ * argument (`db.query(User)` is the case #2576 reports). It suppresses only
619
+ * what cannot be SQL text: composed strings and opaque function-local values
620
+ * stay diagnostic (#2577 review F1).
621
+ */
622
+ export function isSqlAlchemyEntityQueryArgument(node, root) {
623
+ if (!node || !root)
624
+ return false;
625
+ const summary = getPythonProvenanceSummary(root);
626
+ if (isStatementBuilderCall(node, summary))
627
+ return true;
628
+ if (carriesComposedSql(node))
629
+ return false;
630
+ if (node.type !== "identifier")
631
+ return true;
632
+ // A name the enclosing function binds is a runtime value that may hold SQL
633
+ // text (`q = build_sql(uid); db.query(q)`), so it stays diagnostic whatever
634
+ // it was assigned; a free or module-level name is the mapped class this API
635
+ // expects. A model built inside the function (`User = get_model()`) is
636
+ // statically indistinguishable from the first and fires: accepted noise,
637
+ // since following the assignment loses the true positive (#2577 round 3).
638
+ return !summary.isFunctionLocalName(node);
639
+ }
@@ -100,7 +100,12 @@ export async function runQuietWindow(deps) {
100
100
  const taskStart = Date.now();
101
101
  let ok = true;
102
102
  try {
103
- await task.fn();
103
+ await task.fn({
104
+ runtime: deps.runtime,
105
+ cwd: deps.cwd,
106
+ sessionId: deps.sessionId,
107
+ ownerId: deps.ownerId,
108
+ });
104
109
  }
105
110
  catch (err) {
106
111
  ok = false;