@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

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 (385) hide show
  1. package/README.md +8 -6
  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 +320 -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 +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -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 +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  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 +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -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 +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  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 +15 -1
  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-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -13,7 +13,7 @@ import path from "node:path";
13
13
  import { getGlobalPiLensDir, getProjectIgnoreGlobs, isPathIgnoredByProject, } from "../file-utils.js";
14
14
  import { STAGE_TMP_PATTERN } from "../atomic-write-staging.js";
15
15
  import { DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, } from "../file-kinds.js";
16
- import { direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, isFullyQualified, isWindowsPath, } from "../path-utils.js";
16
+ import { direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, isFullyQualified, isWindowsPath, pathsEqual, } from "../path-utils.js";
17
17
  import { ensureTool, getToolEnvironment, getToolPath, } from "../installer/index.js";
18
18
  import { resolveOpengrepConfig } from "../opengrep-config.js";
19
19
  import { isZizmorAuditTarget, resolveZizmorGitHubToken } from "../zizmor-config.js";
@@ -192,6 +192,15 @@ function canInstall(allowInstall) {
192
192
  const DIRECT_LSP_NEGATIVE_TTL_MS = Math.max(30_000, Number.parseInt(process.env.PI_LENS_DIRECT_LSP_NEGATIVE_TTL_MS ?? "600000", 10) || 600_000);
193
193
  const directLspCommandUnavailableUntil = new Map();
194
194
  const directLspCommandSkipLoggedUntil = new Map();
195
+ /** Re-arm direct-command availability for the next session. */
196
+ export function resetDirectLspCommandAvailability() {
197
+ directLspCommandUnavailableUntil.clear();
198
+ directLspCommandSkipLoggedUntil.clear();
199
+ }
200
+ /** Test seam for seeding the real negative-cache path. */
201
+ export function _markDirectLspCommandUnavailableForTests(command) {
202
+ markDirectLspCommandUnavailable(command);
203
+ }
195
204
  function pruneExpiredDirectLspNegativeEntries(now = Date.now()) {
196
205
  for (const [command, until] of directLspCommandUnavailableUntil) {
197
206
  if (until <= now) {
@@ -712,7 +721,10 @@ async function markerExists(dir, pattern) {
712
721
  * NearestRoot(includePatterns, excludePatterns?) → RootFunction
713
722
  *
714
723
  * - includePatterns: file/dir names that signal the project root (e.g. ["package.json"])
715
- * - excludePatterns: if any of these exist in a directory, skip it (e.g. ["node_modules"])
724
+ * - excludePatterns: if any of these exist in a directory, SKIP that directory and
725
+ * keep walking up toward stopDir — it does not abort resolution. A directory that
726
+ * matches an exclude pattern is simply never returned as a root; the walk still
727
+ * continues past it looking for an include-pattern hit higher up (#1671).
716
728
  * - stopDir: walk stops here (defaults to filesystem root; set to project cwd for safety)
717
729
  *
718
730
  * Equivalent to createRootDetector; exported under both names for clarity.
@@ -877,16 +889,28 @@ async function findAncestorFileAmong(startDir, candidateSegmentLists) {
877
889
  return undefined;
878
890
  }
879
891
  /**
880
- * A failed classic-compiler repair must not repeat. `ensureTool` caches
881
- * successful installs, so a repair that works short-circuits later calls on
882
- * its own. A repair that fails leaves nothing behind, and `findTsserverPath`
883
- * has three call sites (TypeScript, Vue, Svelte). Without this guard an
884
- * offline or partial install re-runs a 120 s forced reinstall on every spawn.
892
+ * A failed classic-compiler repair must not repeat within a session.
893
+ * `ensureTool` caches successful installs, so a repair that works
894
+ * short-circuits later calls on its own. A repair that fails leaves nothing
895
+ * behind, and `findTsserverPath` has three call sites (TypeScript, Vue,
896
+ * Svelte). Without this guard an offline or partial install re-runs a 120 s
897
+ * forced reinstall on every spawn.
898
+ *
899
+ * The guard is a plain module-level flag, so without an explicit re-arm it
900
+ * would latch for the whole extension-host process — a repair that failed
901
+ * once (transient registry hiccup) would stay unrepairable for every later
902
+ * session in that process. `resetClassicTsRepairGuard` re-arms it; callers
903
+ * wire that into `session_start` alongside the other per-session resets
904
+ * (#1570).
885
905
  */
886
906
  let classicTsRepairAttempted = false;
907
+ /** Re-arm the classic-repair guard so a new session gets its own attempt. */
908
+ export function resetClassicTsRepairGuard() {
909
+ classicTsRepairAttempted = false;
910
+ }
887
911
  /** Test hook — clears the per-process classic-repair guard. */
888
912
  export function _resetClassicTsRepairForTests() {
889
- classicTsRepairAttempted = false;
913
+ resetClassicTsRepairGuard();
890
914
  }
891
915
  /**
892
916
  * Directories that may hold the TypeScript package next to a resolved `tsc`
@@ -1559,15 +1583,120 @@ export const GoServer = {
1559
1583
  return { ...result, initialization: { ui: { semanticTokens: true } } };
1560
1584
  },
1561
1585
  };
1562
- async function hasWorkspaceSection(cargoPath) {
1586
+ async function readTextFileOrUndefined(filePath) {
1563
1587
  try {
1564
- const { readFile } = await import("node:fs/promises");
1565
- const content = await readFile(cargoPath, "utf-8");
1566
- return /^\s*\[workspace\]/m.test(content);
1588
+ return await readFile(filePath, "utf-8");
1567
1589
  }
1568
1590
  catch {
1591
+ return undefined;
1592
+ }
1593
+ }
1594
+ /**
1595
+ * Slice out ONE top-level TOML table's raw body — from its `[name]` heading
1596
+ * to the next top-level `[...]`/`[[...]]` heading or EOF. `members`/`exclude`
1597
+ * must be read from the `[workspace]` table specifically: `[package]` has its
1598
+ * OWN `exclude` key (the standard cargo-publish exclude list, conventionally
1599
+ * written above `[workspace]` in a virtual-manifest-less root crate), and a
1600
+ * whole-file regex would misread it as workspace membership (#1671 F4).
1601
+ */
1602
+ function extractTomlTableSection(content, tableName) {
1603
+ const heading = new RegExp(`^\\[${tableName}\\][ \\t]*(?:#.*)?$`, "m");
1604
+ const match = heading.exec(content);
1605
+ if (!match)
1606
+ return "";
1607
+ const rest = content.slice(match.index + match[0].length);
1608
+ const nextHeading = rest.match(/^\[{1,2}[^\]]+\]{1,2}[ \t]*(?:#.*)?$/m);
1609
+ return nextHeading?.index !== undefined ? rest.slice(0, nextHeading.index) : rest;
1610
+ }
1611
+ function parseTomlStringArray(content, key) {
1612
+ const match = content.match(new RegExp(`^[ \\t]*${key}[ \\t]*=[ \\t]*\\[([\\s\\S]*?)\\]`, "m"));
1613
+ if (!match)
1614
+ return [];
1615
+ return [...match[1].matchAll(/"([^"]*)"|'([^']*)'/g)].map((m) => (m[1] ?? m[2] ?? "").trim());
1616
+ }
1617
+ /** Turn one `/`-delimited glob SEGMENT into a regex source: `*` matches any
1618
+ * run of characters within the segment, `?` matches exactly one character,
1619
+ * everything else is escaped and literal. */
1620
+ function segmentGlobToRegExpSource(segment) {
1621
+ let out = "";
1622
+ for (const ch of segment) {
1623
+ if (ch === "*")
1624
+ out += "[^/]*";
1625
+ else if (ch === "?")
1626
+ out += "[^/]";
1627
+ else
1628
+ out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1629
+ }
1630
+ return out;
1631
+ }
1632
+ /**
1633
+ * A `members`/`exclude` entry names a path exactly, or globs it segment by
1634
+ * segment: `*` and `?` inside a segment work at any depth — one wildcard
1635
+ * segment (`crates/*`), a bare `*`, or several chained together for a
1636
+ * deeper fixed-depth layout — matching cargo's own `glob`-crate semantics
1637
+ * for a fixed number of path components.
1638
+ *
1639
+ * KNOWN LIMITATION (#1671 F6, documented rather than implemented): a
1640
+ * recursive `**` segment (matching a variable number of path components) is
1641
+ * NOT supported and never matches — cargo workspaces that rely on `**` to
1642
+ * pull in an arbitrarily-nested crate tree will under-hoist (the crate stays
1643
+ * independently rooted instead of joining the workspace). This is
1644
+ * deliberately out of #1671's scope: the common, and the issue's fixture,
1645
+ * shape is an explicit fixed-depth `members` list.
1646
+ */
1647
+ function matchesCargoWorkspacePattern(pattern, relativePath) {
1648
+ const normalized = pattern.replace(/\/+$/, "");
1649
+ if (normalized.includes("**"))
1650
+ return false;
1651
+ const patternSegments = normalized.split("/");
1652
+ const pathSegments = relativePath.split("/");
1653
+ if (patternSegments.length !== pathSegments.length)
1654
+ return false;
1655
+ return patternSegments.every((segment, i) => new RegExp(`^${segmentGlobToRegExpSource(segment)}$`).test(pathSegments[i]));
1656
+ }
1657
+ /**
1658
+ * Given an ancestor Cargo.toml's raw contents that already contains a
1659
+ * `[workspace]` table, decide whether it actually claims `childDir` as a
1660
+ * member. Cargo's own default is that a bare `[workspace]` with no `members`
1661
+ * key claims only the manifest's own package — a nested crate is NOT
1662
+ * implicitly swept in just because it sits underneath the workspace root on
1663
+ * disk. Without this check any Cargo.toml with a `[workspace]` table would
1664
+ * hoist every crate below it, including ones the workspace never declared
1665
+ * (#1671) — the same defect shape as an undeclared Maven sibling module.
1666
+ *
1667
+ * KNOWN LIMITATION (#1671 F7, documented rather than implemented): cargo also
1668
+ * treats a crate as an implicit workspace member when the workspace root's
1669
+ * OWN `[package]` has a `path = "..."` dependency on it and no explicit
1670
+ * `members` key is present at all. That dependency-graph inference is not
1671
+ * evaluated here — a workspace relying on it alone (no `members` key) reads
1672
+ * as claiming nothing, so its crates stay independently rooted rather than
1673
+ * being (correctly, per cargo) swept in. An explicit `members` list — the
1674
+ * common case and the one #1671's fixtures exercise — is unaffected.
1675
+ */
1676
+ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
1677
+ const relativePath = path.relative(parentDir, childDir).split(path.sep).join("/");
1678
+ if (relativePath === "" || relativePath.startsWith(".."))
1679
+ return false;
1680
+ const workspaceSection = extractTomlTableSection(workspaceContent, "workspace");
1681
+ const excluded = parseTomlStringArray(workspaceSection, "exclude");
1682
+ if (excluded.some((pattern) => matchesCargoWorkspacePattern(pattern, relativePath))) {
1569
1683
  return false;
1570
1684
  }
1685
+ const members = parseTomlStringArray(workspaceSection, "members");
1686
+ return members.some((pattern) => matchesCargoWorkspacePattern(pattern, relativePath));
1687
+ }
1688
+ /**
1689
+ * Bound a monorepo-hoist walk-up by the session ceiling: when `file` is inside
1690
+ * the session cwd, the walk must stop AT the cwd rather than climb past it —
1691
+ * matching enforceLspRootCeiling's clamp semantics but avoiding the wasted stat
1692
+ * calls of walking past a boundary we would clamp back down anyway. When
1693
+ * `file` is outside the session (isolated tests, out-of-session API callers),
1694
+ * enforceLspRootCeiling is a no-op, so the walk is left unbounded (`undefined`).
1695
+ */
1696
+ function sessionHoistCeiling(sessionCwd, file) {
1697
+ return isSameOrWithin(path.resolve(sessionCwd), path.resolve(file))
1698
+ ? path.resolve(sessionCwd)
1699
+ : undefined;
1571
1700
  }
1572
1701
  function RustWorkspaceRoot() {
1573
1702
  const crateRoot = createRootDetector(["Cargo.toml", "Cargo.lock"]);
@@ -1575,21 +1704,131 @@ function RustWorkspaceRoot() {
1575
1704
  const root = await crateRoot(file);
1576
1705
  if (!root)
1577
1706
  return undefined;
1707
+ const sessionCwd = process.cwd();
1708
+ // The walk-up for an ancestor Cargo.toml with a [workspace] table stays
1709
+ // bounded by the same session ceiling the crate-root lookup above already
1710
+ // enforces (enforceLspRootCeiling) — a monorepo hoist must never cross the
1711
+ // session boundary just because a workspace manifest happens to sit above
1712
+ // it (#1671).
1713
+ const stop = sessionHoistCeiling(sessionCwd, file);
1578
1714
  let current = root;
1579
1715
  const fsRoot = path.parse(current).root;
1580
1716
  while (true) {
1717
+ if (stop !== undefined && current === stop)
1718
+ break;
1581
1719
  const parent = path.dirname(current);
1582
1720
  if (parent === current || parent === fsRoot)
1583
1721
  break;
1584
- const parentCargo = path.join(parent, "Cargo.toml");
1585
- if (await hasWorkspaceSection(parentCargo)) {
1586
- return parent;
1722
+ const parentCargoPath = path.join(parent, "Cargo.toml");
1723
+ const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
1724
+ if (parentCargoContent !== undefined && /^\s*\[workspace\]/m.test(parentCargoContent)) {
1725
+ // Test membership against the ORIGINAL crate dir (`root`), never the
1726
+ // walk cursor (`current`, which may already have climbed through
1727
+ // gap directories that have no Cargo.toml of their own) — a
1728
+ // `members = ["crates/foo"]` or `["crates/*"]` entry is relative to
1729
+ // the workspace root, spanning the whole gap in one hop (#1671 F1).
1730
+ if (cargoWorkspaceDeclaresMember(parentCargoContent, parent, root)) {
1731
+ return enforceLspRootCeiling(parent, sessionCwd, file);
1732
+ }
1733
+ // A workspace root exists here but its `members`/`exclude` tables do
1734
+ // not claim this crate — stop climbing; the crate stays independently
1735
+ // rooted rather than being swept into a workspace it opted out of.
1736
+ break;
1587
1737
  }
1588
1738
  current = parent;
1589
1739
  }
1590
1740
  return root;
1591
1741
  };
1592
1742
  }
1743
+ /**
1744
+ * Resolve whether `parentPomPath`'s <modules> block declares `childDir` as one
1745
+ * of its member modules. Maven multi-module hoisting must only chain through
1746
+ * poms that actually declare the child (an undeclared sibling directory that
1747
+ * merely happens to sit next to a parent pom stays independently rooted) —
1748
+ * this is the "Maven module-chain verification" #1671 asks for, as opposed to
1749
+ * rust-analyzer's simpler "any ancestor [workspace] wins" hoist.
1750
+ */
1751
+ /**
1752
+ * Strip XML comments to a fixed point rather than in one pass: a single
1753
+ * `.replace()` can leave a residual `<!--` behind on adversarially-nested
1754
+ * input (CodeQL flags this class as "incomplete multi-character
1755
+ * sanitization" — the removal of one comment can expose a delimiter that
1756
+ * was itself inside another). Looping until nothing changes closes that gap.
1757
+ */
1758
+ function stripXmlComments(content) {
1759
+ let result = content;
1760
+ let previous;
1761
+ do {
1762
+ previous = result;
1763
+ result = result.replace(/<!--[\s\S]*?-->/g, "");
1764
+ } while (result !== previous);
1765
+ return result;
1766
+ }
1767
+ async function declaresMavenModule(parentDir, parentPomPath, childDir) {
1768
+ try {
1769
+ const content = stripXmlComments(await readFile(parentPomPath, "utf-8"));
1770
+ const modulesBlock = content.match(/<modules>([\s\S]*?)<\/modules>/);
1771
+ if (!modulesBlock)
1772
+ return false;
1773
+ const resolvedChild = path.resolve(childDir);
1774
+ for (const match of modulesBlock[1].matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
1775
+ // Case-insensitive / realpath-aware compare (#1671 F8): a declared
1776
+ // `<module>Foo</module>` must still match a directory actually named
1777
+ // `foo` on a case-insensitive filesystem, the same class of check the
1778
+ // rest of this codebase does via `pathsEqual` (#1139/#1150).
1779
+ if (pathsEqual(path.resolve(parentDir, match[1]), resolvedChild))
1780
+ return true;
1781
+ }
1782
+ return false;
1783
+ }
1784
+ catch {
1785
+ return false;
1786
+ }
1787
+ }
1788
+ function JavaWorkspaceRoot() {
1789
+ const moduleRoot = createRootDetector(["pom.xml", "build.gradle", ".classpath"]);
1790
+ return async (file) => {
1791
+ const root = await moduleRoot(file);
1792
+ if (!root)
1793
+ return undefined;
1794
+ // Only a Maven (pom.xml) module chain-hoists here — Gradle/.classpath module
1795
+ // roots are returned as found; multi-module Gradle wiring (settings.gradle)
1796
+ // is a different shape and out of scope for #1671.
1797
+ if (!(await markerExists(root, "pom.xml")))
1798
+ return root;
1799
+ const sessionCwd = process.cwd();
1800
+ const stop = sessionHoistCeiling(sessionCwd, file);
1801
+ // `current` is the walk CURSOR — it climbs over gap directories (ones
1802
+ // with no pom.xml of their own, e.g. a `<module>sub/dir</module>` entry
1803
+ // spanning more than one filesystem level). `lastHop` is the last
1804
+ // CONFIRMED module boundary; it is what the next ancestor pom's
1805
+ // <modules> must declare, and it is what actually gets returned — never
1806
+ // the cursor, which can land on a bare gap directory that is not itself
1807
+ // a valid module root (#1671 F2).
1808
+ let current = root;
1809
+ let lastHop = root;
1810
+ const fsRoot = path.parse(current).root;
1811
+ while (true) {
1812
+ if (stop !== undefined && current === stop)
1813
+ break;
1814
+ const parent = path.dirname(current);
1815
+ if (parent === current || parent === fsRoot)
1816
+ break;
1817
+ if (await markerExists(parent, "pom.xml")) {
1818
+ const parentPom = path.join(parent, "pom.xml");
1819
+ if (!(await declaresMavenModule(parent, parentPom, lastHop)))
1820
+ break;
1821
+ current = parent;
1822
+ lastHop = parent;
1823
+ continue;
1824
+ }
1825
+ // No pom.xml here — a gap directory, not a module boundary. Keep
1826
+ // climbing without advancing `lastHop`.
1827
+ current = parent;
1828
+ }
1829
+ return enforceLspRootCeiling(lastHop, sessionCwd, file);
1830
+ };
1831
+ }
1593
1832
  export const RustServer = {
1594
1833
  id: "rust",
1595
1834
  name: "rust-analyzer",
@@ -1819,7 +2058,7 @@ export const JavaServer = createInteractiveServer({
1819
2058
  id: "java",
1820
2059
  name: "JDT Language Server",
1821
2060
  extensions: KIND_EXTENSIONS["java"],
1822
- root: RootWithFallback(createRootDetector(["pom.xml", "build.gradle", ".classpath"])),
2061
+ root: RootWithFallback(JavaWorkspaceRoot()),
1823
2062
  language: "java",
1824
2063
  command: () => process.env.JDTLS_PATH || "jdtls",
1825
2064
  args: (root) => createLombokJdtlsArgs(root),
@@ -2024,6 +2263,20 @@ export const ClojureServer = {
2024
2263
  }, options?.allowInstall);
2025
2264
  },
2026
2265
  };
2266
+ export const CueServer = {
2267
+ id: "cue",
2268
+ name: "CUE Language Server",
2269
+ extensions: KIND_EXTENSIONS["cue"],
2270
+ root: RootWithFallback(createRootDetector(["cue.mod", ".git"])),
2271
+ async spawn(root, options) {
2272
+ return resolveAndLaunch({
2273
+ candidates: ["cue"],
2274
+ args: ["lsp", "serve"],
2275
+ cwd: root,
2276
+ managedToolId: "cue",
2277
+ }, options?.allowInstall);
2278
+ },
2279
+ };
2027
2280
  export const TerraformServer = {
2028
2281
  id: "terraform",
2029
2282
  name: "Terraform LSP",
@@ -2402,6 +2655,13 @@ export const AstGrepServer = {
2402
2655
  // (it falls back to napi when the ast-grep binary is absent — Gate B).
2403
2656
  root: RootWithFallback(createRootDetector(["sgconfig.yml", "sgconfig.yaml"]), RootWithFallback(NearestRoot([".git"]), async () => process.cwd())),
2404
2657
  availabilityKey: "ast-grep",
2658
+ // #1714: the one auxiliary with a measured wedge ceiling. A `lens_diagnostics
2659
+ // mode=full` sweep of 225 files drove this server into an unrecoverable stall
2660
+ // twice in two exposures (writes outstanding 5.9 s and 9.4 s, then a forced
2661
+ // shutdown that timed out both the request and the exit notify). It re-parses
2662
+ // the whole file on every didOpen, so it absorbs a sweep more slowly than the
2663
+ // other scanners — hold it to half the shared default.
2664
+ notifyInflightLimit: 4,
2405
2665
  // First scan of a session compiles the rules.
2406
2666
  initializeTimeoutMs: 15000,
2407
2667
  async spawn(root, options) {
@@ -2564,6 +2824,7 @@ export const LSP_SERVERS = [
2564
2824
  MarksmanServer,
2565
2825
  OCamlServer,
2566
2826
  ClojureServer,
2827
+ CueServer,
2567
2828
  TerraformServer,
2568
2829
  NixServer,
2569
2830
  BashServer,
@@ -0,0 +1,22 @@
1
+ /** Process-lifetime history of successful LSP spawn plus initialize durations. */
2
+ const MAX_SERVER_HISTORIES = 64;
3
+ const successfulSpawnDurationMs = new Map();
4
+ export function recordSuccessfulLspSpawn(serverId, durationMs) {
5
+ if (!Number.isFinite(durationMs) || durationMs < 0)
6
+ return;
7
+ successfulSpawnDurationMs.delete(serverId);
8
+ successfulSpawnDurationMs.set(serverId, durationMs);
9
+ while (successfulSpawnDurationMs.size > MAX_SERVER_HISTORIES) {
10
+ const oldest = successfulSpawnDurationMs.keys().next().value;
11
+ if (oldest === undefined)
12
+ break;
13
+ successfulSpawnDurationMs.delete(oldest);
14
+ }
15
+ }
16
+ export function getSuccessfulLspSpawnDurationMs(serverId) {
17
+ return successfulSpawnDurationMs.get(serverId);
18
+ }
19
+ /** Test-only: isolate process-lifetime history between cases. */
20
+ export function _clearSuccessfulLspSpawnHistoryForTests() {
21
+ successfulSpawnDurationMs.clear();
22
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * LSP `textDocument/didChange` sync-kind negotiation (#1669).
3
+ *
4
+ * A server advertises how it wants document changes described via
5
+ * `ServerCapabilities.textDocumentSync`, either as a bare
6
+ * `TextDocumentSyncKind` number (the legacy pre-3.0 shape) or as a
7
+ * `TextDocumentSyncOptions` object whose `change` field carries the kind.
8
+ * pi-lens always sent a single whole-document `{ text }` change event — valid
9
+ * for `Full` (1) and harmless for `None` (0, since the server ignores content
10
+ * changes entirely), but out of spec for `Incremental` (2): an
11
+ * Incremental-only server expects every change event to carry a `range`.
12
+ *
13
+ * This module is the pure negotiation core, mirroring `position-encoding.ts`'s
14
+ * shape: read the kind from the server's `initialize` reply, defaulting to
15
+ * `Full` when the server doesn't advertise one — the same shape pi-lens has
16
+ * always sent, so an unrecognized/absent value never regresses `Full`/`None`
17
+ * behavior.
18
+ */
19
+ export const TEXT_DOCUMENT_SYNC_KIND_NONE = 0;
20
+ export const TEXT_DOCUMENT_SYNC_KIND_FULL = 1;
21
+ export const TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL = 2;
22
+ function isSyncKind(value) {
23
+ return value === 0 || value === 1 || value === 2;
24
+ }
25
+ /**
26
+ * The `change` sync kind the server negotiated, read from its `initialize`
27
+ * reply. Defaults to `Full` (pi-lens's historical always-whole-document
28
+ * behavior) when the server omits `textDocumentSync` entirely, or advertises
29
+ * a shape/value this function doesn't recognize.
30
+ */
31
+ export function negotiateSyncKind(serverCapabilities) {
32
+ const sync = serverCapabilities?.textDocumentSync;
33
+ // Legacy shape: the whole field IS the kind.
34
+ if (isSyncKind(sync))
35
+ return sync;
36
+ // 3.0+ shape: `TextDocumentSyncOptions.change`.
37
+ if (sync && typeof sync === "object") {
38
+ const change = sync.change;
39
+ if (isSyncKind(change))
40
+ return change;
41
+ }
42
+ return TEXT_DOCUMENT_SYNC_KIND_FULL;
43
+ }
@@ -38,7 +38,40 @@ import { normalizeMapKey } from "../path-utils.js";
38
38
  // Constants
39
39
  // ---------------------------------------------------------------------------
40
40
  export const TSSERVER_REQUEST_COMMAND = "typescript.tsserverRequest";
41
- function classifyProjectInfo(body) {
41
+ /**
42
+ * tsserver's synthetic inferred-project name, as its own source builds it:
43
+ * `"/dev/null/inferredProject" + counter + "*"`. `/dev/null` is a deliberate
44
+ * sentinel prefix — no real config file can live there — and matching the WHOLE
45
+ * string against it is the only safe test.
46
+ *
47
+ * #1645 review F4: an earlier version also matched an UNANCHORED
48
+ * `/inferred[-_ ]?project/i` anywhere in the string. That inverted the verdict
49
+ * for a real project in a directory a human happened to name that way —
50
+ * `/repo/inferred-project/tsconfig.json` classified as INFERRED, which demoted
51
+ * its genuine blocking errors. A classifier that gates authority must never key
52
+ * off user-controlled directory names.
53
+ *
54
+ * The optional drive-letter/backslash arm covers a host that normalizes the
55
+ * sentinel through win32 path handling before it reaches us; the `/dev/null`
56
+ * prefix is still required.
57
+ */
58
+ const INFERRED_PROJECT_SENTINEL = /^(?:[A-Za-z]:)?[/\\]dev[/\\]null[/\\]inferredProject\d*\*?$/i;
59
+ /**
60
+ * Classify one tsserver `projectInfo` response body.
61
+ *
62
+ * `configFileName` is tsserver's own answer to "which project owns this file".
63
+ * A real project answers with a `tsconfig.json`/`jsconfig.json` path; a file
64
+ * that matches no project's `include`/`files` lands in tsserver's synthetic
65
+ * inferred project, whose `configFileName` is a `/dev/null/inferredProject1*`
66
+ * placeholder. The two are the same field, so the placeholder shape is the only
67
+ * thing that separates "checked against the project's real compiler options"
68
+ * from "checked against defaults nobody configured" (#1640).
69
+ *
70
+ * Exported so the diagnostic-demotion seam (`inferred-project.ts`) and the
71
+ * #1412 telemetry probe share ONE classifier — a second hand-rolled
72
+ * configFileName test would be a parallel source of truth for the same verdict.
73
+ */
74
+ export function classifyProjectInfo(body) {
42
75
  if (!body || typeof body !== "object") {
43
76
  return { projectKind: "unassociated", association: "unassociated" };
44
77
  }
@@ -47,8 +80,7 @@ function classifyProjectInfo(body) {
47
80
  ? info.configFileName
48
81
  : undefined;
49
82
  const inferred = configFile
50
- ? /(?:^|[/\\])inferredProject\d*\*?$/i.test(configFile) ||
51
- /inferred[-_ ]?project/i.test(configFile)
83
+ ? INFERRED_PROJECT_SENTINEL.test(configFile)
52
84
  : false;
53
85
  const projectKind = configFile
54
86
  ? inferred
@@ -132,6 +164,50 @@ export async function probeTsserverProjectIdentity(options) {
132
164
  // Best-effort telemetry: command errors/timeouts never reach diagnostics.
133
165
  }
134
166
  }
167
+ /**
168
+ * #1640: ask tsserver which project owns `file`, on demand, through the same
169
+ * `typescript.tsserverRequest` escape hatch the sync-diagnostics helpers use.
170
+ *
171
+ * Unlike {@link probeTsserverProjectIdentity} — fire-and-forget telemetry
172
+ * sampled once per didOpen — this is a request/response call a caller awaits
173
+ * because it needs the answer to decide how to RENDER a diagnostic.
174
+ *
175
+ * Routed through `executeReadOnlyCommandOnLiveClient` ONLY, never
176
+ * `executeCommand` (#1645 review F1/F2). The mutation channel would open the
177
+ * `workspace/applyEdit` acceptance window, clobber a concurrent command's
178
+ * `activeMutationContext`, carry the 30s mutation backstop instead of the short
179
+ * probe one, and spawn a language-server fleet to answer. All four are wrong for
180
+ * a passive render-path question. A service without the read-only channel is
181
+ * UNKNOWN, not a reason to fall back.
182
+ *
183
+ * Returns `undefined` for every "we do not know" path: no read-only channel, no
184
+ * live server for the file, the command is not advertised (non-classic server,
185
+ * older typescript-language-server), the command was not executed, the response
186
+ * envelope is not `{success:true}`, or the call threw/timed out. `undefined`
187
+ * must never be treated as "inferred" — an unanswered probe is not a verdict
188
+ * (AGENTS.md shape 10: an empty result must distinguish clean from unavailable).
189
+ */
190
+ export async function fetchTsserverProjectIdentity(svc, file) {
191
+ try {
192
+ if (typeof svc.executeReadOnlyCommandOnLiveClient !== "function") {
193
+ return undefined;
194
+ }
195
+ // No `getAdvertisedCommands` pre-flight: that helper routes through
196
+ // `getClientForFile`, which SPAWNS. The read-only channel already enforces
197
+ // allowlist-by-advertisement server-side and answers `executed:false` for
198
+ // a server that never advertised the command.
199
+ const outcome = await svc.executeReadOnlyCommandOnLiveClient(file, TSSERVER_REQUEST_COMMAND, ["projectInfo", { file, needFileNameList: false }]);
200
+ if (!outcome.executed)
201
+ return undefined;
202
+ const response = outcome.result;
203
+ if (!response || response.success !== true)
204
+ return undefined;
205
+ return classifyProjectInfo(response.body);
206
+ }
207
+ catch {
208
+ return undefined;
209
+ }
210
+ }
135
211
  // ---------------------------------------------------------------------------
136
212
  // Helpers
137
213
  // ---------------------------------------------------------------------------
@@ -232,6 +232,49 @@ export const SERVER_DIAGNOSTIC_STRATEGIES = {
232
232
  // warm-up attempt and its retry, with zero diagnostics either time).
233
233
  silentOnClean: true,
234
234
  },
235
+ // cue lsp serve (#1522, #1519, #1520). Measured directly against the real
236
+ // v0.17.1 binary (`getWorkspaceDiagnosticsSupport` reports
237
+ // `mode: "push-only"`; no pull support advertised).
238
+ //
239
+ // It reports load/parse (syntax) errors as you type, but leaves
240
+ // conflicting-value/constraint-failure (evaluation) errors to `cue vet`
241
+ // — the #1522 cue-vet auxiliary runner covers that gap. So the syntax
242
+ // diagnostics this strategy governs are a REAL but PARTIAL signal, not
243
+ // full CUE validation.
244
+ //
245
+ // silentOnClean: true — the load-bearing finding. A cold `didOpen` on an
246
+ // already-CLEAN file publishes NOTHING inside the wait budget (measured:
247
+ // `touchFile` returns `inconclusive: true, inconclusiveReason:
248
+ // "diagnostics-wait"` after the full budget, never an affirmative empty
249
+ // array). This reproduces the #1520 review's original "reports
250
+ // conclusively clean while still waiting" concern for the clean-cold-open
251
+ // case specifically. It is NOT silent on every transition, though: once a
252
+ // document is open and a `didChange` lands, the server DOES publish —
253
+ // both the error (edit clean→broken: confirmed, ~930ms warm) and an
254
+ // explicit empty array clearing it (edit broken→clean: confirmed,
255
+ // ~320ms warm, `contentHash`-bound). That edit-triggered empty publish is
256
+ // exactly what the shared push-only clean-confirm gate needs
257
+ // `silentOnClean` for — it only ever governs the "no publish, notify
258
+ // succeeded" case, never a case that already got a real publish.
259
+ //
260
+ // seedFirstPush: true — the one cold-open case that DOES publish (a
261
+ // file that is already broken) sends the complete single diagnostic on
262
+ // its first push; there is no observed partial-then-complete sequence to
263
+ // wait out.
264
+ //
265
+ // aggregateWaitMs: 2000 covers the measured warm edit-to-publish latency
266
+ // (~930ms, syntax reparse) with margin; a cold spawn's first push (~2.1s
267
+ // end-to-end including process start) is bounded separately by
268
+ // maxClientWaitMs, not this per-diagnostics budget.
269
+ cue: {
270
+ seedFirstPush: true,
271
+ pullRetryBudgetMs: 0,
272
+ debounceMs: 150,
273
+ aggregateWaitMs: 2000,
274
+ expectSemanticSecondPush: false,
275
+ reopenOnResync: false,
276
+ silentOnClean: true,
277
+ },
235
278
  };
236
279
  /** Native TS7 can publish multiple versionless partial-program snapshots for a
237
280
  * single open. Its first push is therefore provisional; a quiet window, rather