@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
@@ -1,14 +1,17 @@
1
1
  /** Lazy dispatch integration seam for startup-cost control (#1394). */
2
- let integrationPromise;
2
+ import { createLazyImport } from "../lazy-import.js";
3
+ const lazyIntegration = createLazyImport(() => import("./integration.js"));
3
4
  /** Start loading the runner graph once; callers may fire-and-forget this. */
4
5
  export function warmDispatchIntegration() {
5
- return (integrationPromise ??= import("./integration.js"));
6
+ return lazyIntegration.get();
6
7
  }
7
8
  /** Await the same promise used by session-start warming and first use. */
8
9
  export function loadDispatchIntegration() {
9
10
  return warmDispatchIntegration();
10
11
  }
11
- /** Test-only reset; production sessions intentionally retain the promise. */
12
+ /** Test-only reset; production sessions intentionally retain the promise
13
+ * across successful loads (a rejection already evicts itself — see
14
+ * `createLazyImport`). */
12
15
  export function resetDispatchIntegrationForTests() {
13
- integrationPromise = undefined;
16
+ lazyIntegration.resetForTests();
14
17
  }
@@ -221,6 +221,11 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
221
221
  capabilities: ["types", "lint"],
222
222
  writeGroups: [primary("clojure")],
223
223
  },
224
+ cue: {
225
+ name: "CUE Linting",
226
+ capabilities: ["types", "lint"],
227
+ writeGroups: [primary("cue")],
228
+ },
224
229
  terraform: {
225
230
  name: "Terraform Linting",
226
231
  capabilities: ["types", "lint", "security"],
@@ -15,6 +15,7 @@ import { logLatency } from "../../latency-logger.js";
15
15
  import { hasEslintConfig } from "../../tool-policy.js";
16
16
  import { enabledAuxiliaryLspServerIds } from "../auxiliary-lsp.js";
17
17
  import { classifyDefect } from "../diagnostic-taxonomy.js";
18
+ import { recordDegradationOnce } from "../../degradation-ledger.js";
18
19
  import { isAuxiliaryLspAlive } from "../../lsp/index.js";
19
20
  import { resolveAstGrepNativeExe } from "../../lsp/wait-policy/index.js";
20
21
  import { PRIORITY } from "../priorities.js";
@@ -25,22 +26,179 @@ const UNSUPPORTED_RULE_ID_SAMPLE_SIZE = 5;
25
26
  export function resetAstGrepUnsupportedLanguageLog() {
26
27
  defaultUnsupportedLanguageLog.clear();
27
28
  }
28
- // Lazy load the napi package
29
+ // Lazy load the napi package.
29
30
  let sg;
30
- let sgLoadAttempted = false;
31
+ /**
32
+ * In-flight load, shared by every caller (#1567). The per-edit fallback
33
+ * runner and the session-start scanner (clients/project-diagnostics/scanner.ts)
34
+ * both call `loadSg()` and can race. Pre-fix, the "attempted" flag was set
35
+ * before the load and read by the SECOND caller as "already tried" while the
36
+ * first load was still pending and about to succeed — a false-negative
37
+ * STARVATION, not a duplicate load: the second caller got back `undefined`
38
+ * for a load that was in flight and would have succeeded, not a second
39
+ * redundant `import()`. Sharing one promise means every concurrent caller
40
+ * observes the SAME outcome instead of a subset of them starving.
41
+ *
42
+ * Evicted on settle (#1536's pattern, see clients/tree-sitter-client.ts) — a
43
+ * rejected load must not be remembered as the permanent answer; only
44
+ * concurrent callers during the SAME attempt observe this promise.
45
+ */
46
+ let sgLoadPromise;
47
+ /**
48
+ * Set on ANY load failure and cleared only by `resetAstGrepNapiLoadState()`
49
+ * at session_start (#1567 review round 2, F2).
50
+ *
51
+ * This holds for BOTH genuine and classified-transient failures, which is
52
+ * narrower than the original design (a cooldown-then-retry for transient
53
+ * failures within the same session). The reason: `loadAstGrepNapi()`
54
+ * (clients/deps/ast-grep-napi.ts) resolves the addon to a `file://` URL and
55
+ * dynamically `import()`s it. Node's ESM loader permanently memoizes a
56
+ * module record that threw during evaluation — re-importing the SAME
57
+ * resolved URL replays the cached rejection, it does not re-run the load.
58
+ * A cooldown-then-retry loop would therefore call `loadAstGrepNapi()` again
59
+ * after the cooldown and get back the identical cached rejection every
60
+ * time: a retry that LOOKS like it tries again but structurally cannot
61
+ * succeed within the process. Rather than ship that, every failure holds
62
+ * until the next `session_start`, which is the point this cache can
63
+ * actually be expected to have moved on (a fresh session re-arms the latch;
64
+ * whether the underlying Node module cache also gets a fresh start depends
65
+ * on whether the host recycles the process between sessions, but re-arming
66
+ * the latch is the most this in-process code can honestly promise).
67
+ */
68
+ let sgSessionHold = false;
69
+ /**
70
+ * Why `sgSessionHold` was set — feeds the degradation-ledger message and log
71
+ * line only. It does not change retry behavior: both classes hold
72
+ * identically for the session, per `sgSessionHold`'s doc above.
73
+ */
74
+ let sgHoldReason;
75
+ /** A positively-identified, narrow errno family for a native-addon load: the
76
+ * kind of momentary FS contention (too many open files, a file mid-write,
77
+ * a transient permission/resource block) that says nothing durable about
78
+ * whether the addon itself can load. Everything NOT in this allowlist is
79
+ * genuine by default (#1567 review round 2, F1) — the original classifier
80
+ * inverted this: it matched a handful of message patterns as "genuine" and
81
+ * treated every OTHER error, including the real native-binding failure
82
+ * family (ABI mismatch, a missing platform package, a napi version
83
+ * mismatch, an unsupported arch), as transient. None of those recover on
84
+ * their own; an unrecognized error is far more likely to be one of them
85
+ * than a genuine FS hiccup. */
86
+ const TRANSIENT_ERRNO_ALLOWLIST = new Set([
87
+ "EMFILE",
88
+ "EBUSY",
89
+ "EAGAIN",
90
+ "EPERM",
91
+ "ETXTBSY",
92
+ ]);
93
+ /**
94
+ * `@ast-grep/napi`'s own terminal "this machine cannot run this addon"
95
+ * messages (from its `index.js` platform-detection shim). Already covered
96
+ * by the genuine-by-default policy above; listed explicitly so the
97
+ * classification is legible at the call site instead of an emergent
98
+ * property of "didn't match the allowlist".
99
+ */
100
+ const KNOWN_GENUINE_NATIVE_LOAD_MESSAGES = [
101
+ /cannot find native binding/i,
102
+ /failed to load native binding/i,
103
+ ];
104
+ /**
105
+ * Walk an error's `.cause` chain — Node/napi wrap the real dlopen/ABI
106
+ * failure there rather than on the top-level thrown Error — collecting
107
+ * every `code` and `message` seen along the way, so classification isn't
108
+ * blind to a nested cause (#1567 review round 2, F1).
109
+ */
110
+ function collectErrorChain(err) {
111
+ const codes = [];
112
+ const messages = [];
113
+ let current = err;
114
+ const seen = new Set();
115
+ while (current !== undefined && current !== null && !seen.has(current)) {
116
+ seen.add(current);
117
+ if (current instanceof Error) {
118
+ messages.push(current.message);
119
+ const code = current.code;
120
+ if (typeof code === "string")
121
+ codes.push(code);
122
+ current = current.cause;
123
+ }
124
+ else {
125
+ messages.push(String(current));
126
+ break;
127
+ }
128
+ }
129
+ return { codes, messages };
130
+ }
131
+ /**
132
+ * Classify a `loadAstGrepNapi()` rejection. Transient requires a positively
133
+ * identified errno match somewhere in the cause chain AND no known-genuine
134
+ * native-load message anywhere in that same chain (an errno wrapping a
135
+ * "cannot find native binding" cause is genuine, not transient — the errno
136
+ * describes how the OS reported it, not what actually failed). Everything
137
+ * else — including an error with no `code` at all — is genuine.
138
+ */
139
+ function classifyAstGrepLoadFailure(err) {
140
+ const { codes, messages } = collectErrorChain(err);
141
+ const hasKnownGenuineMessage = messages.some((message) => KNOWN_GENUINE_NATIVE_LOAD_MESSAGES.some((pattern) => pattern.test(message)));
142
+ if (hasKnownGenuineMessage)
143
+ return "genuine";
144
+ const hasTransientErrno = codes.some((code) => TRANSIENT_ERRNO_ALLOWLIST.has(code));
145
+ return hasTransientErrno ? "transient" : "genuine";
146
+ }
147
+ function recordAstGrepUnavailableOnce(reason) {
148
+ recordDegradationOnce({
149
+ kind: "ast-grep-napi-unavailable",
150
+ subject: "ast-grep-napi",
151
+ reason: reason === "genuine"
152
+ ? "native addon failed to load (durable for this session)"
153
+ : "native addon load hit a transient error (durable for this session — see sgSessionHold doc)",
154
+ });
155
+ }
31
156
  export async function loadSg() {
32
157
  if (sg)
33
158
  return sg;
34
- if (sgLoadAttempted)
35
- return undefined; // Don't retry if already failed
36
- sgLoadAttempted = true;
37
- try {
38
- sg = await loadAstGrepNapi();
39
- return sg;
40
- }
41
- catch {
159
+ if (sgLoadPromise)
160
+ return sgLoadPromise;
161
+ if (sgSessionHold) {
162
+ // #1567 review F4: a held/degraded load must be distinguishable from a
163
+ // clean scan that found nothing to report, not silently read as
164
+ // "skipped/empty" by every caller (clients/project-diagnostics/scanner.ts,
165
+ // this file's own runner) — otherwise the two are indistinguishable in
166
+ // the degradation ledger.
167
+ recordAstGrepUnavailableOnce(sgHoldReason ?? "genuine");
42
168
  return undefined;
43
169
  }
170
+ const task = (async () => {
171
+ try {
172
+ const loaded = await loadAstGrepNapi();
173
+ sg = loaded;
174
+ return loaded;
175
+ }
176
+ catch (err) {
177
+ const reason = classifyAstGrepLoadFailure(err);
178
+ sgSessionHold = true;
179
+ sgHoldReason = reason;
180
+ recordAstGrepUnavailableOnce(reason);
181
+ return undefined;
182
+ }
183
+ })();
184
+ sgLoadPromise = task;
185
+ task.finally(() => {
186
+ if (sgLoadPromise === task)
187
+ sgLoadPromise = undefined;
188
+ });
189
+ return task;
190
+ }
191
+ /**
192
+ * Re-arm the napi load latch for a new session (#1567). `sg` itself is left
193
+ * alone — a successful load stays valid and cached for the process — but a
194
+ * session hold (transient or genuine) is session-scoped state and must not
195
+ * outlive the session that set it. Called from `resetDispatchBaselines()`
196
+ * (clients/dispatch/integration.ts) beside `resetAstGrepUnsupportedLanguageLog`,
197
+ * this module's other session latch.
198
+ */
199
+ export function resetAstGrepNapiLoadState() {
200
+ sgSessionHold = false;
201
+ sgHoldReason = undefined;
44
202
  }
45
203
  // Supported extensions for NAPI
46
204
  const SUPPORTED_EXTS = [".ts", ".tsx", ".js", ".jsx", ".css", ".html", ".htm"];
@@ -156,11 +314,21 @@ export function canHandle(filePath) {
156
314
  * file. Without this, `language:` reads as a real filter but isn't one for
157
315
  * ts↔js pairs, so twin rules sharing a base name (e.g. `hardcoded-url` /
158
316
  * `hardcoded-url-js`) both match the same construct in the SAME runner
159
- * invocation (#657). TSX is deliberately its own grammar here: the primary
160
- * ast-grep CLI/LSP also treats `tsx` as distinct from `typescript`, so a
161
- * language-tagged rule only runs on the exact grammar it names. Returns
162
- * undefined for extensions this scoping doesn't apply to (css/html), where
163
- * no filtering is added.
317
+ * invocation (#657). TSX is its own grammar here too — the primary
318
+ * ast-grep CLI/LSP also treats `tsx` as distinct from `typescript` — but
319
+ * the caller's language-match check adds ONE deliberate exception on top
320
+ * of the exact-match rule: a `TypeScript`-tagged rule also runs against
321
+ * a `.tsx` file's fileLang (#1608). This is grounded empirically, not by
322
+ * analogy — tsx's grammar is a syntactic superset of typescript's for
323
+ * every construct the shipped catalog's rules target (JSX productions
324
+ * added, plus the removal of the `<T>expr` cast form, which cannot
325
+ * appear in valid `.tsx` source), and every `language: TypeScript`
326
+ * rule's fixture-test `invalid:` snippet is asserted to still match
327
+ * parsed as tsx (ast-grep-tsx-coverage.test.ts) rather than assumed.
328
+ * Without this exception the entire TS ruleset silently never runs on
329
+ * `.tsx` files. `TSX`-tagged rules stay `.tsx`-exclusive; the exception
330
+ * is TS→TSX only. Returns undefined for extensions this scoping doesn't
331
+ * apply to (css/html), where no filtering is added.
164
332
  */
165
333
  export function ruleLanguageForFile(filePath) {
166
334
  const ext = path.extname(filePath).toLowerCase();
@@ -229,6 +397,29 @@ function appendDuplicateRuleDiagnostics(diagnostics, seenRuleIds, duplicateIds,
229
397
  }
230
398
  return false;
231
399
  }
400
+ /**
401
+ * The four tiers `Diagnostic.severity` accepts (clients/dispatch/types.ts).
402
+ * `YamlRule.severity` is a free-form string straight off disk, so an unknown
403
+ * or missing value must land somewhere deliberate rather than being cast.
404
+ */
405
+ const DIAGNOSTIC_SEVERITY_TIERS = new Set([
406
+ "error",
407
+ "warning",
408
+ "info",
409
+ "hint",
410
+ ]);
411
+ /**
412
+ * Map a rule's declared YAML severity onto a `Diagnostic.severity` tier (#1777).
413
+ *
414
+ * A rule that declares nothing, or declares a value pi-lens does not model
415
+ * (ast-grep also accepts `off`), falls back to `warning` — the tier every such
416
+ * rule already reported at before #1777, so reviving hint/info never silently
417
+ * demotes an existing rule.
418
+ */
419
+ export function normalizeRuleSeverity(raw) {
420
+ const tier = raw;
421
+ return tier && DIAGNOSTIC_SEVERITY_TIERS.has(tier) ? tier : "warning";
422
+ }
232
423
  /**
233
424
  * Run the shipped ast-grep YAML ruleset against a parsed file via napi's native
234
425
  * engine, applying the same suppression policy (linter/tree-sitter overlap,
@@ -343,9 +534,27 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
343
534
  }
344
535
  // Scope TypeScript/JavaScript-tagged rules to the file's actual
345
536
  // grammar (#657) — otherwise a `-js` twin sharing generic node
346
- // kinds with its TS sibling double-fires on every .ts file.
537
+ // kinds with its TS sibling double-fires on every .ts file. TSX
538
+ // is the one deliberate exception (#1608): the tsx grammar is a
539
+ // syntactic superset of typescript's for every non-JSX construct
540
+ // (the `<T>expr` cast form is the only TS-only production, and
541
+ // it can't appear in valid .tsx source anyway), so a
542
+ // `language: TypeScript` rule still matches a tsx-parsed root
543
+ // and must run there too, or the entire TS ruleset (120 of 263
544
+ // rules) goes dark on every .tsx file. `language: TSX` rules
545
+ // stay tsx-exclusive — they're already scoped to fileLang
546
+ // "tsx" by the exact-match check.
347
547
  if (lang && fileLang && lang !== fileLang) {
348
- continue;
548
+ const runsAsTsOnTsx = fileLang === "tsx" && lang === "typescript";
549
+ if (!runsAsTsOnTsx) {
550
+ const key = `mismatch:${lang}->${fileLang}`;
551
+ if (!unsupportedLanguageLog.has(key)) {
552
+ const ids = newlyUnsupported.get(key) ?? [];
553
+ ids.push(rule.id);
554
+ newlyUnsupported.set(key, ids);
555
+ }
556
+ continue;
557
+ }
349
558
  }
350
559
  if (blockingOnly && rule.rule) {
351
560
  const complexity = calculateRuleComplexity(rule.rule);
@@ -387,7 +596,14 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
387
596
  break;
388
597
  const node = match;
389
598
  const range = node.range();
390
- const severity = rule.severity === "error" ? "error" : "warning";
599
+ // #1777: carry the rule's own tier through. The old collapse
600
+ // (`=== "error" ? "error" : "warning"`) erased hint and info,
601
+ // so the quiet tier the #1727 anti-slop rules ship at did not
602
+ // exist anywhere downstream. `Diagnostic.severity` has been
603
+ // 4-valued all along (clients/dispatch/types.ts). The BLOCKING
604
+ // gate is unchanged and deliberately narrower: only `error`
605
+ // blocks, so hint and info stay advisory exactly like warning.
606
+ const severity = normalizeRuleSeverity(rule.severity);
391
607
  const semantic = severity === "error" ? "blocking" : "warning";
392
608
  const defectClass = classifyDefect(rule.id, "ast-grep-napi", rule.message || rule.id);
393
609
  const ruleFix = explicitRuleFixSuggestion(rule);
@@ -6,33 +6,80 @@
6
6
  * mutating files mid-dispatch after LSP sync has already happened.
7
7
  */
8
8
  import * as path from "node:path";
9
+ import { incrementDegradationCount } from "../../degradation-ledger.js";
10
+ import { mapWithConcurrency } from "../../dependency-checker.js";
9
11
  import { safeSpawnAsync } from "../../safe-spawn.js";
10
12
  import { biomeConfigArgs, getAutofixCapability, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
11
13
  import { PRIORITY } from "../priorities.js";
12
14
  import { resolveToolCommandWithInstallFallback } from "./utils/runner-helpers.js";
13
- function parseBiomeJson(raw, filePath) {
15
+ /**
16
+ * Map biome's own severity vocabulary onto the four-tier `Diagnostic.severity`
17
+ * (clients/dispatch/types.ts), the same way `normalizeRuleSeverity` does for
18
+ * ast-grep-napi post-#1787. Biome's real `lint --reporter=json` output
19
+ * (re-probed live for #1810's review round) spells its info tier `"info"`,
20
+ * not `"information"` — the value this switch matched before, which meant a
21
+ * real info-tier diagnostic fell through the `default` and was silently
22
+ * promoted to `"warning"`, one tier too loud. Every other tier is a direct
23
+ * pass-through; an unrecognized value still falls back to `"warning"` — the
24
+ * tier every biome diagnostic reported at before this fix, so reviving
25
+ * hint/info never silently demotes an existing finding.
26
+ */
27
+ export function normalizeBiomeSeverity(raw) {
28
+ switch (raw) {
29
+ case "error":
30
+ return "error";
31
+ case "info":
32
+ return "info";
33
+ case "hint":
34
+ return "hint";
35
+ case "warning":
36
+ return "warning";
37
+ default:
38
+ return "warning";
39
+ }
40
+ }
41
+ /**
42
+ * A diagnostic's `category` is `"lint/<group>/<ruleName>"` — extracts the
43
+ * bare rule name `biome explain` expects. Returns `undefined` for anything
44
+ * that isn't a `lint/` category (e.g. a parse-error pseudo-category), so
45
+ * callers never look up fixability for a rule that was never a real rule.
46
+ */
47
+ export function biomeRuleNameFromCategory(category) {
48
+ if (!category?.startsWith("lint/"))
49
+ return undefined;
50
+ const parts = category.split("/");
51
+ return parts[parts.length - 1] || undefined;
52
+ }
53
+ export function parseBiomeJson(raw, filePath, fixKindByRule) {
14
54
  try {
15
55
  const result = JSON.parse(raw);
16
56
  const diagnostics = result.diagnostics || [];
17
57
  const autofix = getAutofixCapability("biome");
18
58
  return {
19
- diagnostics: diagnostics.map((d) => ({
20
- id: `biome:${d.category}:${d.location.start.line}`,
21
- message: d.message,
22
- filePath,
23
- line: d.location.start.line,
24
- column: d.location.start.column,
25
- severity: d.severity === "error" ? "error" : "warning",
26
- semantic: d.severity === "error" ? "blocking" : "warning",
27
- tool: "biome",
28
- rule: d.category,
29
- fixable: d.tags?.includes("fixable") ?? false,
30
- autoFixAvailable: (d.tags?.includes("fixable") ?? false) &&
31
- (autofix?.safePipelineAutofix ?? false),
32
- fixKind: d.tags?.includes("fixable") && autofix?.fixKind !== "none"
33
- ? autofix?.fixKind
34
- : undefined,
35
- })),
59
+ diagnostics: diagnostics.map((d) => {
60
+ const ruleName = biomeRuleNameFromCategory(d.category);
61
+ const fixKind = ruleName ? fixKindByRule?.get(ruleName) : undefined;
62
+ const isFixable = fixKind === "safe" || fixKind === "unsafe";
63
+ return {
64
+ id: `biome:${d.category}:${d.location.start.line}`,
65
+ message: d.message,
66
+ filePath,
67
+ line: d.location.start.line,
68
+ column: d.location.start.column,
69
+ severity: normalizeBiomeSeverity(d.severity),
70
+ semantic: d.severity === "error" ? "blocking" : "warning",
71
+ tool: "biome",
72
+ rule: d.category,
73
+ fixable: isFixable,
74
+ // Mirrors `biomeClient.fixFileAsync`'s own `lint --write` call
75
+ // (no `--unsafe`, clients/biome-client.ts): the pipeline only
76
+ // ever applies SAFE fixes, so only "safe" earns autoFixAvailable.
77
+ autoFixAvailable: fixKind === "safe" && (autofix?.safePipelineAutofix ?? false),
78
+ fixKind: isFixable && autofix?.fixKind !== "none"
79
+ ? autofix?.fixKind
80
+ : undefined,
81
+ };
82
+ }),
36
83
  };
37
84
  }
38
85
  catch (err) {
@@ -42,6 +89,109 @@ function parseBiomeJson(raw, filePath) {
42
89
  };
43
90
  }
44
91
  }
92
+ // Process-lifetime cache: a rule's fix kind is a static property of the
93
+ // (biome binary, rule name) pair, not of any one diagnostic occurrence, so
94
+ // it's resolved once and reused. Keyed on the resolved `cmd` so a different
95
+ // biome binary (e.g. a different project's pinned version) never reuses
96
+ // another binary's answer. Registered in
97
+ // tests/support/session-state-registry.ts as `process_lifetime` — the
98
+ // answer cannot change without a different biome binary, which is a
99
+ // different cache key, so there is nothing for a session boundary to
100
+ // invalidate.
101
+ const biomeFixKindCache = new Map();
102
+ /** Test-only: drop every cached verdict so a test starts from a clean cache. */
103
+ export function _resetBiomeFixKindCacheForTests() {
104
+ biomeFixKindCache.clear();
105
+ }
106
+ const BIOME_FIX_KIND_PATTERN = /^-\s*Fix:\s*(safe|unsafe)\s*$/m;
107
+ // The explicit "no fix" statement `biome explain` prints for a rule with no
108
+ // autofix at all (e.g. `noConstantCondition`). Matching this SEPARATELY from
109
+ // "the safe/unsafe pattern didn't match" is the point (#1810 review F1/F2):
110
+ // only a POSITIVE read — either a stated fix tier or this explicit negative
111
+ // — is trustworthy enough to cache. Anything else (a differently-shaped
112
+ // `explain` output, e.g. a biome 1.x install) is a parse failure, not a
113
+ // verdict, and must never be cached as one.
114
+ const BIOME_NO_FIX_PATTERN = /^-\s*No fix available\.\s*$/m;
115
+ /** At most this many `biome explain` spawns run concurrently (#1810 review F6). */
116
+ const EXPLAIN_CONCURRENCY = 4;
117
+ /**
118
+ * Resolves each rule referenced by `categories` to its fix kind via
119
+ * `biome explain <rule>` — the one place biome's shipped CLI states
120
+ * fixability as structured text (`- Fix: safe|unsafe`, or `- No fix
121
+ * available.` when absent), sourced live from the running binary's own rule
122
+ * registry rather than a hand-maintained table (#1810; real diagnostics
123
+ * carry no per-occurrence fixability field — see `BiomeDiagnostic`).
124
+ *
125
+ * Only a POSITIVE read is cached: either `- Fix: safe|unsafe` or the
126
+ * explicit `- No fix available.` statement. A spawn failure, a nonzero
127
+ * exit, or output that matches NEITHER pattern (e.g. a biome 1.x install —
128
+ * its `explain` text differs and would otherwise poison every rule as
129
+ * permanently unfixable, #1810 review F1) resolves to `"none"` for THIS
130
+ * call only and is never written to the cache — caching an unparsed result
131
+ * would permanently under-report a genuinely fixable rule for the rest of
132
+ * the process's life (the never-cache-a-transient-failure screen).
133
+ */
134
+ export async function resolveBiomeFixKinds(cmd, cwd, categories) {
135
+ const resolved = new Map();
136
+ const ruleNames = new Set();
137
+ for (const category of categories) {
138
+ const ruleName = biomeRuleNameFromCategory(category);
139
+ if (ruleName)
140
+ ruleNames.add(ruleName);
141
+ }
142
+ const toResolve = [];
143
+ for (const ruleName of ruleNames) {
144
+ const cacheKey = `${cmd}::${ruleName}`;
145
+ const cached = biomeFixKindCache.get(cacheKey);
146
+ if (cached === undefined) {
147
+ toResolve.push(ruleName);
148
+ }
149
+ else {
150
+ resolved.set(ruleName, cached);
151
+ }
152
+ }
153
+ await mapWithConcurrency(toResolve, EXPLAIN_CONCURRENCY, async (ruleName) => {
154
+ const cacheKey = `${cmd}::${ruleName}`;
155
+ const spawned = await safeSpawnAsync(cmd, ["explain", ruleName], {
156
+ timeout: 10000,
157
+ cwd,
158
+ });
159
+ if (spawned.error || spawned.status !== 0) {
160
+ incrementDegradationCount({
161
+ kind: "biome-explain-unavailable",
162
+ subject: ruleName,
163
+ reason: spawned.error
164
+ ? `spawn failed: ${spawned.error}`
165
+ : `explain exited ${spawned.status}`,
166
+ });
167
+ resolved.set(ruleName, "none");
168
+ return;
169
+ }
170
+ const stdout = spawned.stdout || "";
171
+ const fixMatch = stdout.match(BIOME_FIX_KIND_PATTERN);
172
+ if (fixMatch) {
173
+ const kind = fixMatch[1] === "safe" ? "safe" : "unsafe";
174
+ biomeFixKindCache.set(cacheKey, kind);
175
+ resolved.set(ruleName, kind);
176
+ return;
177
+ }
178
+ if (BIOME_NO_FIX_PATTERN.test(stdout)) {
179
+ biomeFixKindCache.set(cacheKey, "none");
180
+ resolved.set(ruleName, "none");
181
+ return;
182
+ }
183
+ // Neither pattern matched — an unrecognized `explain` output shape, not
184
+ // a confirmed negative. Resolve to "none" for this call, but leave the
185
+ // cache untouched so the next batch retries against real output.
186
+ incrementDegradationCount({
187
+ kind: "biome-explain-unavailable",
188
+ subject: ruleName,
189
+ reason: "explain output matched neither the Fix nor No-fix pattern",
190
+ });
191
+ resolved.set(ruleName, "none");
192
+ });
193
+ return resolved;
194
+ }
45
195
  const biomeCheckJsonRunner = {
46
196
  id: "biome-check-json",
47
197
  appliesTo: ["jsts"],
@@ -75,8 +225,26 @@ const biomeCheckJsonRunner = {
75
225
  if (checkResult.error) {
76
226
  return { status: "skipped", diagnostics: [], semantic: "none" };
77
227
  }
228
+ let fixKindByRule;
229
+ if (checkResult.status === 0 || checkResult.status === 1) {
230
+ // Cheap pre-parse just to collect rule categories — the real parse
231
+ // (with parseError handling) happens below via parseBiomeJson. A
232
+ // malformed-JSON pre-parse simply yields no categories, and
233
+ // `resolveBiomeFixKinds` no-ops on an empty list (no spawn, an
234
+ // immediately-resolved empty map) — the real parse still surfaces
235
+ // the parseError diagnostic as before.
236
+ let categories = [];
237
+ try {
238
+ const preParsed = JSON.parse(checkResult.stdout || "{}");
239
+ categories = (preParsed.diagnostics || []).map((d) => d.category);
240
+ }
241
+ catch {
242
+ categories = [];
243
+ }
244
+ fixKindByRule = await resolveBiomeFixKinds(cmd, cwd, categories);
245
+ }
78
246
  const parsed = checkResult.status === 0 || checkResult.status === 1
79
- ? parseBiomeJson(checkResult.stdout || "", ctx.filePath)
247
+ ? parseBiomeJson(checkResult.stdout || "", ctx.filePath, fixKindByRule)
80
248
  : { diagnostics: [] };
81
249
  if (parsed.parseError) {
82
250
  const raw = checkResult.stdout || checkResult.stderr || "";
@@ -124,7 +124,9 @@ function parseGccLikeOutput(raw, filePath) {
124
124
  line: Number.parseInt(lineStr, 10) || 1,
125
125
  column: Number.parseInt(colStr || "1", 10) || 1,
126
126
  severity,
127
- semantic: severity === "error" ? "blocking" : "warning",
127
+ // This single-file syntax check has no compile database, include path,
128
+ // or build flags. It can inform, but cannot prove a project blocker.
129
+ semantic: "warning",
128
130
  tool: "cpp-check",
129
131
  rule: severityLabel.toLowerCase(),
130
132
  fixable: false,
@@ -153,7 +155,8 @@ function parseMsvcOutput(raw, filePath) {
153
155
  line: Number.parseInt(lineStr, 10) || 1,
154
156
  column: Number.parseInt(colStr || "1", 10) || 1,
155
157
  severity,
156
- semantic: severity === "error" ? "blocking" : "warning",
158
+ // MSVC is invoked with the same context-free single-file contract.
159
+ semantic: "warning",
157
160
  tool: "cpp-check",
158
161
  rule,
159
162
  fixable: false,
@@ -217,7 +220,7 @@ const cppCheckRunner = {
217
220
  return {
218
221
  status: hasErrors ? "failed" : "succeeded",
219
222
  diagnostics,
220
- semantic: hasErrors ? "blocking" : "warning",
223
+ semantic: "warning",
221
224
  rawOutput: raw,
222
225
  };
223
226
  },
@@ -3,15 +3,17 @@ import * as fs from "node:fs";
3
3
  import { safeSpawnAsync } from "../../safe-spawn.js";
4
4
  import { PRIORITY } from "../priorities.js";
5
5
  import { createCwdCachedProbe } from "./utils/runner-helpers.js";
6
- // Per-cwd cached `mix credo --version` probe (#120). Before this, the probe
7
- // fired on every Elixir file save in projects with mix.exs.
8
- const probeCredo = createCwdCachedProbe(async (cwd) => {
9
- const r = await safeSpawnAsync("mix", ["credo", "--version"], {
10
- timeout: 10000,
11
- cwd,
12
- });
13
- return !r.error && r.status === 0;
14
- });
6
+ const CREDO_PROBE_BUDGET_MS = 10_000;
7
+ // Per-cwd cached `mix credo --version` probe (#120), governed by the shared
8
+ // availability policy (#1494). The probe returns the spawn result so the policy
9
+ // can tell a cold-BEAM timeout from a genuinely absent credo: the 10 s budget
10
+ // here is the widest in the repo and the most likely to expire on a slow first
11
+ // compile, and latching that as "not installed" used to disable credo for the
12
+ // rest of the session.
13
+ const probeCredo = createCwdCachedProbe((cwd) => safeSpawnAsync("mix", ["credo", "--version"], {
14
+ timeout: CREDO_PROBE_BUDGET_MS,
15
+ cwd,
16
+ }), { tool: "credo", budgetMs: CREDO_PROBE_BUDGET_MS });
15
17
  // `mix credo <file>` is single-file-scoped today, so blanket attribution would
16
18
  // be correct — but map issue.filename (resolved against cwd) for robustness so
17
19
  // this stays correct if the invocation ever widens to a directory (#265 A4).