@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
@@ -13,6 +13,7 @@ import { getKnipIgnorePatterns } from "./file-utils.js";
13
13
  import { formatCacheAgeLabel } from "./finding-delivery-gate.js";
14
14
  import { getFullScanWallClockMs, isWorkspaceSweepActive, runWhenWorkspaceSweepIdle, SWEEP_IDLE_SAFETY_MARGIN_MS, } from "./lsp/workspace-sweep-hold.js";
15
15
  import { isTestRoleCollateral } from "./collateral-test-role.js";
16
+ import { classifyAndFilterFindings, } from "./gitleaks-client.js";
16
17
  import { dedupeSecretFindings, fromAstGrepWarnings, fromGitleaks, fromTrivySecrets, isSecretWarning, secretLocationKey, } from "./secret-findings.js";
17
18
  import { deadCodeIssueKey, deadCodeIssues, formatDeadCodeDelta, stableFindingKey, } from "./dead-code-client.js";
18
19
  import { logDeadCodeScan } from "./dead-code-logger.js";
@@ -49,6 +50,8 @@ import { emitBounded } from "./bounded-telemetry.js";
49
50
  import { degradeDemotedFindingBody, formatDeliveryCapNote, formatRetirementNote, } from "./demoted-finding-render.js";
50
51
  import { STALE_LINE_MARKER } from "./stale-marker.js";
51
52
  import { getActiveSessionId } from "./session-lifecycle.js";
53
+ import { bounded } from "./deadline-utils.js";
54
+ import { HOOK_WALL_BUDGET_MS } from "./hook-budgets.js";
52
55
  import { drainRenderedDependencyDriftFilePaths, getWidgetBlockingFilesForSweep, incrementWidgetDependencyDriftDelivery, markWidgetFileBlockersStale, recordRunner, retireWidgetDependencyDriftBlockers, } from "./widget-state.js";
53
56
  /** Maximum detailed notify-stall coverage-gap rows emitted in one turn. */
54
57
  const LATE_AUX_COVERAGE_GAP_DETAIL_CAP_PER_TURN = 20;
@@ -1483,6 +1486,26 @@ export async function handleTurnEnd(deps) {
1483
1486
  // provenance) — a blocker, since credentials need rotation before merge.
1484
1487
  const gitleaksData = cacheManager.readCache("gitleaks", cwd)?.data;
1485
1488
  const trivySecretsData = trivyCacheEntry?.data;
1489
+ // Gitleaks deliberately scans gitignored local files and nested repositories
1490
+ // so an explicit security audit can still inspect them. The adapter is the
1491
+ // source of truth for whether a finding belongs in a blocking delivery lane;
1492
+ // filter here before freshness handling so demoted findings cannot leak into
1493
+ // either the blocker or stale-secret turn context.
1494
+ const boundedClassification = await bounded(classifyAndFilterFindings(gitleaksData?.findings ?? [], cwd), {
1495
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
1496
+ signal: deps.signal,
1497
+ hook: "turn_end",
1498
+ label: "classifyAndFilterFindings",
1499
+ });
1500
+ const classifiedGitleaksFindings = boundedClassification ?? gitleaksData?.findings ?? [];
1501
+ if (boundedClassification === undefined) {
1502
+ recordDegradationOnce({
1503
+ kind: "gitleaks_classification_timeout",
1504
+ subject: cwd,
1505
+ reason: "gitleaks classification exceeded the turn_end budget; retained raw findings to fail open",
1506
+ });
1507
+ }
1508
+ const blockingGitleaksFindings = classifiedGitleaksFindings.filter((finding) => gitleaksFindingToProjectDiagnostic(cwd, finding).semantic === "blocking");
1486
1509
  // #1461 slice 1 (#1460): the gitleaks cache is TTL-only, so a finding for a
1487
1510
  // file deleted after the scan is still served as a 🔴 blocker for the rest
1488
1511
  // of the 30-minute window — the live case, and 119 of 126 findings in
@@ -1497,7 +1520,7 @@ export async function handleTurnEnd(deps) {
1497
1520
  // would let any edit — malicious or accidental — mute a real secret.
1498
1521
  const gitleaksGate = gateFindingsByPathFreshness({
1499
1522
  store: "gitleaks",
1500
- findings: gitleaksData?.findings ?? [],
1523
+ findings: blockingGitleaksFindings,
1501
1524
  cwd,
1502
1525
  scannedAt: gitleaksData?.scannedAt,
1503
1526
  citedPath: (finding) => finding.file,
@@ -2882,6 +2905,7 @@ export async function handleTurnEnd(deps) {
2882
2905
  let lateAuxCoverageGapDropCount = 0;
2883
2906
  const lateAuxStuckPairs = [];
2884
2907
  if (drainedPairs.length > 0) {
2908
+ const lateObserverDeadline = Date.now() + HOOK_WALL_BUDGET_MS.turn_end;
2885
2909
  const byFile = new Map();
2886
2910
  for (const pair of drainedPairs) {
2887
2911
  const list = byFile.get(pair.filePath);
@@ -2993,6 +3017,19 @@ export async function handleTurnEnd(deps) {
2993
3017
  }
2994
3018
  continue;
2995
3019
  }
3020
+ // A demoted auxiliary still answers through this late path. Feed the
3021
+ // publication-minus-mark interval into the re-promotion streak. This is
3022
+ // delivery latency observed by the drain, not the scanner's total scan
3023
+ // latency. Cache priming is
3024
+ // below the freshness gate so a changed file cannot resurrect stale data.
3025
+ if (typeof service.observeLateAuxiliaryAnswer === "function") {
3026
+ await bounded(service.observeLateAuxiliaryAnswer(lateAuxPath, pair.serverId, cachedEntry.publishedAt - pair.markedAtMs), {
3027
+ ms: Math.max(1, lateObserverDeadline - Date.now()),
3028
+ signal: deps.signal /* late observer */,
3029
+ hook: "turn_end",
3030
+ label: "observeLateAuxiliaryAnswer",
3031
+ });
3032
+ }
2996
3033
  if (rawDiags.length === 0) {
2997
3034
  lateAuxCleanConfirmed += 1;
2998
3035
  continue;
@@ -3045,6 +3082,16 @@ export async function handleTurnEnd(deps) {
3045
3082
  }
3046
3083
  continue;
3047
3084
  }
3085
+ // #2810 round 4: this drain does NOT write the hash-bound
3086
+ // last-known record. The prime it used to call could only fire when
3087
+ // a record already existed at the pair's hash — which requires a
3088
+ // FULLY covered touch of those exact bytes, the one case where the
3089
+ // scanner's findings are already in the record — so it was a no-op
3090
+ // in the demoted steady state it was added for, and a #570/#1470
3091
+ // hazard everywhere else (an auxiliary-only array replacing the
3092
+ // merged one). Late findings reach the agent as the gated advisory
3093
+ // below; the turn-end hash-guarded fast path stays cold for a file
3094
+ // whose touch was partial, which is exactly what #1470 requires.
3048
3095
  const lines = gate.live.map((f) => ` ${displayLateAuxPath}:${f.line}:${f.column} [${f.rule}] ${f.message}`);
3049
3096
  lateAuxDelivered += gate.live.length;
3050
3097
  lateAuxAnswered += 1;
@@ -43,17 +43,6 @@ const DETAIL_PATTERNS = [
43
43
  export function stripAnsi(text) {
44
44
  return text.replace(ANSI_ESCAPE, "").replace(ANSI_ESCAPE_EXTENDED, "");
45
45
  }
46
- /**
47
- * Normalize whitespace in a string.
48
- * Collapses multiple spaces/tabs to single space, trims lines.
49
- */
50
- export function normalizeWhitespace(text) {
51
- return text
52
- .split(/\r?\n/)
53
- .map((line) => line.replace(/\s+/g, " ").trim())
54
- .filter((line) => line.length > 0)
55
- .join("\n");
56
- }
57
46
  /**
58
47
  * Check if a line contains error indicators.
59
48
  */
@@ -1,59 +1,4 @@
1
- import { isExcludedDirName, isTestFile } from "./file-utils.js";
2
- import { collectSourceFiles, collectSourceFilesAsync, isBuildArtifact, } from "./source-filter.js";
3
- /**
4
- * Common parsing logic for ast-grep JSON output (handles both array and NDJSON).
5
- */
6
- // biome-ignore lint/suspicious/noExplicitAny: ast-grep JSON output is untyped
7
- export function parseAstGrepJson(raw) {
8
- if (!raw)
9
- return [];
10
- const trimmed = raw.trim();
11
- if (trimmed.startsWith("[")) {
12
- try {
13
- return JSON.parse(trimmed);
14
- }
15
- catch {
16
- return [];
17
- }
18
- }
19
- return trimmed.split("\n").flatMap((l) => {
20
- try {
21
- return [JSON.parse(l)];
22
- }
23
- catch {
24
- return [];
25
- }
26
- });
27
- }
28
- /**
29
- * Check if a file should be ignored based on project type and common patterns.
30
- *
31
- * @deprecated Use `isBuildArtifact()` from `source-filter.js` instead for artifact
32
- * detection, or compose your own filter using `collectSourceFiles()`. This function
33
- * is kept for backward compatibility.
34
- */
35
- export function shouldIgnoreFile(filePath, isTsProject) {
36
- const relPath = filePath.replace(/\\/g, "/");
37
- // Use new source-filter module for artifact detection
38
- if (isTsProject && isBuildArtifact(filePath))
39
- return true;
40
- // Legacy: simple JS check for non-TS projects (hand-written JS)
41
- const isJs = relPath.endsWith(".js") ||
42
- relPath.endsWith(".mjs") ||
43
- relPath.endsWith(".cjs");
44
- if (isTsProject && isJs)
45
- return true;
46
- // Ignore test scripts and common test patterns
47
- if (isTestFile(filePath))
48
- return true;
49
- // Ignore hidden directories and common build outputs
50
- const pathParts = relPath.split("/").filter(Boolean);
51
- for (const segment of pathParts.slice(0, -1)) {
52
- if (isExcludedDirName(segment))
53
- return true;
54
- }
55
- return false;
56
- }
1
+ import { collectSourceFiles, collectSourceFilesAsync, } from "./source-filter.js";
57
2
  /**
58
3
  * Recursively find source files in a directory, respecting common excludes.
59
4
  *
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import * as path from "node:path";
16
16
  import { createSubsystemLogger } from "./extension-log.js";
17
- import { safeSpawnAsync } from "./safe-spawn.js";
17
+ import { probeToolAsync } from "./tool-probe.js";
18
18
  import { classifyProbeFailure, createAvailabilityLatch, describeInstallAttempt, describeProbeEvidence, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
19
19
  import { createAvailabilityProbeFlight } from "./availability-probe-flight.js";
20
20
  import { createSingleFlight } from "./single-flight.js";
@@ -132,7 +132,7 @@ export class SecurityScanClient {
132
132
  const { findManagedToolBinary } = await import("./installer/index.js");
133
133
  const managed = await findManagedToolBinary(this.toolName);
134
134
  return {
135
- probe: await safeSpawnAsync(managed ?? this.toolName, versionArgs, {
135
+ probe: await probeToolAsync(managed ?? this.toolName, versionArgs, {
136
136
  timeout: 5000,
137
137
  }),
138
138
  binaryPath: managed ?? null,
@@ -64,7 +64,22 @@
64
64
  * wrapper narrows nothing else: a handler bug must stay as loud as it was.
65
65
  */
66
66
  import { emitBounded } from "./bounded-telemetry.js";
67
+ import { recordDegradationOnce } from "./degradation-ledger.js";
67
68
  import { probeCtxActive } from "./session-lifecycle.js";
69
+ import { runWithTurnContext } from "./turn-context.js";
70
+ function stableSessionId(ctx) {
71
+ try {
72
+ return ctx?.sessionManager?.getSessionId?.();
73
+ }
74
+ catch {
75
+ recordDegradationOnce({
76
+ kind: "turn-context-identity-fallback",
77
+ subject: "session-event-guard",
78
+ reason: "stable session identity resolution failed; using detached turn context",
79
+ });
80
+ return undefined;
81
+ }
82
+ }
68
83
  /**
69
84
  * The pi SDK invalidates a captured `pi`/command ctx after a session
70
85
  * replacement or reload; every later `pi.*` call then throws with this
@@ -99,6 +114,59 @@ function recordStaleSkip(eventName, detectedAt) {
99
114
  reason: `${eventName} skipped: extension ctx is stale (${detectedAt})`,
100
115
  });
101
116
  }
117
+ /**
118
+ * The one policy for "a pi hook handler threw and production swallows it"
119
+ * (#2884).
120
+ *
121
+ * Nine catch sites in `index.ts` absorb a crashed handler so a pi-lens bug
122
+ * can never take down the host's session. Each of them used to write only
123
+ * `dbg(...)`, and `dbg` writes nothing under vitest — so a crashed handler was
124
+ * indistinguishable from a completed one. #2859 is what that costs: fourteen
125
+ * `session_start` awaits in `tests/index-integration.test.ts` rejected into
126
+ * one of those catches, every assertion after them was vacuous, and the file
127
+ * stayed green. #2866 closed the hole for `session_start` with an inline
128
+ * `if (process.env.VITEST) throw`; this function is that guard folded into one
129
+ * place so the remaining eight cannot drift from it.
130
+ *
131
+ * Two things happen on every crash, in this order:
132
+ *
133
+ * 1. **The production record.** One bounded `hook-handler-crash` row per
134
+ * handler per session (`recordDegradationOnce`), so a handler that crashes
135
+ * on every turn leaves one durable row and an exact ledger tally instead of
136
+ * a silent no-op. It is written BEFORE the rethrow, so a test can assert the
137
+ * production observability the runner path would otherwise hide.
138
+ * 2. **The runner rethrow.** Under vitest the crash is rethrown, so the test
139
+ * whose `await` caused it fails with the real error instead of resolving.
140
+ * In production nothing is rethrown and the caller's swallow stands.
141
+ *
142
+ * Deliberately `process.env.VITEST` and not `isTestMode()` (kept from #2859):
143
+ * the question is whether a TEST is awaiting this handler, and the #2815 R7
144
+ * case runs under vitest with `PI_LENS_TEST_MODE=0`.
145
+ *
146
+ * The stale-ctx class is NOT this function's business. Callers that classify
147
+ * it (`session_start`, `agent_end`, `turn_end`, the `agent_settled` drain)
148
+ * rethrow `isStaleExtensionCtxError` themselves BEFORE calling in, so a benign
149
+ * session swap keeps its own single record and never lands here as a crash.
150
+ * The quiet-window site is intentionally the exception to the test-runner
151
+ * rethrow: it is fire-and-forget, so a rethrow would be an unhandled rejection
152
+ * that can terminate the pi host before its caller can observe the failure.
153
+ */
154
+ export function surfaceHandlerCrash(handler, err, options = {}) {
155
+ try {
156
+ options.dbg?.(`${handler} crashed: ${err}`);
157
+ options.dbg?.(`${handler} crash stack: ${err?.stack}`);
158
+ }
159
+ catch {
160
+ // A debug sink must never decide whether a crash is recorded.
161
+ }
162
+ recordDegradationOnce({
163
+ kind: "hook-handler-crash",
164
+ subject: handler,
165
+ reason: `${handler} handler crashed and was swallowed: ${String(err)}`,
166
+ });
167
+ if (process.env.VITEST && options.rethrow !== false)
168
+ throw err;
169
+ }
102
170
  function isThenable(value) {
103
171
  return (typeof value?.then === "function");
104
172
  }
@@ -131,7 +199,7 @@ function guardSessionEvent(eventName, handler, onStaleResult, options) {
131
199
  // below.
132
200
  return skip(event, "pre-dispatch");
133
201
  try {
134
- const result = handler(event, ctx);
202
+ const result = runWithTurnContext(stableSessionId(ctx), () => handler(event, ctx));
135
203
  if (isThenable(result)) {
136
204
  // Recover the rejection in place. The host awaits the same promise
137
205
  // it would have awaited anyway; it just resolves instead.
@@ -1,30 +1,6 @@
1
1
  /**
2
2
  * Session Summary — generates turn_end and session-end summaries
3
3
  */
4
- export function formatSessionSummary(stats) {
5
- if (stats.totalShown === 0)
6
- return "";
7
- const lines = [
8
- "## Diagnostic Summary",
9
- "",
10
- `Shown to agent: ${stats.totalShown}`,
11
- `Auto-fixed: ${stats.totalAutoFixed} (${pct(stats.totalAutoFixed, stats.totalShown)}%)`,
12
- `Fixed by agent: ${stats.totalAgentFixed} (${pct(stats.totalAgentFixed, stats.totalShown)}%)`,
13
- "",
14
- ];
15
- if (stats.topViolations.length > 0) {
16
- lines.push("Top violations:");
17
- for (const { ruleId, count } of stats.topViolations.slice(0, 5)) {
18
- lines.push(` - ${ruleId} (${count}x)`);
19
- }
20
- }
21
- return lines.join("\n");
22
- }
23
- function pct(part, total) {
24
- if (total === 0)
25
- return "0";
26
- return Math.round((part / total) * 100).toString();
27
- }
28
4
  export function formatSlopScoreSummary(summary) {
29
5
  if (summary.totalRuleDiagnostics === 0 || summary.totalKlocWritten <= 0) {
30
6
  return "";
@@ -12,6 +12,7 @@ import { getSgCommand, resolveManagedToolClient, } from "./dispatch/runners/util
12
12
  import { getProjectIgnoreGlobs } from "./file-utils.js";
13
13
  import { findGlobalBinary } from "./package-manager.js";
14
14
  import { safeSpawnAsync } from "./safe-spawn.js";
15
+ import { probeToolAsync } from "./tool-probe.js";
15
16
  import { truncatedByOutputCap } from "./spawn-output-cap.js";
16
17
  import { createSingleFlight } from "./single-flight.js";
17
18
  import { classifyProbeFailure, describeInstallAttempt, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
@@ -459,7 +460,7 @@ export class SgRunner {
459
460
  let result;
460
461
  let hostStallMs;
461
462
  try {
462
- result = await safeSpawnAsync(cmd, [...argsPrefix, "--version"], {
463
+ result = await probeToolAsync(cmd, [...argsPrefix, "--version"], {
463
464
  timeout: PROBE_TIMEOUT_MS,
464
465
  });
465
466
  }
@@ -8,7 +8,7 @@ import { findLocalToolConfig } from "./path-utils.js";
8
8
  // ast-grep's root config marker. The `ast-grep lsp` server is workspace-gated:
9
9
  // it only operates in a project that has an `sgconfig.y[a]ml` at (or above) the
10
10
  // file. These are the names ast-grep itself looks for as root markers.
11
- export const SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
11
+ const SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
12
12
  /**
13
13
  * Nearest `sgconfig.y[a]ml` walking up from `startDir`, or undefined if none.
14
14
  * Deliberately UNCEILINGED at $HOME (refs #2472 review round 3, F1): ast-grep
@@ -65,10 +65,6 @@ export function getAstGrepRuleSources(projectRoot = process.cwd()) {
65
65
  return true;
66
66
  });
67
67
  }
68
- /** Compatibility export retained for callers that only need ordered dirs. */
69
- export function shippedRuleDirsInPrecedenceOrder(projectRoot = process.cwd()) {
70
- return getAstGrepRuleSources(projectRoot).map((source) => source.dir);
71
- }
72
68
  function findYamlFiles(dir) {
73
69
  const files = [];
74
70
  let entries;
@@ -115,7 +115,7 @@ function isCleanDryRun(argsAfterVerb) {
115
115
  * replacement for the forbidden `git stash`. Blocking it would push
116
116
  * operators back toward stash.
117
117
  */
118
- export const WORKTREE_MUTATING_GIT_MATCHER = {
118
+ const WORKTREE_MUTATING_GIT_MATCHER = {
119
119
  id: "worktree-mutating",
120
120
  indirectAlwaysMatches: false,
121
121
  // `git checkout --help` opens documentation and changes nothing. This
@@ -0,0 +1,108 @@
1
+ import { logExtension } from "./extension-log.js";
2
+ import { TOOL_REGISTRY } from "./tool-config.js";
3
+ const situationalTools = TOOL_REGISTRY.filter((tool) => "situational" in tool && tool.situational === true).map((tool) => tool.name);
4
+ const situationalToolSet = new Set(situationalTools);
5
+ const activated = new Set();
6
+ const called = new Set();
7
+ let sessionStarted = false;
8
+ let emitted = false;
9
+ // MCP-only connection-terminal latch: once the connection's row is recorded,
10
+ // repeated initialize/tool-call starts must not reopen the session.
11
+ let connectionEnded = false;
12
+ // Which host owns the open session; only MCP may arm connectionEnded. Pi state
13
+ // belongs to the conversation and survives in-process session rebuilds.
14
+ let sessionHost = "mcp";
15
+ function observe(set, name) {
16
+ if (situationalToolSet.has(name)) {
17
+ set.add(name);
18
+ }
19
+ }
20
+ function clearObservations() {
21
+ activated.clear();
22
+ called.clear();
23
+ }
24
+ export function observeSituationalToolActivation(names) {
25
+ for (const name of names)
26
+ observe(activated, name);
27
+ }
28
+ export function observeSituationalToolCall(name) {
29
+ observe(called, name);
30
+ }
31
+ export function resetSituationalToolTelemetry() {
32
+ // A live telemetry session owns its observation sets: both hosts open the
33
+ // session (which resets the sets) BEFORE handleSessionStart runs, and a
34
+ // repeated MCP session_start refresh legitimately re-runs that handler —
35
+ // clearing here would wipe the calls recorded before the refresh. This
36
+ // registered reset therefore only acts when no session is open.
37
+ if (sessionStarted)
38
+ return;
39
+ clearObservations();
40
+ emitted = false;
41
+ }
42
+ /**
43
+ * Open the telemetry session for one host.
44
+ *
45
+ * Pi owns one observation set per session file. A shutdown with a
46
+ * `targetSessionFile` emits the conversation that is ending before the host
47
+ * opens the replacement. Reload keeps the same file, so its factory rebuild
48
+ * does not split the row. A process restart opens an empty set and recovers nothing:
49
+ * pi-lens's own activation memory (`rememberedLazyTools` in `index.ts`) is
50
+ * empty in a new process, so the restore deactivates every situational tool,
51
+ * and the host's restored active set is evidence of REGISTRATION, not of
52
+ * model activation (#2866 review F1). MCP remains connection-scoped.
53
+ */
54
+ export function startSituationalToolTelemetrySession(host) {
55
+ if (host === "mcp") {
56
+ if (connectionEnded)
57
+ return;
58
+ if (sessionStarted)
59
+ return;
60
+ sessionHost = "mcp";
61
+ clearObservations();
62
+ emitted = false;
63
+ sessionStarted = true;
64
+ return;
65
+ }
66
+ if (sessionStarted) {
67
+ sessionHost = "pi";
68
+ return;
69
+ }
70
+ sessionHost = "pi";
71
+ clearObservations();
72
+ emitted = false;
73
+ sessionStarted = true;
74
+ }
75
+ /** Emit the one session-end row and make repeated shutdown calls harmless. */
76
+ export function endSituationalToolTelemetry() {
77
+ if (!sessionStarted)
78
+ return;
79
+ emitSituationalDeadWeight();
80
+ if (sessionHost === "mcp")
81
+ connectionEnded = true;
82
+ clearObservations();
83
+ emitted = false;
84
+ sessionStarted = false;
85
+ }
86
+ export function emitSituationalDeadWeight() {
87
+ if (emitted)
88
+ return;
89
+ emitted = true;
90
+ const used = new Set([...activated, ...called]);
91
+ logExtension({
92
+ subsystem: "tools",
93
+ level: "debug",
94
+ message: "situational tool dead weight",
95
+ metadata: {
96
+ tools: situationalTools.filter((name) => !used.has(name)),
97
+ },
98
+ });
99
+ }
100
+ /** Test-only view of the module's state, for the #1635 session-state registry probe. */
101
+ export function _getSituationalToolTelemetryStateForTests() {
102
+ return {
103
+ activated: activated.size,
104
+ called: called.size,
105
+ emitted,
106
+ sessionStarted,
107
+ };
108
+ }
@@ -58,11 +58,11 @@ import * as path from "node:path";
58
58
  import { getGlobalPiLensLogDir } from "./probe-home-state.js";
59
59
  /** Bounded tail-read budget PER source log file — never a full-file scan. */
60
60
  export const SMELLS_TAIL_BYTES_PER_FILE = 64 * 1024;
61
- export const SMELLS_ROLLING_WINDOW_MS = 24 * 60 * 60_000;
61
+ const SMELLS_ROLLING_WINDOW_MS = 24 * 60 * 60_000;
62
62
  /** Re-check cadence at `turn_end`, mirroring `memory-sampler.ts`'s pattern —
63
63
  * cheap enough (bounded ~128KB I/O) not to need finer throttling, but a
64
64
  * fixed interval keeps it from running on every single turn. */
65
- export const SMELLS_TURN_CHECK_INTERVAL = 20;
65
+ const SMELLS_TURN_CHECK_INTERVAL = 20;
66
66
  /** Trivial threshold constants (#1123 item 3's explicit design ask). Counts
67
67
  * strictly below these never surface anywhere. */
68
68
  export const SMELLS_THRESHOLDS = {
@@ -16,7 +16,7 @@ import { isAtOrAboveHomeDir } from "./path-utils.js";
16
16
  import { getStartupScanMaxSourceFilesDerived } from "./project-scale.js";
17
17
  import { shouldRecurseIntoDir, walkTreeStackAsync, walkTreeStackSync, } from "./source-walker.js";
18
18
  import { getDirectoryMarkers, registerWorkspaceTopologyReset, } from "./workspace-topology.js";
19
- export const PROJECT_ROOT_MARKERS = [
19
+ const PROJECT_ROOT_MARKERS = [
20
20
  ".git",
21
21
  "package.json",
22
22
  "pyproject.toml",
@@ -24,16 +24,8 @@ export const PROJECT_ROOT_MARKERS = [
24
24
  "go.mod",
25
25
  "composer.json",
26
26
  ];
27
- // Deprecated (#776): no longer read directly below — `computeStartupScanContext`
28
- // / `resolveStartupScanContextAsync` now default `maxSourceFiles` to
29
- // `getStartupScanMaxSourceFilesDerived(cwd)` (project-scale.ts's
30
- // `maxProjectFiles` knob), which reproduces this same 2,000 value at the
31
- // default base. Kept exported for tests/callers that still reference the
32
- // literal.
33
- export const MAX_STARTUP_SOURCE_FILES = 2000;
34
27
  // #758: hard ceiling on the number of directory entries the startup source
35
28
  // count walk will visit before it gives up and declares the tree too big to
36
- // warm. The source-file early-exit (MAX_STARTUP_SOURCE_FILES) only fires when
37
29
  // a project has MANY source files — a repo with FEW source files but a huge
38
30
  // pile of non-source files (e.g. a game mod: 300 scripts among 84k data files)
39
31
  // never trips it, so the pre-#758 walk traversed the entire tree, dominated by
@@ -77,7 +69,7 @@ export const _resetStartupScanMaxEntriesForTests = _maxEntries._resetForTests;
77
69
  *
78
70
  * The content-derived reasons `too-many-source-files` and `too-many-entries`
79
71
  * (#758) are the ones that are TTL'd. They can go stale on their own: the repo
80
- * can shrink below `MAX_STARTUP_SOURCE_FILES` (or below the entry ceiling)
72
+ * can shrink below the source-file cap (or below the entry ceiling)
81
73
  * between sessions, and nothing else would notice
82
74
  * — the seq-based freshness check that guards every other
83
75
  * `project-snapshot.json` field never fires for them, because pi-lens never