@herbertgao/pi-extensions 2026.8.5 → 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 (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  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/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Past-EOF validity gate for cited diagnostic line numbers (#1641).
3
+ *
4
+ * A live session served diagnostics at lines 407-410 of a file that was 402
5
+ * lines on disk and UNMODIFIED — the LSP's in-memory document had drifted
6
+ * longer than disk without any file write happening (no didChange lost, just
7
+ * an in-memory buffer that grew past what the file actually contains). That
8
+ * makes this a DIFFERENT invariant than #1622/#1627's freshness gate: those
9
+ * compare a cited file's MTIME against a scan timestamp, which says nothing
10
+ * when the file was never touched on disk at all. The cheap, orthogonal check
11
+ * available here is structural: a diagnostic whose start line exceeds the
12
+ * file's CURRENT line count cannot possibly be describing current content,
13
+ * regardless of what any timestamp says.
14
+ *
15
+ * CONTRACT:
16
+ * - A diagnostic citing a line beyond the file's current line count is
17
+ * DEMOTED (`stale: true`), never dropped — the underlying issue may well
18
+ * still be real, just at a coordinate this gate can no longer vouch for.
19
+ * - RE-ARMS, never latches: the verdict is recomputed from the gate's own
20
+ * inputs (current mtime, current line count) on every call, so a
21
+ * TRANSIENT shrink (truncate-then-write, a formatter pass, a checkout)
22
+ * that later restores the line un-demotes on its own next read. A
23
+ * persisted `stale: true` a caller stores between calls is a cache of the
24
+ * last-derived verdict, never an independent source of truth that
25
+ * overrides the next derivation — the #1633-V1 lesson: derive, don't latch.
26
+ * - Fail open: a file that cannot be stat'ed/read, or exceeds the size gate,
27
+ * yields no verdict at all (every diagnostic passes through unchanged).
28
+ * An unreadable/oversized file is not evidence of anything; demoting on
29
+ * it would manufacture false staleness.
30
+ * - The line count matches the LSP's own addressing, not `wc -l`: a
31
+ * document with N newline characters has N+1 ADDRESSABLE lines (the
32
+ * line after the final `\n`, or the single line of an empty document,
33
+ * is a real position a server can anchor a diagnostic to — e.g. TS1005
34
+ * "'}' expected" at EOF). `WidgetDiagnostic.line` is always
35
+ * `range.start.line + 1` from that same addressing, so the gate must
36
+ * use it too or every trailing-newline file demotes its own valid EOF
37
+ * diagnostics.
38
+ * - Cost discipline: one `statSync` per served file; the file's newlines
39
+ * are counted in bounded chunks via a raw fd read (never a full
40
+ * `readFileSync` — no whole-file string materialization, no per-render
41
+ * re-read of unchanged content), gated by a byte-size cap matching
42
+ * `captureReadContentBinding`'s precedent (`read-guard.ts`). The count is
43
+ * memoized in a small SHARED cache (module-scoped, bounded, evicted FIFO
44
+ * past a cap), keyed on BOTH mtime AND size (review round V1) — mtime
45
+ * ALONE is not a reliable cache key on hosts where its resolution is
46
+ * coarser than back-to-back writes: a truncate-then-write, a formatter
47
+ * write-back, or pi-lens's own auto-format immediately followed by the
48
+ * agent's write can land two DIFFERENT contents on the identical
49
+ * `mtimeMs`, and a mtime-only cache serves the first content's count for
50
+ * the second — measured live at 207/300 shrink/restore cycles. Requiring
51
+ * both to match narrows a same-tick collision to same-mtime-AND-same-size
52
+ * (which a shrink/restore cycle never produces, since the whole point is
53
+ * the size changed). A changed file is always recounted. Tests can still
54
+ * inject an isolated {@link LineCountCache} via {@link createLineCountCache}
55
+ * to avoid cross-test bleed.
56
+ * - On a demotion EDGE (a diagnostic that was not already flagged past-EOF
57
+ * and now is), best-effort trigger a document resync (didClose/didOpen or
58
+ * a full-sync didChange) so the desync HEALS instead of re-serving the
59
+ * same stale verdict next time (#1631's resync-before-revalidate rule).
60
+ * Resync is fire-and-forget: a failure here must never block or fail the
61
+ * render path that discovered the drift. Already-demoted entries and the
62
+ * healing direction never re-trigger a resync — only the rising edge does.
63
+ */
64
+ import * as fs from "node:fs";
65
+ import { logLatency } from "./latency-logger.js";
66
+ import { toProjectRelativePath } from "./path-utils.js";
67
+ import { STALE_LINE_MARKER } from "./stale-marker.js";
68
+ /** Marker rendered in place of a demoted diagnostic's now-untrustworthy line.
69
+ * Shares the base `STALE_LINE_MARKER` vocabulary with sibling freshness
70
+ * gates (#1622/#1633) and adds this gate's own specific reason as a suffix. */
71
+ export const PAST_EOF_STALE_MARKER = `${STALE_LINE_MARKER} — line past EOF`;
72
+ /** How many demoted cited lines a single record names before it stops. */
73
+ const MAX_LOGGED_PAST_EOF_LINES = 3;
74
+ /** Byte-size gate matching `captureReadContentBinding`'s precedent
75
+ * (`clients/read-guard.ts`'s `READ_BINDING_MAX_BYTES`) — a hot render/serve
76
+ * path must never pay to scan an unbounded file. */
77
+ const MAX_GATE_BYTES = 4 * 1024 * 1024;
78
+ /** Chunk size for the bounded newline scan — small enough to keep one read's
79
+ * memory footprint negligible, large enough that a multi-MB file finishes in
80
+ * single-digit reads rather than thousands. */
81
+ const NEWLINE_SCAN_CHUNK_BYTES = 64 * 1024;
82
+ const NEWLINE_BYTE = 0x0a;
83
+ /** Test-only isolation seam. Production code uses the shared default cache. */
84
+ export function createLineCountCache() {
85
+ return new Map();
86
+ }
87
+ // Shared across calls/render passes so N files touched once per session cost
88
+ // N recounts total, not N per render — the actual fix for the cost this gate
89
+ // exists to control. Still cannot go stale: every read re-stats first and a
90
+ // mismatched mtime always recomputes (see the module doc comment). Bounded
91
+ // via FIFO eviction (mirrors `READ_GUARD_MAX_FILES`'s precedent) so a long
92
+ // session's file count cannot grow this without limit.
93
+ const MAX_SHARED_CACHE_ENTRIES = 512;
94
+ const sharedLineCountCache = new Map();
95
+ function rememberInSharedCache(cache, filePath, entry) {
96
+ if (cache === sharedLineCountCache &&
97
+ cache.size >= MAX_SHARED_CACHE_ENTRIES &&
98
+ !cache.has(filePath)) {
99
+ const oldest = cache.keys().next().value;
100
+ if (oldest !== undefined)
101
+ cache.delete(oldest);
102
+ }
103
+ cache.set(filePath, entry);
104
+ }
105
+ /** Test-only: drop the shared memo between test files/cases. */
106
+ export function _resetSharedLineCountCacheForTests() {
107
+ sharedLineCountCache.clear();
108
+ }
109
+ /**
110
+ * Test-only: seed the shared memo with a fabricated entry, deterministically
111
+ * proving a cache HIT (and that the default cache used by callers with no
112
+ * explicit `lineCountCache` is genuinely shared across calls) without relying
113
+ * on a real write's mtime round-tripping through `fs.utimesSync` at full
114
+ * precision — a platform-specific hazard on its own, unrelated to what such a
115
+ * test is actually verifying.
116
+ */
117
+ export function _seedSharedLineCountCacheForTests(filePath, entry) {
118
+ sharedLineCountCache.set(filePath, entry);
119
+ }
120
+ /**
121
+ * Count newline bytes in `filePath` via bounded raw reads — never a full
122
+ * `readFileSync`/UTF-8 decode of the whole file. `0x0A` (LF) never appears as
123
+ * a continuation byte in UTF-8, so counting it at the byte level is exact
124
+ * regardless of encoding, and multi-byte characters spanning a chunk boundary
125
+ * cannot produce a false match (a continuation byte's high bits can never
126
+ * equal `0x0A`).
127
+ */
128
+ function countNewlinesChunked(filePath, sizeBytes) {
129
+ const fd = fs.openSync(filePath, "r");
130
+ try {
131
+ const buffer = Buffer.allocUnsafe(Math.min(NEWLINE_SCAN_CHUNK_BYTES, sizeBytes) || 1);
132
+ let newlineCount = 0;
133
+ let position = 0;
134
+ while (position < sizeBytes) {
135
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position);
136
+ if (bytesRead <= 0)
137
+ break;
138
+ for (let i = 0; i < bytesRead; i++) {
139
+ if (buffer[i] === NEWLINE_BYTE)
140
+ newlineCount++;
141
+ }
142
+ position += bytesRead;
143
+ }
144
+ return newlineCount;
145
+ }
146
+ finally {
147
+ fs.closeSync(fd);
148
+ }
149
+ }
150
+ /**
151
+ * LSP-addressable line count for `filePath`: newline count + 1, matching
152
+ * `range.start.line + 1` (a document with zero newlines still has one
153
+ * addressable line — position 0,0 — and a trailing `\n` adds one more,
154
+ * empty, addressable line after it). Memoized in `cache` (the shared default
155
+ * unless a caller injects its own) and invalidated by mtime AND size (V1 —
156
+ * mtime alone is not a reliable cache key on hosts where its resolution is
157
+ * coarser than back-to-back writes). Returns `undefined` when the file
158
+ * cannot be stat'ed, is zero bytes, exceeds the byte-size gate, or
159
+ * cannot be read (deleted, unreadable, permissions, oversized) — callers MUST
160
+ * treat `undefined` as "no verdict", not as "zero lines" (the empty-result-
161
+ * must-distinguish-clean-from-errored screen).
162
+ */
163
+ export function getCachedLineCount(filePath, cache = sharedLineCountCache) {
164
+ let stat;
165
+ try {
166
+ stat = fs.statSync(filePath);
167
+ }
168
+ catch {
169
+ return undefined;
170
+ }
171
+ // A zero-byte stat can be a truncate-then-write window. It is not evidence
172
+ // that the file has one addressable line, so fail open like an unreadable
173
+ // file rather than manufacturing a verdict from the empty read.
174
+ if (stat.size === 0)
175
+ return undefined;
176
+ const cached = cache.get(filePath);
177
+ if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
178
+ return cached.lineCount;
179
+ }
180
+ if (stat.size > MAX_GATE_BYTES)
181
+ return undefined;
182
+ let newlineCount;
183
+ try {
184
+ newlineCount = countNewlinesChunked(filePath, stat.size);
185
+ }
186
+ catch {
187
+ return undefined;
188
+ }
189
+ const lineCount = newlineCount + 1;
190
+ rememberInSharedCache(cache, filePath, {
191
+ mtimeMs: stat.mtimeMs,
192
+ size: stat.size,
193
+ lineCount,
194
+ });
195
+ return lineCount;
196
+ }
197
+ /**
198
+ * Recompute the past-EOF verdict for every diagnostic in `diagnostics`
199
+ * against `filePath`'s CURRENT line count. Sets `stale: true` on a match and
200
+ * `stale: false` when a previously-past-EOF line is back in bounds (a
201
+ * transient shrink healed) — the verdict is always derived fresh, never
202
+ * latched. Pure apart from the cached stat/read in {@link getCachedLineCount}
203
+ * and the bounded `logLatency`/`resync` calls, which fire only on a RISING
204
+ * edge (a diagnostic that was not already flagged and now is) so an
205
+ * unchanged persistent demotion doesn't re-log or re-resync on every render.
206
+ * Never throws, never mutates the input array.
207
+ */
208
+ export function demotePastEofDiagnostics(args) {
209
+ const { diagnostics } = args;
210
+ if (diagnostics.length === 0) {
211
+ return { diagnostics: [], demotedCount: 0 };
212
+ }
213
+ const lineCount = getCachedLineCount(args.filePath, args.lineCountCache ?? sharedLineCountCache);
214
+ if (lineCount === undefined) {
215
+ // Fail open: no verdict available for this file right now. Leave
216
+ // whatever `stale` value each entry already carries untouched — this
217
+ // gate has nothing to say either direction.
218
+ return { diagnostics: [...diagnostics], demotedCount: 0 };
219
+ }
220
+ const risingEdgeLines = [];
221
+ const out = diagnostics.map((d) => {
222
+ // Another gate's demotion is not this gate's to heal (#1631 drift
223
+ // entries have in-bounds lines; re-deriving here would clear them).
224
+ if (d.stale && d.staleReason !== undefined && d.staleReason !== "past-eof")
225
+ return d;
226
+ const isPastEof = typeof d.line === "number" && d.line > lineCount;
227
+ if (isPastEof === !!d.stale)
228
+ return d; // no transition either direction
229
+ if (isPastEof)
230
+ risingEdgeLines.push(d.line);
231
+ return { ...d, stale: isPastEof, staleReason: isPastEof ? "past-eof" : undefined };
232
+ });
233
+ if (risingEdgeLines.length === 0) {
234
+ return { diagnostics: out, demotedCount: 0 };
235
+ }
236
+ let sizeBytes;
237
+ try {
238
+ sizeBytes = fs.statSync(args.filePath).size;
239
+ }
240
+ catch {
241
+ sizeBytes = 0;
242
+ }
243
+ logLatency({
244
+ type: "phase",
245
+ phase: "diagnostic_past_eof",
246
+ filePath: args.cwd,
247
+ durationMs: 0,
248
+ metadata: {
249
+ store: args.store,
250
+ file: toProjectRelativePath(args.filePath, args.cwd),
251
+ sizeBytes,
252
+ actualLineCount: lineCount,
253
+ demotedCount: risingEdgeLines.length,
254
+ sampleCitedLines: risingEdgeLines.slice(0, MAX_LOGGED_PAST_EOF_LINES),
255
+ },
256
+ });
257
+ if (args.resync) {
258
+ try {
259
+ args.resync(args.filePath);
260
+ }
261
+ catch {
262
+ // Best-effort — a resync failure must never fail the render path.
263
+ }
264
+ }
265
+ return { diagnostics: out, demotedCount: risingEdgeLines.length };
266
+ }
267
+ /**
268
+ * Default resync trigger (#1641 criterion 2): re-open the file with its
269
+ * CURRENT disk content so the LSP's in-memory document is replaced rather
270
+ * than left to re-publish the same divergent diagnostics next time
271
+ * (`LSPService.openFile` sends a full-sync didChange, or a didClose/didOpen
272
+ * pair for servers that only re-scan on a fresh open — see
273
+ * `handleNotifyOpen`'s `reopenOnResync` branch). Dynamic import (mirrors
274
+ * `clients/module-report-lsp.ts`) so this module — used from widget-state.ts
275
+ * and tools/lens-diagnostics.ts — never statically depends on `clients/lsp`,
276
+ * which itself imports widget-state.ts.
277
+ *
278
+ * Fire-and-forget by design: the caller is a synchronous render path and must
279
+ * not block on (or fail because of) a resync. No LSP service, no client for
280
+ * this file's language, or a transient read/spawn failure all degrade to a
281
+ * silent no-op — the next successful touch/dispatch resyncs anyway.
282
+ */
283
+ export function resyncDocumentOnPastEof(filePath) {
284
+ void (async () => {
285
+ try {
286
+ const [{ getLSPService }, content] = await Promise.all([
287
+ import("./lsp/index.js"),
288
+ fs.promises.readFile(filePath, "utf-8"),
289
+ ]);
290
+ await getLSPService().openFile(filePath, content, {
291
+ preserveDiagnostics: false,
292
+ });
293
+ }
294
+ catch {
295
+ // Best-effort heal only.
296
+ }
297
+ })();
298
+ }
@@ -61,7 +61,7 @@
61
61
  */
62
62
  import { logBusEvent } from "./bus-events-logger.js";
63
63
  import { normalizeFilePath } from "./path-utils.js";
64
- import { createLiveBusEmitter, recordStaleBusFailure, } from "./live-bus-emitter.js";
64
+ import { createLiveBusEmitter, recordStaleBusFailure, resolveLiveBusEmitter, } from "./live-bus-emitter.js";
65
65
  import { isBusPublishEnabled } from "./bus-publish.js";
66
66
  export const BUS_DIAGNOSTICS_EVENT = "pilens:diagnostics";
67
67
  export const BUS_DIAGNOSTICS_VERSION = 1;
@@ -149,8 +149,13 @@ export function publishDiagnostics(args) {
149
149
  }
150
150
  return;
151
151
  }
152
- const busEmit = liveEmitter.get();
153
- if (!busEmit) {
152
+ const resolution = resolveLiveBusEmitter(liveEmitter, () => ({
153
+ event: BUS_DIAGNOSTICS_EVENT,
154
+ cwd: normalizeFilePath(args.cwd),
155
+ }));
156
+ if (resolution.outcome === "stale-session")
157
+ return;
158
+ if (resolution.outcome === "unwired") {
154
159
  if (!hasLoggedUnwired) {
155
160
  hasLoggedUnwired = true;
156
161
  logBusEvent({
@@ -161,6 +166,7 @@ export function publishDiagnostics(args) {
161
166
  }
162
167
  return;
163
168
  }
169
+ const busEmit = resolution.emit;
164
170
  try {
165
171
  const fileEntries = args.files.map((f) => {
166
172
  const normPath = normalizeFilePath(f.path);
@@ -204,6 +210,7 @@ export function publishDiagnostics(args) {
204
210
  outcome: "emit_failed",
205
211
  cwd: normalizeFilePath(args.cwd),
206
212
  error: String(err),
213
+ ctxSource: resolution.ctxSource,
207
214
  });
208
215
  if (!hasLoggedFailure) {
209
216
  hasLoggedFailure = true;
@@ -22,13 +22,14 @@ import { detectFileRole } from "../file-role.js";
22
22
  import { isTestFile } from "../file-utils.js";
23
23
  import { getPrimaryDispatchGroup } from "../language-policy.js";
24
24
  import { resolveLanguageRootForFile } from "../language-profile.js";
25
- import { logLatency } from "../latency-logger.js";
25
+ import { logLatency, phaseFinished, phaseStarted } from "../latency-logger.js";
26
26
  import { isSpawnableCommand } from "../installer/index.js";
27
27
  import { normalizeMapKey } from "../path-utils.js";
28
28
  import { loadPiLensProjectConfig } from "../project-lens-config.js";
29
29
  import { RUNTIME_CONFIG, getRunnerTimeoutFloorMs } from "../runtime-config.js";
30
30
  import { safeSpawnAsync } from "../safe-spawn.js";
31
31
  import { classifyDiagnostic } from "./diagnostic-taxonomy.js";
32
+ import { classifyProbeFailure, logAvailabilityDecision, startHostStallSampler, transientRetryDelayMs, } from "./runners/utils/availability-policy.js";
32
33
  import { applyDispositions } from "../diagnostic-dispositions.js";
33
34
  import { applyInlineSuppressions } from "./inline-suppressions.js";
34
35
  import { getToolPlan } from "./plan.js";
@@ -76,12 +77,49 @@ export function normalizeCacheKey(cmd) {
76
77
  const normalized = cmd.replace(/\.(cmd|exe)$/i, "").toLowerCase();
77
78
  return `session.toolCache.${normalized}`;
78
79
  }
79
- async function checkToolAvailability(command, facts) {
80
+ /** Probe budget for the generic `hasTool` availability check, ms. */
81
+ const TOOL_PROBE_TIMEOUT_MS = 5000;
82
+ /**
83
+ * Session fact holding the epoch ms after which a TRANSIENT verdict for a
84
+ * command may be re-probed. Kept beside the boolean fact so both share the
85
+ * session's lifetime — no second global to reset (#1476).
86
+ */
87
+ function transientRetryKey(command) {
88
+ return `${normalizeCacheKey(command)}.retryAt`;
89
+ }
90
+ /**
91
+ * Consecutive transient verdicts for a command, so the cooldown ESCALATES the
92
+ * way the policy documents (30 s, 60 s, 120 s … capped at 5 min) instead of
93
+ * sitting flat at 30 s.
94
+ *
95
+ * This is the highest-traffic availability consumer in the product. A flat
96
+ * cooldown here means a permanently sick host is re-probed every 30 s per
97
+ * command for the whole session — ten times the storm the policy claims to
98
+ * bound, and the one place the bound matters most. `createAvailabilityChecker`
99
+ * tracks the same counter; this keeps the two seams telling one story.
100
+ */
101
+ function transientAttemptsKey(command) {
102
+ return `${normalizeCacheKey(command)}.transientAttempts`;
103
+ }
104
+ /**
105
+ * Is `command` usable right now? Cached per session.
106
+ *
107
+ * Latch policy (#1467/#1476): a `false` from a genuine absence is durable and
108
+ * is remembered for the session. A `false` from a TIMED-OUT probe is not — this
109
+ * is the highest-traffic availability consumer in the codebase, and caching a
110
+ * host stall here silently disabled a healthy tool for every later dispatch in
111
+ * the session. A transient verdict instead holds only for a bounded cooldown,
112
+ * which also stops a sick host from being re-probed on every dispatch.
113
+ */
114
+ export async function checkToolAvailability(command, facts) {
80
115
  const key = normalizeCacheKey(command);
81
116
  const cached = facts.getSessionFact(key);
82
117
  if (cached !== undefined) {
83
118
  return cached;
84
119
  }
120
+ const retryAt = facts.getSessionFact(transientRetryKey(command));
121
+ if (retryAt !== undefined && Date.now() < retryAt)
122
+ return false;
85
123
  // A command that isn't even on disk can't pass a --version probe; the ~μs
86
124
  // stat/PATH walk saves a guaranteed-to-fail spawn round-trip per cold tool.
87
125
  if (!(await isSpawnableCommand(command))) {
@@ -89,12 +127,70 @@ async function checkToolAvailability(command, facts) {
89
127
  return false;
90
128
  }
91
129
  try {
92
- const result = await safeSpawnAsync(command, ["--version"], {
93
- timeout: 5000,
130
+ // The budget is enforced by a HOST-side timer, so measure the loop stall
131
+ // that overlapped the window and let the shared classifier read it.
132
+ const sampler = startHostStallSampler();
133
+ const startedAt = Date.now();
134
+ let result;
135
+ let hostStallMs;
136
+ try {
137
+ result = await safeSpawnAsync(command, ["--version"], {
138
+ timeout: TOOL_PROBE_TIMEOUT_MS,
139
+ });
140
+ }
141
+ finally {
142
+ hostStallMs = sampler.stop();
143
+ }
144
+ const elapsedMs = Date.now() - startedAt;
145
+ if (result.status === 0) {
146
+ facts.setSessionFact(key, true);
147
+ // The tool answered: retire the cooldown facts rather than leaving a
148
+ // stale retry deadline behind a `true`.
149
+ facts.setSessionFact(transientRetryKey(command), 0);
150
+ facts.setSessionFact(transientAttemptsKey(command), 0);
151
+ logAvailabilityDecision({
152
+ tool: command,
153
+ verdict: "available",
154
+ outcome: "success",
155
+ cause: "ok",
156
+ elapsedMs,
157
+ latched: true,
158
+ hostStallMs,
159
+ budgetMs: TOOL_PROBE_TIMEOUT_MS,
160
+ });
161
+ return true;
162
+ }
163
+ // `missing` for anything unclassified preserves the pre-#1476 meaning of
164
+ // a non-zero exit: durable, cached for the session.
165
+ const { outcome, cause, evidence } = classifyProbeFailure(result, {
166
+ hostStallMs,
167
+ unclassifiedFailureOutcome: "missing",
168
+ });
169
+ let retryAfterMs;
170
+ if (outcome === "transient") {
171
+ const attempts = (facts.getSessionFact(transientAttemptsKey(command)) ?? 0) + 1;
172
+ facts.setSessionFact(transientAttemptsKey(command), attempts);
173
+ retryAfterMs = transientRetryDelayMs(attempts, cause);
174
+ facts.setSessionFact(transientRetryKey(command), Date.now() + retryAfterMs);
175
+ }
176
+ else {
177
+ facts.setSessionFact(key, false);
178
+ facts.setSessionFact(transientAttemptsKey(command), 0);
179
+ }
180
+ logAvailabilityDecision({
181
+ tool: command,
182
+ verdict: "unavailable",
183
+ outcome,
184
+ cause,
185
+ elapsedMs,
186
+ latched: outcome !== "transient",
187
+ hostStallMs,
188
+ ...(retryAfterMs !== undefined && { retryAfterMs }),
189
+ budgetMs: TOOL_PROBE_TIMEOUT_MS,
190
+ classifiedBy: "probe",
191
+ evidence,
94
192
  });
95
- const available = result.status === 0;
96
- facts.setSessionFact(key, available);
97
- return available;
193
+ return false;
98
194
  }
99
195
  catch {
100
196
  facts.setSessionFact(key, false);
@@ -128,7 +224,10 @@ export function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, mo
128
224
  /** Authoritative workspace root; `cwd` may be a nested language root. */
129
225
  projectRoot,
130
226
  /** Ordered per-file pipeline token, when this is a post-write dispatch. */
131
- writeIndex) {
227
+ writeIndex,
228
+ /** Runtime telemetry identity, when known (#1448) — threaded to the
229
+ * worklog append; see DispatchContext.telemetryModel's doc. */
230
+ telemetryModel, telemetryProvider) {
132
231
  const absoluteFilePath = resolveRunnerPath(cwd, filePath);
133
232
  const normalizedProjectRoot = normalizeMapKey(path.resolve(projectRoot ?? cwd));
134
233
  const normalizedCwd = normalizeMapKey(resolveLanguageRootForFile(absoluteFilePath, cwd));
@@ -150,6 +249,8 @@ writeIndex) {
150
249
  blockingOnly,
151
250
  modifiedRanges,
152
251
  writeIndex,
252
+ telemetryModel,
253
+ telemetryProvider,
153
254
  async hasTool(command) {
154
255
  return checkToolAvailability(command, facts);
155
256
  },
@@ -307,6 +408,38 @@ function buildCoverageNotice(ctx, runnerLatencies) {
307
408
  const relevant = runnerLatencies.filter((r) => primary.runnerIds.includes(r.runnerId));
308
409
  if (relevant.length === 0)
309
410
  return undefined;
411
+ // #1867 catalog shape 4: this is correlation, not classification. The LSP
412
+ // touch already decided which scanner publications were absent for these
413
+ // bytes; the runner and latency assembly only preserve that exact set.
414
+ const unconfirmedServerIds = [
415
+ ...new Set(relevant.flatMap((r) => r.unconfirmedServerIds ?? [])),
416
+ ];
417
+ if (unconfirmedServerIds.length > 0) {
418
+ // The marker describes this exact silent-scanner set. A scanner can
419
+ // recover while another goes dark on the same file, so the set belongs
420
+ // in the session dedupe identity rather than only kind and path.
421
+ const silentScannerSet = [...new Set(unconfirmedServerIds)]
422
+ .map(normalizeMapKey)
423
+ // Code-unit comparator: the sorted set is a dedupe KEY, so ordering
424
+ // must be deterministic across locales — localeCompare is not.
425
+ .sort((a, b) => Number(a > b) - Number(a < b))
426
+ .join(",");
427
+ const onceKey = `${ctx.kind}:${normalizeMapKey(ctx.filePath)}:${silentScannerSet}`;
428
+ if (coverageNoticeSeen.has(onceKey))
429
+ return undefined;
430
+ coverageNoticeSeen.add(onceKey);
431
+ const shown = unconfirmedServerIds.slice(0, 4);
432
+ const remainder = unconfirmedServerIds.length - shown.length;
433
+ const marker = `${shown.join(", ")}${remainder > 0 ? ` +${remainder}` : ""}`;
434
+ return {
435
+ id: `coverage-partial:${ctx.kind}:${path.basename(ctx.filePath)}`,
436
+ message: `coverage: ${marker} silent — diagnostics are incomplete (not a clean result).`,
437
+ filePath: ctx.filePath,
438
+ severity: "warning",
439
+ semantic: "warning",
440
+ tool: "pi-lens",
441
+ };
442
+ }
310
443
  // Check primary runners first
311
444
  const primaryHasCoverage = relevant.some((r) => r.status === "succeeded" || r.status === "failed");
312
445
  if (primaryHasCoverage)
@@ -481,6 +614,9 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
481
614
  status: result.status,
482
615
  diagnosticCount: result.diagnostics.length,
483
616
  semantic: result.semantic ?? semantic,
617
+ ...(result.unconfirmedServerIds !== undefined && {
618
+ unconfirmedServerIds: result.unconfirmedServerIds,
619
+ }),
484
620
  });
485
621
  logLatency({
486
622
  type: "runner",
@@ -638,11 +774,16 @@ export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
638
774
  const blockers = visibleDiagnostics.filter((d) => d.semantic === "blocking");
639
775
  const warnings = visibleDiagnostics.filter((d) => d.semantic === "warning" || d.semantic === "none");
640
776
  const fixedItems = visibleDiagnostics.filter((d) => d.semantic === "fixed");
641
- // Append fixed and fixable diagnostics to the persistent worklog
777
+ // Append fixed and fixable diagnostics to the persistent worklog, attributed
778
+ // to the runtime's active model/provider when known (#1448).
779
+ const worklogIdentity = {
780
+ model: ctx.telemetryModel,
781
+ provider: ctx.telemetryProvider,
782
+ };
642
783
  if (fixedItems.length > 0) {
643
784
  import("../fix-worklog.js")
644
785
  .then(({ appendToWorklog }) => {
645
- appendToWorklog(ctx.cwd, fixedItems, true);
786
+ appendToWorklog(ctx.cwd, fixedItems, true, worklogIdentity);
646
787
  })
647
788
  .catch(() => { });
648
789
  }
@@ -650,7 +791,7 @@ export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
650
791
  if (fixableWarnings.length > 0) {
651
792
  import("../fix-worklog.js")
652
793
  .then(({ appendToWorklog }) => {
653
- appendToWorklog(ctx.cwd, fixableWarnings, false);
794
+ appendToWorklog(ctx.cwd, fixableWarnings, false, worklogIdentity);
654
795
  })
655
796
  .catch(() => { });
656
797
  }
@@ -755,6 +896,16 @@ function normalizeDiagnosticFilePath(ctx, rawPath) {
755
896
  async function runRunner(ctx, runner, defaultSemantic) {
756
897
  const timeoutMs = Math.max(runner.timeoutMs ?? RUNNER_TIMEOUT_MS, getRunnerTimeoutFloorMs());
757
898
  let timer;
899
+ // #1723: mark this runner as the in-flight phase for the WHOLE race below,
900
+ // not just the runner's own promise — a synchronous CPU hog inside
901
+ // `runner.run` (ast-grep-napi, tree-sitter, …) blocks the event loop before
902
+ // it ever gets to log its own completion, so `recentPhases` (which only
903
+ // records FINISHED phases) can't name it. `phaseFinished` clears on every
904
+ // exit from this race (success, runner error, or timeout) via try/finally,
905
+ // so a `loop_block` sampled after this call returns never sees a stale
906
+ // pointer. See `phaseStarted`'s doc comment for the identity-token
907
+ // reasoning and the cost note (negligible next to a runner invocation).
908
+ const phaseToken = phaseStarted(runner.id);
758
909
  try {
759
910
  const result = await Promise.race([
760
911
  runner.run(ctx).finally(() => clearTimeout(timer)),
@@ -784,6 +935,9 @@ async function runRunner(ctx, runner, defaultSemantic) {
784
935
  failureMessage: message.slice(0, 200),
785
936
  };
786
937
  }
938
+ finally {
939
+ phaseFinished(phaseToken);
940
+ }
787
941
  }
788
942
  // --- Simple Integration Helper ---
789
943
  /**