@herbertgao/pi-extensions 2026.9.0 → 2026.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) 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/@pi-plugins/fast-mode/README.md +5 -3
  11. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  12. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  13. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  14. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  15. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  16. package/node_modules/pi-lens/README.md +3 -0
  17. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  18. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  19. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  22. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  23. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  24. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  25. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  26. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  27. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  28. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  29. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  30. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  31. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  32. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  33. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  34. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  35. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  36. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  37. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  38. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  39. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  40. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  41. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  42. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  43. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  44. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  45. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  46. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  47. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  48. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  49. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  50. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  51. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  52. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  53. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  54. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  55. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  56. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  58. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  59. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  60. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  63. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  64. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  122. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  123. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  124. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  125. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  126. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  128. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  129. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  130. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  131. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  132. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  133. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  134. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  135. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  136. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  137. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  138. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  139. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  140. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  141. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  142. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  143. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  144. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  145. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  146. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  147. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  148. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  149. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  150. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  151. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  153. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  154. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  155. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  157. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  158. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  159. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  160. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  162. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  163. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  164. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  165. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  167. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  168. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  171. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  172. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  173. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  174. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  175. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  177. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  178. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  179. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  180. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  181. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  182. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  183. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  184. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  185. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  186. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  187. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  188. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  189. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  190. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  191. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  195. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  196. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  197. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  198. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  199. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  200. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  201. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  203. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  204. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  205. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  206. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  207. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  208. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  209. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  210. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  211. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  212. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  213. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  214. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  215. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  216. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  217. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  218. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  219. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  220. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  221. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  222. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  223. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  225. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  226. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  227. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  228. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  229. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  230. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  231. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  232. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  233. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  234. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  241. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  242. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  243. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  244. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  245. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  246. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  247. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  249. package/node_modules/pi-lens/dist/index.js +64335 -54300
  250. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  251. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  252. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  253. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  254. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  255. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  256. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  257. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  258. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  259. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  260. package/node_modules/pi-lens/docs/configuration.md +222 -0
  261. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  262. package/node_modules/pi-lens/docs/features.md +44 -5
  263. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  264. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  265. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  266. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  267. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  268. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  269. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  270. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  271. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  272. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  273. package/node_modules/pi-lens/package.json +15 -23
  274. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  275. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  276. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  277. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  278. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  279. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  280. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  281. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  282. package/node_modules/pi-web-access/README.md +62 -15
  283. package/node_modules/pi-web-access/curator-page.ts +3 -1
  284. package/node_modules/pi-web-access/curator-server.ts +5 -1
  285. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  286. package/node_modules/pi-web-access/github-extract.ts +242 -1
  287. package/node_modules/pi-web-access/index.ts +118 -64
  288. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  289. package/node_modules/pi-web-access/package.json +2 -2
  290. package/node_modules/pi-web-access/perplexity.ts +14 -1
  291. package/node_modules/pi-web-access/utils.ts +23 -6
  292. package/node_modules/pi-web-access/xai-search.ts +96 -33
  293. package/package.json +5 -5
@@ -0,0 +1,129 @@
1
+ // The ONE structural replica of pi's own skill-loading walk (#2626 review
2
+ // round 2, F1/F2).
3
+ //
4
+ // Two independent detectors used to answer "does this directory tree hold a
5
+ // skill pi would load" with two DIFFERENT, hand-rolled heuristics that both
6
+ // diverged from pi's real resolver:
7
+ // - `clients/skills-resolver.ts` (a `SKILL.md` one level down only, no
8
+ // recursion, no root `.md` acceptance, no symlink-follow, no dot/
9
+ // node_modules skip)
10
+ // - `scripts/install-selftest.mjs`'s `countSkillFiles` (recursed
11
+ // UNBOUNDED — would double-count a `SKILL.md` sitting inside another
12
+ // skill's own resource subdirectory — with no dot/node_modules skip, no
13
+ // root `.md` acceptance, no symlink handling)
14
+ // Neither matched `loadSkillsFromDirInternal` in
15
+ // `@earendil-works/pi-coding-agent` `dist/core/skills.js` (mirrored by
16
+ // `collectSkillEntries` in `dist/core/package-manager.js`, byte-identical
17
+ // walk, confirmed against the 0.85.1 tarball). This module is the fold: BOTH
18
+ // consumers now derive from `collectSkillEntryPaths` below.
19
+ //
20
+ // Discovery rules, replicated from `loadSkillsFromDirInternal` (verified
21
+ // against the extracted 0.85.1 `skills.js`):
22
+ // 1. If `dir` directly contains a (non-symlink-broken) `SKILL.md`, `dir` IS
23
+ // one skill root — return immediately, do NOT look at `dir`'s other
24
+ // entries or recurse into its subdirectories (a `SKILL.md` inside a
25
+ // skill's own reference/ subfolder is the skill's OWN resource, not a
26
+ // second skill).
27
+ // 2. Otherwise, skip dot-prefixed entries and `node_modules`; recurse into
28
+ // every remaining subdirectory (same two rules, one level down); at the
29
+ // OUTERMOST call only (`isRoot`), a loose `*.md` file directly in `dir`
30
+ // also counts (pi's `includeRootFiles`, true only for the top call).
31
+ // 3. A symlink is followed via `statSync` (broken symlink = skip) for BOTH
32
+ // the `SKILL.md` check and the directory/file classification in step 2.
33
+ //
34
+ // Deliberately NOT replicated (out of #2626's scope — packaging/path
35
+ // resolution, not skill-content validation):
36
+ // - `.gitignore`/`.ignore`/`.fdignore` matching inside the skills tree.
37
+ // - frontmatter parsing: pi additionally requires a non-empty `description`
38
+ // field before a discovered file becomes a loaded skill. A `SKILL.md`
39
+ // file that exists but fails THAT validation is a content-authoring
40
+ // defect a maintainer would see in normal use, not a silent zero.
41
+ // Both gaps mean this predicate can be a bit more OPTIMISTIC than pi's true
42
+ // count in a pathological case (an ignored or description-less file still
43
+ // counts as "found" here); it is never more PESSIMISTIC — every real skill
44
+ // pi loads is found by this walk too — which is the direction #2626 cares
45
+ // about (never claim "no skills" when pi would load one).
46
+ import * as fs from "node:fs";
47
+ import * as path from "node:path";
48
+ /**
49
+ * Symlink-aware entry classification, mirroring `loadSkillsFromDirInternal`'s
50
+ * own `entry.isSymbolicLink() ? statSync(fullPath) : entry`.
51
+ * @param {string} fullPath
52
+ * @param {import("node:fs").Dirent} entry
53
+ * @returns {"file" | "dir" | "other"}
54
+ */
55
+ export function resolvedEntryKind(fullPath, entry) {
56
+ if (entry.isSymbolicLink()) {
57
+ try {
58
+ const stats = fs.statSync(fullPath);
59
+ if (stats.isDirectory())
60
+ return "dir";
61
+ if (stats.isFile())
62
+ return "file";
63
+ return "other";
64
+ }
65
+ catch {
66
+ return "other"; // broken symlink
67
+ }
68
+ }
69
+ if (entry.isDirectory())
70
+ return "dir";
71
+ if (entry.isFile())
72
+ return "file";
73
+ return "other";
74
+ }
75
+ /**
76
+ * The structural walk over an ALREADY-READ directory listing — split out
77
+ * from `collectSkillEntryPaths` so a caller that already has `entries` (a
78
+ * top-level ENOENT/EACCES distinction, say) is not forced into a second
79
+ * `readdirSync`.
80
+ * @param {string} dir
81
+ * @param {import("node:fs").Dirent[]} entries
82
+ * @param {boolean} isRoot
83
+ * @returns {string[]} absolute paths of every `SKILL.md` (or, at the root,
84
+ * loose `.md`) file pi's own resolver would treat as a skill entry point.
85
+ */
86
+ export function scanEntriesForSkills(dir, entries, isRoot) {
87
+ for (const entry of entries) {
88
+ if (entry.name === "SKILL.md" &&
89
+ resolvedEntryKind(path.join(dir, entry.name), entry) === "file") {
90
+ return [path.join(dir, entry.name)];
91
+ }
92
+ }
93
+ const found = [];
94
+ for (const entry of entries) {
95
+ if (entry.name.startsWith(".") || entry.name === "node_modules") {
96
+ continue;
97
+ }
98
+ const fullPath = path.join(dir, entry.name);
99
+ const kind = resolvedEntryKind(fullPath, entry);
100
+ if (kind === "dir") {
101
+ found.push(...collectSkillEntryPaths(fullPath, false));
102
+ continue;
103
+ }
104
+ if (isRoot && kind === "file" && entry.name.endsWith(".md")) {
105
+ found.push(fullPath);
106
+ }
107
+ }
108
+ return found;
109
+ }
110
+ /**
111
+ * Every skill entry point under `dir`, recursively, per pi's discovery
112
+ * rules. Fail-closed: a `readdirSync` error at `dir` (absent, unreadable) or
113
+ * any recursed-into subdirectory returns `[]` for that branch rather than
114
+ * throwing — matching `loadSkillsFromDirInternal`'s own bare `try { … }
115
+ * catch {}` per directory.
116
+ * @param {string} dir
117
+ * @param {boolean} [isRoot]
118
+ * @returns {string[]}
119
+ */
120
+ export function collectSkillEntryPaths(dir, isRoot = true) {
121
+ let entries;
122
+ try {
123
+ entries = fs.readdirSync(dir, { withFileTypes: true });
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ return scanEntriesForSkills(dir, entries, isRoot);
129
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * effective_config pi tool (#2427) — the in-process half of
3
+ * `pilens_effective_config`.
4
+ *
5
+ * A thin wrapper over the `effectiveConfig()` engine seam
6
+ * (`clients/lens-engine.ts`), like `tools/symbol-search.ts` over
7
+ * `symbolSearch()`: the payload is byte-identical to the MCP mirror's because
8
+ * both render the SAME view object, and neither adapter shapes it. The whole
9
+ * reason the capability is an engine method rather than an MCP handler is that
10
+ * a second projection would be a second chance to disagree about which server
11
+ * is running.
12
+ */
13
+ import { Type } from "../clients/deps/typebox.js";
14
+ import { effectiveConfig, isEffectiveFileViewError, } from "../clients/lens-engine.js";
15
+ import { compactRenderResult } from "./render-compact.js";
16
+ /** One line per decision, for the compact renderer. */
17
+ function summarize(view) {
18
+ const documents = `${view.documents.length} config file(s)`;
19
+ if (!view.file)
20
+ return `effective_config — ${documents}`;
21
+ if (isEffectiveFileViewError(view.file)) {
22
+ return `effective_config — ${documents} · ${view.file.error}`;
23
+ }
24
+ const selected = view.file.servers.filter((entry) => entry.selected).length;
25
+ const denied = view.file.servers.filter((entry) => entry.reason === "disabled-by-config").length;
26
+ const tools = view.file.tools.filter((entry) => entry.selected).length;
27
+ // The denial clause is built first rather than nested inside the summary
28
+ // template: a conditional expression inside a template inside a template is
29
+ // the S3358/nested-template shape SonarCloud flags, and it hid the one
30
+ // number a reader is looking for (#2427 review round 2, F3).
31
+ const deniedClause = denied > 0 ? `, ${denied} denied by config` : "";
32
+ return (`effective_config ${view.file.path} — ${documents} · ` +
33
+ `${selected} server(s) selected${deniedClause} · ` +
34
+ `${tools} tool(s)`);
35
+ }
36
+ export function createEffectiveConfigTool(getProjectRoot) {
37
+ return {
38
+ name: "effective_config",
39
+ label: "Effective Config",
40
+ description: "The resolved pi-lens configuration with the provenance of every decision — which file and tier each setting came from, and the trust decision that applied. Pass `file` to also get, for that path, its language, every LSP server with the reason it was selected or denied (including which tier's config denied it, which a nearer file cannot lift), and the lint/format runners that would dispatch. Answers 'why is X running/selected' without reading logs. Redacted by construction: it reports sources, never values — no env values, no command arguments beyond the binary, and config paths are home-relative.",
41
+ promptSnippet: "Resolved config + provenance — why a server or tool is selected",
42
+ renderResult: compactRenderResult(({ details, isError }) => isError
43
+ ? "effective_config — failed"
44
+ : (details?.summary ?? "effective_config")),
45
+ parameters: Type.Object({
46
+ file: Type.Optional(Type.String({
47
+ description: "Path to explain: adds the resolved language plus the per-server and " +
48
+ "per-runner selection decisions for it. Must resolve inside `cwd` " +
49
+ "(a sibling package in the same monorepo does not count) — a file " +
50
+ "outside it is rejected with the `cwd` it was measured against; " +
51
+ "re-query with `cwd` set to that file's own workspace instead.",
52
+ })),
53
+ }),
54
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
55
+ const cwd = getProjectRoot() || ctx.cwd || ".";
56
+ const view = await effectiveConfig({
57
+ cwd,
58
+ ...(params.file === undefined ? {} : { file: params.file }),
59
+ redact: true,
60
+ });
61
+ const summary = summarize(view);
62
+ return {
63
+ content: [
64
+ {
65
+ type: "text",
66
+ text: `${summary}\n\n${JSON.stringify(view)}`,
67
+ },
68
+ ],
69
+ // Summary-shaped, like `symbol_search`'s: the full view is already
70
+ // in `content[0].text`, and `details` exists for the renderer.
71
+ // Duplicating a whole provenance dump into it would double the
72
+ // payload of every call for one line of output.
73
+ details: {
74
+ summary,
75
+ documents: view.documents.length,
76
+ provenance: view.provenance.length,
77
+ ...(view.file === undefined
78
+ ? {}
79
+ : isEffectiveFileViewError(view.file)
80
+ ? { fileError: view.file.error }
81
+ : {
82
+ file: view.file.path,
83
+ selectedServers: view.file.servers.filter((entry) => entry.selected).length,
84
+ }),
85
+ },
86
+ };
87
+ },
88
+ };
89
+ }
@@ -11,8 +11,10 @@
11
11
  import { promises as fs } from "node:fs";
12
12
  import * as fsSync from "node:fs";
13
13
  import * as path from "node:path";
14
+ import { BoundedLruCache } from "../clients/bounded-cache.js";
14
15
  import { Type } from "../clients/deps/typebox.js";
15
16
  import { anchorsForDiagnostic, applyDispositions, applyWeakDispositions, hasAnyDispositionMarks, hasStrictDispositionMarks, getDisposition, } from "../clients/diagnostic-dispositions.js";
17
+ import { DEPENDENCY_DRIFT_MAX_DELIVERIES } from "../clients/blocker-freshness.js";
16
18
  import { freshnessFromMtime } from "../clients/freshness.js";
17
19
  import { applyInlineSuppressions } from "../clients/dispatch/inline-suppressions.js";
18
20
  import { gateFindingsByPathFreshness } from "../clients/advisory-provenance.js";
@@ -34,7 +36,7 @@ import { ANALYZER_IDS, fetchFreshProjectDiagnostics, } from "../clients/project-
34
36
  import { loadBootstrapClients } from "../clients/bootstrap.js";
35
37
  import { generatedSkipNotice, scanTruncationNotice, } from "../clients/lens-engine.js";
36
38
  import { scanProjectDiagnostics } from "../clients/project-diagnostics/scanner.js";
37
- import { getFileDiagnosticSummaries, reconcileCorrelatedScanDiagnostics, reconcileScanDiagnostics, reconcileStaleWidgetDependencyBlockers, reconcileStaleWidgetFiles, widgetDiagnosticUri, } from "../clients/widget-state.js";
39
+ import { classifyDiagnosticTier, getFileDiagnosticSummaries, reconcileCorrelatedScanDiagnostics, reconcileScanDiagnostics, reconcileStaleWidgetDependencyBlockers, reconcileStaleWidgetFiles, widgetDiagnosticUri, } from "../clients/widget-state.js";
38
40
  import { logLatency } from "../clients/latency-logger.js";
39
41
  import { convertLspDiagnostics } from "../clients/dispatch/utils/lsp-diagnostics.js";
40
42
  import { retagAuxiliaryDiagnostics } from "../clients/dispatch/auxiliary-lsp.js";
@@ -203,9 +205,15 @@ captureLspStatusRepaint, getRuntime) {
203
205
  const b = details?.totalBlocking ?? 0;
204
206
  const e = details?.totalErrors ?? 0;
205
207
  const w = details?.totalWarnings ?? 0;
208
+ // #2414: hint/info tier never contributes to `b + e + w` — a session
209
+ // with only style opinions is genuinely "clean" of code defects, but
210
+ // the one-line summary still names the advisory count so it isn't
211
+ // indistinguishable from a session with zero findings at all.
212
+ const adv = details?.totalAdvisories ?? 0;
206
213
  const files = details?.filesWithIssues ?? details?.filesChecked ?? 0;
207
214
  if (b + e + w === 0) {
208
- return `lens_diagnostics ${mode} clean (${files} files)${coldSuffix}${failedSuffix}`;
215
+ const advisorySuffix = adv > 0 ? `, ${adv} hint${adv === 1 ? "" : "s"}` : "";
216
+ return `lens_diagnostics ${mode} — clean${advisorySuffix} (${files} files)${coldSuffix}${failedSuffix}`;
209
217
  }
210
218
  const parts = [`${b} blocking`];
211
219
  if (b === 0 && e > 0)
@@ -417,15 +425,12 @@ function appendProjectDiagnosticsDeltaLines(lines, cwd, report, severity, includ
417
425
  * TTL. Insertion-ordered LRU cap bounds resident source bytes; repeated
418
426
  * queries and multi-file reports that cite the same file reuse one read.
419
427
  */
420
- const CACHED_CONTENT_MEMO_CAP = 64;
421
- const cachedContentMemo = new Map();
428
+ export const CACHED_CONTENT_MEMO_CAP = 64;
429
+ const cachedContentMemo = new BoundedLruCache(CACHED_CONTENT_MEMO_CAP);
422
430
  function readContentForAnchors(filePath, stat) {
423
431
  const key = normalizeEphemeralMapKey(filePath);
424
432
  const hit = cachedContentMemo.get(key);
425
433
  if (hit && hit.mtimeMs === stat.mtimeMs && hit.size === stat.size) {
426
- // Refresh LRU position.
427
- cachedContentMemo.delete(key);
428
- cachedContentMemo.set(key, hit);
429
434
  return hit.content;
430
435
  }
431
436
  let content;
@@ -435,11 +440,6 @@ function readContentForAnchors(filePath, stat) {
435
440
  catch {
436
441
  return undefined;
437
442
  }
438
- if (cachedContentMemo.size >= CACHED_CONTENT_MEMO_CAP) {
439
- const oldest = cachedContentMemo.keys().next().value;
440
- if (oldest !== undefined)
441
- cachedContentMemo.delete(oldest);
442
- }
443
443
  cachedContentMemo.set(key, {
444
444
  mtimeMs: stat.mtimeMs,
445
445
  size: stat.size,
@@ -447,6 +447,11 @@ function readContentForAnchors(filePath, stat) {
447
447
  });
448
448
  return content;
449
449
  }
450
+ /** #2442 test-only: exercise the bounded content memo (LRU) directly,
451
+ * without a full lens_diagnostics dispatch. */
452
+ export function _readContentForAnchorsForTests(filePath, stat) {
453
+ return readContentForAnchors(filePath, stat);
454
+ }
450
455
  /**
451
456
  * ONE disposition seam for every cache-only lens_diagnostics surface —
452
457
  * mode=all summaries, mode=delta's actionable/quality warnings, the delta
@@ -548,7 +553,35 @@ function loadProjectRulePolicyMap(cwd) {
548
553
  * `clients/finding-delivery-gate.ts` surface `lens-diagnostics:mode-delta`.
549
554
  */
550
555
  function applyDeltaFreshnessGate(files, cwd, generatedAt) {
551
- if (!generatedAt)
556
+ // #2504 review round 4 (F1): an actionable-warnings report is no longer the
557
+ // product of exactly ONE pass. A deferred off-hook LSP pull upserts its
558
+ // per-file entries into whatever report is persisted when it lands, so one
559
+ // report can carry entries observed minutes apart. Judging the older half
560
+ // against the newer report-level stamp would pass an out-of-band edit made
561
+ // in between off as live -- exactly the leak this gate exists to stop. Each
562
+ // distinct stamp is gated on its own; the ordinary single-stamp report
563
+ // takes one pass, as before, and the original file order is preserved.
564
+ const stamps = new Set(files.map((file) => file.generatedAt ?? generatedAt ?? ""));
565
+ if (stamps.size > 1) {
566
+ const gatedByPath = new Map();
567
+ for (const stamp of stamps) {
568
+ const group = files.filter((file) => (file.generatedAt ?? generatedAt ?? "") === stamp);
569
+ for (const gated of applyDeltaFreshnessGate(group.map((file) => ({
570
+ filePath: file.filePath,
571
+ warnings: file.warnings,
572
+ })), cwd, stamp === "" ? undefined : stamp)) {
573
+ gatedByPath.set(gated.filePath, gated);
574
+ }
575
+ }
576
+ return files
577
+ .map((file) => gatedByPath.get(file.filePath))
578
+ .filter((entry) => entry !== undefined);
579
+ }
580
+ // Every entry shares one stamp here; prefer the per-entry one when it has
581
+ // it, so a merged report's single surviving half is still aged by its own.
582
+ const stampedAt = [...stamps][0];
583
+ const effectiveAt = stampedAt !== undefined && stampedAt !== "" ? stampedAt : generatedAt;
584
+ if (!effectiveAt)
552
585
  return files;
553
586
  const flat = [];
554
587
  for (const file of files) {
@@ -561,7 +594,7 @@ function applyDeltaFreshnessGate(files, cwd, generatedAt) {
561
594
  store: "lens-diagnostics-delta",
562
595
  findings: flat,
563
596
  cwd,
564
- scannedAt: generatedAt,
597
+ scannedAt: effectiveAt,
565
598
  citedPath: (f) => f.filePath,
566
599
  onMissing: "drop",
567
600
  });
@@ -611,6 +644,9 @@ function formatDeltaMode(cacheManager, cwd, severity, pathsScope) {
611
644
  .filter((file) => includeFile(file.filePath))
612
645
  .map((file) => ({
613
646
  filePath: file.filePath,
647
+ // #2504 r4 (F1): carried through, so a merged report's older
648
+ // entries keep their own age all the way to the freshness gate.
649
+ generatedAt: file.generatedAt,
614
650
  warnings: applyRulePolicy(applyCachedDispositions(file.warnings ?? [], cwd, file.filePath), policyMap),
615
651
  }))
616
652
  .filter((file) => file.warnings.length > 0);
@@ -886,6 +922,7 @@ function summarizeDiagnostics(filePath, diagnostics, hasFinalSnapshot) {
886
922
  let blocking = 0;
887
923
  let errors = 0;
888
924
  let warnings = 0;
925
+ let advisories = 0;
889
926
  for (const diagnostic of diagnostics) {
890
927
  // #1641: a demoted past-EOF entry keeps its severity for display but
891
928
  // drops out of the tallies — same reasoning as widget-state's `isBlocking`.
@@ -893,24 +930,28 @@ function summarizeDiagnostics(filePath, diagnostics, hasFinalSnapshot) {
893
930
  continue;
894
931
  if (diagnostic.semantic === "blocking")
895
932
  blocking++;
896
- if (diagnostic.severity === "error")
933
+ // #2414: severity tier classification is shared with
934
+ // `countDiagnostics`/`countAdvisories` in `clients/widget-state.ts` via
935
+ // `classifyDiagnosticTier` — mode=all reads that store's tally and
936
+ // mode=full reads this one, so the two must agree. `hint`/`info` no
937
+ // longer inflate `warnings` (they used to, matching a pre-#2414
938
+ // `countDiagnostics`, so a hint-only file scored 0/0/0 there too and the
939
+ // `withIssues` filter below dropped it — that gap is now closed via
940
+ // `advisories`, not by re-folding hints into `warnings`).
941
+ const tier = classifyDiagnosticTier(diagnostic);
942
+ if (tier === "error")
897
943
  errors++;
898
- // #1777: `hint` and `info` tally alongside `warning`, matching
899
- // `countDiagnostics` in `clients/widget-state.ts` — mode=all reads that
900
- // tally and mode=full reads this one, so the two must agree. An exact
901
- // `=== "warning"` test scored a hint-only file 0/0/0, the `withIssues`
902
- // filter below then dropped it, and mode=full rendered "No issues" for a
903
- // file whose own `details` still carried the diagnostic.
904
- else if (diagnostic.severity === "warning" ||
905
- diagnostic.severity === "hint" ||
906
- diagnostic.severity === "info")
944
+ else if (tier === "warning")
907
945
  warnings++;
946
+ else if (tier === "advisory")
947
+ advisories++;
908
948
  }
909
949
  return {
910
950
  filePath,
911
951
  blocking,
912
952
  errors,
913
953
  warnings,
954
+ advisories,
914
955
  hasFinalSnapshot,
915
956
  diagnostics,
916
957
  };
@@ -1839,6 +1880,13 @@ async function formatFullMode(cwd, severity, lspService, cacheManager, options =
1839
1880
  }
1840
1881
  // @delivery-surface: lens-diagnostics:mode-all
1841
1882
  function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0) {
1883
+ // #2275 review F2: the widget footer stops DRAWING a dependency-drift
1884
+ // demotion once it hits `DEPENDENCY_DRIFT_MAX_DELIVERIES` unconfirmed
1885
+ // deliveries, but the record stays here (dropping it would make an
1886
+ // unconfirmed LSP error read as clean). Say so, through the same note
1887
+ // channel as the demotion itself, so the agent knows why the footer went
1888
+ // quiet and that a re-run can still confirm the finding.
1889
+ const footerCapped = summaries.reduce((n, s) => n + (s.diagnostics ?? []).filter((d) => d.footerRetired === true).length, 0);
1842
1890
  // Files changed/deleted since their diagnostics were recorded have already
1843
1891
  // been dropped by reconcileStaleWidgetFiles; note them so the agent knows
1844
1892
  // those aren't "clean", just un-rescanned (use mode=full to refresh).
@@ -1847,6 +1895,9 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
1847
1895
  : "") +
1848
1896
  (dependencyDemoted > 0
1849
1897
  ? ` (${dependencyDemoted} blocking finding${dependencyDemoted === 1 ? "" : "s"} demoted to [stale] — an imported file changed since; re-run to confirm)`
1898
+ : "") +
1899
+ (footerCapped > 0
1900
+ ? ` (${footerCapped} demoted finding${footerCapped === 1 ? "" : "s"} capped after ${DEPENDENCY_DRIFT_MAX_DELIVERIES} unconfirmed deliveries — no longer shown in the pi-lens footer, still listed here; re-run to confirm)`
1850
1901
  : "");
1851
1902
  // mode=full already actively scanned exactly the requested paths, so a zero
1852
1903
  // result there IS a legitimate clean read — the cache-only note only applies
@@ -1901,11 +1952,18 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
1901
1952
  const withIssues = visibleSummaries.filter((s) => {
1902
1953
  if (severity === "error")
1903
1954
  return s.blocking > 0 || s.errors > 0;
1955
+ // #2414: a "warning" filter must not admit hint/info — that is exactly
1956
+ // the "present hints as warnings" defect this issue exists to close.
1904
1957
  if (severity === "warning")
1905
1958
  return s.warnings > 0;
1959
+ // severity: "all" — a hint/info-only file (`advisories > 0`,
1960
+ // warnings === 0) must still surface here, or the #2414 fix that stops
1961
+ // hints inflating `warnings` would silently drop that file from the
1962
+ // detailed listing instead of just from the compact warning tally.
1906
1963
  return (s.blocking > 0 ||
1907
1964
  s.errors > 0 ||
1908
1965
  s.warnings > 0 ||
1966
+ s.advisories > 0 ||
1909
1967
  (s.diagnostics ?? []).some((d) => d.stale));
1910
1968
  });
1911
1969
  const pathsNote = isFullMode ? "" : pathsScopeCacheOnlyNote(pathsScope);
@@ -1930,6 +1988,7 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
1930
1988
  let totalBlocking = 0;
1931
1989
  let totalErrors = 0;
1932
1990
  let totalWarnings = 0;
1991
+ let totalAdvisories = 0;
1933
1992
  for (const s of sorted) {
1934
1993
  const rel = path.relative(cwd, s.filePath);
1935
1994
  const parts = [];
@@ -1939,6 +1998,11 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
1939
1998
  parts.push(`${s.errors}E`);
1940
1999
  if (s.warnings > 0)
1941
2000
  parts.push(`${s.warnings}W`);
2001
+ // #2414: hint/info never inflate `warnings`, but a hint-only row still
2002
+ // needs a reason to be listed — otherwise it shows a bare filename with
2003
+ // no count at all.
2004
+ if (s.advisories > 0)
2005
+ parts.push(`${s.advisories} hint${s.advisories === 1 ? "" : "s"}`);
1942
2006
  if (!s.hasFinalSnapshot)
1943
2007
  parts.push(`(pending)`);
1944
2008
  const staleCount = (s.diagnostics ?? []).filter((d) => d.stale).length;
@@ -1984,6 +2048,7 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
1984
2048
  totalBlocking += s.blocking;
1985
2049
  totalErrors += s.errors;
1986
2050
  totalWarnings += s.warnings;
2051
+ totalAdvisories += s.advisories;
1987
2052
  }
1988
2053
  // #1799: `totalBlocking` and `totalErrors` count the same findings UNLESS a
1989
2054
  // dependency-drift demotion (#1631) has revoked blocking authority from an
@@ -2004,6 +2069,11 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
2004
2069
  totalWarnings > 0
2005
2070
  ? ` ${totalWarnings} warning${totalWarnings === 1 ? "" : "s"}`
2006
2071
  : null,
2072
+ // #2414: reported separately from `warnings` — hint/info are style
2073
+ // opinions, not defects, and must not read as unresolved code issues.
2074
+ totalAdvisories > 0
2075
+ ? ` ${totalAdvisories} hint/info (style — not counted as warnings)`
2076
+ : null,
2007
2077
  ]
2008
2078
  .filter(Boolean)
2009
2079
  .join("\n");
@@ -2017,6 +2087,7 @@ function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(),
2017
2087
  totalBlocking,
2018
2088
  totalErrors,
2019
2089
  totalWarnings,
2090
+ totalAdvisories,
2020
2091
  staleDropped,
2021
2092
  },
2022
2093
  };