@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
@@ -23,39 +23,7 @@ import { isBlocking, reconcileScanDiagnostics, } from "../clients/widget-state.j
23
23
  import { baseName, compactRenderResult } from "./render-compact.js";
24
24
  import { makeProgressReporter, scanningSummaryLine } from "./scan-progress.js";
25
25
  import { isWarmAttached, tryWarmAttachedDiagnostics, } from "../clients/warm-attach.js";
26
- const LANG_EXTENSIONS = {
27
- ".ts": [".ts", ".tsx", ".mts", ".cts"],
28
- ".tsx": [".ts", ".tsx", ".mts", ".cts"],
29
- ".js": [".js", ".jsx", ".mjs", ".cjs"],
30
- ".py": [".py", ".pyi"],
31
- ".rs": [".rs"],
32
- ".go": [".go"],
33
- ".rb": [".rb", ".rake", ".gemspec"],
34
- ".java": [".java"],
35
- ".kt": [".kt", ".kts"],
36
- ".swift": [".swift"],
37
- ".cs": [".cs"],
38
- ".cpp": [".cpp", ".cc", ".cxx", ".hpp", ".hxx"],
39
- ".c": [".c", ".h"],
40
- ".zig": [".zig", ".zon"],
41
- ".hs": [".hs", ".lhs"],
42
- ".ex": [".ex", ".exs"],
43
- ".gleam": [".gleam"],
44
- ".tf": [".tf", ".tfvars"],
45
- ".nix": [".nix"],
46
- ".sh": [".sh", ".bash", ".zsh"],
47
- ".php": [".php"],
48
- ".lua": [".lua"],
49
- ".dart": [".dart"],
50
- ".vue": [".vue"],
51
- ".svelte": [".svelte"],
52
- ".css": [".css", ".scss", ".less"],
53
- ".html": [".html", ".htm"],
54
- ".json": [".json", ".jsonc"],
55
- ".yaml": [".yaml", ".yml"],
56
- ".toml": [".toml"],
57
- ".prisma": [".prisma"],
58
- };
26
+ import { extensionsForLanguage, SCAN_LANGUAGE_PRIORITY, } from "../clients/language-registry.js";
59
27
  const MAX_FILES = 100;
60
28
  const MAX_BATCH_FILES = 100;
61
29
  const MAX_DIAGNOSTICS = 200;
@@ -192,9 +160,10 @@ function projectIgnorePredicate(root) {
192
160
  * This walk was fully synchronous with NO yielding of any kind, bounded only by
193
161
  * `maxFiles` *kept* — an ignored-heavy or cloud-backed (OneDrive/network) tree
194
162
  * could traverse unboundedly many entries, and a single stalled `readdirSync`
195
- * held the loop for the whole stall. It is also called once PER LANGUAGE in
196
- * `runDirectoryDiagnostics`'s `LANG_EXTENSIONS` loop, so a directory-mode
197
- * `lsp_diagnostics` could pay that cost several times over.
163
+ * held the loop for the whole stall. It is also called once PER FAMILY in
164
+ * `runDirectoryDiagnostics`'s `SCAN_LANGUAGE_PRIORITY` loop (#2434, grouped by
165
+ * family since #2458 fix-round F1), so a directory-mode `lsp_diagnostics`
166
+ * could pay that cost several times over.
198
167
  *
199
168
  * The traversal is deliberately still **depth-first with immediate descent**
200
169
  * (not the shared stack-based `walkTreeStackAsync`): the `maxFiles` cap makes
@@ -276,6 +245,10 @@ onConfirmedNoBlockers) {
276
245
  // compact-render as a bare "N diagnostics" — that erases the fact some
277
246
  // files' clean status was never actually confirmed by the server.
278
247
  const unconfirmedFiles = details?.unconfirmedFiles ?? 0;
248
+ const navigationOnlyFiles = details?.navigationOnlyFiles ?? 0;
249
+ if (navigationOnlyFiles > 0) {
250
+ return `lsp_diagnostics${scope} — ${count} ${noun} · ${details?.cleanFiles ?? 0} clean · ${navigationOnlyFiles} navigation-only`;
251
+ }
279
252
  if (unconfirmedFiles > 0) {
280
253
  const cleanFiles = details?.cleanFiles ?? 0;
281
254
  const timedOutFiles = details?.timedOutFiles ?? 0;
@@ -456,13 +429,17 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
456
429
  // `touchFile` is the authoritative collection boundary for every scope: it
457
430
  // preserves per-touch timeout, content-binding, and silent-clean confirmation
458
431
  // metadata while returning diagnostics from only the requested clients. The
459
- // legacy openFile/getDiagnostics path remains only for an older/mock service
460
- // without touchFile, or when touchFile cannot resolve any clients.
432
+ // getDiagnostics fallback below remains for the two states a REAL service
433
+ // still reaches: the file read threw (no content to touch with), or the
434
+ // touch resolved no clients and returned `undefined` (clients/lsp/index.ts
435
+ // `touchFile`'s `no_clients`/`destroyed` returns). #2598 deleted the third
436
+ // reason — "an older/mock service without touchFile" — because the real
437
+ // `LSPService` has always defined the method unconditionally, so that arm
438
+ // was reachable only from a partial test double (AGENTS.md shape 7).
461
439
  // #1179: the result is an explicit wrapper so side-channel fields survive
462
440
  // array copies; confirmation was added when Marksman's lower-level clean verdict
463
441
  // proved that a successful empty collection also needs explicit provenance.
464
442
  let touched;
465
- let usedTouch = false;
466
443
  try {
467
444
  content = fs.readFileSync(absPath, "utf-8");
468
445
  if (isWarmAttached()) {
@@ -497,39 +474,30 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
497
474
  // field across the socket. An older incumbent omits it → empty → the
498
475
  // pre-#1470 handling, unchanged.
499
476
  unconfirmedServerIds: attached.response.unconfirmedServerIds ?? [],
477
+ diagnosticsUnsupportedServerIds: [],
500
478
  content,
501
479
  };
502
480
  }
503
481
  }
504
- const serviceWithTouch = lspService;
505
- if (typeof serviceWithTouch.touchFile === "function") {
506
- usedTouch = true;
507
- touched = await serviceWithTouch.touchFile(absPath, content, {
508
- diagnostics: "document",
509
- collectDiagnostics: true,
510
- maxClientWaitMs: waitMs,
511
- source: "lsp_diagnostics",
512
- clientScope: serverScope,
513
- });
514
- timedOut = touched?.inconclusive === true;
515
- }
516
- else {
517
- await lspService.openFile(absPath, content, {
518
- preserveDiagnostics: false,
519
- });
520
- }
482
+ touched = await lspService.touchFile(absPath, content, {
483
+ diagnostics: "document",
484
+ collectDiagnostics: true,
485
+ maxClientWaitMs: waitMs,
486
+ source: "lsp_diagnostics",
487
+ clientScope: serverScope,
488
+ });
489
+ timedOut = touched?.inconclusive === true;
521
490
  }
522
491
  catch {
523
492
  // Non-fatal: getDiagnostics may still have stale/health information.
524
493
  }
525
494
  // Only fall through to the unscoped getDiagnostics() read when the touch
526
- // branch wasn't taken (openFile-only path, which never collected anything
527
- // and genuinely needs the follow-up call) or couldn't resolve any clients
528
- // at all (touched stays undefined despite usedTouch). When touched IS
529
- // defined it's already the answer — reusing it is what makes
530
- // serverScope:"primary" actually skip auxiliary scanners and drops the
531
- // common case back to a single LSP round trip instead of two.
532
- const diagnostics = usedTouch && touched !== undefined
495
+ // produced nothing to read — it threw, the file read threw before it ran, or
496
+ // it resolved no clients at all. When touched IS defined it's already the
497
+ // answer — reusing it is what makes serverScope:"primary" actually skip
498
+ // auxiliary scanners and drops the common case back to a single LSP round
499
+ // trip instead of two.
500
+ const diagnostics = touched !== undefined
533
501
  ? touched.diags
534
502
  : await lspService.getDiagnostics(absPath, waitMs !== undefined ? "document" : "full");
535
503
  // #586: honor each auxiliary profile's native inline-suppression comment
@@ -546,25 +514,26 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
546
514
  fileRole: detectFileRole(absPath, content),
547
515
  })
548
516
  : diagnostics;
549
- // #1095: surface the touch's content binding (only the touch path carries
550
- // one; the openFile-only / getDiagnostics fallback leaves it undefined →
517
+ // #1095: surface the touch's content binding (only a touch that resolved
518
+ // clients carries one; the getDiagnostics fallback leaves it undefined →
551
519
  // "unknown", no demotion).
552
- const binding = usedTouch ? touched?.binding : undefined;
520
+ const binding = touched?.binding;
553
521
  // #1470: `"partial"` counts here. `confirmedByTouch` feeds
554
522
  // `canTrustTouchConfirmation`, which asks about the PRIMARY's own verdict —
555
523
  // and a partial touch is one whose primary confirmed while an auxiliary was
556
524
  // cut off. Excluding it would render "Primary LSP: unconfirmed" for a primary
557
525
  // that did confirm. The coverage gap is carried separately, below.
558
- const confirmedByTouch = usedTouch && touchCompletedConfirmationPolicy(touched);
526
+ const confirmedByTouch = touchCompletedConfirmationPolicy(touched);
559
527
  return {
560
528
  diagnostics: filtered,
561
529
  timedOut,
562
530
  skipReason: touched?.skipReason,
563
531
  confirmedByTouch,
564
- // #1470: only a touch actually contributes a coverage gap; the
565
- // openFile+getDiagnostics fallback never reports one, which is honest —
566
- // that path claims no confirmation at all.
567
- unconfirmedServerIds: usedTouch ? touchCoverageGap(touched) : [],
532
+ // #1470: only a touch that resolved clients contributes a coverage gap;
533
+ // the getDiagnostics fallback never reports one, which is honest — that
534
+ // path claims no confirmation at all.
535
+ unconfirmedServerIds: touchCoverageGap(touched),
536
+ diagnosticsUnsupportedServerIds: touched?.diagnosticsUnsupportedServerIds ?? [],
568
537
  content,
569
538
  binding,
570
539
  };
@@ -814,7 +783,7 @@ onConfirmedNoBlockers) {
814
783
  };
815
784
  }
816
785
  }
817
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
786
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
818
787
  const health = lspService.getDiagnosticsHealth?.(file);
819
788
  // #570: a timed-out priming check is never a confirmed "clean" — treat it
820
789
  // as unconfirmed without consulting the (unrelated) silent-tier
@@ -825,7 +794,12 @@ onConfirmedNoBlockers) {
825
794
  // be merged in rather than discarded.
826
795
  let effectiveRawDiags = rawDiags;
827
796
  let confirmation;
828
- if (skipReason !== undefined) {
797
+ if (diagnosticsUnsupportedServerIds.length > 0) {
798
+ // No diagnostic provider and no push evidence is a capability boundary,
799
+ // not a clean result and not a timeout.
800
+ confirmation = undefined;
801
+ }
802
+ else if (skipReason !== undefined) {
829
803
  confirmation = "unconfirmed";
830
804
  }
831
805
  else if (timedOut) {
@@ -888,6 +862,7 @@ onConfirmedNoBlockers) {
888
862
  if (cacheCtx &&
889
863
  scopeKey !== undefined &&
890
864
  confirmation !== "unconfirmed" &&
865
+ diagnosticsUnsupportedServerIds.length === 0 &&
891
866
  unconfirmedServerIds.length === 0) {
892
867
  cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
893
868
  ? hashDiagnosticContent(collectedContent)
@@ -901,6 +876,7 @@ onConfirmedNoBlockers) {
901
876
  timedOut: confirmation === "unconfirmed" ? timedOut : undefined,
902
877
  ...(skipReason !== undefined && { skipReason }),
903
878
  primaryServerId: primaryServerId(file),
879
+ diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
904
880
  };
905
881
  }
906
882
  async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(),
@@ -910,7 +886,7 @@ onConfirmedNoBlockers) {
910
886
  // Reserve the token before awaiting this file's LSP result. The direct-file
911
887
  // path performs its own confirmation/reconciliation below (#1198).
912
888
  const writeIndex = nextWriteIndex?.();
913
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
889
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
914
890
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
915
891
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
916
892
  // #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
@@ -924,7 +900,10 @@ onConfirmedNoBlockers) {
924
900
  // diagnostics it surfaces are merged in, not discarded.
925
901
  let effectiveRawDiags = rawDiags;
926
902
  let confirmation;
927
- if (skipReason !== undefined) {
903
+ if (diagnosticsUnsupportedServerIds.length > 0) {
904
+ confirmation = undefined;
905
+ }
906
+ else if (skipReason !== undefined) {
928
907
  confirmation = "unconfirmed";
929
908
  }
930
909
  else if (timedOut) {
@@ -992,6 +971,9 @@ onConfirmedNoBlockers) {
992
971
  // auxiliary findings exist — a wall of ast-grep/opengrep noise must never
993
972
  // bury whether the actual language server confirmed the file clean.
994
973
  const primaryLine = (() => {
974
+ if (diagnosticsUnsupportedServerIds.length > 0) {
975
+ return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only — diagnostics unsupported (no pull provider or publish evidence).`;
976
+ }
995
977
  if (timedOut) {
996
978
  return ("Primary LSP: check timed out — NOT the same as 0 diagnostics; the " +
997
979
  "file may still have errors that just hadn't been reported yet. " +
@@ -1068,6 +1050,7 @@ onConfirmedNoBlockers) {
1068
1050
  truncated,
1069
1051
  unconfirmed,
1070
1052
  timedOut: unconfirmed ? timedOut : undefined,
1053
+ navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : undefined,
1071
1054
  ...(skipReason !== undefined && { skipReason }),
1072
1055
  // #1470: which servers this result does NOT speak for. Absent when it
1073
1056
  // speaks for all of them.
@@ -1091,7 +1074,12 @@ function tallyConfirmation(results) {
1091
1074
  let clean = 0;
1092
1075
  let unconfirmed = 0;
1093
1076
  let timedOut = 0;
1077
+ let navigationOnly = 0;
1094
1078
  for (const result of results) {
1079
+ if (result.diagnosticsUnsupported) {
1080
+ navigationOnly += 1;
1081
+ continue;
1082
+ }
1095
1083
  if (result.diagnostics.length > 0)
1096
1084
  continue;
1097
1085
  if (result.confirmation === "unconfirmed") {
@@ -1105,11 +1093,13 @@ function tallyConfirmation(results) {
1105
1093
  clean += 1;
1106
1094
  }
1107
1095
  }
1108
- return { clean, unconfirmed, timedOut };
1096
+ return { clean, unconfirmed, timedOut, navigationOnly };
1109
1097
  }
1110
1098
  function classifyBatchFileOutcome(result) {
1111
1099
  if (result.error)
1112
1100
  return "failed";
1101
+ if (result.diagnosticsUnsupported)
1102
+ return "unsupported";
1113
1103
  // A timed-out/unconfirmed answer may contain partial findings, but it cannot
1114
1104
  // honestly be called a complete findings result. Keep the raw findings for
1115
1105
  // investigation while making the aggregate incomplete.
@@ -1265,6 +1255,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1265
1255
  };
1266
1256
  }
1267
1257
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, outcomeCounts, incompleteFiles, } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
1258
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1268
1259
  const lines = [
1269
1260
  `Files checked: ${results.length}`,
1270
1261
  `Total diagnostics: ${total}`,
@@ -1335,6 +1326,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1335
1326
  truncated,
1336
1327
  cleanFiles: clean,
1337
1328
  unconfirmedFiles: unconfirmed,
1329
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1338
1330
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1339
1331
  outcomes: results.map((result) => ({
1340
1332
  file: result.file,
@@ -1350,18 +1342,42 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1350
1342
  },
1351
1343
  };
1352
1344
  }
1345
+ /**
1346
+ * The scan-language DECISION `runDirectoryDiagnostics` makes, pulled out as
1347
+ * its own function so `tests/tools/lsp-diagnostics-scan-family.test.ts` can
1348
+ * pin it against the golden `LANG_EXTENSIONS` table for every 1- and
1349
+ * 2-extension combination in that table's universe without spinning up one
1350
+ * real directory (and the full LSP-mocked tool) per combination — `hasMatch`
1351
+ * is the only I/O-shaped seam, so the caller decides whether it is backed by
1352
+ * a real `collectFiles` walk (production) or an in-memory extension-set probe
1353
+ * (tests).
1354
+ *
1355
+ * Walks `SCAN_LANGUAGE_PRIORITY`'s FAMILIES in order and unions each member
1356
+ * id's {@link extensionsForLanguage} into ONE `hasMatch` call — the first
1357
+ * family with any match wins the whole directory for this pass. Grouping by
1358
+ * family (not trying each id alone) is what makes a directory mixing BOTH
1359
+ * extensions of a registry-split pair (`.ts`+`.tsx`, `.css`+`.scss`, ...)
1360
+ * behavior-preserving vs the pre-#2434 bundled-key table: a flat per-id loop
1361
+ * stopped at the first id with ANY match, silently losing the sibling half of
1362
+ * a split pair present in the same directory (#2458 fix-round F1).
1363
+ */
1364
+ export async function resolveDirectoryScanExtensions(hasMatch) {
1365
+ for (const family of SCAN_LANGUAGE_PRIORITY) {
1366
+ const exts = family.flatMap((languageId) => extensionsForLanguage(languageId));
1367
+ if (await hasMatch(exts)) {
1368
+ return exts;
1369
+ }
1370
+ }
1371
+ return undefined;
1372
+ }
1353
1373
  async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1354
- let extension;
1355
1374
  let collectedFiles = [];
1356
1375
  const isIgnored = projectIgnorePredicate(absPath);
1357
- for (const [ext, exts] of Object.entries(LANG_EXTENSIONS)) {
1358
- collectedFiles = await collectFiles(absPath, exts, MAX_FILES + 1, isIgnored);
1359
- if (collectedFiles.length > 0) {
1360
- extension = ext;
1361
- break;
1362
- }
1363
- }
1364
- if (!extension || collectedFiles.length === 0) {
1376
+ await resolveDirectoryScanExtensions(async (exts) => {
1377
+ collectedFiles = await collectFiles(absPath, [...exts], MAX_FILES + 1, isIgnored);
1378
+ return collectedFiles.length > 0;
1379
+ });
1380
+ if (collectedFiles.length === 0) {
1365
1381
  return {
1366
1382
  content: [
1367
1383
  {
@@ -1379,7 +1395,8 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1379
1395
  }
1380
1396
  const wasCapped = collectedFiles.length > MAX_FILES;
1381
1397
  const filesToProcess = collectedFiles.slice(0, MAX_FILES);
1382
- const { fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1398
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1399
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1383
1400
  let text;
1384
1401
  if (total === 0) {
1385
1402
  // #533/#570: an unconfirmed-containing directory result must never
@@ -1463,6 +1480,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1463
1480
  truncated,
1464
1481
  cleanFiles: clean,
1465
1482
  unconfirmedFiles: unconfirmed,
1483
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1466
1484
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1467
1485
  fileErrors: fileErrors.length > 0 ? fileErrors : undefined,
1468
1486
  lspHealthWarnings: lspHealthWarnings.length > 0 ? lspHealthWarnings : undefined,
@@ -10,6 +10,8 @@ import { Type } from "../clients/deps/typebox.js";
10
10
  import { logLatency } from "../clients/latency-logger.js";
11
11
  import { newLspMutationCorrelationId, recordLspMutationOutcome, } from "../clients/lsp-mutation.js";
12
12
  import { uriToPath } from "../clients/path-utils.js";
13
+ import { escapeRegExp } from "../clients/string-utils.js";
14
+ import { isRecordableProjectPath } from "../clients/file-utils.js";
13
15
  import { compactRenderResult } from "./render-compact.js";
14
16
  import { applyWorkspaceEdit, summarizeWorkspaceEdit, } from "../clients/lsp/edits.js";
15
17
  import { getLSPService, } from "../clients/lsp/index.js";
@@ -106,9 +108,6 @@ function emptyReasonForOperation(operation) {
106
108
  return "no-call-hierarchy-results";
107
109
  return "no-results";
108
110
  }
109
- function escapeRegExp(value) {
110
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
111
- }
112
111
  function parseSymbolSelector(symbol) {
113
112
  const trimmed = symbol.trim();
114
113
  const match = /^([^#]*)(?:#(-?\d+))?$/.exec(trimmed);
@@ -530,16 +529,16 @@ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = fal
530
529
  if (!fileContent)
531
530
  return;
532
531
  try {
533
- if (typeof lspService.touchFile === "function") {
534
- await lspService.touchFile(filePath, fileContent, {
535
- diagnostics: waitForDiagnostics ? "document" : "none",
536
- source: "lsp_navigation",
537
- clientScope: waitForDiagnostics ? "all" : "primary",
538
- });
539
- }
540
- else {
541
- await lspService.openFile(filePath, fileContent);
542
- }
532
+ // #2598: `touchFile` is defined unconditionally on the real `LSPService`
533
+ // (clients/lsp/index.ts), so the former `typeof … === "function"` hedge
534
+ // and its `openFile` arm were reachable only from a partial test double
535
+ // (AGENTS.md shape 7). Nothing here reads the result — a touch that
536
+ // resolves no clients is already the no-op this helper wants.
537
+ await lspService.touchFile(filePath, fileContent, {
538
+ diagnostics: waitForDiagnostics ? "document" : "none",
539
+ source: "lsp_navigation",
540
+ clientScope: waitForDiagnostics ? "all" : "primary",
541
+ });
543
542
  }
544
543
  catch {
545
544
  /* LSP server may not be ready yet — proceed anyway */
@@ -729,7 +728,7 @@ getFlag, mutationDeps) {
729
728
  },
730
729
  ],
731
730
  details: {
732
- ...(payload.details ?? {}),
731
+ ...payload.details,
733
732
  failureKind: meta.failureKind,
734
733
  },
735
734
  };
@@ -779,15 +778,37 @@ getFlag, mutationDeps) {
779
778
  if (requestedApply &&
780
779
  ["rename", "rename_file", "executeCommand"].includes(operation)) {
781
780
  const cwd = ctx.cwd || ".";
781
+ // Name the specific LSP operation in the receipt rather than a
782
+ // generic "lsp-edit" tag, so a rename's change-log entry reads
783
+ // differently from an executeCommand-solicited edit (#2450).
784
+ const mutationSource = operation === "executeCommand" ? "lsp-execute-command" : "lsp-rename";
782
785
  mutationContext = {
783
786
  cwd,
784
787
  correlationId: newLspMutationCorrelationId(_toolCallId),
785
- tool: "lsp_navigation",
786
- source: "lsp-edit",
788
+ tool: `lsp_navigation:${operation}`,
789
+ source: mutationSource,
787
790
  ...mutationDeps,
788
791
  readGuard: getFlag("no-read-guard", cwd)
789
792
  ? undefined
790
793
  : mutationDeps?.readGuard,
794
+ // #2450 review round 2 (F4)/round 3 (F2, F4): the SAME gate
795
+ // `registerMutationBridge` applies internally in `index.ts`, so
796
+ // this directly-threaded path and the bridge fallback
797
+ // (`clients/lsp-mutation.ts`, reached when `mutationDeps` is
798
+ // absent/partial — e.g. the MCP server) agree on which files
799
+ // count as project source, rather than the direct path
800
+ // recording an ignored/vendor write the fallback would drop.
801
+ // `no-read-guard` is intentionally NOT checked here (round 3
802
+ // F2): it gates only the read-guard stamp above, the same
803
+ // canonical split `clients/runtime-tool-result.ts` applies
804
+ // (`:1120`, `:1485`) — bookkeeping (turn-state / receipts)
805
+ // still runs under `--no-read-guard`. Judge against the
806
+ // project root, not the request `cwd` (round 3 F4): a
807
+ // sub-package `cwd` must not read a sibling-package rename as
808
+ // external.
809
+ isRecordable: (filePath) => {
810
+ return isRecordableProjectPath(filePath, mutationDeps?.runtime?.projectRoot ?? cwd);
811
+ },
791
812
  };
792
813
  }
793
814
  const isCallHierarchyTraversal = operation === "incomingCalls" || operation === "outgoingCalls";
@@ -9,7 +9,7 @@ function finiteNumber(value) {
9
9
  ? value
10
10
  : undefined;
11
11
  }
12
- export function lspStatusFromFailureKind(failureKind) {
12
+ function lspStatusFromFailureKind(failureKind) {
13
13
  if (failureKind === "success" || failureKind === "fallback_success") {
14
14
  return "success";
15
15
  }
@@ -145,7 +145,7 @@ function collectCallHierarchyLocationSummaries(result, operation) {
145
145
  }
146
146
  return out;
147
147
  }
148
- export function collectLspToolLocationsForOperation(operation, result, filePath) {
148
+ function collectLspToolLocationsForOperation(operation, result, filePath) {
149
149
  if ([
150
150
  "definition",
151
151
  "typeDefinition",
@@ -210,7 +210,7 @@ function parseBalancedJsonPrefix(text) {
210
210
  }
211
211
  return undefined;
212
212
  }
213
- export function parseLspNavigationTextPayload(text) {
213
+ function parseLspNavigationTextPayload(text) {
214
214
  const notes = [];
215
215
  const hints = [];
216
216
  const errors = [];
@@ -65,7 +65,7 @@ export function selectCompactText(result, args, expanded, summarize) {
65
65
  }
66
66
  /** Apply a CompactStyle to text. `brand` uses raw blue ANSI; the rest defer to
67
67
  * the theme so error-red and normal output stay consistent with the host. */
68
- export function paintCompact(style, text, theme) {
68
+ function paintCompact(style, text, theme) {
69
69
  if (style === "brand") {
70
70
  return `${PI_LENS_BLUE_FG}${text}${RESET_FG}`;
71
71
  }
@@ -22,7 +22,6 @@ export const LANGUAGES = [
22
22
  "ruby",
23
23
  "rust",
24
24
  "scala",
25
- "solidity",
26
25
  "swift",
27
26
  "tsx",
28
27
  "typescript",
@@ -8,7 +8,7 @@
8
8
  import * as path from "node:path";
9
9
  import { Type } from "../clients/deps/typebox.js";
10
10
  import { symbolSearch, } from "../clients/lens-engine.js";
11
- import { baseName, compactRenderResult } from "./render-compact.js";
11
+ import { compactRenderResult } from "./render-compact.js";
12
12
  /**
13
13
  * Machine-actionable follow-up hint (#771) — mirrors ast-grep-search.ts's
14
14
  * `suggestedDump` pattern. Cheap enough to attach per hit rather than only the
@@ -153,4 +153,3 @@ export function createSymbolSearchTool(getProjectRoot) {
153
153
  }
154
154
  // Re-exported so tests importing from this module can reach baseName without
155
155
  // a second import path.
156
- export { baseName };
@@ -45,10 +45,20 @@ On every write/edit, and at session/turn boundaries, pi-lens runs — without yo
45
45
  | **Structural rules** | ast-grep (NAPI engine) + tree-sitter rules flag correctness/security smells. |
46
46
  | **Opengrep security scan** | Always-on: per-edit via an auxiliary LSP, plus a cached project-wide CLI scan for `mode=full`. |
47
47
  | **Other scanners** | Config-/presence-gated: gitleaks (secrets), trivy (CVEs/IaC/license), govulncheck (Go), knip/jscpd/madge (JS/TS dead-code/dupes/cycles), vulture (Python), zizmor (GH Actions), typos. |
48
- | **Test-runner-on-write** | Related/affected tests are run asynchronously for the next turn's findings (edit-scoped, not a full-suite run). |
48
+ | **Test-runner-on-write** | Related/affected tests are run asynchronously for the next turn's findings (edit-scoped, not a full-suite run). Integration/e2e tests are never auto-fired — see below. |
49
49
  | **Read-guard** | Tracks that you read a file before editing it; blocks/warns zero-read or stale-range edits. |
50
50
  | **Context injection** | Injects session-start guidance and turn-end findings into your context (see §2). |
51
51
 
52
+ **Test-runner-on-write scope.** A built-in exclusion list keeps integration/e2e
53
+ suites out of the auto-fired batch — `**/integration/**`, `**/e2e/**`,
54
+ `**/*.integration.*`, `**/*.e2e.*` — since those commonly spawn external
55
+ processes (another CLI, a browser, a live provider) that don't belong on an
56
+ unattended per-edit turn. This is a fixed, built-in list; there is no
57
+ per-project config knob for it. If a runner itself fails to complete (a
58
+ timeout, or a missing provider/binary — not a failing assertion), pi-lens
59
+ delivers it as an advisory ("could not complete last turn"), not as a
60
+ "fix before continuing" blocker — you didn't introduce anything to fix.
61
+
52
62
  You don't invoke these. They happen. Your job is to **read the results** and **respond**.
53
63
 
54
64
  ---
@@ -185,9 +195,62 @@ pi-lens writes to files **outside your own tool calls** (`docs/features.md`
185
195
  - **`write` then `edit` on the same file, same turn:** the write's autofix
186
196
  demotes to deferred too, so the file's mutation history stays coherent. This
187
197
  resets at the next turn.
198
+ - **Any other tool that edits a file:** pi-lens recognizes it by the SHAPE of
199
+ its arguments rather than its name (`clients/mutating-tool.ts`), so a host or
200
+ extension tool called `replace` or `insert` gets the same chain `edit` gets:
201
+ a turn-state entry, a change-log receipt attributed to that tool, and a
202
+ *deferred* auto-fix. Its lines are resolved when the anchor is unambiguous
203
+ (roughly two-thirds of anchors in practice — `clients/hashline-anchor.ts`);
204
+ otherwise the mutation is recorded whole-file with lines unknown, and the
205
+ read-before-edit guard takes its no-line-info arm rather than guessing.
206
+ Deferred is the default for every edit-shaped tool pi-lens cannot place,
207
+ because formatting between the steps of a multi-call rewrite fights the tool
208
+ that is still writing.
209
+ - **A tool whose shape is unrecognized too:** pi-lens watches instead of
210
+ guessing (`clients/observed-mutation.ts`). A call that names a file gets a
211
+ bounded pre/post snapshot of THAT PATH — the file itself, or a directory's own
212
+ entries — and nothing else, so a write landing on a neighbouring file is never
213
+ attributed to it. Anything that changed is replayed through the same chain,
214
+ and the tool is then remembered as mutating: for this session on the first
215
+ sighting, persisted under the project's data directory on the second, so a
216
+ later session classifies it by name with no snapshot at all. Three quiet
217
+ observations in a row withdraw a session attribution again, and a withdrawn
218
+ tool can be learned back from a later real edit. An observation pi-lens could
219
+ not finish — a directory with more entries than it watches — counts as
220
+ neither, so a wide codemod is never written off on a look it never took. A
221
+ call that names
222
+ no file is caught at `agent_settled` by an incremental content check over the
223
+ files pi-lens has already read, written, diagnosed or opened on a language
224
+ server — a rotating window per turn, reading only what actually moved; a file
225
+ it has never seen has no baseline, so that last-resort net does not cover it,
226
+ and a file it cannot verify is reported as such rather than reformatted on a
227
+ timestamp alone.
188
228
  - The conservative actionable-warnings autofix (LSP quickfixes, hard-capped)
189
229
  is unchanged: it always runs at `agent_end`.
190
230
 
231
+ **Extension authors: record your own writes.** If your extension writes files
232
+ outside pi-lens's tool events — its own registered tool, a spawned rewriter —
233
+ tell pi-lens through the in-process mutation bridge, the write-side sibling of
234
+ the read bridge:
235
+
236
+ ```js
237
+ const bridge = globalThis[Symbol.for("pi-lens:mutation-bridge")];
238
+ if (bridge?.version === 1) {
239
+ bridge.recordMutation({
240
+ filePath, // absolute path
241
+ kind: "edit", // "write" replaces the whole file; "edit" is partial
242
+ editRanges: [[12, 18]], // optional, 1-based inclusive
243
+ consumer: "my-extension",
244
+ });
245
+ }
246
+ ```
247
+
248
+ Check `version` before calling; a version you do not recognize is unsupported.
249
+ Calling when pi-lens is absent or the guard is disabled is safe — the bridge is
250
+ missing or drops the call. `recordMutation` returns `true` when pi-lens took the
251
+ record and `false` when it dropped it, so you can count your own drops. pi-lens's
252
+ own `ast_grep_replace apply:true` records through this same bridge.
253
+
191
254
  Consequences for you:
192
255
 
193
256
  - Your exact written bytes may be reformatted/fixed. **This is expected pipeline