@herbertgao/pi-extensions 2026.8.6 → 2026.8.7

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 (343) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +1 -0
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -10
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +2 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  49. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  50. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  51. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  52. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  53. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  54. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  55. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  56. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  57. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  58. package/node_modules/pi-lens/CHANGELOG.md +909 -0
  59. package/node_modules/pi-lens/README.md +11 -3
  60. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  61. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  62. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  63. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  64. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  65. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  66. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  67. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  68. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  69. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  70. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  71. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  72. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  73. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  74. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  75. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  76. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  77. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  78. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  79. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  80. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  81. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  82. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  83. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  84. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  86. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  87. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  122. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  123. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  124. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  125. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  126. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  127. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  128. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  129. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  130. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  131. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  134. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  135. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  136. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  137. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  138. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  139. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  140. package/node_modules/pi-lens/dist/clients/jscpd-client.js +82 -6
  141. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  142. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  143. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  144. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  145. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  146. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  147. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  148. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +102 -7
  149. package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
  150. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  151. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  152. package/node_modules/pi-lens/dist/clients/lsp/index.js +2274 -147
  153. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  154. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  155. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  156. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  157. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  158. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  159. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  160. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  161. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  163. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  164. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  165. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  166. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  167. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  168. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  169. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  170. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  171. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  172. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  173. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  174. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  175. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  176. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  177. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  178. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  179. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  180. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  181. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  182. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  183. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  185. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  186. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  187. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  188. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  189. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  190. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +308 -45
  191. package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -9
  192. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  193. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  194. package/node_modules/pi-lens/dist/clients/runtime-turn.js +575 -67
  195. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  196. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  197. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  198. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  199. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  200. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  201. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  202. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  203. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  204. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  205. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  210. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  211. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  212. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  213. package/node_modules/pi-lens/dist/index.js +67738 -55739
  214. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
  215. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  216. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  217. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  218. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  219. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  220. package/node_modules/pi-lens/docs/features.md +19 -5
  221. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  222. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  223. package/node_modules/pi-lens/docs/mcp.md +2 -2
  224. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  225. package/node_modules/pi-lens/docs/settings.md +19 -1
  226. package/node_modules/pi-lens/docs/usage.md +1 -0
  227. package/node_modules/pi-lens/package.json +10 -6
  228. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  237. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  238. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  239. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  240. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  241. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  252. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  253. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  254. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  255. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  256. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  257. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  258. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  288. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  289. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  290. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  291. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  292. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  293. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  294. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  295. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  296. package/node_modules/pi-mcp-adapter/README.md +47 -5
  297. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  298. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  299. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  300. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  301. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  302. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  303. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  304. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  305. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  306. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  307. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  308. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  309. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  310. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  311. package/node_modules/pi-mcp-adapter/package.json +3 -1
  312. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  313. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  314. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  315. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  316. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  317. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  318. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  319. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  320. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  321. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  322. package/node_modules/pi-web-access/CHANGELOG.md +23 -0
  323. package/node_modules/pi-web-access/README.md +27 -9
  324. package/node_modules/pi-web-access/anysearch.ts +4 -2
  325. package/node_modules/pi-web-access/brave.ts +16 -4
  326. package/node_modules/pi-web-access/curator-page.ts +6 -3
  327. package/node_modules/pi-web-access/curator-server.ts +4 -1
  328. package/node_modules/pi-web-access/exa.ts +18 -7
  329. package/node_modules/pi-web-access/extract.ts +28 -3
  330. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  331. package/node_modules/pi-web-access/index.ts +41 -57
  332. package/node_modules/pi-web-access/package.json +1 -1
  333. package/node_modules/pi-web-access/page-query.ts +4 -2
  334. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  335. package/node_modules/pi-web-access/parallel.ts +2 -2
  336. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  337. package/node_modules/pi-web-access/serper.ts +211 -0
  338. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  339. package/node_modules/pi-web-access/summary-review.ts +44 -22
  340. package/node_modules/pi-web-access/tavily.ts +16 -4
  341. package/node_modules/pi-web-access/utils.ts +87 -0
  342. package/node_modules/pi-web-access/valyu.ts +199 -0
  343. package/package.json +13 -13
@@ -0,0 +1,175 @@
1
+ /**
2
+ * #1618: a full workspace sweep (`LSPService.runWorkspaceDiagnostics`, the
3
+ * engine behind `lens_diagnostics mode=full`) grants itself a wall-clock
4
+ * ceiling (`FULL_SCAN_WALL_CLOCK_MS`, see {@link getFullScanWallClockMs})
5
+ * that can outlive the detached LSP idle-reset timer armed by
6
+ * `clients/runtime-turn.ts`'s `handleTurnEnd` on a file-less turn. Nothing
7
+ * previously stopped that timer from firing mid-sweep and destroying the
8
+ * very service the sweep was actively touching — every file the sweep had
9
+ * not yet reached then failed with zero trace and got mislabeled as budget
10
+ * exhaustion.
11
+ *
12
+ * This module is the shared hold: `runWorkspaceDiagnostics` acquires it for
13
+ * its own lifetime (try/finally, so a throw or an aborted sweep still
14
+ * releases it) and the idle-reset timer consults it before firing. Holds are
15
+ * tracked as TOKENS (acquire-time-stamped), not a bare counter — two
16
+ * overlapping `lens_diagnostics mode=full` calls (or a sweep started while
17
+ * another is still finishing) must each be free to release independently
18
+ * without the other's hold being dropped early.
19
+ *
20
+ * A leaked hold that never re-arms idle reset is the inverse defect (state
21
+ * that must re-arm cannot hide behind a stuck guard), so this module screens
22
+ * against it on TWO axes, matching the repo's own session-boundary +
23
+ * bounded-lifetime conventions:
24
+ * 1. `clearWorkspaceSweepHoldForSessionStart` — `resetLSPService({reason:
25
+ * "session_start"})` clears every held token unconditionally. A session
26
+ * boundary means no in-flight sweep from a PRIOR generation is still
27
+ * meaningful to wait on.
28
+ * 2. `reapStaleHolds` (internal, called from every liveness check) —
29
+ * force-releases any token older than {@link getWorkspaceSweepMaxHoldAgeMs},
30
+ * derived from the sweep's own wall-clock ceiling plus a safety margin.
31
+ * A real sweep is either done or self-aborted well within that window
32
+ * (`tools/lens-diagnostics.ts` races `FULL_SCAN_WALL_CLOCK_MS`), so a
33
+ * token still held past it can only be a bug — never a legitimate long
34
+ * sweep — and gets force-released with its own distinct log record.
35
+ *
36
+ * `getFullScanWallClockMs` also lives here (rather than duplicated between
37
+ * `tools/lens-diagnostics.ts` and `clients/runtime-turn.ts`) so the idle-reset
38
+ * delay on every path (base AND the subagent/budget-shortened paths) can be
39
+ * DERIVED from the sweep's own ceiling instead of the constants drifting
40
+ * apart again (#1618 acceptance criterion 6).
41
+ */
42
+ import { logLatency } from "../latency-logger.js";
43
+ /** Extra headroom beyond the sweep's own wall-clock ceiling before an
44
+ * idle-reset delay (or the max-hold-age failsafe) trusts that a real sweep
45
+ * must be done. Single source for both derivations below AND for every
46
+ * idle-reset path `clients/runtime-turn.ts` derives (base, subagent, and
47
+ * budget-shortened) — never a second independently-tunable literal. */
48
+ export const SWEEP_IDLE_SAFETY_MARGIN_MS = 60_000;
49
+ let holds = new Map(); // holdId -> acquiredAt (Date.now())
50
+ let nextHoldId = 1;
51
+ let idleWaiters = [];
52
+ function flushIdleWaitersIfEmpty() {
53
+ if (holds.size > 0)
54
+ return;
55
+ if (idleWaiters.length === 0)
56
+ return;
57
+ const waiters = idleWaiters;
58
+ idleWaiters = [];
59
+ for (const waiter of waiters) {
60
+ try {
61
+ waiter();
62
+ }
63
+ catch {
64
+ // Best-effort — a waiter failure must never propagate into the
65
+ // releasing/reaping call stack.
66
+ }
67
+ }
68
+ }
69
+ /**
70
+ * A held token past this age can only be a bug (leaked release, a sweep that
71
+ * hung outside its own abort signal, …) — a legitimate sweep is done or
72
+ * self-aborted well before it. Derived, not a separate literal, so it can't
73
+ * drift out of sync with `getFullScanWallClockMs` the way the idle-reset
74
+ * timer and the sweep's own ceiling once did.
75
+ */
76
+ export function getWorkspaceSweepMaxHoldAgeMs() {
77
+ return getFullScanWallClockMs() + SWEEP_IDLE_SAFETY_MARGIN_MS;
78
+ }
79
+ function reapStaleHolds() {
80
+ if (holds.size === 0)
81
+ return;
82
+ const maxAgeMs = getWorkspaceSweepMaxHoldAgeMs();
83
+ const now = Date.now();
84
+ for (const [holdId, acquiredAt] of holds) {
85
+ const ageMs = now - acquiredAt;
86
+ if (ageMs <= maxAgeMs)
87
+ continue;
88
+ holds.delete(holdId);
89
+ logLatency({
90
+ type: "phase",
91
+ phase: "lsp_workspace_sweep_hold_force_released",
92
+ filePath: "",
93
+ durationMs: ageMs,
94
+ metadata: { holdId, maxHoldAgeMs: maxAgeMs },
95
+ });
96
+ }
97
+ flushIdleWaitersIfEmpty();
98
+ }
99
+ /**
100
+ * Acquire the hold for one `runWorkspaceDiagnostics` call. Returns a release
101
+ * function; callers MUST call it from a `finally` block so an overlapping or
102
+ * throwing sweep still releases its hold. Idempotent — calling the returned
103
+ * function more than once past the first call, or after the hold was already
104
+ * force-released (session reset / max-age reap), is a no-op.
105
+ */
106
+ export function acquireWorkspaceSweepHold() {
107
+ const holdId = nextHoldId++;
108
+ holds.set(holdId, Date.now());
109
+ return () => {
110
+ if (!holds.delete(holdId))
111
+ return;
112
+ flushIdleWaitersIfEmpty();
113
+ };
114
+ }
115
+ /** True while at least one `runWorkspaceDiagnostics` sweep is in flight.
116
+ * Reaps stale holds first, so a leaked/hung hold can't permanently read as
117
+ * "active" past its own max age. */
118
+ export function isWorkspaceSweepActive() {
119
+ reapStaleHolds();
120
+ return holds.size > 0;
121
+ }
122
+ /**
123
+ * Run `cb` once the hold count next returns to zero. Runs synchronously and
124
+ * immediately when no sweep is active right now — the common case, and the
125
+ * one every non-sweep idle-reset arm/fire takes.
126
+ */
127
+ export function runWhenWorkspaceSweepIdle(cb) {
128
+ reapStaleHolds();
129
+ if (holds.size === 0) {
130
+ cb();
131
+ return;
132
+ }
133
+ idleWaiters.push(cb);
134
+ }
135
+ /**
136
+ * `resetLSPService({reason: "session_start"})` calls this: a session
137
+ * boundary means no hold from a PRIOR generation is still meaningful to wait
138
+ * on, so every held token is cleared unconditionally (state that must
139
+ * re-arm at session_start cannot hide behind a leaked/stuck guard from the
140
+ * generation before it). Only logs when there was actually something to
141
+ * clear — the common case (no sweep in flight at session boundary) stays
142
+ * silent.
143
+ */
144
+ export function clearWorkspaceSweepHoldForSessionStart() {
145
+ if (holds.size === 0)
146
+ return;
147
+ logLatency({
148
+ type: "phase",
149
+ phase: "lsp_workspace_sweep_hold_session_reset",
150
+ filePath: "",
151
+ durationMs: 0,
152
+ metadata: { clearedHolds: holds.size },
153
+ });
154
+ holds.clear();
155
+ flushIdleWaitersIfEmpty();
156
+ }
157
+ /** Test-only: reset the module-scope hold state between tests. */
158
+ export function _resetWorkspaceSweepHoldForTests() {
159
+ holds = new Map();
160
+ idleWaiters = [];
161
+ nextHoldId = 1;
162
+ }
163
+ /**
164
+ * Wall-clock ceiling for one `lens_diagnostics mode=full` scan
165
+ * (`runWorkspaceDiagnostics` plus its concurrent project-runner/analyzer
166
+ * work). Single source for `tools/lens-diagnostics.ts` (the sweep caller),
167
+ * `clients/runtime-turn.ts` (every idle-reset path, whose delay must outlive
168
+ * it), and this module's own max-hold-age failsafe — lazy env read, never
169
+ * memoized, matching this codebase's house style so a test can flip the
170
+ * override mid-run.
171
+ */
172
+ export function getFullScanWallClockMs() {
173
+ const raw = Number(process.env.PI_LENS_LENS_DIAGNOSTICS_FULL_TIMEOUT_MS);
174
+ return Number.isFinite(raw) && raw > 0 ? raw : 300_000; // 5 min default
175
+ }
@@ -1,7 +1,8 @@
1
1
  /** Shared lazy LSP service seam (#1394). */
2
- let lspPromise;
2
+ import { createLazyImport } from "./lazy-import.js";
3
+ const lazyLsp = createLazyImport(() => import("./lsp/index.js"));
3
4
  export function warmLspService() {
4
- return (lspPromise ??= import("./lsp/index.js"));
5
+ return lazyLsp.get();
5
6
  }
6
7
  export function loadLspService() {
7
8
  return warmLspService();
@@ -32,7 +32,12 @@ import { handleTurnEnd } from "../runtime-turn.js";
32
32
  import { createMcpHost } from "./host-shim.js";
33
33
  let contextPromise;
34
34
  let resolvedRuntime;
35
- /** Lazily build (once) the persistent session context shared across MCP calls. */
35
+ /**
36
+ * Lazily build (once) the persistent session context shared across MCP
37
+ * calls. A rejected build (e.g. `loadBootstrapClients` hitting a transient
38
+ * fs error) must not poison every later MCP call for the rest of the
39
+ * process — evict the memo on rejection so the next call retries (#1570).
40
+ */
36
41
  export function getMcpSessionContext() {
37
42
  contextPromise ??= (async () => {
38
43
  const context = {
@@ -43,7 +48,10 @@ export function getMcpSessionContext() {
43
48
  };
44
49
  resolvedRuntime = context.runtime;
45
50
  return context;
46
- })();
51
+ })().catch((err) => {
52
+ contextPromise = undefined;
53
+ throw err;
54
+ });
47
55
  return contextPromise;
48
56
  }
49
57
  /** Synchronous peek at the session runtime, for construction sites that cannot
@@ -162,6 +162,14 @@ async function extractFile(absPath, languageId, content) {
162
162
  let callbacks = [];
163
163
  let callbackError;
164
164
  try {
165
+ // SAFETY: `ModuleReportNode` is this module's own structural
166
+ // subset of a tree-sitter node: `type`, `text`, `children`,
167
+ // `parent`, `isNamed`, and the two positions. A real node
168
+ // implements all of it, but the two types are declared in
169
+ // unrelated packages so TypeScript sees no relation. The cast
170
+ // asserts only that structural subset; widen
171
+ // `ModuleReportNode` past what tree-sitter nodes provide and
172
+ // it stops holding.
165
173
  callbacks = extractCallbacks(tree.rootNode, owners, languageId, warnings);
166
174
  }
167
175
  catch (error) {
@@ -42,13 +42,19 @@
42
42
  * So `opengrepResultToProjectDiagnostics` deliberately applies NO suppression
43
43
  * filtering of its own — doing so would be redundant at best.
44
44
  *
45
- * Refs: #584, #111 (opengrep adoption), #387 (workspace-sweep serialization)
45
+ * Scope note (#1562 class fix): the CLI scan is handed `--exclude` per shared
46
+ * scratch/cache tree (`scratch-tree-policy.ts`) so a directory that isn't
47
+ * gitignored (opengrep's own default exclusion) still doesn't reach the
48
+ * agent as a finding — same `EXCLUDED_DIRS`-derived list gitleaks/trivy use.
49
+ *
50
+ * Refs: #584, #111 (opengrep adoption), #387 (workspace-sweep serialization), #1562
46
51
  */
47
52
  import * as fs from "node:fs";
48
53
  import * as os from "node:os";
49
54
  import * as path from "node:path";
50
55
  import { mkdtempSync } from "node:fs";
51
56
  import { resolveOpengrepConfig } from "./opengrep-config.js";
57
+ import { getScratchTreeDirNames } from "./scratch-tree-policy.js";
52
58
  import { safeSpawnAsync } from "./safe-spawn.js";
53
59
  import { SecurityScanClient } from "./security-scan-client.js";
54
60
  const EMPTY_RESULT = {
@@ -116,6 +122,14 @@ export class OpengrepClient extends SecurityScanClient {
116
122
  "--no-error",
117
123
  "--quiet",
118
124
  "--disable-version-check",
125
+ // #1562 class fix: opengrep's own `.gitignore` respect covers the
126
+ // common case (scratch trees are usually gitignored), but not a
127
+ // scratch/cache tree that ISN'T (e.g. an un-gitignored worktree
128
+ // cache) — `--exclude` is semgrep-compatible, so a slash-free
129
+ // pattern matches that directory name anywhere in the tree,
130
+ // independent of gitignore. Same `EXCLUDED_DIRS`-derived list
131
+ // gitleaks/trivy use, so the three scanners can't drift apart.
132
+ ...getScratchTreeDirNames().flatMap((name) => ["--exclude", name]),
119
133
  cwd,
120
134
  ], { cwd, timeout: SCAN_TIMEOUT_MS });
121
135
  if (result.error) {
@@ -16,7 +16,8 @@
16
16
  import * as fs from "node:fs";
17
17
  import * as os from "node:os";
18
18
  import * as path from "node:path";
19
- import { isCommandAvailableAsync, safeSpawnAsync } from "./safe-spawn.js";
19
+ import { classifyProbeFailure, createAvailabilityLatch, isTransientDecision, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
20
+ import { safeSpawnAsync } from "./safe-spawn.js";
20
21
  /**
21
22
  * Fallback preference when nothing is declared (or the declared manager is
22
23
  * missing). npm first for maximum compatibility; bun last. A project lockfile
@@ -72,18 +73,142 @@ function readPackageManagerField(targetPath) {
72
73
  // ============================================================================
73
74
  // AVAILABILITY (cached per process; reset in tests)
74
75
  // ============================================================================
75
- const availabilityCache = new Map();
76
- function isAvailable(pm) {
77
- let cached = availabilityCache.get(pm);
78
- if (!cached) {
79
- cached = isCommandAvailableAsync(pm);
80
- availabilityCache.set(pm, cached);
76
+ /**
77
+ * Budget for the `where`/`which <pm>` probe. Matches the previous
78
+ * `isCommandAvailableAsync` default so the observable timing is unchanged.
79
+ */
80
+ const PROBE_TIMEOUT_MS = 5_000;
81
+ /**
82
+ * A timed-out probe used to be memoized as a permanent `false` — the process
83
+ * would silently downgrade a declared pnpm/yarn manager to npm for its whole
84
+ * life. The blast radius was internal: this resolver only serves installs
85
+ * into pi-lens's own managed tools directory and `pilens_rebuild` on a
86
+ * pi-lens source checkout, never a user project's lockfile (#1496). Each
87
+ * manager now sits behind the shared transient-aware latch (#1467/#1476):
88
+ * only a genuine absence (`where`/`which` exits non-zero, or ENOENT) latches;
89
+ * a timeout, abort or host stall expires on a cooldown and is re-probed.
90
+ */
91
+ const availabilityLatches = new Map();
92
+ const inFlightProbes = new Map();
93
+ function getLatch(pm) {
94
+ let latch = availabilityLatches.get(pm);
95
+ if (!latch) {
96
+ latch = createAvailabilityLatch();
97
+ availabilityLatches.set(pm, latch);
98
+ }
99
+ return latch;
100
+ }
101
+ async function probeAvailability(pm) {
102
+ const latch = getLatch(pm);
103
+ const startedAt = Date.now();
104
+ const finder = onWindows() ? "where" : "which";
105
+ const sampler = startHostStallSampler();
106
+ let result;
107
+ let hostStallMs;
108
+ try {
109
+ result = await safeSpawnAsync(finder, [pm], { timeout: PROBE_TIMEOUT_MS });
81
110
  }
82
- return cached;
111
+ finally {
112
+ hostStallMs = sampler.stop();
113
+ }
114
+ const elapsedMs = Date.now() - startedAt;
115
+ if (result.status === 0 && !result.error) {
116
+ latch.noteAvailable();
117
+ logAvailabilityDecision({
118
+ tool: pm,
119
+ verdict: "available",
120
+ outcome: "success",
121
+ cause: "ok",
122
+ elapsedMs,
123
+ latched: true,
124
+ hostStallMs,
125
+ budgetMs: PROBE_TIMEOUT_MS,
126
+ });
127
+ return true;
128
+ }
129
+ const { outcome, cause, evidence } = classifyProbeFailure(result, {
130
+ hostStallMs,
131
+ // Preserve pre-#1496 meaning for anything the classifier can't place: a
132
+ // present manager that rejects its probe is durable, same as before.
133
+ unclassifiedFailureOutcome: "missing",
134
+ });
135
+ const retryAfterMs = latch.noteUnavailable(outcome, cause);
136
+ logAvailabilityDecision({
137
+ tool: pm,
138
+ verdict: "unavailable",
139
+ outcome,
140
+ cause,
141
+ elapsedMs,
142
+ latched: outcome !== "transient",
143
+ hostStallMs,
144
+ ...(retryAfterMs > 0 && { retryAfterMs }),
145
+ budgetMs: PROBE_TIMEOUT_MS,
146
+ classifiedBy: "probe",
147
+ evidence,
148
+ });
149
+ return false;
150
+ }
151
+ /**
152
+ * Resolve whether `pm` is available. `onTransient`, when given, fires
153
+ * whenever the resolved `false` came from a probe that never got a fair
154
+ * hearing (a stall, an abort, a host-level failure classified `transient` by
155
+ * `classifyProbeFailure`) rather than a genuine absence — including when that
156
+ * verdict is served from the latch's own cooldown-bounded memo, not just on a
157
+ * fresh probe (#1585).
158
+ *
159
+ * Before this, the return type was a bare boolean: a caller like
160
+ * `allAvailableGlobalBinDirs` could not tell "pnpm isn't installed" from
161
+ * "the `where pnpm` probe stalled", so a transient miss silently dropped
162
+ * pnpm's global bin dir with no way to flag the result as provisional —
163
+ * exactly the boolean-collapse `getToolPath`'s `onTransient` plumbing (#1569)
164
+ * was built to avoid, one layer up.
165
+ */
166
+ function isAvailable(pm, onTransient) {
167
+ const latch = getLatch(pm);
168
+ const reportIfTransient = (result) => {
169
+ if (!result && isTransientDecision({ outcome: latch.getOutcome() })) {
170
+ onTransient?.();
171
+ }
172
+ return result;
173
+ };
174
+ const memo = latch.read();
175
+ if (memo !== null)
176
+ return Promise.resolve(reportIfTransient(memo));
177
+ // A verdict can now expire, so concurrent callers arriving just after a
178
+ // cooldown must share ONE probe rather than each spawning their own.
179
+ const inFlight = inFlightProbes.get(pm);
180
+ if (inFlight)
181
+ return inFlight.then(reportIfTransient);
182
+ // #1653 review F1: a probe started before a session reset can settle AFTER
183
+ // a later session's own probe for the same manager is already in flight.
184
+ // An unconditional delete-by-key would evict that NEWER entry out from
185
+ // under it, so a third caller in the gap finds nothing in-flight and
186
+ // spawns a duplicate. Only remove the entry if it is still THIS call's
187
+ // promise — the same identity guard `resolveMadge` uses in
188
+ // dependency-checker.ts for the equivalent race.
189
+ const probe = probeAvailability(pm).finally(() => {
190
+ if (inFlightProbes.get(pm) === probe)
191
+ inFlightProbes.delete(pm);
192
+ });
193
+ inFlightProbes.set(pm, probe);
194
+ return probe.then(reportIfTransient);
83
195
  }
84
- /** Clear the process-wide availability cache. Intended for tests. */
196
+ /**
197
+ * Clear the process-wide availability cache: pnpm/yarn/bun/npm each sit
198
+ * behind their own module-local `AvailabilityLatch` in `availabilityLatches`,
199
+ * so `resetDispatchAvailabilityState`'s generation counter (the mechanism
200
+ * most dispatch runners inherit) never reaches them — the same shape as
201
+ * psscriptanalyzer's (#1490) and zizmor's (#1535) module-local latches.
202
+ * Without this wired into `session_start`, a genuine "pnpm is missing"
203
+ * verdict from one session stayed latched into the next: install pnpm mid
204
+ * day, start a fresh session, pi-lens still reports it missing until a
205
+ * process restart (#1653). Called from `handleSessionStart`'s per-session
206
+ * reset block beside `resetZizmorTokenAvailability()` /
207
+ * `resetPsScriptAnalyzerAvailability()`; also used directly by tests.
208
+ */
85
209
  export function _resetPackageManagerCache() {
86
- availabilityCache.clear();
210
+ availabilityLatches.clear();
211
+ inFlightProbes.clear();
87
212
  }
88
213
  // ============================================================================
89
214
  // RESOLUTION
@@ -134,6 +259,24 @@ export function installArgs(pm, pkg, options = {}) {
134
259
  args.push(pkg);
135
260
  return args;
136
261
  }
262
+ /**
263
+ * Args to re-resolve an already-declared dependency to the newest version its
264
+ * recorded range still permits. npm/pnpm/bun spell this `update`; yarn classic
265
+ * spells it `upgrade`.
266
+ *
267
+ * This is the command that repairs a dependency frozen by its own lockfile.
268
+ * `installArgs` re-runs the install, and a lockfile entry that already
269
+ * satisfies the range makes that a no-op: pi-lens's managed tools tree stayed
270
+ * on the version written at first install for the life of the machine even
271
+ * though the declared caret range permitted 28 newer minors (#1730).
272
+ */
273
+ export function updateArgs(pm, pkg, options = {}) {
274
+ const args = [pm === "yarn" ? "upgrade" : "update"];
275
+ if (options.ignoreScripts)
276
+ args.push("--ignore-scripts");
277
+ args.push(pkg);
278
+ return args;
279
+ }
137
280
  /**
138
281
  * Args to install a single package **globally** (`-g`). npm/pnpm/bun spell this
139
282
  * `install -g` / `add -g`; yarn uses `global add` (yarn classic — Berry removed
@@ -204,12 +347,18 @@ async function globalBinDirsFor(pm) {
204
347
  * Global bin directories for every installed manager, deduped. Used to locate a
205
348
  * globally-installed tool binary when PATH is stale (e.g. right after an
206
349
  * `install -g`) or when the tool was installed via a non-npm manager.
350
+ *
351
+ * `onTransient`, when given, fires once per manager whose `isAvailable` probe
352
+ * was transient (stalled/unspawnable, not a genuine absence) — the boolean
353
+ * `allAvailableGlobalBinDirs` returns can't itself distinguish "not
354
+ * installed" from "couldn't tell", so a caller that persists this result
355
+ * needs the callback to know the answer may be incomplete (#1585).
207
356
  */
208
- export async function allAvailableGlobalBinDirs() {
357
+ export async function allAvailableGlobalBinDirs(onTransient) {
209
358
  const dirs = [];
210
359
  const seen = new Set();
211
360
  for (const pm of PREFERENCE) {
212
- if (!(await isAvailable(pm)))
361
+ if (!(await isAvailable(pm, onTransient)))
213
362
  continue;
214
363
  for (const dir of await globalBinDirsFor(pm)) {
215
364
  const normalized = path.resolve(dir);
@@ -251,8 +400,15 @@ export async function findGlobalBinary(command, windowsExt = ".cmd") {
251
400
  }
252
401
  return undefined;
253
402
  }
254
- /** Local `node_modules/.bin/<tool>` walking up from `startDir` to the fs root. */
255
- function findLocalBinUpwards(tool, startDir, windowsExt) {
403
+ /**
404
+ * Local `node_modules/.bin/<tool>` walking up from `startDir` to the fs root.
405
+ *
406
+ * Exported so callers that must NOT pay for global-bin discovery can reuse this
407
+ * walk instead of copying it. `findNodeToolBinary` below adds `findGlobalBinary`,
408
+ * which spawns a probe per package manager; a caller resolving a command on
409
+ * every run (knip, #1721) needs the filesystem half only.
410
+ */
411
+ export function findLocalBinUpwards(tool, startDir, windowsExt = ".cmd") {
256
412
  const names = onWindows() ? [`${tool}${windowsExt}`, tool] : [tool];
257
413
  let dir = path.resolve(startDir);
258
414
  const root = path.parse(dir).root;
@@ -0,0 +1,27 @@
1
+ import { logLatency } from "./latency-logger.js";
2
+ // Verified guesses are expected host behavior, not degradations. Keep their
3
+ // exact session count in memory and publish one bounded row at session end.
4
+ // This tally is intentionally memory-only: a process crash loses its count.
5
+ // The rollup is best-effort session telemetry, not durable accounting.
6
+ let verifiedGuessCount = 0;
7
+ export function recordVerifiedPathAttributionGuess() {
8
+ verifiedGuessCount += 1;
9
+ }
10
+ export function getVerifiedPathAttributionGuessCount() {
11
+ return verifiedGuessCount;
12
+ }
13
+ export function emitVerifiedPathAttributionRollup(filePath) {
14
+ if (verifiedGuessCount === 0)
15
+ return;
16
+ logLatency({
17
+ type: "phase",
18
+ phase: "path_attribution_verified_rollup",
19
+ filePath,
20
+ durationMs: 0,
21
+ metadata: { count: verifiedGuessCount },
22
+ });
23
+ verifiedGuessCount = 0;
24
+ }
25
+ export function resetVerifiedPathAttributionGuessCount() {
26
+ verifiedGuessCount = 0;
27
+ }