@herbertgao/pi-extensions 2026.9.0 → 2026.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  2. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  11. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  12. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  13. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  14. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  15. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  16. package/node_modules/pi-lens/README.md +3 -0
  17. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  18. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  19. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  22. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  23. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  24. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  25. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  26. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  27. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  28. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  29. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  30. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  31. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  32. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  33. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  34. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  35. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  36. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  37. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  38. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  39. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  40. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  41. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  42. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  43. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  44. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  45. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  46. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  47. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  48. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  49. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  50. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  51. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  52. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  53. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  54. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  55. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  56. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  58. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  59. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  60. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  63. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  64. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  122. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  123. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  124. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  125. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  126. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  128. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  129. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  130. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  131. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  132. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  133. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  134. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  135. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  136. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  137. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  138. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  139. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  140. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  141. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  142. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  143. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  144. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  145. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  146. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  147. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  148. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  149. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  150. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  151. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  153. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  154. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  155. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  157. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  158. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  159. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  160. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  162. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  163. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  164. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  165. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  167. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  168. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  171. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  172. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  173. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  174. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  175. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  177. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  178. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  179. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  180. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  181. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  182. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  183. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  184. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  185. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  186. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  187. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  188. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  189. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  190. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  191. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  195. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  196. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  197. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  198. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  199. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  200. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  201. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  203. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  204. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  205. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  206. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  207. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  208. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  209. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  210. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  211. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  212. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  213. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  214. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  215. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  216. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  217. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  218. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  219. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  220. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  221. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  222. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  223. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  225. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  226. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  227. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  228. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  229. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  230. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  231. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  232. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  233. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  234. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  241. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  242. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  243. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  244. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  245. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  246. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  247. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  249. package/node_modules/pi-lens/dist/index.js +64335 -54300
  250. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  251. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  252. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  253. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  254. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  255. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  256. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  257. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  258. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  259. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  260. package/node_modules/pi-lens/docs/configuration.md +222 -0
  261. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  262. package/node_modules/pi-lens/docs/features.md +44 -5
  263. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  264. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  265. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  266. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  267. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  268. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  269. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  270. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  271. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  272. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  273. package/node_modules/pi-lens/package.json +15 -23
  274. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  275. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  276. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  277. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  278. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  279. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  280. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  281. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  282. package/node_modules/pi-web-access/README.md +62 -15
  283. package/node_modules/pi-web-access/curator-page.ts +3 -1
  284. package/node_modules/pi-web-access/curator-server.ts +5 -1
  285. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  286. package/node_modules/pi-web-access/github-extract.ts +242 -1
  287. package/node_modules/pi-web-access/index.ts +118 -64
  288. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  289. package/node_modules/pi-web-access/package.json +2 -2
  290. package/node_modules/pi-web-access/perplexity.ts +14 -1
  291. package/node_modules/pi-web-access/utils.ts +23 -6
  292. package/node_modules/pi-web-access/xai-search.ts +96 -33
  293. package/package.json +5 -5
@@ -34,7 +34,8 @@ import { clearGraphCache } from "./review-graph/builder.js";
34
34
  import { BoundedLruCache } from "./bounded-cache.js";
35
35
  import { RUNTIME_CONFIG } from "./runtime-config.js";
36
36
  import { getAmbientAbortSignal, safeSpawnAsync } from "./safe-spawn.js";
37
- import { combineAbortSignals } from "./deadline-utils.js";
37
+ import { bounded } from "./deadline-utils.js";
38
+ import { enabledAuxiliaryLspServerIds } from "./dispatch/auxiliary-lsp.js";
38
39
  import { recordDegradationOnce } from "./degradation-ledger.js";
39
40
  import { dropFindingsForMissingPaths } from "./advisory-provenance.js";
40
41
  import { getAutofixPolicyForFile, getPreferredAutofixTools, getRubocopCommand, hasBiomeConfig, hasDetektConfig, hasEslintConfig, hasGolangciConfig, hasKtfmtConfig, hasKtlintConfig, hasOxlintConfig, hasRubocopConfig, hasSqlfluffConfig, hasStylelintConfig, markdownlintConfigArgs, } from "./tool-policy.js";
@@ -687,7 +688,6 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
687
688
  const abort = getAmbientAbortSignal();
688
689
  if (abort?.aborted)
689
690
  return;
690
- const bail = combineAbortSignals(abort, AbortSignal.timeout(budgetMs));
691
691
  const startedAt = Date.now();
692
692
  const touch = lspService
693
693
  .touchFile(filePath, fileContent, {
@@ -701,13 +701,45 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
701
701
  dbg(`LSP resync after autofix error: ${err}`);
702
702
  return "done";
703
703
  });
704
- const bailed = new Promise((resolve) => {
705
- if (!bail || bail.aborted)
706
- return resolve("bailed");
707
- bail.addEventListener("abort", () => resolve("bailed"), { once: true });
704
+ // #2540: Kick off auxiliary server acquisition concurrently and unawaited
705
+ // with the ambient turn signal so auxiliary warmup overlaps with the primary
706
+ // server during resync. Leaves the single-occupancy deferred slot (#2509)
707
+ // free for actionable-warnings while Escape mid-turn abandons the wait.
708
+ //
709
+ // #2582 F3 — the #1766 F3 invariant re-opened for a different method.
710
+ // #2540 placed this kick-off BEFORE the primary touch, inside
711
+ // resyncLspFile's swallow-all catch, so anything that threw out of
712
+ // auxiliary acquisition abandoned the PRIMARY sync before it started:
713
+ // no touchFile, and no lsp_sync_abandoned record for the stall.
714
+ // Auxiliary warmup is a best-effort OVERLAP, never a precondition:
715
+ // the primary touch is already in flight above, and the async IIFE
716
+ // turns a synchronous throw out of the call into a rejection so
717
+ // `.catch` covers both failure directions. Order and containment are
718
+ // the invariant; do not hoist this back above the touch.
719
+ const auxServerIds = enabledAuxiliaryLspServerIds(getFlag);
720
+ if (auxServerIds.length > 0) {
721
+ void (async () => lspService.getAuxiliaryClientsForFile(filePath, new Set(auxServerIds), undefined, LSP_SPAWN_BUDGET_MS, abort, "tool_result_edit"))().catch(() => { });
722
+ }
723
+ // #2523 slice 2: this was `combineAbortSignals(abort,
724
+ // AbortSignal.timeout(budgetMs))` fed into a hand-rolled
725
+ // `Promise.race` — a private fifth copy of "deadline AND signal".
726
+ // `bounded()` is the same thing with the abandonment recorded, and
727
+ // without the composite signal that #2530 review F4 measured
728
+ // accumulating on the SOURCE.
729
+ //
730
+ // `touch` never rejects (it catches above) and never resolves
731
+ // `undefined`, so `undefined` here means exactly "a bound fired".
732
+ const outcome = await bounded(touch, {
733
+ ms: budgetMs,
734
+ // The ambient turn signal: set for the whole tool_result path and
735
+ // absent only in a bare unit harness, where the wall budget is
736
+ // still live (`bounded()` reads a missing signal as one that
737
+ // never aborts).
738
+ signal: abort,
739
+ hook: "tool_result_edit",
740
+ label: "resyncLspFile",
708
741
  });
709
- const outcome = await Promise.race([touch, bailed]);
710
- if (outcome === "bailed") {
742
+ if (outcome === undefined) {
711
743
  // Abandon the still-pending write; the edit continues. Log it so this
712
744
  // stall — previously an invisible hang — is queryable in latency.log.
713
745
  //
@@ -717,12 +749,18 @@ export async function resyncLspFile(filePath, fileContent, needsContentRefresh,
717
749
  // old wording blamed as "slow/wedged" did not exist yet. Distinguish
718
750
  // the two via a fresh, synchronous inFlight lookup so the record keeps
719
751
  // the discriminating identity (which server, which lifecycle state).
720
- // Guarded: a test double or future service shape lacking the method
721
- // must degrade to the old "timeout"/slow-wedged wording, not throw
722
- // into the catch below and suppress this record entirely (#1766 F3).
723
- const spawnInFlight = !abort?.aborted &&
724
- typeof lspService.isSpawnInFlight === "function" &&
725
- lspService.isSpawnInFlight(filePath);
752
+ // #2592: the `typeof lspService.isSpawnInFlight === "function"`
753
+ // hedge that used to wrap this call is GONE. It existed for one
754
+ // reason — 19 hand-rolled `getLSPService` doubles that lacked the
755
+ // method, whose TypeError would have landed in the catch below and
756
+ // suppressed this record entirely (#1766 F3) — and that population
757
+ // is now zero: every double is seeded from
758
+ // `makeLspServiceDouble`, `tests/support/lsp-double-baseline.json`
759
+ // is `{}`, and `tests/config/lsp-service-double-sweep.test.ts` reds
760
+ // on a fresh hand-rolled one. The real `LSPService` has always had
761
+ // the method (clients/lsp/index.ts), so the fallback was a test
762
+ // shape leaking into production — AGENTS.md shape 7.
763
+ const spawnInFlight = !abort?.aborted && lspService.isSpawnInFlight(filePath);
726
764
  const reason = abort?.aborted
727
765
  ? "aborted"
728
766
  : spawnInFlight
@@ -783,12 +821,29 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
783
821
  let formatChanged = false;
784
822
  let formattersUsed = [];
785
823
  const formatFailures = [];
824
+ const formatUnavailable = [];
786
825
  let fileContent;
787
826
  const formatService = getFormatService();
788
827
  try {
789
828
  formatService.recordRead(filePath);
790
829
  const result = await formatService.formatFile(filePath);
791
- formattersUsed = result.formatters.map((f) => f.name);
830
+ // An unavailable tool is NOT a formatter that ran (#2413): keep it out of
831
+ // `formattersUsed` (which drives change bookkeeping / turn summaries) and
832
+ // out of `formatFailures` (which requeues). Record it once, distinctly.
833
+ for (const f of result.formatters) {
834
+ if (f.outcome !== "unavailable")
835
+ continue;
836
+ const reason = f.error ?? "formatter executable not found";
837
+ formatUnavailable.push({ formatter: f.name, reason });
838
+ recordDegradationOnce({
839
+ kind: "formatter-unavailable",
840
+ subject: `${f.name}:${path.basename(filePath)}`,
841
+ reason,
842
+ });
843
+ }
844
+ formattersUsed = result.formatters
845
+ .filter((f) => f.outcome !== "unavailable")
846
+ .map((f) => f.name);
792
847
  if (result.anyChanged) {
793
848
  formatChanged = true;
794
849
  dbg("autoformat: " +
@@ -828,7 +883,13 @@ export async function runFormatPhase(filePath, getFormatService, dbg) {
828
883
  catch {
829
884
  fileContent = undefined;
830
885
  }
831
- return { formatChanged, formattersUsed, formatFailures, fileContent };
886
+ return {
887
+ formatChanged,
888
+ formattersUsed,
889
+ formatFailures,
890
+ formatUnavailable,
891
+ fileContent,
892
+ };
832
893
  }
833
894
  /**
834
895
  * Build the 🔴 STOP blocker output with an inline code snippet for each
@@ -0,0 +1,228 @@
1
+ /**
2
+ * The #2506 probe-home redirect, whole: the decision, the per-process memo it
3
+ * is cached in, and the degradation event that decision produced.
4
+ *
5
+ * WHAT IT DECIDES. An ad-hoc probe against the BUILT `clients/*.js` — a bare
6
+ * `node -e`, a throwaway `.mjs`, a harness script — has no `PI_LENS_HOME` pin,
7
+ * so every logger and ledger it touches used to write into the maintainer's
8
+ * REAL `~/.pi-lens`. Confirmed live on 2026-09-02: two review probes left 42
9
+ * rows of `/p/.pi-lens.json` fixture garbage in real telemetry. When
10
+ * `PI_LENS_HOME` is unset and the process looks like a probe — `PILENS_PROBE=1`,
11
+ * a `cwd` inside a specific agent worktree, or a `cwd` under `os.tmpdir()` —
12
+ * `resolveProbeHomeDir()` answers with a per-probe directory instead.
13
+ *
14
+ * WHY IT ALL LIVES HERE and not beside its `getGlobalPiLensDir()` sibling in
15
+ * `file-utils.ts`, where round 2 of #2516 left it. `file-utils.ts` sits on an
16
+ * import cycle (it still does, via `safe-spawn.js` -> `resource-sampler.js` ->
17
+ * `instance-reaper.js` -> `file-utils.js`; the shorter route through
18
+ * `degradation-ledger.js` -> `extension-log.js` was this move's doing —
19
+ * `extension-log.ts` imported `file-utils.ts` for THIS function and nothing
20
+ * else, so the edge went with it, and the known-violations baseline dropped
21
+ * from 33 pinned cycles to 12 with none added). Fifteen log-family modules
22
+ * call `getGlobalPiLensLogDir()` at their OWN module
23
+ * top level. Under vitest's vite-SSR transform each import becomes a
24
+ * `__vite_ssr_import_N__` variable assigned in source order, so one of those
25
+ * top-level calls re-enters `file-utils.ts` while its body is only PARTWAY
26
+ * through those assignments — and a resolver sitting there then dereferences a
27
+ * binding that has not been assigned yet, throwing `ReferenceError: Cannot
28
+ * access '__vite_ssr_import_N__' before initialization`. Measured live: the
29
+ * re-entry arrives through `file-utils.ts`'s FIFTH import,
30
+ * `spawn-timeout-cooldown.js`, so every binding declared from the sixth import
31
+ * onward is unsafe on that path — which is most of the file.
32
+ *
33
+ * Round 2 of #2516 papered over that with an `isTestMode()` early return placed
34
+ * ahead of the first unsafe dereference. That was load-order luck, not a
35
+ * design: moving `file-utils.ts`'s own `isTestMode` import from fifth to last
36
+ * makes the GUARD LINE ITSELF throw the same error. It also DEFEATED the
37
+ * redirect for every process that has `VITEST` set but no `PI_LENS_HOME` pin —
38
+ * vitest's own globalSetup/child processes — sending their logs to the real
39
+ * home: the exact hermeticity hole the redirect exists to close.
40
+ *
41
+ * THIS module is off that cycle. It imports only node builtins and
42
+ * `path-utils.js`, itself a leaf (node builtins plus the vendored, zero-import
43
+ * `deps/minimatch.js`). A module off every cycle is fully evaluated before any
44
+ * importer's body runs — in plain ESM and under the vite-SSR transform alike —
45
+ * so none of its own bindings can ever be observed uninitialized, wherever an
46
+ * importer chooses to place the import. That is what makes the guard
47
+ * unnecessary rather than merely well-placed, and it is why module-scope
48
+ * `const`s (including the `Symbol.for` below) are ordinary here where they were
49
+ * a hazard in `file-utils.ts`.
50
+ *
51
+ * `tests/config/global-dir-probe-redirect.test.ts` pins the behaviour through
52
+ * real spawned `node` children, including the VITEST-set-but-unpinned case.
53
+ */
54
+ import * as fs from "node:fs";
55
+ import * as os from "node:os";
56
+ import * as path from "node:path";
57
+ import { isUnderDir, normalizeFilePath } from "./path-utils.js";
58
+ /**
59
+ * The memo lives in a process-wide `globalThis` slot rather than a plain
60
+ * module-scope `let` so that a bundled `dist/index.js` and the loose
61
+ * `clients/*.js` loaded into the same process — the shape #335 ships — share
62
+ * ONE decision instead of memoizing two. A `Symbol.for` key is interned by
63
+ * string, so both copies of this module resolve to the same slot.
64
+ */
65
+ const RESOLUTION_SLOT = Symbol.for("pi-lens.probe-home-state.resolution");
66
+ /** The memoized resolution for this process, or `undefined` before first use. */
67
+ export function getProbeHomeResolution() {
68
+ return globalThis[RESOLUTION_SLOT];
69
+ }
70
+ /**
71
+ * The degradation row for a redirect that actually happened, or `undefined`
72
+ * when this process was not redirected (or has not resolved yet).
73
+ * `degradation-ledger.ts`'s `getDegradationSummary()` is the only caller — it
74
+ * folds this at READ time rather than having the resolver push a row, because a
75
+ * direct import between `file-utils.ts` and `degradation-ledger.ts` in EITHER
76
+ * direction (a dynamic `import()` included) adds unpinned `no-client-cycles`
77
+ * violations to the cycle named in this module's doc comment.
78
+ */
79
+ export function getProbeHomeRedirectEvent() {
80
+ return getProbeHomeResolution()?.event;
81
+ }
82
+ /**
83
+ * Where pi-lens writes its LOGS, ledger rows and debug dumps: `~/.pi-lens/` by
84
+ * default, exactly like `getGlobalPiLensDir()` in `file-utils.ts` — but
85
+ * redirected to a per-probe directory when this process looks like an ad-hoc
86
+ * probe rather than a real pi session (#2506).
87
+ *
88
+ * Every log-family writer routes here: `latency.log`, `extension.log`,
89
+ * `sessionstart.log`, `cascade.log`, `read-guard.log`, `tree-sitter.log`,
90
+ * `word-index.log`, `bus-events.log`, `dispositions.log`, `dead-code.log`,
91
+ * `ast-grep-tools.log`, `actionable-warnings.log`, `review-graph.log`, the
92
+ * `logs/*.jsonl` diagnostic dir, the debug handle/heap dumps,
93
+ * `log-cleanup.ts`'s sweep over all of the above, and `smells-rollup.ts`'s
94
+ * read-side tail of `latency.log`/`bus-events.log` (a reader, but it must
95
+ * follow the same root or it tails a file nobody is writing).
96
+ *
97
+ * WHY IT LIVES IN THIS MODULE and not next to its `getGlobalPiLensDir()`
98
+ * sibling in `file-utils.ts` — see this file's header. Fifteen of the writers
99
+ * listed above call this function at their OWN module top level, and
100
+ * `file-utils.ts` is on an import cycle, so resolving from there could observe
101
+ * `file-utils.ts` mid-initialization. Resolving from a module off every cycle
102
+ * cannot.
103
+ *
104
+ * `PI_LENS_HOME` wins over any redirect, which is how
105
+ * `tests/support/vitest-setup.ts` keeps test workers hermetic. Note the
106
+ * redirect still applies under vitest for processes that setup does not cover
107
+ * (globalSetup, and children spawned without the pin): #2516 round 2's
108
+ * `isTestMode()` guard sent exactly those to the developer's real home.
109
+ *
110
+ * Deliberately does NOT write a line to the terminal when it fires. #1333 gives
111
+ * pi sole ownership of the TTY, and
112
+ * `tests/clients/extension-terminal-silence.test.ts` enforces that nothing
113
+ * under `clients/` calls `process.std*.write` or `console.*` (round 2 of
114
+ * #2506 shipped a `process.stderr.write` here and was red in CI for exactly
115
+ * that). Nor can it log through `extension-log.ts`'s
116
+ * `createSubsystemLogger`: that module imports THIS one, so an import back
117
+ * the other way would close a new, unpinned `no-client-cycles` cycle — and
118
+ * `extension.log` is itself redirected by this very function, so the line
119
+ * would land in the probe home nobody reads. The durable signal is the
120
+ * `global-dir-probe-redirect` degradation row instead, folded into
121
+ * `getDegradationSummary()` at read time via
122
+ * `getProbeHomeRedirectEvent()` above.
123
+ */
124
+ export function getGlobalPiLensLogDir() {
125
+ const override = process.env.PI_LENS_HOME?.trim();
126
+ if (override)
127
+ return path.resolve(override);
128
+ return resolveProbeHomeDir() ?? path.join(os.homedir(), ".pi-lens");
129
+ }
130
+ /**
131
+ * Resolve the probe-home redirect at most ONCE per process, caching both the
132
+ * "yes, here" and the "no redirect" answers (#2506 round 3, F5). Round 2 read
133
+ * `process.cwd()` live on every call, which gave a single process up to three
134
+ * different global dirs as it chdir'd, and re-ran the whole decision on the hot
135
+ * logging path.
136
+ *
137
+ * `undefined` means "no redirect applies"; `getGlobalPiLensLogDir()` above
138
+ * falls back to the real home. This function never consults `PI_LENS_HOME` —
139
+ * that pin is handled by its caller, ahead of this decision, so a pinned
140
+ * process never memoizes a probe answer it would then keep after the pin
141
+ * changed.
142
+ */
143
+ function resolveProbeHomeDir() {
144
+ const slot = globalThis;
145
+ const cached = slot[RESOLUTION_SLOT];
146
+ if (cached)
147
+ return cached.probeHome;
148
+ const cwd = process.cwd();
149
+ const probeHome = computeProbeHomeDir(cwd);
150
+ slot[RESOLUTION_SLOT] = {
151
+ probeHome,
152
+ event: probeHome ? { probeHome, cwd } : undefined,
153
+ };
154
+ return probeHome;
155
+ }
156
+ /**
157
+ * Clears the memoized decision AND the event it produced, so the next
158
+ * `getGlobalPiLensLogDir()` call re-resolves from scratch. Exercised by
159
+ * `tests/config/global-dir-probe-redirect.test.ts`, which drives the resolver
160
+ * repeatedly under different in-process conditions; without it the first call
161
+ * in a worker would pin the answer for every later case in the same file.
162
+ */
163
+ export function _resetProbeHomeRedirectStateForTests() {
164
+ globalThis[RESOLUTION_SLOT] = undefined;
165
+ }
166
+ /** The redirect decision itself. */
167
+ function computeProbeHomeDir(cwd) {
168
+ // The explicit force wins first: it is how a probe run from an ordinary
169
+ // project checkout opts in, and it must work even under a harness that has
170
+ // set a test-mode marker but no PI_LENS_HOME.
171
+ if (process.env.PILENS_PROBE === "1") {
172
+ return path.join(cwd, ".pi-lens-probe-home");
173
+ }
174
+ const worktreeRoot = findAgentWorktreeRoot(cwd);
175
+ if (worktreeRoot)
176
+ return path.join(worktreeRoot, ".pi-lens-probe-home");
177
+ if (isUnderRealDir(cwd, os.tmpdir())) {
178
+ return path.join(cwd, ".pi-lens-probe-home");
179
+ }
180
+ return undefined;
181
+ }
182
+ /**
183
+ * The directory of the SPECIFIC agent worktree containing `cwd`, or
184
+ * `undefined` when `cwd` is not inside one.
185
+ *
186
+ * Requires at least one path segment AFTER `worktrees/`, and anchors the
187
+ * probe home at that segment rather than at the live `cwd` (#2506 round 3,
188
+ * F5). Round 2's trailing `(\/|$)` alternative also matched a `cwd` of
189
+ * `.claude/worktrees` ITSELF, which would have put the probe home in the
190
+ * SHARED worktrees parent — every concurrent agent on the box writing into
191
+ * one directory, the cross-agent collision this redirect is supposed to
192
+ * prevent. Anchoring at the worktree also means a probe run from a
193
+ * subdirectory (`<worktree>/clients`) and one run from the worktree root
194
+ * agree on a single probe home instead of scattering one per cwd.
195
+ */
196
+ function findAgentWorktreeRoot(cwd) {
197
+ const normalized = normalizeFilePath(path.resolve(cwd));
198
+ const match = /(?:^|\/)\.claude\/worktrees\/[^/]+(?:\/|$)/.exec(normalized);
199
+ if (!match)
200
+ return undefined;
201
+ return normalized.slice(0, match.index + match[0].length).replace(/\/$/, "");
202
+ }
203
+ /**
204
+ * `isUnderDir`, but with both sides resolved through `realpath` first
205
+ * (#2506 round 3, F7).
206
+ *
207
+ * On macOS `os.tmpdir()` reports `/var/folders/...` while a `cwd` created
208
+ * under it resolves to `/private/var/folders/...` (`/var` is a symlink to
209
+ * `/private/var`), so the two NEVER prefix-match unresolved and the tmpdir
210
+ * branch would be dead on every Mac. Windows has the same shape with 8.3
211
+ * short names and substituted drives. Both sides go through the same
212
+ * function, so `realpathSync.native`'s canonical Windows casing applies
213
+ * consistently; the result is used only for this containment test, never to
214
+ * build the returned path, so no casing change can leak into a log path.
215
+ * Best-effort by design: a path that does not exist yet (or an EPERM) falls
216
+ * back to its literal form rather than throwing on the logging hot path.
217
+ */
218
+ function isUnderRealDir(child, parent) {
219
+ return isUnderDir(toRealPath(child), toRealPath(parent));
220
+ }
221
+ function toRealPath(target) {
222
+ try {
223
+ return fs.realpathSync.native(target);
224
+ }
225
+ catch {
226
+ return target;
227
+ }
228
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Shared process-bridge registration helper (#2437).
3
+ *
4
+ * `clients/read-bridge.ts` (#1265) and `clients/mutation-bridge.ts` (#2423)
5
+ * each mount a versioned, frozen method-table object at a well-known
6
+ * `globalThis[Symbol.for("pi-lens:...")]` key so a same-process producer can
7
+ * reach pi-lens's bookkeeping without importing its internals. Both bridges
8
+ * carried an identical mount body — a first-wins existence check,
9
+ * `Object.freeze`, and a non-writable/non-configurable `Object.defineProperty`
10
+ * — differing only in their key and method table (named by the #2432 review,
11
+ * refs #2423). This module owns that body once.
12
+ *
13
+ * NOT `clients/process-singletons.ts` (#2146): that module keeps a single
14
+ * MUTABLE, adopt-if-version-compatible value per "family" inside ONE shared
15
+ * container (a different, single key mounts the container itself, and a
16
+ * version mismatch there means "discard and rebuild"). A bridge's contract is
17
+ * the opposite: the documented producer protocol reads
18
+ * `globalThis[Symbol.for("pi-lens:mutation-bridge")]` directly as a frozen,
19
+ * first-wins, unreplaceable property, and a version mismatch is the
20
+ * PRODUCER's problem — an unsupported bridge it should not call — never a
21
+ * signal for pi-lens to silently reset its own mount. Reusing the singleton
22
+ * container would change that documented external contract, so this stays a
23
+ * separate, deliberately smaller leaf built for the bridge shape only.
24
+ *
25
+ * STATIC IMPORTS: none, deliberately. Both `read-bridge.ts` and
26
+ * `mutation-bridge.ts` import this module; it must stay a dependency leaf so
27
+ * neither gains an edge onto anything else in `clients/` (`no-client-cycles`).
28
+ */
29
+ /**
30
+ * Mount `build()`'s result at `globalThis[key]`, once per process.
31
+ *
32
+ * First-wins: if `key` already exists on `globalThis` — from an earlier call
33
+ * in this process, whether this bridge's own prior registration or a
34
+ * redundant re-activation — `build()` is never invoked and the existing
35
+ * mount is left untouched. The mounted value is frozen and installed
36
+ * non-writable/non-configurable so no later code can silently replace or
37
+ * extend it; the `in` check above is what keeps a redundant call from
38
+ * throwing on that now-frozen property.
39
+ */
40
+ export function registerProcessBridge(key, build) {
41
+ if (key in globalThis)
42
+ return;
43
+ const bridge = Object.freeze(build());
44
+ Object.defineProperty(globalThis, key, {
45
+ value: bridge,
46
+ writable: false,
47
+ configurable: false,
48
+ enumerable: false,
49
+ });
50
+ }
51
+ /**
52
+ * The bridge mounted at `key`, or `undefined` when nothing is mounted, the
53
+ * mounted value is not an object, or its `version` does not equal `version`.
54
+ * A version mismatch means "not a bridge this caller recognizes" — never
55
+ * adopted and never reset; the reset-on-mismatch behavior belongs to
56
+ * `process-singletons.ts`'s different contract, not this one.
57
+ */
58
+ export function getProcessBridge(key, version) {
59
+ const bridge = globalThis[key];
60
+ if (!bridge || typeof bridge !== "object")
61
+ return undefined;
62
+ const candidate = bridge;
63
+ if (candidate.version !== version)
64
+ return undefined;
65
+ return candidate;
66
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The extension runtime's door onto the ONE process-table seam (#2443).
3
+ *
4
+ * `scripts/lib/process-scan.mjs` owns what a process listing IS: the field →
5
+ * platform-column table, the Windows CIM/WQL and POSIX `ps` command
6
+ * composition, the WQL escaping, and the row parser. This module owns how
7
+ * clients/ RUNS one, which is genuinely different from how a script runs one
8
+ * and stays different on purpose:
9
+ *
10
+ * - the child and its stdout pipe are `unref`'d (#1155) so a one-shot
11
+ * `pi --print` can settle without waiting on a CIM query;
12
+ * - a child that blows its timeout is terminated through the caller's own
13
+ * tree-kill-and-verify machinery (#1864 F3), injected as `onTimeout` — a
14
+ * sweep that leaks its own scanner is the defect the sweep exists to fix;
15
+ * - the outcome carries a `SpawnCollectStatus`, so "the table is empty" and
16
+ * "the query never ran" stay distinguishable and the caller can record the
17
+ * degradation (#1857).
18
+ *
19
+ * WHY THE SEAM IS IMPORTED FROM scripts/ RATHER THAN LIVING HERE: the .mjs
20
+ * header argues it in full. Short version — `scripts/prune-agent-worktrees.mjs`
21
+ * is a SessionStart/SubagentStop hook that runs inside freshly created agent
22
+ * worktrees, where `clients/*.js` (gitignored build output) does not exist
23
+ * yet, so the shared half has to be the side that needs no build. This file
24
+ * is the single crossing point; nothing else in clients/ imports scripts/.
25
+ */
26
+ import { spawnCollectStdoutResult, } from "./child-unref.js";
27
+ import { buildProcessQuery, parseProcessTable, } from "../scripts/lib/process-scan.mjs";
28
+ /**
29
+ * Re-exported, not redefined. `windowsExe` resolves an absolute System32
30
+ * interpreter path for a spawn this process is about to make (a bare
31
+ * `powershell.exe`/`taskkill.exe` is resolvable through a PATH a caller can
32
+ * shadow, and these spawns decide what gets killed) — one definition of that
33
+ * rule is the whole point of the seam.
34
+ */
35
+ export { windowsExe } from "../scripts/lib/process-scan.mjs";
36
+ /**
37
+ * Run one process-table query on the extension's spawn rails.
38
+ *
39
+ * Never throws and never rejects: an unbuildable query (a Windows-only column
40
+ * asked for on POSIX, an empty filter) resolves as `spawn-error` with no rows,
41
+ * the same shape every other failure takes, because every caller here is
42
+ * best-effort instrumentation or a best-effort sweep.
43
+ */
44
+ export async function queryProcessTable(request, options) {
45
+ let query;
46
+ try {
47
+ query = buildProcessQuery(request.fields, {
48
+ filter: request.filter,
49
+ excludeSelfPid: request.excludeSelfPid,
50
+ });
51
+ }
52
+ catch {
53
+ return { rows: [], status: "spawn-error", serverSideFiltered: false };
54
+ }
55
+ const result = await spawnCollectStdoutResult(query.command, query.args, { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }, { timeoutMs: options.timeoutMs, onTimeout: options.onTimeout });
56
+ return {
57
+ // Whatever the collector kept is parsed rather than discarded here: it
58
+ // drops stdout on a non-zero exit (that output is not evidence of what
59
+ // is NOT running) but KEEPS the partial table from a timed-out child,
60
+ // and the caller decides what a partial table is worth by reading
61
+ // `status`. Re-deciding that here would be a second policy.
62
+ rows: parseProcessTable(result.stdout, query.tabSeparated, query.fields),
63
+ status: result.status,
64
+ exitCode: result.exitCode,
65
+ timeoutKill: result.timeoutKill,
66
+ serverSideFiltered: query.serverSideFiltered,
67
+ };
68
+ }
@@ -415,9 +415,10 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
415
415
  // Deliberately never calls `writeCache`: there is nothing fresher to write
416
416
  // back, only what turn_end already produced.
417
417
  //
418
- // No double-count / honesty gap (#533): `consumeTestFindings`
419
- // (`runtime-context.ts`) reads-and-clears this SAME cache key once, to
420
- // inject a one-shot "fix before continuing" message into the NEXT turn.
418
+ // No double-count / honesty gap (#533): explicit `pilens_turn_end`
419
+ // delivery may call `consumeTestFindings` (`runtime-context.ts`) to
420
+ // read-and-clear this SAME cache key once. Automatic Pi delivery uses a
421
+ // non-context custom entry and never consumes this pull-diagnostics cache.
421
422
  // This task only ever reads (never clears) it, so it can't race that
422
423
  // consumption into re-delivering a message twice — at most it surfaces
423
424
  // the same underlying failures a second time, through a different
@@ -1,3 +1,4 @@
1
+ import { isRunnerErrorResult, } from "../../test-runner-client.js";
1
2
  import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
4
  import { advisoryPathKey, validateAdvisoryProvenance, } from "../../advisory-provenance.js";
@@ -59,6 +60,31 @@ export function testResultToProjectDiagnostics(result, stale = false) {
59
60
  source: "project-scan",
60
61
  }));
61
62
  }
63
+ // #2532: a RUNNER error (timeout, missing provider/binary, a config
64
+ // failure — the suite itself never produced a verdict) is not a finding
65
+ // the agent introduced, exactly like the turn-end delivery framing
66
+ // (#2522) already treats it. `isRunnerErrorResult` is the single seam
67
+ // both surfaces read so the identical `TestResult` cannot classify
68
+ // differently here than it does in the turn-end message.
69
+ if (isRunnerErrorResult(result)) {
70
+ return [
71
+ {
72
+ filePath: result.file,
73
+ severity: "info",
74
+ semantic: "none",
75
+ tool: "test-runner",
76
+ runner: result.runner,
77
+ rule: `test:${result.runner}`,
78
+ message: `${stalePrefix}Test run error: ${result.error}`,
79
+ source: "project-scan",
80
+ },
81
+ ];
82
+ }
83
+ // #2532 review S3: a counted failure stays blocking even when `error` is
84
+ // ALSO set (pytest exit 2 "Interrupted" after `2 failed, 1 passed` — see
85
+ // `isRunnerErrorResult`'s doc). The pre-fix message mentioned `error`
86
+ // here via a ternary this PR's first version dropped; restored so the
87
+ // interruption is not silently lost from a still-blocking finding.
62
88
  return [
63
89
  {
64
90
  filePath: result.file,
@@ -67,9 +93,7 @@ export function testResultToProjectDiagnostics(result, stale = false) {
67
93
  tool: "test-runner",
68
94
  runner: result.runner,
69
95
  rule: `test:${result.runner}`,
70
- message: `${stalePrefix}${result.error
71
- ? `Test run error: ${result.error}`
72
- : `${result.failed} test(s) failed`}`,
96
+ message: `${stalePrefix}${result.failed} test(s) failed${result.error ? ` (runner also reported: ${result.error})` : ""}`,
73
97
  source: "project-scan",
74
98
  },
75
99
  ];