@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

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 (360) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  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 +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -0,0 +1,336 @@
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
+
12
+ function isNoSuchProcessError(error: unknown): boolean {
13
+ return typeof error === "object" && error !== null && "code" in error && (error as NodeJS.ErrnoException).code === "ESRCH";
14
+ }
15
+
16
+ function runPosixPs(args: string[]): { status: number | null; stdout: string } {
17
+ if (process.env.PI_MCP_ADAPTER_TEST_FAIL_PS === "1") return { status: 1, stdout: "" };
18
+ return spawnSync("ps", args, { encoding: "utf8" });
19
+ }
20
+
21
+ function collectPosixDescendantPids(rootPid: number): number[] {
22
+ const result = runPosixPs(["-axo", "pid=,ppid="]);
23
+ if (result.status !== 0) {
24
+ throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
25
+ }
26
+
27
+ const childrenByParent = new Map<number, number[]>();
28
+ for (const line of result.stdout.split("\n")) {
29
+ const [pidText, ppidText] = line.trim().split(/\s+/, 2);
30
+ const pid = Number(pidText);
31
+ const ppid = Number(ppidText);
32
+ if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;
33
+ const children = childrenByParent.get(ppid);
34
+ if (children) children.push(pid);
35
+ else childrenByParent.set(ppid, [pid]);
36
+ }
37
+
38
+ const descendants: number[] = [];
39
+ const stack = [...(childrenByParent.get(rootPid) ?? [])];
40
+ while (stack.length > 0) {
41
+ const pid = stack.pop()!;
42
+ descendants.push(pid);
43
+ stack.push(...(childrenByParent.get(pid) ?? []));
44
+ }
45
+ return descendants;
46
+ }
47
+
48
+ function collectPosixCleanupTokenPids(cleanupToken: string): number[] {
49
+ const result = runPosixPs(["axeww", "-o", "pid=,command="]);
50
+ if (result.status !== 0) {
51
+ throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
52
+ }
53
+
54
+ const needle = `${CLEANUP_TOKEN_ENV}=${cleanupToken}`;
55
+ const pids: number[] = [];
56
+ for (const line of result.stdout.split("\n")) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed.includes(needle)) continue;
59
+ const [pidText] = trimmed.split(/\s+/, 1);
60
+ const pid = Number(pidText);
61
+ if (Number.isInteger(pid) && pid !== process.pid) pids.push(pid);
62
+ }
63
+ return pids;
64
+ }
65
+
66
+ function assertPosixProcessDiscoveryAvailable(): void {
67
+ collectPosixDescendantPids(process.pid);
68
+ collectPosixCleanupTokenPids(`${process.pid}-preflight`);
69
+ }
70
+
71
+ function isTaskkillNoSuchProcess(result: ReturnType<typeof spawnSync>): boolean {
72
+ return `${result.stdout ?? ""}\n${result.stderr ?? ""}`.toLowerCase().includes("not found");
73
+ }
74
+
75
+ function signalPid(pid: number, signal: NodeJS.Signals): void {
76
+ try {
77
+ process.kill(pid, signal);
78
+ } catch (error) {
79
+ if (!isNoSuchProcessError(error)) throw error;
80
+ }
81
+ }
82
+
83
+ function signalProcessGroup(pid: number, signal: NodeJS.Signals): void {
84
+ try {
85
+ process.kill(-pid, signal);
86
+ } catch (error) {
87
+ if (!isNoSuchProcessError(error)) throw error;
88
+ }
89
+ }
90
+
91
+ function killRequestHeadersCommand(child: ChildProcess, trackedPosixDescendantPids = new Set<number>(), cleanupToken?: string): void {
92
+ if (process.platform === "win32" && child.pid !== undefined) {
93
+ const result = spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
94
+ encoding: "utf8",
95
+ windowsHide: true,
96
+ });
97
+ if (result.status === 0 || isTaskkillNoSuchProcess(result)) return;
98
+ throw new Error(`HTTP request headers command cleanup failed: taskkill exited with code ${result.status ?? "unknown"}`);
99
+ }
100
+
101
+ if (USE_PROCESS_GROUP && child.pid !== undefined) {
102
+ const frozenPids = new Set<number>();
103
+ let cleanupError: Error | undefined;
104
+ try {
105
+ signalProcessGroup(child.pid, "SIGSTOP");
106
+ for (const pid of trackedPosixDescendantPids) {
107
+ signalPid(pid, "SIGSTOP");
108
+ frozenPids.add(pid);
109
+ }
110
+ let stablePasses = 0;
111
+ for (let pass = 0; pass < 16; pass++) {
112
+ const candidates = [
113
+ ...collectPosixDescendantPids(child.pid),
114
+ ...(cleanupToken ? collectPosixCleanupTokenPids(cleanupToken) : []),
115
+ ];
116
+ const newPids = candidates.filter(pid => !frozenPids.has(pid));
117
+ if (newPids.length === 0) {
118
+ stablePasses++;
119
+ if (stablePasses >= 2) return;
120
+ continue;
121
+ }
122
+ stablePasses = 0;
123
+ for (const pid of newPids) {
124
+ signalPid(pid, "SIGSTOP");
125
+ frozenPids.add(pid);
126
+ }
127
+ }
128
+ cleanupError = new Error("HTTP request headers command cleanup failed: descendant process tree did not stabilize");
129
+ } catch (error) {
130
+ cleanupError = error instanceof Error ? error : new Error(String(error));
131
+ } finally {
132
+ signalProcessGroup(child.pid, "SIGKILL");
133
+ for (const pid of frozenPids) signalPid(pid, "SIGKILL");
134
+ }
135
+ if (cleanupError) throw cleanupError;
136
+ return;
137
+ }
138
+
139
+ child.kill("SIGKILL");
140
+ }
141
+
142
+ export interface HttpRequestCommandEnvelope {
143
+ version: 1;
144
+ method: string;
145
+ url: string;
146
+ bodyBase64: string;
147
+ }
148
+
149
+ type CommandResult =
150
+ | { status: "error"; error: Error }
151
+ | { status: "success"; headers: Headers };
152
+
153
+ function resolvedCommand(config: HttpRequestHeadersCommand): {
154
+ command: string;
155
+ args: string[];
156
+ env: NodeJS.ProcessEnv;
157
+ timeoutMs: number;
158
+ } {
159
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
160
+ throw new Error("HTTP request headers command must be an object");
161
+ }
162
+ if (typeof config.command !== "string" || config.command.trim() === "") {
163
+ throw new Error("HTTP request headers command requires a non-empty command");
164
+ }
165
+ if (config.args !== undefined && (!Array.isArray(config.args) || config.args.some(arg => typeof arg !== "string"))) {
166
+ throw new Error("HTTP request headers command args must be strings");
167
+ }
168
+ if (config.env !== undefined && (
169
+ typeof config.env !== "object"
170
+ || Array.isArray(config.env)
171
+ || Object.values(config.env).some(value => typeof value !== "string")
172
+ )) {
173
+ throw new Error("HTTP request headers command env values must be strings");
174
+ }
175
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
176
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 60_000) {
177
+ throw new Error("HTTP request headers command timeoutMs must be an integer between 1 and 60000");
178
+ }
179
+ return {
180
+ command: interpolateEnvVars(config.command),
181
+ args: (config.args ?? []).map(interpolateEnvVars),
182
+ env: {
183
+ ...process.env,
184
+ ...Object.fromEntries(
185
+ Object.entries(config.env ?? {}).map(([key, value]) => [key, interpolateEnvVars(value)]),
186
+ ),
187
+ },
188
+ timeoutMs,
189
+ };
190
+ }
191
+
192
+ async function invokeRequestHeadersCommand(
193
+ config: HttpRequestHeadersCommand,
194
+ envelope: HttpRequestCommandEnvelope,
195
+ signal: AbortSignal,
196
+ ): Promise<Headers> {
197
+ const resolved = resolvedCommand(config);
198
+ if (USE_PROCESS_GROUP) assertPosixProcessDiscoveryAvailable();
199
+ return new Promise<Headers>((resolve, reject) => {
200
+ let stdout = Buffer.alloc(0);
201
+ let settled = false;
202
+ const cleanupToken = randomUUID();
203
+ const child = spawn(resolved.command, resolved.args, {
204
+ env: { ...resolved.env, [CLEANUP_TOKEN_ENV]: cleanupToken },
205
+ stdio: ["pipe", "pipe", "ignore"],
206
+ windowsHide: true,
207
+ detached: USE_PROCESS_GROUP,
208
+ });
209
+
210
+ const trackedPosixDescendantPids = new Set<number>();
211
+ let trackingError: Error | undefined;
212
+ const trackPosixDescendants = () => {
213
+ if (!USE_PROCESS_GROUP || child.pid === undefined || settled || trackingError) return;
214
+ try {
215
+ for (const pid of collectPosixDescendantPids(child.pid)) trackedPosixDescendantPids.add(pid);
216
+ for (const pid of collectPosixCleanupTokenPids(cleanupToken)) trackedPosixDescendantPids.add(pid);
217
+ } catch (error) {
218
+ trackingError = error instanceof Error ? error : new Error(String(error));
219
+ }
220
+ };
221
+ const descendantTracker = USE_PROCESS_GROUP ? setInterval(trackPosixDescendants, 50) : undefined;
222
+ descendantTracker?.unref();
223
+
224
+ const finish = (result: CommandResult) => {
225
+ if (settled) return;
226
+ settled = true;
227
+ clearTimeout(timer);
228
+ if (descendantTracker) clearInterval(descendantTracker);
229
+ signal.removeEventListener("abort", abort);
230
+ if (result.status === "error") reject(result.error);
231
+ else resolve(result.headers);
232
+ };
233
+ const finishAfterKill = (result: CommandResult) => {
234
+ try {
235
+ killRequestHeadersCommand(child, trackedPosixDescendantPids, cleanupToken);
236
+ finish(result);
237
+ } catch (cleanupError) {
238
+ finish({ status: "error", error: cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError)) });
239
+ }
240
+ };
241
+ const failAfterKill = (message: string) => {
242
+ finishAfterKill({ status: "error", error: trackingError ?? new Error(message) });
243
+ };
244
+ const abort = () => {
245
+ failAfterKill("HTTP request headers command aborted");
246
+ };
247
+ const timer = setTimeout(() => {
248
+ failAfterKill(`HTTP request headers command timed out after ${resolved.timeoutMs}ms`);
249
+ }, resolved.timeoutMs);
250
+
251
+ signal.addEventListener("abort", abort, { once: true });
252
+ if (signal.aborted) {
253
+ abort();
254
+ return;
255
+ }
256
+
257
+ child.on("error", () => finish({ status: "error", error: new Error("HTTP request headers command failed to start") }));
258
+ child.stdout.on("data", (chunk: Buffer | string) => {
259
+ if (settled) return;
260
+ stdout = Buffer.concat([stdout, Buffer.from(chunk)]);
261
+ if (stdout.byteLength > MAX_OUTPUT_BYTES) {
262
+ failAfterKill("HTTP request headers command output exceeded 64 KiB");
263
+ }
264
+ });
265
+ child.on("close", code => {
266
+ if (settled) return;
267
+ if (trackingError) {
268
+ failAfterKill(trackingError.message);
269
+ return;
270
+ }
271
+ if (code !== 0) {
272
+ failAfterKill(`HTTP request headers command exited with code ${code ?? "unknown"}`);
273
+ return;
274
+ }
275
+ let parsed: unknown;
276
+ try {
277
+ parsed = JSON.parse(stdout.toString("utf8"));
278
+ } catch {
279
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned invalid JSON") });
280
+ return;
281
+ }
282
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
283
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command must return a JSON object") });
284
+ return;
285
+ }
286
+ const entries = Object.entries(parsed);
287
+ if (entries.some(([, value]) => typeof value !== "string")) {
288
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command values must be strings") });
289
+ return;
290
+ }
291
+ try {
292
+ finishAfterKill({ status: "success", headers: new Headers(entries as Array<[string, string]>) });
293
+ } catch {
294
+ finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned an invalid header") });
295
+ }
296
+ });
297
+
298
+ child.stdin.on("error", () => {});
299
+ child.stdin.end(JSON.stringify(envelope));
300
+ });
301
+ }
302
+
303
+ /** Wrap fetch so a trusted command can derive headers from the exact request. */
304
+ export function createRequestHeadersCommandFetch(
305
+ config: HttpRequestHeadersCommand,
306
+ delegate: FetchLike = globalThis.fetch,
307
+ ): FetchLike {
308
+ // Validate static configuration before the first request.
309
+ resolvedCommand(config);
310
+ return async (input: URL | RequestInfo, init?: RequestInit): Promise<Response> => {
311
+ const request = new Request(input, init);
312
+ const body = Buffer.from(await request.clone().arrayBuffer());
313
+ const derived = await invokeRequestHeadersCommand(config, {
314
+ version: 1,
315
+ method: request.method.toUpperCase(),
316
+ url: request.url,
317
+ bodyBase64: body.toString("base64"),
318
+ }, request.signal);
319
+ const headers = new Headers(request.headers);
320
+ derived.forEach((value, name) => headers.set(name, value));
321
+ return delegate(new URL(request.url), {
322
+ method: request.method,
323
+ headers,
324
+ ...(request.method === "GET" || request.method === "HEAD" ? {} : { body }),
325
+ signal: request.signal,
326
+ cache: request.cache,
327
+ credentials: request.credentials,
328
+ integrity: request.integrity,
329
+ keepalive: request.keepalive,
330
+ mode: request.mode,
331
+ redirect: request.redirect,
332
+ referrer: request.referrer,
333
+ referrerPolicy: request.referrerPolicy,
334
+ });
335
+ };
336
+ }
@@ -1,6 +1,6 @@
1
1
  import type { McpExtensionState } from "./state.ts";
2
- import type { ToolMetadata } from "./types.ts";
3
- import { getServerPrefix, isServerDisabled } from "./types.ts";
2
+ import type { ServerEntry, ToolMetadata, ToolPrefix } from "./types.ts";
3
+ import { getServerPrefix, getToolNameCandidates, isServerDisabled, matchesToolPattern, resolveToolPrefix } from "./types.ts";
4
4
 
5
5
  /**
6
6
  * Shortest field token allowed to stem-match a longer query token.
@@ -14,6 +14,7 @@ const FIELD_WEIGHTS = {
14
14
  originalName: 10,
15
15
  server: 8,
16
16
  description: 5,
17
+ keywords: 5,
17
18
  } as const;
18
19
 
19
20
  export interface RankedToolMatch {
@@ -22,6 +23,36 @@ export interface RankedToolMatch {
22
23
  score: number;
23
24
  }
24
25
 
26
+ /**
27
+ * Resolve the configured searchKeywords entries that apply to a tool.
28
+ * Keys match by original name, prefixed name, or glob — the same candidate
29
+ * set includeTools/excludeTools use — and all matching entries are unioned.
30
+ */
31
+ export function resolveSearchKeywords(
32
+ definition: ServerEntry | undefined,
33
+ toolOriginalName: string,
34
+ serverName: string,
35
+ globalPrefix: ToolPrefix,
36
+ ): string[] {
37
+ const map = definition?.searchKeywords;
38
+ if (!map || typeof map !== "object" || Array.isArray(map)) return [];
39
+ const candidates = getToolNameCandidates(toolOriginalName, serverName, resolveToolPrefix(definition, globalPrefix));
40
+ const keywords: string[] = [];
41
+ const seen = new Set<string>();
42
+ for (const [pattern, values] of Object.entries(map)) {
43
+ if (!Array.isArray(values)) continue;
44
+ if (!matchesToolPattern(candidates, [pattern])) continue;
45
+ for (const value of values) {
46
+ if (typeof value !== "string") continue;
47
+ const trimmed = value.trim();
48
+ if (!trimmed || seen.has(trimmed)) continue;
49
+ seen.add(trimmed);
50
+ keywords.push(trimmed);
51
+ }
52
+ }
53
+ return keywords;
54
+ }
55
+
25
56
  export function normalizeSearchText(value: string): string {
26
57
  return value
27
58
  .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
@@ -33,7 +64,7 @@ export function tokenize(value: string): string[] {
33
64
  return normalizeSearchText(value).split(/[^a-z0-9]+/).filter(Boolean);
34
65
  }
35
66
 
36
- export function scoreToolMatch(tool: ToolMetadata, server: string, query: string): number | null {
67
+ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string, keywords?: string[]): number | null {
37
68
  const normalizedQuery = normalizeSearchText(query).trim();
38
69
  const queryTokens = tokenize(query);
39
70
  if (queryTokens.length === 0) return null;
@@ -78,6 +109,43 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
78
109
  }
79
110
  }
80
111
 
112
+ // Configured keywords are discrete phrases, so the phrase-level bonus is
113
+ // computed per phrase (best match wins) rather than on a joined string,
114
+ // which would phrase-match queries spanning two unrelated keywords.
115
+ if (keywords !== undefined && keywords.length > 0) {
116
+ const weight = FIELD_WEIGHTS.keywords;
117
+ const phrases = keywords.map(keyword => normalizeSearchText(keyword).trim()).filter(Boolean);
118
+ let phraseScore = 0;
119
+ for (const phrase of phrases) {
120
+ if (phrase === normalizedQuery) {
121
+ phraseScore = Math.max(phraseScore, weight * 14);
122
+ phraseMatched = true;
123
+ wholeFieldExact = true;
124
+ } else if (phrase.startsWith(normalizedQuery)) {
125
+ phraseScore = Math.max(phraseScore, weight * 9);
126
+ phraseMatched = true;
127
+ } else if (phrase.includes(normalizedQuery)) {
128
+ phraseScore = Math.max(phraseScore, weight * 6);
129
+ phraseMatched = true;
130
+ }
131
+ }
132
+ score += phraseScore;
133
+
134
+ const keywordTokens = phrases.flatMap(tokenize);
135
+ for (const token of queryTokens) {
136
+ if (keywordTokens.includes(token)) {
137
+ score += weight * 4;
138
+ matchedTokens.add(token);
139
+ } else if (keywordTokens.some(keywordToken => keywordToken.startsWith(token) || (keywordToken.length >= MIN_STEM_LENGTH && token.startsWith(keywordToken)))) {
140
+ score += weight * 2;
141
+ matchedTokens.add(token);
142
+ } else if (phrases.some(phrase => phrase.includes(token))) {
143
+ score += weight;
144
+ matchedTokens.add(token);
145
+ }
146
+ }
147
+ }
148
+
81
149
  const coverage = matchedTokens.size / queryTokens.length;
82
150
  if (!phraseMatched && (queryTokens.length <= 2 ? coverage !== 1 : coverage < 0.6)) return null;
83
151
 
@@ -88,13 +156,24 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
88
156
  return score;
89
157
  }
90
158
 
91
- export function rankToolMatches(state: McpExtensionState, query: string, server?: string): RankedToolMatch[] {
159
+ export function rankToolMatches(
160
+ state: McpExtensionState,
161
+ query: string,
162
+ server?: string,
163
+ includeKeywords = true,
164
+ ): RankedToolMatch[] {
92
165
  const matches: RankedToolMatch[] = [];
166
+ const globalPrefix = state.config.settings?.toolPrefix ?? "server";
93
167
  for (const [serverName, metadata] of state.toolMetadata.entries()) {
94
168
  if (server && serverName !== server) continue;
95
- if (isServerDisabled(state.config.mcpServers[serverName])) continue;
169
+ const definition = state.config.mcpServers[serverName];
170
+ if (isServerDisabled(definition)) continue;
171
+ const hasKeywords = includeKeywords && definition?.searchKeywords !== undefined;
96
172
  for (const tool of metadata) {
97
- const score = scoreToolMatch(tool, serverName, query);
173
+ const keywords = hasKeywords
174
+ ? resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix)
175
+ : undefined;
176
+ const score = scoreToolMatch(tool, serverName, query, keywords);
98
177
  if (score !== null) matches.push({ server: serverName, tool, score });
99
178
  }
100
179
  }
@@ -123,5 +202,5 @@ export function rankSuggestions(state: McpExtensionState, name: string, limit: n
123
202
  .sort((a, b) => b.length - a.length)
124
203
  .map(candidate => name.slice(candidate.length + 1));
125
204
  const query = stripped[0] ?? name;
126
- return rankToolMatches(state, query).slice(0, limit).map(match => match.tool.name);
205
+ return rankToolMatches(state, query, undefined, false).slice(0, limit).map(match => match.tool.name);
127
206
  }
@@ -31,7 +31,7 @@ import { createJsonSchemaValidator } from "./json-schema-validator.ts";
31
31
  import { logger } from "./logger.ts";
32
32
  import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
33
33
  import { extractOAuthConfig, supportsOAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
34
- import type { AuthStorageOptions } from "./mcp-auth.ts";
34
+ import { invalidateAuthEntryCache, type AuthStorageOptions } from "./mcp-auth.ts";
35
35
  import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.ts";
36
36
  import {
37
37
  handleUrlElicitation,
@@ -39,6 +39,7 @@ import {
39
39
  type ServerElicitationConfig,
40
40
  } from "./elicitation-handler.ts";
41
41
  import {
42
+ interpolateEnvVars,
42
43
  resolveBearerToken,
43
44
  resolveCommandSecret,
44
45
  resolveCommandSecretsRecord,
@@ -55,6 +56,7 @@ import {
55
56
  traceTransportKind,
56
57
  wrapTransportWithMcpTrace,
57
58
  } from "./mcp-trace.ts";
59
+ import { createRequestHeadersCommandFetch } from "./request-headers-command.ts";
58
60
 
59
61
  const MAX_CAPTURED_STDERR_BYTES = 8 * 1024;
60
62
  const MAX_CAPTURED_STDERR_LINES = 3;
@@ -129,8 +131,11 @@ export interface ServerConnection {
129
131
  lastUsedAt: number;
130
132
  inFlight: number;
131
133
  status: "connected" | "closed" | "needs-auth";
134
+ /** True once this needs-auth episode discarded the cached credential. */
135
+ credentialsInvalidated?: boolean;
132
136
  }
133
137
 
138
+
134
139
  type UiStreamListener = (serverName: string, notification: ServerStreamResultPatchNotification["params"]) => void;
135
140
  type MetadataListChangedListener = (serverName: string, reason: string) => void;
136
141
 
@@ -238,10 +243,12 @@ export class McpServerManager {
238
243
  return existing;
239
244
  }
240
245
 
246
+ const credentialsInvalidated = existing?.status === "needs-auth"
247
+ && existing.credentialsInvalidated === true;
241
248
  const generation = this.closeGenerations.get(name) ?? 0;
242
249
  const attemptController = new AbortController();
243
250
  const attemptSignal = combineAbortSignals(ownedSignal, attemptController.signal);
244
- const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal);
251
+ const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal, credentialsInvalidated);
245
252
  const promise = definition.url
246
253
  ? connectionAttempt.catch(async error => { throw await this.enrichHttpConnectionError(definition, error); })
247
254
  : connectionAttempt;
@@ -324,6 +331,7 @@ export class McpServerManager {
324
331
  definition: ServerDefinition,
325
332
  signal?: AbortSignal,
326
333
  requestSignal?: AbortSignal,
334
+ credentialsInvalidated = false,
327
335
  ): Promise<ServerConnection> {
328
336
  throwIfAborted(signal);
329
337
 
@@ -338,6 +346,7 @@ export class McpServerManager {
338
346
  let client: Client;
339
347
  let transport: Transport;
340
348
  let clientConnected = false;
349
+ let invalidated = credentialsInvalidated;
341
350
  let transportAlreadyTraced = false;
342
351
  let stderrTail: Buffer<ArrayBufferLike> = Buffer.alloc(0);
343
352
  const configuredTransports = [definition.command, definition.url, definition.socket]
@@ -351,7 +360,7 @@ export class McpServerManager {
351
360
  if (definition.command) {
352
361
  client = this.createClient(name, definition);
353
362
  let command = definition.command;
354
- let args = definition.args ?? [];
363
+ let args = (definition.args ?? []).map(interpolateEnvVars);
355
364
 
356
365
  if (command === "npx" || command === "npm") {
357
366
  const resolved = await resolveNpxBinary(command, args, signal);
@@ -387,9 +396,11 @@ export class McpServerManager {
387
396
  requestOptions,
388
397
  signal,
389
398
  traceObserver,
399
+ invalidated,
390
400
  );
391
401
  client = httpConnection.client;
392
402
  transport = httpConnection.transport;
403
+ invalidated = httpConnection.credentialsInvalidated;
393
404
  if (httpConnection.status === "needs-auth") {
394
405
  return {
395
406
  client,
@@ -401,6 +412,7 @@ export class McpServerManager {
401
412
  lastUsedAt: Date.now(),
402
413
  inFlight: 0,
403
414
  status: "needs-auth",
415
+ credentialsInvalidated: invalidated,
404
416
  };
405
417
  }
406
418
  clientConnected = true;
@@ -477,6 +489,10 @@ export class McpServerManager {
477
489
  // A cleanup failure remains a setup failure rather than being hidden
478
490
  // behind needs-auth.
479
491
  if (isUnauthorizedHttpError(error) && supportsOAuth(definition) && cleanupFailures.length === 0) {
492
+ if (!invalidated) {
493
+ invalidateAuthEntryCache(name);
494
+ invalidated = true;
495
+ }
480
496
  return {
481
497
  client,
482
498
  transport,
@@ -487,6 +503,7 @@ export class McpServerManager {
487
503
  lastUsedAt: Date.now(),
488
504
  inFlight: 0,
489
505
  status: "needs-auth",
506
+ credentialsInvalidated: invalidated,
490
507
  };
491
508
  }
492
509
 
@@ -694,7 +711,8 @@ export class McpServerManager {
694
711
  requestOptions: RequestOptions | undefined,
695
712
  signal?: AbortSignal,
696
713
  traceObserver?: McpTraceObserver,
697
- ): Promise<{ client: Client; transport: Transport; status: "connected" | "needs-auth" }> {
714
+ credentialsInvalidated = false,
715
+ ): Promise<{ client: Client; transport: Transport; status: "connected" | "needs-auth"; credentialsInvalidated: boolean }> {
698
716
  throwIfAborted(signal);
699
717
  const serverUrl = resolveServerUrl(definition)!;
700
718
  const url = new URL(serverUrl);
@@ -729,6 +747,9 @@ export class McpServerManager {
729
747
  }
730
748
 
731
749
  const requestInit = Object.keys(headers).length > 0 ? { headers } : undefined;
750
+ const requestFetch = definition.requestHeadersCommand
751
+ ? createRequestHeadersCommandFetch(definition.requestHeadersCommand)
752
+ : undefined;
732
753
  const createAuthProvider = (): McpOAuthProvider => new McpOAuthProvider(
733
754
  serverName,
734
755
  serverUrl,
@@ -755,6 +776,7 @@ export class McpServerManager {
755
776
  const authProvider = "provider" in authState ? authState.provider : undefined;
756
777
  const transportOptions = {
757
778
  ...(requestInit !== undefined ? { requestInit } : {}),
779
+ ...(requestFetch !== undefined ? { fetch: requestFetch } : {}),
758
780
  ...(authProvider !== undefined ? { authProvider } : {}),
759
781
  ...(authProvider !== undefined
760
782
  && definition.oauth !== false
@@ -791,9 +813,10 @@ export class McpServerManager {
791
813
  // OAuth challenge; use SSE only for definitive endpoint incompatibility.
792
814
  // Agent Plugins set httpTransport, and their declared transport is used without fallback.
793
815
  let kind: "streamable-http" | "sse" = definition.httpTransport ?? "streamable-http";
816
+ let invalidated = credentialsInvalidated;
794
817
  for (;;) {
795
818
  const result = await attempt(kind);
796
- if (result.status === "connected") return result;
819
+ if (result.status === "connected") return { ...result, credentialsInvalidated: invalidated };
797
820
  if (result.error instanceof AggregateError
798
821
  && result.error.message === "MCP connection abort cleanup failed") {
799
822
  throw result.error;
@@ -806,7 +829,16 @@ export class McpServerManager {
806
829
  }
807
830
  if (isUnauthorizedHttpError(result.error)) {
808
831
  if (supportsOAuth(definition)) {
809
- return { client: result.client, transport: result.transport, status: "needs-auth" };
832
+ if (!invalidated) {
833
+ invalidateAuthEntryCache(serverName);
834
+ invalidated = true;
835
+ }
836
+ return {
837
+ client: result.client,
838
+ transport: result.transport,
839
+ status: "needs-auth",
840
+ credentialsInvalidated: invalidated,
841
+ };
810
842
  }
811
843
  throw result.error;
812
844
  }
@@ -850,6 +882,7 @@ export class McpServerManager {
850
882
  return { prompts, failed: false };
851
883
  } catch (error) {
852
884
  if (requestOptions?.signal?.aborted) throwIfAborted(requestOptions.signal);
885
+ if (isUnauthorizedHttpError(error)) throw error;
853
886
  const message = error instanceof Error ? error.message : String(error);
854
887
  logger.debug(`MCP: prompts/list failed: ${message}`);
855
888
  return { prompts: [], failed: true };
@@ -871,10 +904,11 @@ export class McpServerManager {
871
904
  } while (cursor);
872
905
 
873
906
  return allResources;
874
- } catch {
907
+ } catch (error) {
875
908
  if (requestOptions?.signal?.aborted) {
876
909
  throwIfAborted(requestOptions.signal);
877
910
  }
911
+ if (isUnauthorizedHttpError(error)) throw error;
878
912
  // The server advertises resources but the listing failed
879
913
  return [];
880
914
  }