@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -0,0 +1,168 @@
1
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import stripJsonComments from "strip-json-comments";
4
+ import { getAgentDir, getConfigDirName } from "./agent-dir.ts";
5
+ import type { McpConfig, ServerEntry } from "./types.ts";
6
+
7
+ interface PackageSetting {
8
+ source: string;
9
+ }
10
+
11
+ interface PackageManifest {
12
+ name?: unknown;
13
+ pi?: { mcp?: unknown };
14
+ }
15
+
16
+ export function loadPackageMcpConfigs(cwd = process.cwd()): McpConfig {
17
+ const mcpServers: Record<string, ServerEntry> = {};
18
+ const seen = new Set<string>();
19
+
20
+ for (const packageRoot of getConfiguredPackageRoots(cwd)) {
21
+ const manifest = readPackageManifest(packageRoot);
22
+ if (!manifest || typeof manifest.name !== "string" || !manifest.name) continue;
23
+ const paths = getManifestMcpPaths(manifest.pi?.mcp, manifest.name);
24
+ if (!paths) continue;
25
+
26
+ const packagePrefix = formatPackageName(manifest.name);
27
+ const packageServers = new Set<string>();
28
+ for (const path of paths) {
29
+ const configPath = resolvePackageConfigPath(packageRoot, path);
30
+ if (!configPath) {
31
+ console.warn(`Pi package ${manifest.name} skips MCP config ${path}: file must stay inside the package`);
32
+ continue;
33
+ }
34
+
35
+ const config = readMcpConfig(configPath, manifest.name);
36
+ if (!config) continue;
37
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
38
+ const normalizedName = `${packagePrefix}__${formatServerName(serverName)}`;
39
+ if (packageServers.has(normalizedName) || seen.has(normalizedName)) {
40
+ console.warn(`Pi package ${manifest.name} skips duplicate normalized MCP server ${normalizedName}`);
41
+ continue;
42
+ }
43
+ packageServers.add(normalizedName);
44
+ seen.add(normalizedName);
45
+ mcpServers[normalizedName] = server;
46
+ }
47
+ }
48
+ }
49
+
50
+ return { mcpServers };
51
+ }
52
+
53
+ function getConfiguredPackageRoots(cwd: string): string[] {
54
+ const roots: string[] = [];
55
+ for (const [settingsPath, scope] of [
56
+ [join(cwd, getConfigDirName(), "settings.json"), "project"],
57
+ [join(getAgentDir(), "settings.json"), "user"],
58
+ ] as const) {
59
+ const settings = readOptionalJson(settingsPath, `${scope} Pi settings`);
60
+ if (settings === undefined) continue;
61
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) {
62
+ throw new Error(`${scope} Pi settings ${settingsPath} must be a JSON object`);
63
+ }
64
+ const packages = (settings as { packages?: unknown }).packages;
65
+ if (packages === undefined) continue;
66
+ if (!Array.isArray(packages)) throw new Error(`${scope} Pi settings ${settingsPath} packages must be an array`);
67
+ for (const entry of packages) {
68
+ const source = typeof entry === "string"
69
+ ? entry
70
+ : entry && typeof entry === "object" && !Array.isArray(entry) && typeof (entry as PackageSetting).source === "string"
71
+ ? (entry as PackageSetting).source
72
+ : undefined;
73
+ if (!source) throw new Error(`${scope} Pi settings ${settingsPath} package entries must be strings or objects with a string source`);
74
+ const root = resolvePackageRoot(source, scope, cwd);
75
+ if (root && !roots.includes(root)) roots.push(root);
76
+ }
77
+ }
78
+ return roots;
79
+ }
80
+
81
+ function resolvePackageRoot(source: string, scope: "user" | "project", cwd: string): string | null {
82
+ const baseDir = scope === "user" ? getAgentDir() : join(cwd, getConfigDirName());
83
+ if (source.startsWith("npm:")) {
84
+ const name = source.slice(4).trim().match(/^(@?[^@]+(?:\/[^@]+)?)/)?.[1];
85
+ return name ? resolveContainedPath(join(baseDir, "npm", "node_modules"), name) : null;
86
+ }
87
+ const gitSource = source.startsWith("git:")
88
+ ? source.slice(4).trim()
89
+ : /^(?:(?:https?|ssh):\/\/|git@[^:]+:)/.test(source) ? source : null;
90
+ if (gitSource) {
91
+ const value = gitSource
92
+ .replace(/^ssh:\/\/git@/, "")
93
+ .replace(/^git@([^:]+):/, "$1/")
94
+ .replace(/^[a-z]+:\/\//i, "");
95
+ const path = value.replace(/@[^/]+$/, "").replace(/\.git$/, "");
96
+ return path && !path.startsWith("/") ? resolveContainedPath(join(baseDir, "git"), path) : null;
97
+ }
98
+ return isAbsolute(source) ? resolve(source) : resolve(baseDir, source);
99
+ }
100
+
101
+ function readPackageManifest(packageRoot: string): PackageManifest | null {
102
+ const manifest = readOptionalJson(join(packageRoot, "package.json"), `Pi package manifest ${packageRoot}`);
103
+ return manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest as PackageManifest : null;
104
+ }
105
+
106
+ function getManifestMcpPaths(value: unknown, packageName: string): string[] | null {
107
+ const paths = typeof value === "string" ? [value] : Array.isArray(value) && value.every((path): path is string => typeof path === "string") ? value : null;
108
+ if (value !== undefined && !paths) console.warn(`Pi package ${packageName} ignores invalid pi.mcp manifest entry`);
109
+ return paths;
110
+ }
111
+
112
+ function readMcpConfig(path: string, packageName: string): McpConfig | null {
113
+ const config = readRequiredJson(path, `Pi package ${packageName} MCP config`);
114
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
115
+ throw new Error(`Pi package ${packageName} MCP config ${path} must be a JSON object`);
116
+ }
117
+ const servers = (config as { mcpServers?: unknown }).mcpServers;
118
+ if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
119
+ throw new Error(`Pi package ${packageName} MCP config ${path} must contain a JSON object mcpServers field`);
120
+ }
121
+ const mcpServers: Record<string, ServerEntry> = {};
122
+ for (const [name, server] of Object.entries(servers)) {
123
+ if (!server || typeof server !== "object" || Array.isArray(server)) {
124
+ throw new Error(`Pi package ${packageName} MCP config ${path} server ${name} must be a JSON object`);
125
+ }
126
+ mcpServers[name] = server as ServerEntry;
127
+ }
128
+ return { mcpServers };
129
+ }
130
+
131
+ function readRequiredJson(path: string, description: string): unknown {
132
+ try {
133
+ return JSON.parse(stripJsonComments(readFileSync(path, "utf8"), { trailingCommas: true }));
134
+ } catch (error) {
135
+ throw new Error(`${description} ${path} contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
136
+ }
137
+ }
138
+
139
+ function readOptionalJson(path: string, description: string): unknown | undefined {
140
+ if (!existsSync(path)) return undefined;
141
+ return readRequiredJson(path, description);
142
+ }
143
+
144
+ function resolveContainedPath(root: string, path: string): string | null {
145
+ const resolved = resolve(root, path);
146
+ const rel = relative(root, resolved);
147
+ return rel === "" || (!rel.startsWith("..") && !rel.startsWith(sep) && !isAbsolute(rel)) ? resolved : null;
148
+ }
149
+
150
+ function resolvePackageConfigPath(packageRoot: string, path: string): string | null {
151
+ const lexicalPath = resolveContainedPath(packageRoot, path);
152
+ if (!lexicalPath || !existsSync(lexicalPath) || !statSync(lexicalPath).isFile()) return null;
153
+ try {
154
+ const realPackageRoot = realpathSync(packageRoot);
155
+ const realConfigPath = realpathSync(lexicalPath);
156
+ return resolveContainedPath(realPackageRoot, realConfigPath);
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ function formatPackageName(name: string): string {
163
+ return name.replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "") || "package";
164
+ }
165
+
166
+ function formatServerName(name: string): string {
167
+ return name.replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "") || "server";
168
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-adapter",
3
- "version": "2.26.0",
3
+ "version": "2.27.0",
4
4
  "description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
@@ -88,6 +88,7 @@
88
88
  "ui-tool-visibility.ts",
89
89
  "ui-app-bridge-helpers.ts",
90
90
  "config.ts",
91
+ "package-mcp-loader.ts",
91
92
  "server-manager.ts",
92
93
  "request-headers-command.ts",
93
94
  "unix-socket-transport.ts",
@@ -115,6 +116,7 @@
115
116
  "oauth-handler.ts",
116
117
  "oauth.ts",
117
118
  "mcp-auth.ts",
119
+ "mcp-bearer-store.ts",
118
120
  "mcp-keyring-helper.cjs",
119
121
  "mcp-oauth-provider.ts",
120
122
  "mcp-callback-server.ts",
@@ -13,7 +13,7 @@ import { reconstructPromptMetadata } from "./metadata-cache.ts";
13
13
  import { resolveMcpResultContent, transformMcpContent, transformMcpResourceContents } from "./tool-registrar.ts";
14
14
  import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
15
15
  import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
16
- import { formatAuthRequiredMessage, formatMcpStatus, resolveServerUrl, truncateAtWord } from "./utils.ts";
16
+ import { formatAuthRequiredMessage, formatMcpStatus, normalizeToolArguments, resolveServerUrl, truncateAtWord } from "./utils.ts";
17
17
  import { authenticate, completeAuthFromInput, startAuth, supportsOAuth } from "./mcp-auth-flow.ts";
18
18
  import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session-recovery.ts";
19
19
  import { paginate, rankSuggestions, rankToolMatches, resolveSearchKeywords } from "./search-ranking.ts";
@@ -1136,11 +1136,12 @@ export async function executeCall(
1136
1136
  return disabledCallResult(serverName, toolMeta);
1137
1137
  }
1138
1138
 
1139
+ const normalizedArgs = toolMeta.resourceUri ? args ?? {} : normalizeToolArguments(args);
1139
1140
  const approval = await ensureToolCallApproved(
1140
1141
  state,
1141
1142
  serverName,
1142
1143
  toolMeta,
1143
- args,
1144
+ normalizedArgs,
1144
1145
  ownedSignal,
1145
1146
  origin ?? (toolMeta.resourceUri ? "resource" : "proxy"),
1146
1147
  );
@@ -1217,7 +1218,7 @@ export async function executeCall(
1217
1218
  ? await maybeStartUiSession(state, {
1218
1219
  serverName,
1219
1220
  toolName: toolMeta.originalName,
1220
- toolArgs: args ?? {},
1221
+ toolArgs: normalizedArgs,
1221
1222
  uiResourceUri: toolMeta.uiResourceUri,
1222
1223
  ...(toolMeta.uiStreamMode !== undefined ? { streamMode: toolMeta.uiStreamMode } : {}),
1223
1224
  ...(signal ? { signal } : {}),
@@ -1235,7 +1236,7 @@ export async function executeCall(
1235
1236
  serverName,
1236
1237
  (conn) => abortable(conn.client.callTool({
1237
1238
  name: toolMeta.originalName,
1238
- arguments: args ?? {},
1239
+ arguments: normalizedArgs,
1239
1240
  _meta: uiSession?.requestMeta,
1240
1241
  }, requestOptions), ownedSignal),
1241
1242
  );
@@ -8,64 +8,62 @@ const DEFAULT_TIMEOUT_MS = 10_000;
8
8
  const MAX_OUTPUT_BYTES = 64 * 1024;
9
9
  const USE_PROCESS_GROUP = process.platform !== "win32";
10
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;
11
15
 
12
16
  function isNoSuchProcessError(error: unknown): boolean {
13
17
  return typeof error === "object" && error !== null && "code" in error && (error as NodeJS.ErrnoException).code === "ESRCH";
14
18
  }
15
19
 
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" });
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 };
19
24
  }
20
25
 
21
- function collectPosixDescendantPids(rootPid: number): number[] {
22
- const result = runPosixPs(["-axo", "pid=,ppid="]);
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="]);
23
34
  if (result.status !== 0) {
24
- throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
35
+ throw new Error(`HTTP request headers command cleanup failed: ${psFailureReason(result)}`);
25
36
  }
26
37
 
27
38
  const childrenByParent = new Map<number, number[]>();
39
+ const processPids = new Set<number>();
40
+ const needle = cleanupToken ? `${CLEANUP_TOKEN_ENV}=${cleanupToken}` : undefined;
28
41
  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);
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]);
32
46
  if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;
33
47
  const children = childrenByParent.get(ppid);
34
48
  if (children) children.push(pid);
35
49
  else childrenByParent.set(ppid, [pid]);
50
+ if (needle && pid !== process.pid && match[3]?.includes(needle)) processPids.add(pid);
36
51
  }
37
52
 
38
- const descendants: number[] = [];
39
53
  const stack = [...(childrenByParent.get(rootPid) ?? [])];
40
54
  while (stack.length > 0) {
41
55
  const pid = stack.pop()!;
42
- descendants.push(pid);
56
+ processPids.add(pid);
43
57
  stack.push(...(childrenByParent.get(pid) ?? []));
44
58
  }
45
- return descendants;
59
+ return [...processPids];
46
60
  }
47
61
 
48
- function collectPosixCleanupTokenPids(cleanupToken: string): number[] {
49
- const result = runPosixPs(["axeww", "-o", "pid=,command="]);
62
+ function assertPosixProcessDiscoveryAvailable(): void {
63
+ const result = runPosixPs(["axeww", "-o", "pid=,ppid=,command="]);
50
64
  if (result.status !== 0) {
51
- throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
65
+ throw new Error(`HTTP request headers command cleanup failed: ${psFailureReason(result)}`);
52
66
  }
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
67
  }
70
68
 
71
69
  function isTaskkillNoSuchProcess(result: ReturnType<typeof spawnSync>): boolean {
@@ -109,10 +107,7 @@ function killRequestHeadersCommand(child: ChildProcess, trackedPosixDescendantPi
109
107
  }
110
108
  let stablePasses = 0;
111
109
  for (let pass = 0; pass < 16; pass++) {
112
- const candidates = [
113
- ...collectPosixDescendantPids(child.pid),
114
- ...(cleanupToken ? collectPosixCleanupTokenPids(cleanupToken) : []),
115
- ];
110
+ const candidates = collectPosixProcessPids(child.pid, cleanupToken);
116
111
  const newPids = candidates.filter(pid => !frozenPids.has(pid));
117
112
  if (newPids.length === 0) {
118
113
  stablePasses++;
@@ -212,8 +207,7 @@ async function invokeRequestHeadersCommand(
212
207
  const trackPosixDescendants = () => {
213
208
  if (!USE_PROCESS_GROUP || child.pid === undefined || settled || trackingError) return;
214
209
  try {
215
- for (const pid of collectPosixDescendantPids(child.pid)) trackedPosixDescendantPids.add(pid);
216
- for (const pid of collectPosixCleanupTokenPids(cleanupToken)) trackedPosixDescendantPids.add(pid);
210
+ for (const pid of collectPosixProcessPids(child.pid, cleanupToken)) trackedPosixDescendantPids.add(pid);
217
211
  } catch (error) {
218
212
  trackingError = error instanceof Error ? error : new Error(String(error));
219
213
  }
@@ -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));