@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -0,0 +1,330 @@
1
+ import { spawn, spawnSync, type ChildProcess } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import type { FetchLike } from "@modelcontextprotocol/client";
4
+ import type { HttpRequestHeadersCommand } from "./types.ts";
5
+ import { interpolateEnvVars } from "./utils.ts";
6
+
7
+ const DEFAULT_TIMEOUT_MS = 10_000;
8
+ const MAX_OUTPUT_BYTES = 64 * 1024;
9
+ const USE_PROCESS_GROUP = process.platform !== "win32";
10
+ const CLEANUP_TOKEN_ENV = "PI_MCP_REQUEST_HEADERS_CLEANUP_TOKEN";
11
+ // Busy hosts can exceed spawnSync's 1 MiB default when `ps axeww` dumps each
12
+ // process environment. Keep discovery below a bounded cap instead of letting
13
+ // `ps` get SIGTERM'd and reporting a false cleanup failure.
14
+ const PS_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
15
+
16
+ function isNoSuchProcessError(error: unknown): boolean {
17
+ return typeof error === "object" && error !== null && "code" in error && (error as NodeJS.ErrnoException).code === "ESRCH";
18
+ }
19
+
20
+ function runPosixPs(args: string[]): { status: number | null; signal: NodeJS.Signals | null; stdout: string } {
21
+ if (process.env.PI_MCP_ADAPTER_TEST_FAIL_PS === "1") return { status: 1, signal: null, stdout: "" };
22
+ const result = spawnSync("ps", args, { encoding: "utf8", maxBuffer: PS_MAX_BUFFER_BYTES });
23
+ return { status: result.status, signal: result.signal, stdout: result.stdout };
24
+ }
25
+
26
+ function psFailureReason(result: { status: number | null; signal: NodeJS.Signals | null }): string {
27
+ return result.status === null
28
+ ? `ps was killed by signal ${result.signal ?? "unknown"}`
29
+ : `ps exited with code ${result.status}`;
30
+ }
31
+
32
+ function collectPosixProcessPids(rootPid: number, cleanupToken?: string): number[] {
33
+ const result = runPosixPs(["axeww", "-o", "pid=,ppid=,command="]);
34
+ if (result.status !== 0) {
35
+ throw new Error(`HTTP request headers command cleanup failed: ${psFailureReason(result)}`);
36
+ }
37
+
38
+ const childrenByParent = new Map<number, number[]>();
39
+ const processPids = new Set<number>();
40
+ const needle = cleanupToken ? `${CLEANUP_TOKEN_ENV}=${cleanupToken}` : undefined;
41
+ for (const line of result.stdout.split("\n")) {
42
+ const match = /^\s*(\d+)\s+(\d+)(?:\s+(.*))?$/.exec(line);
43
+ if (!match) continue;
44
+ const pid = Number(match[1]);
45
+ const ppid = Number(match[2]);
46
+ if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;
47
+ const children = childrenByParent.get(ppid);
48
+ if (children) children.push(pid);
49
+ else childrenByParent.set(ppid, [pid]);
50
+ if (needle && pid !== process.pid && match[3]?.includes(needle)) processPids.add(pid);
51
+ }
52
+
53
+ const stack = [...(childrenByParent.get(rootPid) ?? [])];
54
+ while (stack.length > 0) {
55
+ const pid = stack.pop()!;
56
+ processPids.add(pid);
57
+ stack.push(...(childrenByParent.get(pid) ?? []));
58
+ }
59
+ return [...processPids];
60
+ }
61
+
62
+ function assertPosixProcessDiscoveryAvailable(): void {
63
+ const result = runPosixPs(["axeww", "-o", "pid=,ppid=,command="]);
64
+ if (result.status !== 0) {
65
+ throw new Error(`HTTP request headers command cleanup failed: ${psFailureReason(result)}`);
66
+ }
67
+ }
68
+
69
+ function isTaskkillNoSuchProcess(result: ReturnType<typeof spawnSync>): boolean {
70
+ return `${result.stdout ?? ""}\n${result.stderr ?? ""}`.toLowerCase().includes("not found");
71
+ }
72
+
73
+ function signalPid(pid: number, signal: NodeJS.Signals): void {
74
+ try {
75
+ process.kill(pid, signal);
76
+ } catch (error) {
77
+ if (!isNoSuchProcessError(error)) throw error;
78
+ }
79
+ }
80
+
81
+ function signalProcessGroup(pid: number, signal: NodeJS.Signals): void {
82
+ try {
83
+ process.kill(-pid, signal);
84
+ } catch (error) {
85
+ if (!isNoSuchProcessError(error)) throw error;
86
+ }
87
+ }
88
+
89
+ function killRequestHeadersCommand(child: ChildProcess, trackedPosixDescendantPids = new Set<number>(), cleanupToken?: string): void {
90
+ if (process.platform === "win32" && child.pid !== undefined) {
91
+ const result = spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
92
+ encoding: "utf8",
93
+ windowsHide: true,
94
+ });
95
+ if (result.status === 0 || isTaskkillNoSuchProcess(result)) return;
96
+ throw new Error(`HTTP request headers command cleanup failed: taskkill exited with code ${result.status ?? "unknown"}`);
97
+ }
98
+
99
+ if (USE_PROCESS_GROUP && child.pid !== undefined) {
100
+ const frozenPids = new Set<number>();
101
+ let cleanupError: Error | undefined;
102
+ try {
103
+ signalProcessGroup(child.pid, "SIGSTOP");
104
+ for (const pid of trackedPosixDescendantPids) {
105
+ signalPid(pid, "SIGSTOP");
106
+ frozenPids.add(pid);
107
+ }
108
+ let stablePasses = 0;
109
+ for (let pass = 0; pass < 16; pass++) {
110
+ const candidates = collectPosixProcessPids(child.pid, cleanupToken);
111
+ const newPids = candidates.filter(pid => !frozenPids.has(pid));
112
+ if (newPids.length === 0) {
113
+ stablePasses++;
114
+ if (stablePasses >= 2) return;
115
+ continue;
116
+ }
117
+ stablePasses = 0;
118
+ for (const pid of newPids) {
119
+ signalPid(pid, "SIGSTOP");
120
+ frozenPids.add(pid);
121
+ }
122
+ }
123
+ cleanupError = new Error("HTTP request headers command cleanup failed: descendant process tree did not stabilize");
124
+ } catch (error) {
125
+ cleanupError = error instanceof Error ? error : new Error(String(error));
126
+ } finally {
127
+ signalProcessGroup(child.pid, "SIGKILL");
128
+ for (const pid of frozenPids) signalPid(pid, "SIGKILL");
129
+ }
130
+ if (cleanupError) throw cleanupError;
131
+ return;
132
+ }
133
+
134
+ child.kill("SIGKILL");
135
+ }
136
+
137
+ export interface HttpRequestCommandEnvelope {
138
+ version: 1;
139
+ method: string;
140
+ url: string;
141
+ bodyBase64: string;
142
+ }
143
+
144
+ type CommandResult =
145
+ | { status: "error"; error: Error }
146
+ | { status: "success"; headers: Headers };
147
+
148
+ function resolvedCommand(config: HttpRequestHeadersCommand): {
149
+ command: string;
150
+ args: string[];
151
+ env: NodeJS.ProcessEnv;
152
+ timeoutMs: number;
153
+ } {
154
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
155
+ throw new Error("HTTP request headers command must be an object");
156
+ }
157
+ if (typeof config.command !== "string" || config.command.trim() === "") {
158
+ throw new Error("HTTP request headers command requires a non-empty command");
159
+ }
160
+ if (config.args !== undefined && (!Array.isArray(config.args) || config.args.some(arg => typeof arg !== "string"))) {
161
+ throw new Error("HTTP request headers command args must be strings");
162
+ }
163
+ if (config.env !== undefined && (
164
+ typeof config.env !== "object"
165
+ || Array.isArray(config.env)
166
+ || Object.values(config.env).some(value => typeof value !== "string")
167
+ )) {
168
+ throw new Error("HTTP request headers command env values must be strings");
169
+ }
170
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
171
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 60_000) {
172
+ throw new Error("HTTP request headers command timeoutMs must be an integer between 1 and 60000");
173
+ }
174
+ return {
175
+ command: interpolateEnvVars(config.command),
176
+ args: (config.args ?? []).map(interpolateEnvVars),
177
+ env: {
178
+ ...process.env,
179
+ ...Object.fromEntries(
180
+ Object.entries(config.env ?? {}).map(([key, value]) => [key, interpolateEnvVars(value)]),
181
+ ),
182
+ },
183
+ timeoutMs,
184
+ };
185
+ }
186
+
187
+ async function invokeRequestHeadersCommand(
188
+ config: HttpRequestHeadersCommand,
189
+ envelope: HttpRequestCommandEnvelope,
190
+ signal: AbortSignal,
191
+ ): Promise<Headers> {
192
+ const resolved = resolvedCommand(config);
193
+ if (USE_PROCESS_GROUP) assertPosixProcessDiscoveryAvailable();
194
+ return new Promise<Headers>((resolve, reject) => {
195
+ let stdout = Buffer.alloc(0);
196
+ let settled = false;
197
+ const cleanupToken = randomUUID();
198
+ const child = spawn(resolved.command, resolved.args, {
199
+ env: { ...resolved.env, [CLEANUP_TOKEN_ENV]: cleanupToken },
200
+ stdio: ["pipe", "pipe", "ignore"],
201
+ windowsHide: true,
202
+ detached: USE_PROCESS_GROUP,
203
+ });
204
+
205
+ const trackedPosixDescendantPids = new Set<number>();
206
+ let trackingError: Error | undefined;
207
+ const trackPosixDescendants = () => {
208
+ if (!USE_PROCESS_GROUP || child.pid === undefined || settled || trackingError) return;
209
+ try {
210
+ for (const pid of collectPosixProcessPids(child.pid, cleanupToken)) trackedPosixDescendantPids.add(pid);
211
+ } catch (error) {
212
+ trackingError = error instanceof Error ? error : new Error(String(error));
213
+ }
214
+ };
215
+ const descendantTracker = USE_PROCESS_GROUP ? setInterval(trackPosixDescendants, 50) : undefined;
216
+ descendantTracker?.unref();
217
+
218
+ const finish = (result: CommandResult) => {
219
+ if (settled) return;
220
+ settled = true;
221
+ clearTimeout(timer);
222
+ if (descendantTracker) clearInterval(descendantTracker);
223
+ signal.removeEventListener("abort", abort);
224
+ if (result.status === "error") reject(result.error);
225
+ else resolve(result.headers);
226
+ };
227
+ const finishAfterKill = (result: CommandResult) => {
228
+ try {
229
+ killRequestHeadersCommand(child, trackedPosixDescendantPids, cleanupToken);
230
+ finish(result);
231
+ } catch (cleanupError) {
232
+ finish({ status: "error", error: cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError)) });
233
+ }
234
+ };
235
+ const failAfterKill = (message: string) => {
236
+ finishAfterKill({ status: "error", error: trackingError ?? new Error(message) });
237
+ };
238
+ const abort = () => {
239
+ failAfterKill("HTTP request headers command aborted");
240
+ };
241
+ const timer = setTimeout(() => {
242
+ failAfterKill(`HTTP request headers command timed out after ${resolved.timeoutMs}ms`);
243
+ }, resolved.timeoutMs);
244
+
245
+ signal.addEventListener("abort", abort, { once: true });
246
+ if (signal.aborted) {
247
+ abort();
248
+ return;
249
+ }
250
+
251
+ child.on("error", () => finish({ status: "error", error: new Error("HTTP request headers command failed to start") }));
252
+ child.stdout.on("data", (chunk: Buffer | string) => {
253
+ if (settled) return;
254
+ stdout = Buffer.concat([stdout, Buffer.from(chunk)]);
255
+ if (stdout.byteLength > MAX_OUTPUT_BYTES) {
256
+ failAfterKill("HTTP request headers command output exceeded 64 KiB");
257
+ }
258
+ });
259
+ child.on("close", code => {
260
+ if (settled) return;
261
+ if (trackingError) {
262
+ failAfterKill(trackingError.message);
263
+ return;
264
+ }
265
+ if (code !== 0) {
266
+ failAfterKill(`HTTP request headers command exited with code ${code ?? "unknown"}`);
267
+ return;
268
+ }
269
+ let parsed: unknown;
270
+ try {
271
+ parsed = JSON.parse(stdout.toString("utf8"));
272
+ } catch {
273
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned invalid JSON") });
274
+ return;
275
+ }
276
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
277
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command must return a JSON object") });
278
+ return;
279
+ }
280
+ const entries = Object.entries(parsed);
281
+ if (entries.some(([, value]) => typeof value !== "string")) {
282
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command values must be strings") });
283
+ return;
284
+ }
285
+ try {
286
+ finishAfterKill({ status: "success", headers: new Headers(entries as Array<[string, string]>) });
287
+ } catch {
288
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned an invalid header") });
289
+ }
290
+ });
291
+
292
+ child.stdin.on("error", () => {});
293
+ child.stdin.end(JSON.stringify(envelope));
294
+ });
295
+ }
296
+
297
+ /** Wrap fetch so a trusted command can derive headers from the exact request. */
298
+ export function createRequestHeadersCommandFetch(
299
+ config: HttpRequestHeadersCommand,
300
+ delegate: FetchLike = globalThis.fetch,
301
+ ): FetchLike {
302
+ // Validate static configuration before the first request.
303
+ resolvedCommand(config);
304
+ return async (input: URL | RequestInfo, init?: RequestInit): Promise<Response> => {
305
+ const request = new Request(input, init);
306
+ const body = Buffer.from(await request.clone().arrayBuffer());
307
+ const derived = await invokeRequestHeadersCommand(config, {
308
+ version: 1,
309
+ method: request.method.toUpperCase(),
310
+ url: request.url,
311
+ bodyBase64: body.toString("base64"),
312
+ }, request.signal);
313
+ const headers = new Headers(request.headers);
314
+ derived.forEach((value, name) => headers.set(name, value));
315
+ return delegate(new URL(request.url), {
316
+ method: request.method,
317
+ headers,
318
+ ...(request.method === "GET" || request.method === "HEAD" ? {} : { body }),
319
+ signal: request.signal,
320
+ cache: request.cache,
321
+ credentials: request.credentials,
322
+ integrity: request.integrity,
323
+ keepalive: request.keepalive,
324
+ mode: request.mode,
325
+ redirect: request.redirect,
326
+ referrer: request.referrer,
327
+ referrerPolicy: request.referrerPolicy,
328
+ });
329
+ };
330
+ }
@@ -23,6 +23,26 @@ export interface RankedToolMatch {
23
23
  score: number;
24
24
  }
25
25
 
26
+ type SearchField = Exclude<keyof typeof FIELD_WEIGHTS, "keywords">;
27
+
28
+ interface PreparedToolSearch {
29
+ tool: ToolMetadata;
30
+ fields: Array<[SearchField, string, string[]]>;
31
+ nameTokens: string[];
32
+ keywordPhrases: string[];
33
+ keywordTokens: string[];
34
+ }
35
+
36
+ interface CachedServerSearch {
37
+ metadata: ToolMetadata[];
38
+ searchKeywords: ServerEntry["searchKeywords"];
39
+ toolPrefix: ReturnType<typeof resolveToolPrefix>;
40
+ withKeywords?: PreparedToolSearch[];
41
+ withoutKeywords?: PreparedToolSearch[];
42
+ }
43
+
44
+ const searchCache = new WeakMap<McpExtensionState, Map<string, CachedServerSearch>>();
45
+
26
46
  /**
27
47
  * Resolve the configured searchKeywords entries that apply to a tool.
28
48
  * Keys match by original name, prefixed name, or glob — the same candidate
@@ -64,25 +84,37 @@ export function tokenize(value: string): string[] {
64
84
  return normalizeSearchText(value).split(/[^a-z0-9]+/).filter(Boolean);
65
85
  }
66
86
 
67
- export function scoreToolMatch(tool: ToolMetadata, server: string, query: string, keywords?: string[]): number | null {
68
- const normalizedQuery = normalizeSearchText(query).trim();
69
- const queryTokens = tokenize(query);
70
- if (queryTokens.length === 0) return null;
71
-
87
+ function prepareToolSearch(tool: ToolMetadata, server: string, keywords?: string[]): PreparedToolSearch {
72
88
  const fields = {
73
89
  name: normalizeSearchText(tool.name),
74
90
  originalName: normalizeSearchText(tool.originalName),
75
91
  server: normalizeSearchText(server),
76
92
  description: normalizeSearchText(tool.description),
77
93
  };
94
+ const preparedFields = (Object.entries(fields) as Array<[SearchField, string]>)
95
+ .map(([field, value]): [SearchField, string, string[]] => [field, value, tokenize(value)]);
96
+ const keywordPhrases = keywords?.map(keyword => normalizeSearchText(keyword).trim()).filter(Boolean) ?? [];
97
+ return {
98
+ tool,
99
+ fields: preparedFields,
100
+ nameTokens: preparedFields[0]![2],
101
+ keywordPhrases,
102
+ keywordTokens: keywordPhrases.flatMap(tokenize),
103
+ };
104
+ }
105
+
106
+ function scorePreparedToolMatch(
107
+ prepared: PreparedToolSearch,
108
+ normalizedQuery: string,
109
+ queryTokens: string[],
110
+ ): number | null {
78
111
  let score = 0;
79
112
  let phraseMatched = false;
80
113
  let wholeFieldExact = false;
81
114
  const matchedTokens = new Set<string>();
82
115
 
83
- for (const [field, value] of Object.entries(fields) as Array<[keyof typeof FIELD_WEIGHTS, string]>) {
116
+ for (const [field, value, fieldTokens] of prepared.fields) {
84
117
  const weight = FIELD_WEIGHTS[field];
85
- const fieldTokens = tokenize(value);
86
118
  if (value === normalizedQuery) {
87
119
  score += weight * 14;
88
120
  phraseMatched = true;
@@ -112,11 +144,10 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
112
144
  // Configured keywords are discrete phrases, so the phrase-level bonus is
113
145
  // computed per phrase (best match wins) rather than on a joined string,
114
146
  // which would phrase-match queries spanning two unrelated keywords.
115
- if (keywords !== undefined && keywords.length > 0) {
147
+ if (prepared.keywordPhrases.length > 0) {
116
148
  const weight = FIELD_WEIGHTS.keywords;
117
- const phrases = keywords.map(keyword => normalizeSearchText(keyword).trim()).filter(Boolean);
118
149
  let phraseScore = 0;
119
- for (const phrase of phrases) {
150
+ for (const phrase of prepared.keywordPhrases) {
120
151
  if (phrase === normalizedQuery) {
121
152
  phraseScore = Math.max(phraseScore, weight * 14);
122
153
  phraseMatched = true;
@@ -131,15 +162,14 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
131
162
  }
132
163
  score += phraseScore;
133
164
 
134
- const keywordTokens = phrases.flatMap(tokenize);
135
165
  for (const token of queryTokens) {
136
- if (keywordTokens.includes(token)) {
166
+ if (prepared.keywordTokens.includes(token)) {
137
167
  score += weight * 4;
138
168
  matchedTokens.add(token);
139
- } else if (keywordTokens.some(keywordToken => keywordToken.startsWith(token) || (keywordToken.length >= MIN_STEM_LENGTH && token.startsWith(keywordToken)))) {
169
+ } else if (prepared.keywordTokens.some(keywordToken => keywordToken.startsWith(token) || (keywordToken.length >= MIN_STEM_LENGTH && token.startsWith(keywordToken)))) {
140
170
  score += weight * 2;
141
171
  matchedTokens.add(token);
142
- } else if (phrases.some(phrase => phrase.includes(token))) {
172
+ } else if (prepared.keywordPhrases.some(phrase => phrase.includes(token))) {
143
173
  score += weight;
144
174
  matchedTokens.add(token);
145
175
  }
@@ -151,11 +181,52 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
151
181
 
152
182
  score += coverage === 1 ? 25 : Math.round(coverage * 10);
153
183
  const firstQueryToken = queryTokens[0];
154
- if (firstQueryToken !== undefined && tokenize(fields.name).includes(firstQueryToken)) score += 8;
184
+ if (firstQueryToken !== undefined && prepared.nameTokens.includes(firstQueryToken)) score += 8;
155
185
  if (wholeFieldExact) score += 20;
156
186
  return score;
157
187
  }
158
188
 
189
+ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string, keywords?: string[]): number | null {
190
+ const normalizedQuery = normalizeSearchText(query).trim();
191
+ const queryTokens = tokenize(query);
192
+ if (queryTokens.length === 0) return null;
193
+ return scorePreparedToolMatch(prepareToolSearch(tool, server, keywords), normalizedQuery, queryTokens);
194
+ }
195
+
196
+ function getPreparedTools(
197
+ state: McpExtensionState,
198
+ serverName: string,
199
+ metadata: ToolMetadata[],
200
+ definition: ServerEntry | undefined,
201
+ globalPrefix: ToolPrefix,
202
+ includeKeywords: boolean,
203
+ ): PreparedToolSearch[] {
204
+ let stateCache = searchCache.get(state);
205
+ if (!stateCache) {
206
+ stateCache = new Map();
207
+ searchCache.set(state, stateCache);
208
+ }
209
+ const toolPrefix = resolveToolPrefix(definition, globalPrefix);
210
+ let cached = stateCache.get(serverName);
211
+ if (cached?.metadata !== metadata || cached.searchKeywords !== definition?.searchKeywords || cached.toolPrefix !== toolPrefix) {
212
+ cached = { metadata, searchKeywords: definition?.searchKeywords, toolPrefix };
213
+ stateCache.set(serverName, cached);
214
+ }
215
+ const cacheKey = includeKeywords ? "withKeywords" : "withoutKeywords";
216
+ let prepared = cached[cacheKey];
217
+ if (!prepared) {
218
+ prepared = metadata.map(tool => prepareToolSearch(
219
+ tool,
220
+ serverName,
221
+ includeKeywords && definition?.searchKeywords !== undefined
222
+ ? resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix)
223
+ : undefined,
224
+ ));
225
+ cached[cacheKey] = prepared;
226
+ }
227
+ return prepared;
228
+ }
229
+
159
230
  export function rankToolMatches(
160
231
  state: McpExtensionState,
161
232
  query: string,
@@ -163,18 +234,17 @@ export function rankToolMatches(
163
234
  includeKeywords = true,
164
235
  ): RankedToolMatch[] {
165
236
  const matches: RankedToolMatch[] = [];
237
+ const normalizedQuery = normalizeSearchText(query).trim();
238
+ const queryTokens = tokenize(query);
239
+ if (queryTokens.length === 0) return matches;
166
240
  const globalPrefix = state.config.settings?.toolPrefix ?? "server";
167
241
  for (const [serverName, metadata] of state.toolMetadata.entries()) {
168
242
  if (server && serverName !== server) continue;
169
243
  const definition = state.config.mcpServers[serverName];
170
244
  if (isServerDisabled(definition)) continue;
171
- const hasKeywords = includeKeywords && definition?.searchKeywords !== undefined;
172
- for (const tool of metadata) {
173
- const keywords = hasKeywords
174
- ? resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix)
175
- : undefined;
176
- const score = scoreToolMatch(tool, serverName, query, keywords);
177
- if (score !== null) matches.push({ server: serverName, tool, score });
245
+ for (const prepared of getPreparedTools(state, serverName, metadata, definition, globalPrefix, includeKeywords)) {
246
+ const score = scorePreparedToolMatch(prepared, normalizedQuery, queryTokens);
247
+ if (score !== null) matches.push({ server: serverName, tool: prepared.tool, score });
178
248
  }
179
249
  }
180
250
  return matches.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name));