@herbertgao/pi-extensions 2026.9.1 → 2026.9.3

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 (341) hide show
  1. package/README.md +3 -2
  2. package/THIRD_PARTY_NOTICES.md +24 -0
  3. package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
  4. package/node_modules/@herbertgao/sol-pi/README.md +159 -0
  5. package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
  6. package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
  7. package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
  8. package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
  9. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
  10. package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
  11. package/node_modules/@herbertgao/sol-pi/package.json +76 -0
  12. package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
  13. package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
  14. package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
  15. package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
  16. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
  17. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
  18. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
  19. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
  20. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
  21. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
  22. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
  23. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
  24. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
  25. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
  26. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
  27. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
  28. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
  29. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
  30. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
  31. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
  32. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
  33. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
  34. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
  35. package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
  36. package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
  37. package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
  38. package/node_modules/@narumitw/pi-btw/README.md +57 -141
  39. package/node_modules/@narumitw/pi-btw/dist/index.ts +478 -43
  40. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  41. package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
  42. package/node_modules/@narumitw/pi-btw/package.json +2 -1
  43. package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -3
  44. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
  45. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
  46. package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
  47. package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
  48. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
  49. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
  50. package/node_modules/pi-lens/CHANGELOG.md +260 -0
  51. package/node_modules/pi-lens/README.md +13 -8
  52. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
  53. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
  54. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
  55. package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
  56. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
  57. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -8
  58. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
  59. package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
  60. package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
  61. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
  62. package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
  63. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
  64. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -5
  65. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
  66. package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -6
  67. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
  68. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
  69. package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
  70. package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
  71. package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
  72. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
  73. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
  74. package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
  75. package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
  76. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +3 -1
  77. package/node_modules/pi-lens/dist/clients/config-resolve.js +10 -7
  78. package/node_modules/pi-lens/dist/clients/config-schema.js +56 -2
  79. package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
  80. package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
  81. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
  82. package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
  83. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
  84. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
  86. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -41
  87. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
  91. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
  93. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +7 -2
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +3 -2
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +3 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +15 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +3 -3
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +3 -2
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +5 -3
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  146. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +5 -3
  147. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
  148. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
  149. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
  150. package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
  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 -0
  153. package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
  154. package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
  155. package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
  157. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
  158. package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
  159. package/node_modules/pi-lens/dist/clients/formatters.js +66 -93
  160. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
  161. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
  162. package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
  163. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
  164. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
  165. package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
  166. package/node_modules/pi-lens/dist/clients/installer/index.js +416 -61
  167. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
  168. package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
  169. package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
  170. package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
  171. package/node_modules/pi-lens/dist/clients/language-profile.js +12 -11
  172. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  173. package/node_modules/pi-lens/dist/clients/latency-logger.js +7 -7
  174. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
  175. package/node_modules/pi-lens/dist/clients/lens-config.js +23 -0
  176. package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
  177. package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
  179. package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
  180. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
  181. package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/config.js +37 -7
  183. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
  184. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +79 -1
  185. package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
  186. package/node_modules/pi-lens/dist/clients/lsp/index.js +491 -64
  187. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
  188. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
  189. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  190. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
  191. package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
  192. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
  193. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
  194. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
  195. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
  196. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
  197. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
  198. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
  200. package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
  201. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
  202. package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
  204. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
  205. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
  206. package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
  207. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
  208. package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
  209. package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
  210. package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
  211. package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
  212. package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
  213. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +9 -4
  214. package/node_modules/pi-lens/dist/clients/pipeline.js +5 -4
  215. package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
  216. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
  217. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
  218. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
  219. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  220. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  221. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  222. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  223. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/project-lens-config.js +49 -1
  225. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
  226. package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
  227. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +3 -1
  228. package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
  229. package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
  230. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
  231. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
  232. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
  233. package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
  234. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +10 -1
  235. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
  236. package/node_modules/pi-lens/dist/clients/runtime-session.js +58 -14
  237. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
  238. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
  239. package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
  240. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
  241. package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
  242. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
  243. package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
  244. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
  245. package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
  246. package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
  247. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
  248. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
  249. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  250. package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
  251. package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
  252. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
  253. package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
  254. package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
  255. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  256. package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
  257. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
  258. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
  259. package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
  260. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
  261. package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
  262. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
  263. package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
  264. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
  265. package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
  266. package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
  267. package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
  268. package/node_modules/pi-lens/dist/index.js +19969 -17481
  269. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
  270. package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
  271. package/node_modules/pi-lens/dist/mcp/server.js +135 -159
  272. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
  273. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
  274. package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
  275. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
  276. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
  277. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
  278. package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
  279. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
  280. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +354 -86
  281. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +54 -112
  282. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
  283. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
  284. package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
  285. package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
  286. package/node_modules/pi-lens/dist/tools/render-compact.js +281 -2
  287. package/node_modules/pi-lens/dist/tools/symbol-search.js +3 -4
  288. package/node_modules/pi-lens/docs/agent-guide.md +3 -4
  289. package/node_modules/pi-lens/docs/agent-tools.md +46 -22
  290. package/node_modules/pi-lens/docs/configuration.md +9 -0
  291. package/node_modules/pi-lens/docs/environment-variables.md +0 -5
  292. package/node_modules/pi-lens/docs/features.md +5 -5
  293. package/node_modules/pi-lens/docs/globalconfig.md +31 -1
  294. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
  295. package/node_modules/pi-lens/docs/mcp.md +13 -1
  296. package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
  297. package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
  298. package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
  299. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +41 -0
  300. package/node_modules/pi-lens/docs/pi-lens-subagent.md +21 -2
  301. package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
  302. package/node_modules/pi-lens/docs/real-harness.md +46 -0
  303. package/node_modules/pi-lens/docs/release-qa-baseline.md +17 -5
  304. package/node_modules/pi-lens/docs/servercapabilities.md +2 -3
  305. package/node_modules/pi-lens/docs/settings.md +37 -2
  306. package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
  307. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  308. package/node_modules/pi-lens/docs/usage.md +13 -3
  309. package/node_modules/pi-lens/docs/word-index.md +1 -1
  310. package/node_modules/pi-lens/package.json +15 -15
  311. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
  312. package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
  313. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
  314. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
  315. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
  316. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
  317. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
  318. package/node_modules/pi-web-access/CHANGELOG.md +29 -0
  319. package/node_modules/pi-web-access/README.md +33 -22
  320. package/node_modules/pi-web-access/abortable.ts +17 -0
  321. package/node_modules/pi-web-access/crawl4ai.ts +204 -0
  322. package/node_modules/pi-web-access/credential-source.ts +1 -0
  323. package/node_modules/pi-web-access/curator-page.ts +20 -2
  324. package/node_modules/pi-web-access/curator-run.ts +44 -0
  325. package/node_modules/pi-web-access/curator-server.ts +3 -1
  326. package/node_modules/pi-web-access/duckduckgo.ts +1 -1
  327. package/node_modules/pi-web-access/extract.ts +57 -12
  328. package/node_modules/pi-web-access/fetch-params.ts +1 -1
  329. package/node_modules/pi-web-access/gemini-search.ts +9 -5
  330. package/node_modules/pi-web-access/index.ts +25 -18
  331. package/node_modules/pi-web-access/openai-search.ts +35 -13
  332. package/node_modules/pi-web-access/package.json +2 -2
  333. package/node_modules/pi-web-access/page-query.ts +5 -2
  334. package/node_modules/pi-web-access/query-rewrite.ts +5 -2
  335. package/node_modules/pi-web-access/serpapi.ts +220 -0
  336. package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
  337. package/node_modules/pi-web-access/storage.ts +10 -0
  338. package/node_modules/pi-web-access/summary-review.ts +34 -21
  339. package/node_modules/pi-web-access/utils.ts +6 -4
  340. package/package.json +7 -4
  341. package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
@@ -6,7 +6,6 @@
6
6
  */
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
- import { Type } from "../clients/deps/typebox.js";
10
9
  import { getProjectIgnoreMatcher, isExcludedDirName, } from "../clients/file-utils.js";
11
10
  import { getLSPService, groupFilesByPrimaryServer, runPerServerGroups, } from "../clients/lsp/index.js";
12
11
  import { buildScopeKey, createWorkspaceDiagnosticsCacheContext, } from "../clients/lsp/workspace-diagnostics-cache.js";
@@ -20,23 +19,26 @@ import { attemptTsserverSyncDiagnostics } from "../clients/lsp/tsserver-sync.js"
20
19
  import { convertLspDiagnostics } from "../clients/dispatch/utils/lsp-diagnostics.js";
21
20
  import { demoteInferredProjectDiagnostics } from "../clients/lsp/inferred-project.js";
22
21
  import { isBlocking, reconcileScanDiagnostics, } from "../clients/widget-state.js";
23
- import { baseName, compactRenderResult } from "./render-compact.js";
24
- import { makeProgressReporter, scanningSummaryLine } from "./scan-progress.js";
22
+ import { makeProgressReporter } from "./scan-progress.js";
25
23
  import { isWarmAttached, tryWarmAttachedDiagnostics, } from "../clients/warm-attach.js";
26
24
  import { extensionsForLanguage, SCAN_LANGUAGE_PRIORITY, } from "../clients/language-registry.js";
27
25
  const MAX_FILES = 100;
28
- const MAX_BATCH_FILES = 100;
26
+ export const MAX_BATCH_FILES = 100;
29
27
  const MAX_DIAGNOSTICS = 200;
30
28
  const DEFAULT_BATCH_CONCURRENCY = 8;
31
29
  const MAX_BATCH_CONCURRENCY = 16;
32
30
  const DEFAULT_BATCH_FILE_DEADLINE_MS = 15_000;
33
31
  // LSP severities: 1=Error, 2=Warning, 3=Information, 4=Hint
34
- const SEVERITY_NAMES = {
32
+ export const SEVERITY_NAMES = {
35
33
  1: "error",
36
34
  2: "warning",
37
35
  3: "information",
38
36
  4: "hint",
39
37
  };
38
+ export const LSP_SEVERITY_FILTERS = [
39
+ ...Object.values(SEVERITY_NAMES),
40
+ "all",
41
+ ];
40
42
  function batchFileDeadlineMs() {
41
43
  const raw = Number(process.env.PI_LENS_LSP_BATCH_FILE_MS);
42
44
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_BATCH_FILE_DEADLINE_MS;
@@ -216,98 +218,8 @@ nextWriteIndex,
216
218
  // injecting "Unresolved from this turn" for three more turns after answering
217
219
  // "confirmed clean" for the same file. index.ts injects
218
220
  // `runtime.retireInlineBlockerOnConfirmedClean`. Optional/undefined in tests.
219
- onConfirmedNoBlockers) {
221
+ onConfirmedNoBlockers, getService = getLSPService) {
220
222
  return {
221
- name: "lsp_diagnostics",
222
- label: "LSP Diagnostics",
223
- description: "Get errors, warnings, and hints from language servers for a file or directory. " +
224
- "Use BEFORE running builds to proactively check for issues. " +
225
- "Works on directories by auto-detecting file extensions and scanning all matching files.",
226
- promptSnippet: "Get LSP diagnostics for a file or directory (use before builds)",
227
- renderResult: compactRenderResult(({ details, args, isError, text }) => {
228
- // Streaming progress partials render the live bar (see scanningSummaryLine).
229
- const scanning = scanningSummaryLine(details, text);
230
- if (scanning)
231
- return scanning;
232
- if (isError) {
233
- return `lsp_diagnostics — ${text.split("\n")[0] ?? "error"}`;
234
- }
235
- const count = details?.totalDiagnostics ?? details?.diagnostics?.length ?? 0;
236
- const target = baseName(details?.filePath ?? args.path) || "workspace";
237
- const files = details?.filesChecked ?? details?.filesScanned;
238
- const scope = typeof files === "number" && files > 1
239
- ? ` across ${files} files`
240
- : target
241
- ? ` ${target}`
242
- : "";
243
- const noun = count === 1 ? "diagnostic" : "diagnostics";
244
- // #533: a batch/directory result with any unconfirmed files must NEVER
245
- // compact-render as a bare "N diagnostics" — that erases the fact some
246
- // files' clean status was never actually confirmed by the server.
247
- const unconfirmedFiles = details?.unconfirmedFiles ?? 0;
248
- if (unconfirmedFiles > 0) {
249
- const cleanFiles = details?.cleanFiles ?? 0;
250
- const timedOutFiles = details?.timedOutFiles ?? 0;
251
- const suffix = timedOutFiles > 0 ? ` (${timedOutFiles} timed out)` : "";
252
- return `lsp_diagnostics${scope} — ${count} ${noun} · ${cleanFiles} clean · ${unconfirmedFiles} unconfirmed${suffix}`;
253
- }
254
- const outcomeCounts = details?.outcomeCounts;
255
- const notConfirmed = outcomeCounts
256
- ? (outcomeCounts.inconclusive ?? 0) +
257
- (outcomeCounts.unavailable ?? 0) +
258
- (outcomeCounts.unsupported ?? 0) +
259
- (outcomeCounts.failed ?? 0)
260
- : 0;
261
- if (notConfirmed > 0) {
262
- return `lsp_diagnostics${scope} — ${count} ${noun} · ${notConfirmed} checks not confirmed`;
263
- }
264
- if ((details?.incompleteFiles ?? 0) > 0) {
265
- return `lsp_diagnostics${scope} — incomplete (${details?.incompleteFiles} files not confirmed)`;
266
- }
267
- // Single-file mode: 0 diagnostics from an unconfirmed result — either a
268
- // silent-on-clean server or (#570) a timed-out check — is not a clean
269
- // render either.
270
- if (count === 0 && details?.unconfirmed) {
271
- return details?.timedOut
272
- ? `lsp_diagnostics${scope} — timed out (result may be incomplete)`
273
- : `lsp_diagnostics${scope} — unconfirmed (server cannot confirm clean)`;
274
- }
275
- return `lsp_diagnostics${scope} — ${count} ${noun}`;
276
- }),
277
- parameters: Type.Object({
278
- path: Type.Optional(Type.String({
279
- description: "File or directory path to check. For directories, all matching source files are scanned.",
280
- })),
281
- paths: Type.Optional(Type.Array(Type.String(), {
282
- minItems: 1,
283
- maxItems: MAX_BATCH_FILES,
284
- description: "Explicit files to check as a bounded-concurrency batch. When provided, path is ignored.",
285
- })),
286
- severity: Type.Optional(Type.String({
287
- enum: ["error", "warning", "information", "hint", "all"],
288
- description: "Filter by severity level (default: all)",
289
- })),
290
- concurrency: Type.Optional(Type.Number({
291
- description: "Batch/directory concurrency, in distinct LSP server groups run in parallel " +
292
- "(default 8, max 16) — not individual files. Files sharing one server " +
293
- "(e.g. a same-language batch) are always processed one at a time against " +
294
- "that server regardless of this value; this caps how many DIFFERENT " +
295
- "servers run concurrently.",
296
- })),
297
- waitMs: Type.Optional(Type.Number({
298
- description: "Optional per-file LSP wait budget for batch diagnostics. Uses server defaults when omitted.",
299
- })),
300
- serverScope: Type.Optional(Type.String({
301
- enum: ["primary", "all"],
302
- description: "'primary' (fast, low-noise): only the file's actual language " +
303
- "server (e.g. typescript) — for 'does this have real type " +
304
- "errors'. 'all' (default): also touches cross-cutting auxiliary " +
305
- "scanners (ast-grep, opengrep, zizmor, typos, marksman) attached " +
306
- "to this file, including findings for files not yet dispatched " +
307
- "this session. Primary confirmation is always reported " +
308
- "separately from auxiliary findings regardless of this setting.",
309
- })),
310
- }),
311
223
  async execute(_toolCallId, params, _signal, onUpdate, ctx) {
312
224
  // Escape aborts the turn via ctx.signal; honor both it and the tool-call
313
225
  // signal so a batch/directory scan cancels rather than grinding on.
@@ -323,7 +235,7 @@ onConfirmedNoBlockers) {
323
235
  ? Math.floor(typedParams.waitMs)
324
236
  : undefined;
325
237
  const serverScope = typedParams.serverScope === "primary" ? "primary" : "all";
326
- const lspService = getLSPService();
238
+ const lspService = getService();
327
239
  if (!lspService) {
328
240
  return {
329
241
  content: [
@@ -446,7 +358,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
446
358
  const attached = await tryWarmAttachedDiagnostics(absPath, content, attachedBudgetMs, "sweep");
447
359
  if (attached?.available) {
448
360
  const scopedDiagnostics = serverScope === "primary"
449
- ? attached.response.diagnostics.filter((item) => item.source === primaryServerId(absPath))
361
+ ? attached.response.diagnostics.filter((item) => item.serverId === primaryServerId(absPath))
450
362
  : attached.response.diagnostics;
451
363
  const filtered = applyAuxiliarySuppressions(scopedDiagnostics, content, { fileRole: detectFileRole(absPath, content) });
452
364
  return {
@@ -470,6 +382,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
470
382
  // field across the socket. An older incumbent omits it → empty → the
471
383
  // pre-#1470 handling, unchanged.
472
384
  unconfirmedServerIds: attached.response.unconfirmedServerIds ?? [],
385
+ diagnosticsUnsupportedServerIds: [],
473
386
  content,
474
387
  };
475
388
  }
@@ -528,6 +441,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
528
441
  // the getDiagnostics fallback never reports one, which is honest — that
529
442
  // path claims no confirmation at all.
530
443
  unconfirmedServerIds: touchCoverageGap(touched),
444
+ diagnosticsUnsupportedServerIds: touched?.diagnosticsUnsupportedServerIds ?? [],
531
445
  content,
532
446
  binding,
533
447
  };
@@ -540,6 +454,7 @@ function diagnosticsToFileDiags(file, diagnostics) {
540
454
  severity: d.severity,
541
455
  message: d.message,
542
456
  source: d.source,
457
+ serverId: d.serverId,
543
458
  code: d.code,
544
459
  }));
545
460
  }
@@ -698,7 +613,9 @@ observedAt) {
698
613
  scanOrigin: "lsp_diagnostics",
699
614
  });
700
615
  const retagged = retagAuxiliaryDiagnostics(diagnostics, rawDiags, content ?? "", { cwd, fileRole: detectFileRole(file, content) });
701
- reconcileScanDiagnostics(file, retagged, true, writeIndex, observedAt);
616
+ const reconciled = reconcileScanDiagnostics(file, retagged, true, writeIndex, observedAt);
617
+ if (!reconciled)
618
+ return { confirmed: false, blocking: true };
702
619
  // #1561: the blocking tally comes from the SAME `isBlocking` predicate the
703
620
  // footer counts with — no second severity rule for the blocker-retire
704
621
  // decision to drift away from.
@@ -777,7 +694,7 @@ onConfirmedNoBlockers) {
777
694
  };
778
695
  }
779
696
  }
780
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
697
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
781
698
  const health = lspService.getDiagnosticsHealth?.(file);
782
699
  // #570: a timed-out priming check is never a confirmed "clean" — treat it
783
700
  // as unconfirmed without consulting the (unrelated) silent-tier
@@ -788,7 +705,12 @@ onConfirmedNoBlockers) {
788
705
  // be merged in rather than discarded.
789
706
  let effectiveRawDiags = rawDiags;
790
707
  let confirmation;
791
- if (skipReason !== undefined) {
708
+ if (diagnosticsUnsupportedServerIds.length > 0) {
709
+ // No diagnostic provider and no push evidence is a capability boundary,
710
+ // not a clean result and not a timeout.
711
+ confirmation = undefined;
712
+ }
713
+ else if (skipReason !== undefined) {
792
714
  confirmation = "unconfirmed";
793
715
  }
794
716
  else if (timedOut) {
@@ -851,6 +773,7 @@ onConfirmedNoBlockers) {
851
773
  if (cacheCtx &&
852
774
  scopeKey !== undefined &&
853
775
  confirmation !== "unconfirmed" &&
776
+ diagnosticsUnsupportedServerIds.length === 0 &&
854
777
  unconfirmedServerIds.length === 0) {
855
778
  cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
856
779
  ? hashDiagnosticContent(collectedContent)
@@ -864,6 +787,7 @@ onConfirmedNoBlockers) {
864
787
  timedOut: confirmation === "unconfirmed" ? timedOut : undefined,
865
788
  ...(skipReason !== undefined && { skipReason }),
866
789
  primaryServerId: primaryServerId(file),
790
+ diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
867
791
  };
868
792
  }
869
793
  async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(),
@@ -873,7 +797,7 @@ onConfirmedNoBlockers) {
873
797
  // Reserve the token before awaiting this file's LSP result. The direct-file
874
798
  // path performs its own confirmation/reconciliation below (#1198).
875
799
  const writeIndex = nextWriteIndex?.();
876
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
800
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
877
801
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
878
802
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
879
803
  // #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
@@ -887,7 +811,10 @@ onConfirmedNoBlockers) {
887
811
  // diagnostics it surfaces are merged in, not discarded.
888
812
  let effectiveRawDiags = rawDiags;
889
813
  let confirmation;
890
- if (skipReason !== undefined) {
814
+ if (diagnosticsUnsupportedServerIds.length > 0) {
815
+ confirmation = undefined;
816
+ }
817
+ else if (skipReason !== undefined) {
891
818
  confirmation = "unconfirmed";
892
819
  }
893
820
  else if (timedOut) {
@@ -949,12 +876,15 @@ onConfirmedNoBlockers) {
949
876
  });
950
877
  }
951
878
  const primaryId = primaryServerId(absPath);
952
- const primaryDiags = limited.filter((d) => d.source === primaryId);
953
- const auxiliaryDiags = limited.filter((d) => d.source !== primaryId);
879
+ const primaryDiags = limited.filter((d) => d.serverId === primaryId);
880
+ const auxiliaryDiags = limited.filter((d) => d.serverId !== primaryId);
954
881
  // Primary confirmation is always its own line, independent of how many
955
882
  // auxiliary findings exist — a wall of ast-grep/opengrep noise must never
956
883
  // bury whether the actual language server confirmed the file clean.
957
884
  const primaryLine = (() => {
885
+ if (diagnosticsUnsupportedServerIds.length > 0) {
886
+ return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only — diagnostics unsupported (no pull provider or publish evidence).`;
887
+ }
958
888
  if (timedOut) {
959
889
  return ("Primary LSP: check timed out — NOT the same as 0 diagnostics; the " +
960
890
  "file may still have errors that just hadn't been reported yet. " +
@@ -980,7 +910,7 @@ onConfirmedNoBlockers) {
980
910
  // this result does NOT speak for, so "no auxiliary findings" can never be read
981
911
  // as "the security scanners found nothing".
982
912
  const coverageLine = unconfirmedServerIds.length > 0
983
- ? `Auxiliary coverage INCOMPLETE — ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit, or use waitMs to wait longer.`
913
+ ? `Auxiliary coverage INCOMPLETE — ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit. waitMs can extend an ordinary wait, but it cannot override a session-demoted scanner.`
984
914
  : undefined;
985
915
  let text;
986
916
  if (total === 0) {
@@ -1031,6 +961,7 @@ onConfirmedNoBlockers) {
1031
961
  truncated,
1032
962
  unconfirmed,
1033
963
  timedOut: unconfirmed ? timedOut : undefined,
964
+ navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : undefined,
1034
965
  ...(skipReason !== undefined && { skipReason }),
1035
966
  // #1470: which servers this result does NOT speak for. Absent when it
1036
967
  // speaks for all of them.
@@ -1054,7 +985,12 @@ function tallyConfirmation(results) {
1054
985
  let clean = 0;
1055
986
  let unconfirmed = 0;
1056
987
  let timedOut = 0;
988
+ let navigationOnly = 0;
1057
989
  for (const result of results) {
990
+ if (result.diagnosticsUnsupported) {
991
+ navigationOnly += 1;
992
+ continue;
993
+ }
1058
994
  if (result.diagnostics.length > 0)
1059
995
  continue;
1060
996
  if (result.confirmation === "unconfirmed") {
@@ -1068,11 +1004,13 @@ function tallyConfirmation(results) {
1068
1004
  clean += 1;
1069
1005
  }
1070
1006
  }
1071
- return { clean, unconfirmed, timedOut };
1007
+ return { clean, unconfirmed, timedOut, navigationOnly };
1072
1008
  }
1073
1009
  function classifyBatchFileOutcome(result) {
1074
1010
  if (result.error)
1075
1011
  return "failed";
1012
+ if (result.diagnosticsUnsupported)
1013
+ return "unsupported";
1076
1014
  // A timed-out/unconfirmed answer may contain partial findings, but it cannot
1077
1015
  // honestly be called a complete findings result. Keep the raw findings for
1078
1016
  // investigation while making the aggregate incomplete.
@@ -1201,8 +1139,8 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
1201
1139
  // `clean`/`unconfirmed` above already reflect ONLY the primary server's
1202
1140
  // confirmation; this split does the same job for the listed diagnostics.
1203
1141
  const primaryIdByFile = new Map(results.map((r) => [r.file, r.primaryServerId]));
1204
- const primaryDisplay = display.filter((d) => d.source === primaryIdByFile.get(d.file));
1205
- const auxiliaryDisplay = display.filter((d) => d.source !== primaryIdByFile.get(d.file));
1142
+ const primaryDisplay = display.filter((d) => d.serverId === primaryIdByFile.get(d.file));
1143
+ const auxiliaryDisplay = display.filter((d) => d.serverId !== primaryIdByFile.get(d.file));
1206
1144
  return {
1207
1145
  results,
1208
1146
  fileErrors,
@@ -1228,6 +1166,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1228
1166
  };
1229
1167
  }
1230
1168
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, outcomeCounts, incompleteFiles, } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
1169
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1231
1170
  const lines = [
1232
1171
  `Files checked: ${results.length}`,
1233
1172
  `Total diagnostics: ${total}`,
@@ -1298,13 +1237,14 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1298
1237
  truncated,
1299
1238
  cleanFiles: clean,
1300
1239
  unconfirmedFiles: unconfirmed,
1240
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1301
1241
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1302
1242
  outcomes: results.map((result) => ({
1303
1243
  file: result.file,
1304
1244
  outcome: result.outcome,
1305
1245
  reason: result.inconclusiveReason ?? result.error ?? result.unavailable,
1306
- primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.source === result.primaryServerId).length,
1307
- auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.source !== result.primaryServerId).length,
1246
+ primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId === result.primaryServerId).length,
1247
+ auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId !== result.primaryServerId).length,
1308
1248
  })),
1309
1249
  outcomeCounts,
1310
1250
  incompleteFiles: incompleteFiles > 0 ? incompleteFiles : undefined,
@@ -1366,7 +1306,8 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1366
1306
  }
1367
1307
  const wasCapped = collectedFiles.length > MAX_FILES;
1368
1308
  const filesToProcess = collectedFiles.slice(0, MAX_FILES);
1369
- const { fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1309
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1310
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1370
1311
  let text;
1371
1312
  if (total === 0) {
1372
1313
  // #533/#570: an unconfirmed-containing directory result must never
@@ -1450,6 +1391,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1450
1391
  truncated,
1451
1392
  cleanFiles: clean,
1452
1393
  unconfirmedFiles: unconfirmed,
1394
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1453
1395
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1454
1396
  fileErrors: fileErrors.length > 0 ? fileErrors : undefined,
1455
1397
  lspHealthWarnings: lspHealthWarnings.length > 0 ? lspHealthWarnings : undefined,
@@ -557,29 +557,8 @@ getFlag, mutationDeps) {
557
557
  return {
558
558
  name: "lsp_navigation",
559
559
  label: "LSP Navigate",
560
- description: "Navigate code using LSP (Language Server Protocol). LSP is enabled by default; disable with --no-lsp.\n" +
561
- "Operations:\n" +
562
- "- definition: Jump to where a symbol is defined\n" +
563
- "- typeDefinition: Jump to the definition of a symbol's TYPE (e.g. the class/interface of a variable)\n" +
564
- "- declaration: Jump to a symbol's declaration (e.g. an extern/forward decl, distinct from its definition)\n" +
565
- "- references: Find all usages of a symbol\n" +
566
- "- hover: Get type/doc info at a position\n" +
567
- "- signatureHelp: Show callable signatures at cursor\n" +
568
- "- documentSymbol: List all symbols (functions/classes/vars) in a file\n" +
569
- "- findSymbol: Search document symbols in a file by name/detail with optional kind/top-level/exact filters\n" +
570
- "- workspaceSymbol: Search symbols across the whole project (best with path context)\n" +
571
- "- codeAction: Find available quick fixes/refactors at a range\n" +
572
- "- rename: Compute or apply workspace edits for renaming a symbol\n" +
573
- "- rename_file: Preview/apply LSP-aware source file rename notifications\n" +
574
- "- implementation: Jump to interface implementations\n" +
575
- "- prepareCallHierarchy: Get callable item at position (for incoming/outgoing)\n" +
576
- "- incomingCalls: Find all functions/methods that CALL this function\n" +
577
- "- outgoingCalls: Find all functions/methods CALLED by this function\n" +
578
- "- executeCommand: Run a server-advertised command via workspace/executeCommand. HARDENED: allowlisted to commands the server advertised; dry-run by default (reports whether advertised) — set apply:true to actually run. Pass command (+ optional commandArguments).\n" +
579
- "- workspaceDiagnostics: List all diagnostics tracked by active LSP clients\n" +
580
- "- capabilities: Show cached operation support for active LSP servers\n\n" +
581
- "Line and character are 1-based (as shown in editors). For position-based operations, prefer passing symbol when you know the line but not the exact character; character can be omitted or -1 and pi-lens will resolve the symbol column. Use symbol#N for repeated symbols on the same line (1-based occurrence).",
582
- promptSnippet: "Find definitions, references, and hover info via LSP",
560
+ description: 'Navigate source with language-server operations such as definition, references, hover, and rename. Example: use `{operation: "references", path: "src/app.ts", line: 12}`.',
561
+ promptSnippet: "Navigate definitions and references with LSP",
583
562
  renderResult: compactRenderResult(({ details, args, isError, text }) => {
584
563
  const op = details?.operation ??
585
564
  (typeof args.operation === "string" ? args.operation : "lsp");
@@ -594,56 +573,55 @@ getFlag, mutationDeps) {
594
573
  }),
595
574
  parameters: Type.Object({
596
575
  operation: Type.String({
597
- description: "LSP operation to perform. Valid values: " +
598
- VALID_OPERATIONS.join(", "),
576
+ description: "LSP operation to perform. Valid values: definition, typeDefinition, declaration, references, hover, signatureHelp, documentSymbol, findSymbol, workspaceSymbol, codeAction, rename, rename_file, implementation, prepareCallHierarchy, incomingCalls, outgoingCalls, executeCommand, workspaceDiagnostics, capabilities.",
599
577
  }),
600
578
  path: Type.Optional(Type.String({
601
- description: "Absolute or relative file path. Required for file-scoped operations; optional for workspaceSymbol/workspaceDiagnostics.",
579
+ description: "Target file path.",
602
580
  })),
603
581
  line: Type.Optional(Type.Number({
604
- description: "Line number (1-based). Required for definition/references/hover/implementation",
582
+ description: "1-based line number.",
605
583
  })),
606
584
  character: Type.Optional(Type.Number({
607
- description: "Character offset (1-based). Optional when symbol is provided; use -1 to force symbol-column resolution.",
585
+ description: "1-based character offset.",
608
586
  })),
609
587
  symbol: Type.Optional(Type.String({
610
- description: "Symbol name on the target line for automatic character resolution. Use symbol#N to select the Nth occurrence on the line.",
588
+ description: "Symbol for automatic character resolution.",
611
589
  })),
612
590
  endLine: Type.Optional(Type.Number({
613
- description: "End line (1-based). Optional; used by codeAction range.",
591
+ description: "1-based range end line.",
614
592
  })),
615
593
  endCharacter: Type.Optional(Type.Number({
616
- description: "End character (1-based). Optional; used by codeAction range.",
594
+ description: "1-based range end character.",
617
595
  })),
618
596
  newName: Type.Optional(Type.String({
619
- description: "Required for rename operation.",
597
+ description: "New symbol name.",
620
598
  })),
621
599
  newFilePath: Type.Optional(Type.String({
622
- description: "Required for rename_file operation.",
600
+ description: "New file path.",
623
601
  })),
624
602
  apply: Type.Optional(Type.Boolean({
625
- description: "rename/executeCommand: apply for real. rename defaults to preview; executeCommand defaults to a dry-run that only reports whether the command is advertised — set apply:true to actually run it.",
603
+ description: "Apply a mutation instead of previewing it.",
626
604
  })),
627
605
  command: Type.Optional(Type.String({
628
- description: "executeCommand only: the server command id to run. Must be one the server advertised (see the capabilities operation).",
606
+ description: "Server command identifier.",
629
607
  })),
630
608
  commandArguments: Type.Optional(Type.Array(Type.Unknown(), {
631
- description: "executeCommand only: arguments array passed to workspace/executeCommand.",
609
+ description: "Arguments for the server command.",
632
610
  })),
633
611
  query: Type.Optional(Type.String({
634
- description: "Symbol name to search. Used by workspaceSymbol and findSymbol.",
612
+ description: "Symbol search query.",
635
613
  })),
636
614
  kinds: Type.Optional(Type.Array(Type.String(), {
637
- description: "findSymbol only: restrict matches to symbol kind labels such as function, class, method, variable, interface.",
615
+ description: "Symbol-kind filters.",
638
616
  })),
639
617
  exactMatch: Type.Optional(Type.Boolean({
640
- description: "findSymbol only: match whole symbol names/details exactly instead of substring matching.",
618
+ description: "Require an exact symbol match.",
641
619
  })),
642
620
  topLevelOnly: Type.Optional(Type.Boolean({
643
- description: "findSymbol only: do not search nested child symbols.",
621
+ description: "Exclude nested symbols.",
644
622
  })),
645
623
  maxResults: Type.Optional(Type.Number({
646
- description: "findSymbol only: maximum matches to return. Default 20.",
624
+ description: "Maximum symbol matches.",
647
625
  })),
648
626
  callHierarchyItem: Type.Optional(Type.Object({
649
627
  name: Type.String(),
@@ -669,9 +647,7 @@ getFlag, mutationDeps) {
669
647
  character: Type.Number(),
670
648
  }),
671
649
  }),
672
- }, {
673
- description: "Call hierarchy item. Required for incomingCalls/outgoingCalls",
674
- })),
650
+ }, {})),
675
651
  }),
676
652
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
677
653
  const startedAt = Date.now();
@@ -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 = [];
@@ -26,12 +26,8 @@ export function createModuleReportTool(getProjectRoot) {
26
26
  return {
27
27
  name: "module_report",
28
28
  label: "Module Report",
29
- description: "Structured, navigable overview of a source module — a token-efficient substitute for reading the whole file. Returns each symbol's name/kind/signature/line-range (plus a first-line `doc` summary when a doc comment is attached), important inline callbacks/closures/lambdas with stable handles, plus who-uses-this, risk flags, and ranked recommendedReads. To read a symbol's body: call read/read_symbol with offset=startLine, limit=endLine-startLine+1 on THIS report's `path` — those aren't repeated per symbol. Prefer this before a full read; then use read_symbol (or read) for the exact body you need.\n" +
30
- "Single mode: language-uniform tree-sitter outline + review-graph who-uses-this + inline executable extraction; degrades to outline-only when no cached graph is available. `semantic.source` reports whether graph data was used.\n" +
31
- 'Pass `blastRadius: true` to also get the cross-file blast radius — the transitive dependents of this module aggregated to ranked file `read` args ("if you change this, verify these files"). Read-only over the cached graph; omitted on a cold cache. Supersedes the standalone impact query.\n' +
32
- "Pass `callGraph: true` to include bounded derived callers/callees from the cached FunctionCallGraph; unavailable cache state is explicit and never reported as zero calls.\n" +
33
- '`view: "compact"` returns a line-oriented text rendering (one line per symbol/callback, cheapest option) instead of JSON — same data, roughly a quarter of the token cost; use it for a quick skim. Default view returns JSON. An outline shows shape, not bodies — it does NOT count as having read a symbol\'s body for editing; use read_symbol for that.',
34
- promptSnippet: "Navigable file outline — a cheap substitute for reading a whole file",
29
+ description: "Return a navigable source-module outline with symbols, references, and read handles. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. On a cold cache, project_report and symbol_search return available: false with a retry hint and start a non-blocking background build; module_report degrades to outline-only with cache freshness explicit. Example: use module_report on `src/app.ts` before read_symbol.",
30
+ promptSnippet: "Outline a source module before reading a body",
35
31
  renderResult: compactRenderResult(({ details, args, isError }) => {
36
32
  const base = baseName(args.path) || "module";
37
33
  if (isError || details?.available === false) {
@@ -48,7 +44,7 @@ export function createModuleReportTool(getProjectRoot) {
48
44
  }),
49
45
  parameters: Type.Object({
50
46
  path: Type.String({
51
- description: "Absolute or workspace-relative path to the source file.",
47
+ description: "Source file, e.g. `src/app.ts`.",
52
48
  }),
53
49
  maxRefsPerSymbol: Type.Optional(Type.Number({
54
50
  description: "Cap on who-uses-this entries per symbol (default 10).",
@@ -157,7 +153,7 @@ export function createReadSymbolTool(getProjectRoot, recordSymbolRead) {
157
153
  return {
158
154
  name: "read_symbol",
159
155
  label: "Read Symbol",
160
- description: "Return the verbatim source of a single named symbol or module_report callback handle in a file — a targeted, cheap alternative to reading the whole file. Pair with module_report: module_report finds the symbol/callback handle, read_symbol shows its body. Unlike an outline, this delivers the actual lines, so it counts as having read that symbol for the read-before-edit guard. The returned body includes an attached doc comment when one exists. Accepts a dotted `Class.method` name to resolve a member directly, falling back to a plain top-level lookup when the qualifier doesn't resolve. A miss embeds the ~3 nearest symbol names in the file so a typo self-corrects without a second call. When multiple same-file symbols share a name (overloads, a type and a value sharing a name), the first is returned with an `ambiguous` note; pass `kind` to pick a specific one.",
156
+ description: "Return one symbol's verbatim source. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. Example: use read_symbol after module_report identifies `parseConfig`.",
161
157
  promptSnippet: "Read one symbol's body instead of the whole file",
162
158
  renderResult: compactRenderResult(({ details, args, isError, lineCount }) => {
163
159
  const base = baseName(args.path);
@@ -259,7 +255,7 @@ export function createReadEnclosingTool(getProjectRoot, recordSymbolRead) {
259
255
  return {
260
256
  name: "read_enclosing",
261
257
  label: "Read Enclosing",
262
- description: "Return the verbatim source for the smallest useful symbol/callback enclosing a line in a file. Use after ast_grep_search, diagnostics, or LSP locations when you need exact body text without reading the whole file. Uses tree-sitter only — no LSP or graph build — and records read-guard coverage for the returned range.",
258
+ description: "Return the smallest symbol or callback enclosing a line. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. Example: use read_enclosing after a diagnostic points to line 42.",
263
259
  promptSnippet: "Read the enclosing symbol or callback body for a line",
264
260
  renderResult: compactRenderResult(({ details, args, isError }) => {
265
261
  const base = baseName(args.path);
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { Type } from "../clients/deps/typebox.js";
11
11
  import { projectReport, renderCompactProjectReport, } from "../clients/lens-engine.js";
12
- import { compactRenderResult } from "./render-compact.js";
12
+ import { compactRenderResult, renderToolText, } from "./render-compact.js";
13
13
  function errorMessage(err) {
14
14
  return err instanceof Error ? err.message : String(err);
15
15
  }
@@ -17,9 +17,8 @@ export function createProjectReportTool(getProjectRoot) {
17
17
  return {
18
18
  name: "project_report",
19
19
  label: "Project Report",
20
- description: "Project-level orientation from the review graph — 'orient me in this project' before drilling into any one file. First step of a wider discovery funnel: project_report orients, module_report explains a file, read_symbol reads a body. Six capped, ranked sections: a trust header (graph freshness, file coverage, edge-resolution-quality mix), hubs (top fan-in files — the repo's contract surface), entry points (near-zero fan-in / high fan-out files — activation/CLI/mains), a directory-level subsystem map (import cycles + layering violations, e.g. a forbidden clients/ -> tools/ edge), risk hotspots (fan-in × max per-symbol cyclomatic complexity), and suspected dead weight (zero-importer files, shipped with a low-confidence disclaimer — dynamic imports/runtime registration/test-only reachability all produce false positives). Every file line carries a `suggestedNext` module_report call. No per-symbol detail and no prose summary — structural facts only. Read-only over the cached graph: returns `available: false` with a retry hint on a cold cache and kicks off a background build (never blocks this call).\n" +
21
- '`view: "compact"` returns a line-oriented text rendering instead of JSON (cheapest option); default view returns JSON. Pass `focus` to re-rank every section toward a task hint (does not expand scope).',
22
- promptSnippet: "Project-level orientation from the review graph",
20
+ description: "Orient in a project from its review graph, with ranked hubs and entry points. On a cold cache, project_report and symbol_search return available: false with a retry hint and start a non-blocking background build; module_report degrades to outline-only with cache freshness explicit. Example: use project_report before module_report when the target file is unknown.",
21
+ promptSnippet: "Orient in a project before choosing a file",
23
22
  renderResult: compactRenderResult(({ details, isError }) => {
24
23
  if (isError || details?.available === false) {
25
24
  return `project_report — unavailable${details?.hint ? `: ${details.hint}` : ""}`;
@@ -28,7 +27,9 @@ export function createProjectReportTool(getProjectRoot) {
28
27
  `${details?.hubs ?? 0} hub(s)`,
29
28
  `${details?.entryPoints ?? 0} entry point(s)`,
30
29
  ];
31
- const view = details?.view && details.view !== "default" ? ` [${details.view}]` : "";
30
+ const view = details?.view && details.view !== "default"
31
+ ? ` [${details.view}]`
32
+ : "";
32
33
  return `project_report ${parts.join(" · ")}${view}`;
33
34
  }),
34
35
  parameters: Type.Object({
@@ -55,24 +56,14 @@ export function createProjectReportTool(getProjectRoot) {
55
56
  }
56
57
  catch (err) {
57
58
  return {
58
- content: [
59
- {
60
- type: "text",
61
- text: `Project report failed: ${errorMessage(err)}`,
62
- },
63
- ],
59
+ ...renderToolText(`Project report failed: ${errorMessage(err)}`),
64
60
  isError: true,
65
61
  details: { available: false },
66
62
  };
67
63
  }
68
64
  if (!report.available) {
69
65
  return {
70
- content: [
71
- {
72
- type: "text",
73
- text: report.hint ?? "No review graph cached for this workspace yet.",
74
- },
75
- ],
66
+ ...renderToolText(report.hint ?? "No review graph cached for this workspace yet.", report),
76
67
  isError: true,
77
68
  details: { available: false, hint: report.hint },
78
69
  };
@@ -82,6 +73,7 @@ export function createProjectReportTool(getProjectRoot) {
82
73
  : JSON.stringify(report);
83
74
  return {
84
75
  content: [{ type: "text", text }],
76
+ isError: false,
85
77
  details: {
86
78
  available: true,
87
79
  hubs: report.hubs?.length ?? 0,