@herbertgao/pi-extensions 2026.8.6 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -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 +1 -0
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -10
  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 +2 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  49. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  50. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  51. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  52. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  53. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  54. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  55. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  56. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  57. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  58. package/node_modules/pi-lens/CHANGELOG.md +909 -0
  59. package/node_modules/pi-lens/README.md +11 -3
  60. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  61. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  62. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  63. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  64. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  65. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  66. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  67. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  68. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  69. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  70. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  71. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  72. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  73. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  74. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  75. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  76. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  77. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  78. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  79. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  80. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  81. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  82. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  83. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  84. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  86. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  87. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  122. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  123. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  124. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  125. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  126. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  127. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  128. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  129. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  130. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  131. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  134. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  135. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  136. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  137. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  138. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  139. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  140. package/node_modules/pi-lens/dist/clients/jscpd-client.js +82 -6
  141. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  142. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  143. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  144. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  145. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  146. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  147. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  148. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +102 -7
  149. package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
  150. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  151. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  152. package/node_modules/pi-lens/dist/clients/lsp/index.js +2274 -147
  153. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  154. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  155. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  156. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  157. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  158. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  159. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  160. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  161. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  163. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  164. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  165. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  166. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  167. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  168. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  169. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  170. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  171. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  172. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  173. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  174. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  175. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  176. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  177. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  178. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  179. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  180. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  181. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  182. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  183. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  185. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  186. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  187. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  188. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  189. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  190. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +308 -45
  191. package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -9
  192. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  193. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  194. package/node_modules/pi-lens/dist/clients/runtime-turn.js +575 -67
  195. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  196. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  197. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  198. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  199. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  200. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  201. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  202. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  203. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  204. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  205. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  210. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  211. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  212. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  213. package/node_modules/pi-lens/dist/index.js +67738 -55739
  214. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
  215. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  216. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  217. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  218. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  219. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  220. package/node_modules/pi-lens/docs/features.md +19 -5
  221. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  222. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  223. package/node_modules/pi-lens/docs/mcp.md +2 -2
  224. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  225. package/node_modules/pi-lens/docs/settings.md +19 -1
  226. package/node_modules/pi-lens/docs/usage.md +1 -0
  227. package/node_modules/pi-lens/package.json +10 -6
  228. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  237. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  238. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  239. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  240. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  241. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  252. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  253. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  254. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  255. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  256. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  257. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  258. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  288. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  289. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  290. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  291. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  292. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  293. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  294. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  295. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  296. package/node_modules/pi-mcp-adapter/README.md +47 -5
  297. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  298. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  299. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  300. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  301. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  302. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  303. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  304. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  305. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  306. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  307. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  308. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  309. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  310. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  311. package/node_modules/pi-mcp-adapter/package.json +3 -1
  312. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  313. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  314. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  315. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  316. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  317. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  318. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  319. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  320. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  321. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  322. package/node_modules/pi-web-access/CHANGELOG.md +23 -0
  323. package/node_modules/pi-web-access/README.md +27 -9
  324. package/node_modules/pi-web-access/anysearch.ts +4 -2
  325. package/node_modules/pi-web-access/brave.ts +16 -4
  326. package/node_modules/pi-web-access/curator-page.ts +6 -3
  327. package/node_modules/pi-web-access/curator-server.ts +4 -1
  328. package/node_modules/pi-web-access/exa.ts +18 -7
  329. package/node_modules/pi-web-access/extract.ts +28 -3
  330. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  331. package/node_modules/pi-web-access/index.ts +41 -57
  332. package/node_modules/pi-web-access/package.json +1 -1
  333. package/node_modules/pi-web-access/page-query.ts +4 -2
  334. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  335. package/node_modules/pi-web-access/parallel.ts +2 -2
  336. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  337. package/node_modules/pi-web-access/serper.ts +211 -0
  338. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  339. package/node_modules/pi-web-access/summary-review.ts +44 -22
  340. package/node_modules/pi-web-access/tavily.ts +16 -4
  341. package/node_modules/pi-web-access/utils.ts +87 -0
  342. package/node_modules/pi-web-access/valyu.ts +199 -0
  343. package/package.json +13 -13
@@ -126,6 +126,42 @@ Each directory must contain a valid Agent Plugins 1.0 `plugin.json`. If it also
126
126
 
127
127
  Agent Plugins is a portable package format. Native Pi MCP config remains `.mcp.json`, `~/.config/mcp/mcp.json`, and Pi-owned overrides.
128
128
 
129
+ ### Pi package manifests
130
+
131
+ A Pi package can ship MCP servers for the installed adapter without requiring a separate MCP config file. Declare a package-relative config in its `package.json`:
132
+
133
+ ```json
134
+ {
135
+ "pi": {
136
+ "mcp": "./mcp.json"
137
+ }
138
+ }
139
+ ```
140
+
141
+ `pi.mcp` can also be an array of package-relative paths. Each file uses the normal `mcpServers` object shape, but package manifests load only server entries: package `settings` and `imports` are ignored. Server names are prefixed with the sanitized package name, such as `acme_tools__docs`, and user/global/project MCP config has higher precedence. The adapter loads only Pi packages listed in Pi settings; it does not scan `node_modules`.
142
+
143
+ ### Runtime registration from other extensions
144
+
145
+ An extension can register MCP servers with the installed adapter at runtime, for example a plugin host that discovers plugins after load:
146
+
147
+ ```ts
148
+ import { registerMcpServer } from "pi-mcp-adapter";
149
+
150
+ export default function pluginHost(pi) {
151
+ const registration = registerMcpServer({
152
+ pi,
153
+ name: "acme__docs",
154
+ definition: {
155
+ url: "https://mcp.example.com/mcp",
156
+ },
157
+ });
158
+ // Later, when the plugin is uninstalled:
159
+ await registration.dispose();
160
+ }
161
+ ```
162
+
163
+ Runtime registrations are session scoped and never written to config files. Duplicate server names fail closed against configured servers and other registrations. Registered servers use the normal lazy connection, OAuth, approval, and shutdown behavior, but they are proxy-tool-only and their tools become visible at the next tool sync. To change a definition, dispose the registration and register again. Registration throws when no adapter is installed for the given Pi instance.
164
+
129
165
  ### SDK configuration
130
166
 
131
167
  Use `createMcpAdapter` when an SDK or server integration already owns its MCP configuration:
@@ -178,7 +214,7 @@ pi.events.on(MCP_STATUS_EVENT, (snapshot) => {
178
214
  });
179
215
  ```
180
216
 
181
- The snapshot is read-only machine-readable data with copied per-server entries. It includes `totalTools`, `totalResources`, `connectedCount`, and `disabledCount`; each server includes `name`, `status`, `toolCount`, and `disabled`, with `resourceCount` when known and `failedAgoSeconds` only for an active failure. Reading status never connects a lazy server, starts authentication, or exposes SDK clients, transports, credentials, or server definitions. An initial snapshot is emitted after initialization, updates are emitted for status and metadata changes, and an empty snapshot is emitted when the session shuts down.
217
+ The snapshot is read-only machine-readable data with copied per-server entries. It includes `totalTools`, `totalResources`, `connectedCount`, and `disabledCount`; each server includes `name`, `status`, `toolCount`, and `disabled`, with `resourceCount` when known and `failedAgoSeconds` only for an active failure. Reading status never connects a lazy server, starts authentication, or exposes SDK clients, transports, credentials, or server definitions. An initial snapshot is emitted after initialization, updates are emitted for status and metadata changes, and an empty snapshot is emitted when the session shuts down. Initialization withholds that first snapshot until authoritative metadata has been reconciled into Pi's active direct-tool registry. A `connected` snapshot therefore follows model-facing tool-surface synchronization, including removal of stale cached tools when the authoritative catalog is empty.
182
218
 
183
219
  In the configuration examples below, `30000` is illustrative only. If `requestTimeoutMs` is omitted or set to `<= 0`, the MCP SDK default timeout is used.
184
220
 
@@ -219,6 +255,7 @@ In the configuration examples below, `30000` is illustrative only. If `requestTi
219
255
  | `oauth.logoUri` | Client logo URL advertised during Dynamic Client Registration fallback (RFC 7591 `logo_uri`). Must be an absolute `http(s)` URL — consent screens fetch it server-side, so local paths render nothing. Omitted from the registration request when unset |
220
256
  | `oauth.skipIssuerMetadataValidation` | `true` disables the OAuth authorization-server metadata issuer check for this server. This weakens OAuth mix-up protection and should only be used for known-misconfigured internal servers while their metadata is being fixed. |
221
257
  | `bearerToken` / `bearerTokenEnv` | Token or env var name; `bearerToken` supports `${VAR}` and `$env:VAR` interpolation. A leading `!` in `bearerToken` runs a command when the HTTP server connects; use `!!` for a literal leading `!`. |
258
+ | `bearerTokenStore` | Set to `true` to read a static bearer token from the adapter-owned OS credential store when `auth` is `"bearer"` and no `bearerToken` or `bearerTokenEnv` is configured. Stored records are keyed only by the server name, bind to the resolved server URL, and are never named by config. Store a token with `pi-mcp-adapter token set <server>`, which reads it from a masked prompt or stdin pipe and never from an argument. `/mcp token status <server>` and `/mcp token remove <server>` manage non-secret state inside Pi; `/mcp token set` stays disabled until Pi exposes masked secret input. |
222
259
  | `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, or `"lazy-keep-alive"` |
223
260
  | `idleTimeout` | Minutes before idle disconnect (overrides global) |
224
261
  | `requestTimeoutMs` | Request timeout in milliseconds for live MCP calls (overrides global; if omitted or `<= 0`, the MCP SDK default timeout is used) |
@@ -249,6 +286,8 @@ If an internal authorization server publishes mismatched OAuth metadata and cann
249
286
 
250
287
  Secret values in `headers`, `bearerToken`, `oauth.clientSecret`, and stdio `env` may use a leading `!command` to obtain their value at connection or authentication time. The command runs with stdin and stderr suppressed, stdout is limited to 1 MiB and trimmed, and it must finish within 10 seconds with non-empty output; failures stop the connection or authentication flow. Commands are not run during OAuth discovery or while reading, merging, previewing, hashing, or rendering configuration. Use `!!` to escape a literal leading `!`; ordinary and escaped values retain environment interpolation.
251
288
 
289
+ For local desktop bearer tokens, `bearerTokenStore: true` can opt in to the adapter-owned credential-store namespace. It never falls back to plaintext if the store is unavailable, if the stored record is malformed, or if the stored URL differs from the effective server URL. Literal tokens, command tokens, and environment tokens keep precedence so existing configs do not change. Create or rotate a stored token with `pi-mcp-adapter token set <server>` (masked prompt on a terminal, or piped stdin such as `security find-generic-password -s my-token -w | pi-mcp-adapter token set <server>`); the record binds to the effective configured URL at write time. Token commands need Node 22.18+.
290
+
252
291
  ### Shared MCP processes with rmcp-mux
253
292
 
254
293
  To share one stdio MCP server across Pi sessions, run it under [`rmcp-mux`](https://github.com/VetCoders/rmcp-mux) and point each session at the service socket:
@@ -293,8 +332,10 @@ You can also pass only the `code` query parameter with `args: { code: "..." }`.
293
332
 
294
333
  - **`lazy`** (default) — Don't connect at startup. Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
295
334
  - **`eager`** — Connect at startup but don't auto-reconnect if the connection drops. No idle timeout by default (set `idleTimeout` explicitly to enable).
296
- - **`keep-alive`** — Connect at startup. Auto-reconnect via health checks. No idle timeout. Use for servers you always need available.
297
- - **`lazy-keep-alive`** — Don't connect at startup. Connect on first tool call (like `lazy`). Once spawned, never idle-shut down and auto-reconnect via health checks if the process dies (like `keep-alive`). Use for servers that are expensive to start but should stay resident after their first use.
335
+ - **`keep-alive`** — Connect at startup. Remote HTTP servers refresh their tool catalog during health checks, before user input, and before adapter-triggered turns, reconnecting when the server reports that the session expired. No idle timeout. Use for servers you always need available.
336
+ - **`lazy-keep-alive`** — Don't connect at startup. Connect on first tool call (like `lazy`). Once spawned, never idle-shut down and use the same catalog refresh and reconnect checks as `keep-alive`. Use for servers that are expensive to start but should stay resident after their first use.
337
+
338
+ For remote HTTP keep-alive servers, the authoritative `tools/list` refresh is also the fallback when `list_changed` notifications are unavailable or their stream is lost. Each `tools/list` or `ping` request is capped at 5 seconds, up to 10 servers are checked concurrently, and transient failures use bounded backoff. A successful refresh updates metadata without reconnecting; a response proving that the HTTP session expired triggers a full reconnect and reinstalls the notification handlers. Dynamic direct-tool registration follows the refreshed metadata unless `freezeDirectTools` is enabled.
298
339
 
299
340
  When any enabled server uses `eager` or `keep-alive`, initialization also starts when the extension loads. This supports hosts that embed Pi programmatically and never emit `session_start`; if a session does start later, the session-owned runtime supersedes the load-time runtime.
300
341
 
@@ -658,7 +699,7 @@ Prefer `.mcp.json` for project-local shared MCP config. Use `.pi/mcp.json` only
658
699
 
659
700
  `mcp({ connect: "server-name" })` refreshes an already connected server, so new tools, resources, prompts, and instructions can load without restarting Pi.
660
701
 
661
- MCP proxy and direct-tool results use compact self-rendered rows by default. Collapsed success output shows the call title and the first result line, with a `Ctrl+O to expand` hint when more text is hidden. The full result remains available when expanded and is still returned unchanged to the model. Set `settings.toolResultRendering` to `"boxed"` to restore the legacy boxed Pi row, or set `settings.collapsedResultLines` to `2` or `3` when you want more collapsed text.
702
+ MCP proxy and direct-tool results use compact self-rendered rows by default. Collapsed success output shows the call title, a bounded one-line input preview when arguments exist, and the first result line, with a `Ctrl+O to expand` hint when more text is hidden. The full result remains available when expanded and is still returned unchanged to the model. Set `settings.toolResultRendering` to `"boxed"` to restore the legacy boxed Pi row, or set `settings.collapsedResultLines` to `2` or `3` when you want more collapsed text.
662
703
 
663
704
  Search includes both MCP tools and Pi tools (from extensions). Pi tools appear first with `[pi tool]` prefix. Space-separated words are ranked by weighted matches across name, server, description, and any configured `searchKeywords`, then returned one page at a time (`limit` defaults to 12). Use `details.nextOffset` for the next page. Regex search is still available with `regex: true`, but regex results are paginated without ranking.
664
705
 
@@ -698,6 +739,7 @@ Servers that provide usage guidance via the MCP `instructions` field surface it
698
739
  | Command | What it does |
699
740
  |---------|--------------|
700
741
  | `/mcp` | Interactive panel and first-run onboarding surface |
742
+ | `/pi-mcp` | Alias for `/mcp` when the host reserves `/mcp` |
701
743
  | `/mcp setup` | Guided setup for imports, a minimal `.mcp.json`, curated known servers, RepoPrompt quick-add, and config-path inspection |
702
744
  | `/mcp tools` | List all tools |
703
745
  | `/mcp prompts` | List all MCP prompts registered as slash commands |
@@ -725,7 +767,7 @@ Advertised tool `outputSchema` values support JSON Schema draft-07 and 2020-12.
725
767
  - Idle servers disconnect after 10 minutes (configurable), reconnect automatically on next use
726
768
  - npx-based servers resolve to direct binary paths, skipping the ~143 MB npm parent process
727
769
  - MCP server validates arguments, not the adapter
728
- - Keep-alive servers get health checks and auto-reconnect
770
+ - Remote keep-alive servers force-refresh their tool catalog during health checks, before user input, and before adapter-triggered turns, with bounded reconnect backoff
729
771
  - Specific tools can be promoted from the proxy to first-class Pi tools via `directTools` config, so the LLM sees them directly instead of having to search
730
772
 
731
773
  ## Limitations
@@ -73,6 +73,11 @@ function printHelp(log = console.log) {
73
73
  log(" pi-mcp-adapter init Detect host configs and scaffold Pi imports");
74
74
  log(" pi-mcp-adapter init --dry-run");
75
75
  log(" pi-mcp-adapter init --discover-host-configs Opt in to host config fallback discovery");
76
+ log("");
77
+ log("Bearer token storage (servers configured with auth: \"bearer\" and bearerTokenStore: true):");
78
+ log(" pi-mcp-adapter token set <server> Store a token read from stdin (masked prompt or pipe; never argv)");
79
+ log(" pi-mcp-adapter token status <server> Report whether a stored token matches the configured URL");
80
+ log(" pi-mcp-adapter token remove <server> Remove the stored token");
76
81
  }
77
82
 
78
83
  function readJsonFile(filePath) {
@@ -194,7 +199,160 @@ async function runInit(argv, log = console.log) {
194
199
  return 0;
195
200
  }
196
201
 
197
- export async function main(argv = process.argv.slice(2), log = console.log, error = console.error) {
202
+ async function importTokenModules(error) {
203
+ try {
204
+ const [store, config, utils] = await Promise.all([
205
+ import("./mcp-bearer-store.ts"),
206
+ import("./config.ts"),
207
+ import("./utils.ts"),
208
+ ]);
209
+ return { store, config, utils };
210
+ } catch (err) {
211
+ error("Token commands need a Node.js version that can load TypeScript modules (Node 22.18+ or 23+).");
212
+ error(`Import failed: ${err instanceof Error ? err.message : String(err)}`);
213
+ return undefined;
214
+ }
215
+ }
216
+
217
+ function readTokenFromStdin(stdin) {
218
+ if (stdin.isTTY) return readTokenMasked(stdin);
219
+ return new Promise((resolve, reject) => {
220
+ let data = "";
221
+ stdin.setEncoding("utf8");
222
+ stdin.on("data", (chunk) => {
223
+ data += chunk;
224
+ });
225
+ stdin.on("end", () => resolve(data.replace(/[\r\n]+$/, "")));
226
+ stdin.on("error", reject);
227
+ });
228
+ }
229
+
230
+ // Raw-mode masked prompt: the token never echoes to the terminal.
231
+ function readTokenMasked(stdin) {
232
+ return new Promise((resolve, reject) => {
233
+ process.stderr.write("Enter bearer token (input hidden): ");
234
+ stdin.setRawMode(true);
235
+ stdin.resume();
236
+ stdin.setEncoding("utf8");
237
+ let token = "";
238
+ const onData = (chunk) => {
239
+ for (const char of chunk) {
240
+ if (char === "\u0003") {
241
+ cleanup();
242
+ reject(new Error("Token entry cancelled"));
243
+ return;
244
+ }
245
+ if (char === "\r" || char === "\n") {
246
+ cleanup();
247
+ resolve(token);
248
+ return;
249
+ }
250
+ if (char === "\u007f" || char === "\b") {
251
+ token = token.slice(0, -1);
252
+ continue;
253
+ }
254
+ token += char;
255
+ }
256
+ };
257
+ const cleanup = () => {
258
+ stdin.setRawMode(false);
259
+ stdin.pause();
260
+ stdin.off("data", onData);
261
+ process.stderr.write("\n");
262
+ };
263
+ stdin.on("data", onData);
264
+ });
265
+ }
266
+
267
+ async function runToken(argv, log, error, stdin) {
268
+ const [action, serverName, ...extra] = argv;
269
+ if (!["set", "status", "remove"].includes(action) || !serverName) {
270
+ error("Usage: pi-mcp-adapter token <set|status|remove> <server>");
271
+ error("`token set` reads the token from stdin only. Never pass the token as an argument.");
272
+ return 1;
273
+ }
274
+ if (extra.length > 0) {
275
+ error("Unexpected extra arguments. The token must not be passed on the command line; pipe it on stdin or use the interactive prompt.");
276
+ return 1;
277
+ }
278
+
279
+ const modules = await importTokenModules(error);
280
+ if (!modules) return 1;
281
+
282
+ // Argv-derived text is sanitized before terminal output, and the resolved
283
+ // URL is never printed: it can embed userinfo or interpolated secrets.
284
+ const safeName = modules.utils.sanitizeTerminalText(serverName);
285
+ const definition = modules.config.loadMcpConfig(undefined, process.cwd()).mcpServers[serverName];
286
+ if (!definition) {
287
+ error(`Server "${safeName}" not found in the effective MCP config.`);
288
+ return 1;
289
+ }
290
+ if (definition.auth !== "bearer" || definition.bearerTokenStore !== true) {
291
+ error(`Server "${safeName}" is not configured for bearerTokenStore. Set "auth": "bearer" and "bearerTokenStore": true.`);
292
+ return 1;
293
+ }
294
+ // resolveServerUrl embeds the interpolated URL in its exceptions, so catch
295
+ // and redact: the URL can carry userinfo or interpolated secrets.
296
+ let serverUrl;
297
+ try {
298
+ serverUrl = modules.utils.resolveServerUrl(definition);
299
+ } catch {
300
+ error(`Server "${safeName}" has an invalid or unresolvable URL. Fix the server's url in the MCP config and retry.`);
301
+ return 1;
302
+ }
303
+ if (!serverUrl) {
304
+ error(`Server "${safeName}" has no URL configured.`);
305
+ return 1;
306
+ }
307
+
308
+ if (action === "set") {
309
+ const token = await readTokenFromStdin(stdin);
310
+ if (!token) {
311
+ error("No token provided on stdin.");
312
+ return 1;
313
+ }
314
+ try {
315
+ modules.store.saveBearerTokenForUrl(serverName, token, serverUrl);
316
+ } catch (err) {
317
+ error(modules.utils.sanitizeTerminalText(err instanceof Error ? err.message : String(err)));
318
+ return 1;
319
+ }
320
+ log(`Bearer token stored for "${safeName}" and bound to the configured server URL.`);
321
+ if (definition.bearerToken !== undefined || definition.bearerTokenEnv !== undefined) {
322
+ log("Note: this server also configures bearerToken or bearerTokenEnv, which take precedence over the stored token.");
323
+ }
324
+ return 0;
325
+ }
326
+
327
+ if (action === "status") {
328
+ const status = modules.store.inspectBearerTokenForUrl(serverName, serverUrl);
329
+ if (status.status === "present") {
330
+ log(`Bearer token is stored for "${safeName}".`);
331
+ return 0;
332
+ }
333
+ if (status.status === "url-mismatch") {
334
+ log(`Bearer token is stored for "${safeName}", but its URL does not match the current server URL. Run \`pi-mcp-adapter token set ${safeName}\` to rebind it.`);
335
+ return 1;
336
+ }
337
+ if (status.status === "unavailable") {
338
+ error(status.message);
339
+ return 1;
340
+ }
341
+ log(`No bearer token is stored for "${safeName}".`);
342
+ return 1;
343
+ }
344
+
345
+ try {
346
+ modules.store.removeBearerToken(serverName);
347
+ } catch (err) {
348
+ error(modules.utils.sanitizeTerminalText(err instanceof Error ? err.message : String(err)));
349
+ return 1;
350
+ }
351
+ log(`Bearer token removed for "${safeName}".`);
352
+ return 0;
353
+ }
354
+
355
+ export async function main(argv = process.argv.slice(2), log = console.log, error = console.error, stdin = process.stdin) {
198
356
  const [command, ...rest] = argv;
199
357
 
200
358
  if (!command || command === "help" || command === "--help" || command === "-h") {
@@ -202,6 +360,10 @@ export async function main(argv = process.argv.slice(2), log = console.log, erro
202
360
  return 0;
203
361
  }
204
362
 
363
+ if (command === "token") {
364
+ return runToken(rest, log, error, stdin);
365
+ }
366
+
205
367
  if (command === "install") {
206
368
  error("The custom downloader has been retired.");
207
369
  error("Use `pi install npm:pi-mcp-adapter` instead, then optionally run `pi-mcp-adapter init`.");
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { OverlayHandle } from "@earendil-works/pi-tui";
2
3
  import type { McpExtensionState } from "./state.ts";
3
4
  import { isServerDisabled, type McpAuthResult, type McpConfig, type McpPanelCallbacks, type McpPanelResult, type ImportKind } from "./types.ts";
4
5
  import {
@@ -20,6 +21,7 @@ import { loadMetadataCache, reconstructPromptMetadata } from "./metadata-cache.t
20
21
  import { buildToolMetadata } from "./tool-metadata.ts";
21
22
  import { supportsOAuth, authenticate, removeAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
22
23
  import { getAuthStorageOptions, inspectAuthForUrl } from "./mcp-auth.ts";
24
+ import { inspectBearerTokenForUrl, removeBearerToken } from "./mcp-bearer-store.ts";
23
25
  import { loadOnboardingState, markSetupCompleted as persistSetupCompleted, markSharedConfigHintShown } from "./onboarding-state.ts";
24
26
  import { openPath, resolveServerUrl, sanitizeTerminalText } from "./utils.ts";
25
27
  import { isAbortError } from "./runtime-owner.ts";
@@ -28,6 +30,19 @@ function terminalHyperlink(label: string, url: string): string {
28
30
  return `\u001B]8;;${sanitizeTerminalText(url)}\u001B\\${sanitizeTerminalText(label)}\u001B]8;;\u001B\\`;
29
31
  }
30
32
 
33
+ /**
34
+ * True when this run mode can display a `ctx.ui.custom()` overlay.
35
+ *
36
+ * `ctx.hasUI` only reports that *some* UI context is bound. In rpc/print mode
37
+ * that context is a headless stub whose `custom()` returns immediately without
38
+ * ever invoking the factory or the `done` callback, so a panel awaited through
39
+ * `new Promise(resolve => ctx.ui.custom(...))` never settles and the command
40
+ * hangs. Overlay panels therefore additionally require `ctx.mode === "tui"`.
41
+ */
42
+ function canRenderPanel(ctx: ExtensionContext): boolean {
43
+ return ctx.hasUI && ctx.mode === "tui";
44
+ }
45
+
31
46
  export async function showStatus(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
32
47
  if (!ctx.hasUI) return;
33
48
 
@@ -274,25 +289,14 @@ export async function authenticateServer(
274
289
  const authStorageOptions = getAuthStorageOptions(config.settings?.oauthDir, cwd);
275
290
  const status = await authenticate(serverName, serverUrl, definition, {
276
291
  ...(authStorageOptions.baseDir ? { authStorageOptions } : {}),
277
- onAuthorizationUrl: (authorizationUrl) => {
278
- ui.notify(
279
- `Open this URL to authenticate ${serverName}:\n\n${terminalHyperlink(authorizationUrl, authorizationUrl)}\n\n` +
280
- "After approving, Pi will complete automatically if the browser can reach its localhost callback. " +
281
- "On a remote machine, copy the full localhost URL from the browser address bar and paste it into Pi.",
282
- "info"
283
- );
284
- },
292
+ onAuthorizationUrl: () => {},
285
293
  onAuthorizationInput: async (authorizationUrl, inputSignal) => {
286
- const readyToPaste = await ui.confirm(
287
- `Authorize ${serverName}`,
288
- `Open this link in your browser:\n${terminalHyperlink(authorizationUrl, authorizationUrl)}\n\n` +
289
- "After approving access, select Yes to paste the callback URL.",
290
- { signal: inputSignal },
291
- );
292
- if (!readyToPaste || inputSignal.aborted) return undefined;
294
+ if (inputSignal.aborted) return undefined;
293
295
  return ui.input(
294
- `Complete ${serverName} OAuth`,
295
- "Paste the full callback URL",
296
+ `Complete ${serverName} OAuth\n\n` +
297
+ `${terminalHyperlink("Open authorization page", authorizationUrl)}\n${authorizationUrl}\n\n` +
298
+ "Approve access, then paste the full localhost callback URL below.",
299
+ undefined,
296
300
  { signal: inputSignal },
297
301
  );
298
302
  },
@@ -368,6 +372,75 @@ export async function logoutServer(
368
372
  return { ok: true, message };
369
373
  }
370
374
 
375
+ function validateBearerTokenStoreServer(
376
+ serverName: string,
377
+ state: McpExtensionState,
378
+ ): { ok: true; serverUrl: string } | { ok: false; message: string; type: "error" | "warning" } {
379
+ const safeName = sanitizeTerminalText(serverName);
380
+ const definition = state.config.mcpServers[serverName];
381
+ if (!definition) return { ok: false, message: `Server "${safeName}" not found in config`, type: "error" };
382
+ if (isServerDisabled(definition)) return { ok: false, message: `Server "${safeName}" is disabled. Run /mcp enable ${safeName}, then /reload.`, type: "warning" };
383
+ if (definition.auth !== "bearer" || definition.bearerTokenStore !== true) {
384
+ return { ok: false, message: `Server "${safeName}" is not configured for bearerTokenStore.`, type: "error" };
385
+ }
386
+ // resolveServerUrl embeds the interpolated URL in its exceptions; redact it
387
+ // because the URL can carry userinfo or interpolated secrets.
388
+ let serverUrl: string | undefined;
389
+ try {
390
+ serverUrl = resolveServerUrl(definition);
391
+ } catch {
392
+ return { ok: false, message: `Server "${safeName}" has an invalid or unresolvable URL.`, type: "error" };
393
+ }
394
+ if (!serverUrl) return { ok: false, message: `Server "${safeName}" has no URL configured.`, type: "error" };
395
+ return { ok: true, serverUrl };
396
+ }
397
+
398
+ export async function manageBearerToken(
399
+ action: "set" | "remove" | "status",
400
+ serverName: string,
401
+ state: McpExtensionState,
402
+ ctx: ExtensionContext,
403
+ ): Promise<{ ok: boolean; message: string }> {
404
+ const ui = ctx.hasUI ? ctx.ui : undefined;
405
+ const safeName = sanitizeTerminalText(serverName);
406
+ const validation = validateBearerTokenStoreServer(serverName, state);
407
+ if (!validation.ok) {
408
+ if (ui) ui.notify(validation.message, validation.type);
409
+ return { ok: false, message: validation.message };
410
+ }
411
+
412
+ if (action === "set") {
413
+ const message = `Cannot store bearer token here: Pi extension UI has no masked secret input primitive. Run \`pi-mcp-adapter token set ${safeName}\` in a terminal; it reads the token from stdin only.`;
414
+ if (ui) ui.notify(message, "error");
415
+ return { ok: false, message };
416
+ }
417
+
418
+ if (action === "status") {
419
+ const status = inspectBearerTokenForUrl(serverName, validation.serverUrl);
420
+ const message = status.status === "present"
421
+ ? `Bearer token is stored for "${safeName}".`
422
+ : status.status === "url-mismatch"
423
+ ? `Bearer token is stored for "${safeName}", but its URL does not match the current server URL.`
424
+ : status.status === "unavailable"
425
+ ? status.message
426
+ : `No bearer token is stored for "${safeName}".`;
427
+ if (ui) ui.notify(message, status.status === "unavailable" ? "error" : "info");
428
+ return { ok: status.status !== "unavailable", message };
429
+ }
430
+
431
+ try {
432
+ removeBearerToken(serverName);
433
+ } catch (error) {
434
+ const message = error instanceof Error ? error.message : String(error);
435
+ if (ui) ui.notify(`Failed to remove bearer token for "${safeName}": ${sanitizeTerminalText(message)}`, "error");
436
+ return { ok: false, message };
437
+ }
438
+
439
+ const message = `Bearer token removed for "${safeName}".`;
440
+ if (ui) ui.notify(message, "info");
441
+ return { ok: true, message };
442
+ }
443
+
371
444
  export interface PanelFlowResult {
372
445
  configChanged: boolean;
373
446
  }
@@ -399,6 +472,10 @@ export async function openMcpSetup(
399
472
  options: { includeHostConfigs?: boolean } = {},
400
473
  ): Promise<PanelFlowResult> {
401
474
  if (!ctx.hasUI) return { configChanged: false };
475
+ if (!canRenderPanel(ctx)) {
476
+ ctx.ui.notify(`The interactive MCP setup panel is only available in the terminal UI (current mode: ${ctx.mode}). Edit .mcp.json directly, or run /mcp status to review servers.`, "info");
477
+ return { configChanged: false };
478
+ }
402
479
  if (state.programmaticConfig) {
403
480
  ctx.ui.notify("MCP setup is unavailable when config is supplied by createMcpAdapter().", "info");
404
481
  return { configChanged: false };
@@ -467,6 +544,7 @@ function buildMcpPanelCallbacks(
467
544
  state: McpExtensionState,
468
545
  config: McpConfig,
469
546
  ctx: ExtensionContext,
547
+ getOverlayHandle?: () => OverlayHandle | undefined,
470
548
  ): McpPanelCallbacks {
471
549
  // Panel-only diagnostics keep status inspection from mutating connection
472
550
  // failure state while allowing the existing panel failure UI to show why the
@@ -479,7 +557,16 @@ function buildMcpPanelCallbacks(
479
557
  const definition = config.mcpServers[serverName];
480
558
  return definition ? !isServerDisabled(definition) && supportsOAuth(definition) : false;
481
559
  },
482
- authenticate: (serverName: string) => authenticateServer(serverName, config, ctx, state.owner?.signal, state.oauthRuntime),
560
+ authenticate: async (serverName: string) => {
561
+ const overlay = getOverlayHandle?.();
562
+ overlay?.setHidden(true);
563
+ try {
564
+ return await authenticateServer(serverName, config, ctx, state.owner?.signal, state.oauthRuntime);
565
+ } finally {
566
+ overlay?.setHidden(false);
567
+ overlay?.focus();
568
+ }
569
+ },
483
570
  getConnectionStatus: (serverName: string) => {
484
571
  authStatusFailures.delete(serverName);
485
572
  const definition = config.mcpServers[serverName];
@@ -533,6 +620,11 @@ export async function openMcpPanel(
533
620
  }
534
621
  return { configChanged: false };
535
622
  }
623
+ if (!canRenderPanel(ctx)) {
624
+ // No overlay here, but the same information is available as text.
625
+ await showStatus(state, ctx);
626
+ return { configChanged: false };
627
+ }
536
628
  if (Object.keys(state.config.mcpServers).length === 0) {
537
629
  return openMcpSetup(state, pi, ctx, configOverridePath, "empty", { includeHostConfigs: false });
538
630
  }
@@ -543,7 +635,8 @@ export async function openMcpPanel(
543
635
  const provenanceMap = getServerProvenance(configPath, ctx.cwd);
544
636
  const { lines: noticeLines, fingerprint } = buildSharedConfigNoticeLines(configPath, ctx.cwd);
545
637
 
546
- const callbacks = buildMcpPanelCallbacks(state, config, ctx);
638
+ let overlayHandle: OverlayHandle | undefined;
639
+ const callbacks = buildMcpPanelCallbacks(state, config, ctx, () => overlayHandle);
547
640
 
548
641
  const { createMcpPanel } = await import("./mcp-panel.ts");
549
642
  let configChanged = false;
@@ -569,7 +662,11 @@ export async function openMcpPanel(
569
662
  });
570
663
  }, { noticeLines, keybindings });
571
664
  },
572
- { overlay: true, overlayOptions: { anchor: "center", width: 82 } },
665
+ {
666
+ overlay: true,
667
+ overlayOptions: { anchor: "center", width: 82 },
668
+ onHandle: (handle) => { overlayHandle = handle; },
669
+ },
573
670
  );
574
671
  });
575
672
 
@@ -587,6 +684,10 @@ export async function openMcpAuthPanel(
587
684
  configOverridePath?: string,
588
685
  ): Promise<PanelFlowResult> {
589
686
  if (!ctx.hasUI) return { configChanged: false };
687
+ if (!canRenderPanel(ctx)) {
688
+ ctx.ui.notify(`The interactive MCP auth panel is only available in the terminal UI (current mode: ${ctx.mode}). Use /mcp-auth <server> to authenticate a specific server.`, "info");
689
+ return { configChanged: false };
690
+ }
590
691
  if (state.programmaticConfig) {
591
692
  ctx.ui.notify("Use /mcp-auth <server> to authenticate a server from the in-memory SDK config.", "info");
592
693
  return { configChanged: false };
@@ -604,7 +705,8 @@ export async function openMcpAuthPanel(
604
705
  const cache = loadMetadataCache();
605
706
  const configPath = pi.getFlag("mcp-config") as string | undefined ?? configOverridePath;
606
707
  const provenanceMap = getServerProvenance(configPath, ctx.cwd);
607
- const callbacks = buildMcpPanelCallbacks(state, config, ctx);
708
+ let overlayHandle: OverlayHandle | undefined;
709
+ const callbacks = buildMcpPanelCallbacks(state, config, ctx, () => overlayHandle);
608
710
  const { createMcpPanel } = await import("./mcp-panel.ts");
609
711
 
610
712
  await new Promise<void>((resolve) => {
@@ -619,7 +721,11 @@ export async function openMcpAuthPanel(
619
721
  noticeLines: ["Select an OAuth MCP server and press Enter or ctrl+a to authenticate."],
620
722
  });
621
723
  },
622
- { overlay: true, overlayOptions: { anchor: "center", width: 82 } },
724
+ {
725
+ overlay: true,
726
+ overlayOptions: { anchor: "center", width: 82 },
727
+ onHandle: (handle) => { overlayHandle = handle; },
728
+ },
623
729
  );
624
730
  });
625
731
 
@@ -6,6 +6,7 @@ import { parse as parseToml } from "smol-toml";
6
6
  import stripJsonComments from "strip-json-comments";
7
7
  import { getAgentPath, getConfigDirName } from "./agent-dir.ts";
8
8
  import { getAgentPluginSummaries, loadAgentPluginConfigs, type AgentPluginSummary } from "./agent-plugin-loader.ts";
9
+ import { loadPackageMcpConfigs } from "./package-mcp-loader.ts";
9
10
  import { isServerDisabled, type HostConfigDiscovery, type McpConfig, type ServerEntry, type McpSettings, type ImportKind, type ServerProvenance } from "./types.ts";
10
11
  import { toStringRecord } from "./utils.ts";
11
12
 
@@ -306,8 +307,12 @@ export function loadMcpConfig(overridePath?: string, cwd = process.cwd()): McpCo
306
307
  config = mergeConfigs(config, expandImports(loaded, cwd));
307
308
  }
308
309
 
310
+ const packageConfig = loadPackageMcpConfigs(cwd);
309
311
  const pluginConfig = loadAgentPluginConfigs(config.settings?.agentPluginPaths, cwd);
310
- return mergeConfigs(pluginConfig, config);
312
+ const packageServers = Object.fromEntries(
313
+ Object.entries(packageConfig.mcpServers).filter(([name]) => !Object.hasOwn(pluginConfig.mcpServers, name)),
314
+ );
315
+ return mergeConfigs({ mcpServers: packageServers }, mergeConfigs(pluginConfig, config));
311
316
  }
312
317
 
313
318
  function getMergedSettings(overridePath?: string, cwd = process.cwd()): McpSettings | undefined {
@@ -471,7 +476,7 @@ function mergeConfigs(base: McpConfig, next: McpConfig): McpConfig {
471
476
  // different url, these MUST NOT be inherited from the lower-precedence entry —
472
477
  // otherwise the original endpoint's credentials would be shipped to the new
473
478
  // url. See the SECURITY note in mergeServerMaps.
474
- const URL_BOUND_AUTH_FIELDS = ["headers", "bearerToken", "bearerTokenEnv", "requestHeadersCommand"] as const;
479
+ const URL_BOUND_AUTH_FIELDS = ["headers", "bearerToken", "bearerTokenEnv", "bearerTokenStore", "requestHeadersCommand"] as const;
475
480
 
476
481
  function mergeServerMaps(
477
482
  base: Record<string, ServerEntry>,
@@ -491,17 +496,30 @@ function mergeServerMaps(
491
496
  // applies (it is spread last). Behaviour is unchanged when the url is
492
497
  // identical or the override omits `url` (partial overrides still inherit).
493
498
  let baseEntry: ServerEntry = existing ?? {};
494
- if (existing && typeof definition.socket === "string") {
499
+ if (existing && typeof definition.command === "string") {
495
500
  baseEntry = { ...existing };
496
501
  for (const field of [
497
- "command", "args", "env", "cwd", "url", "headers", "auth",
498
- "bearerToken", "bearerTokenEnv", "oauth",
502
+ "url", "headers", "requestHeadersCommand", "auth", "bearerToken",
503
+ "bearerTokenEnv", "oauth", "httpTransport", "socket",
499
504
  ] as const) {
500
505
  delete baseEntry[field];
501
506
  }
502
- } else if (existing?.socket && (typeof definition.command === "string" || typeof definition.url === "string")) {
507
+ } else if (existing && typeof definition.url === "string") {
503
508
  baseEntry = { ...existing };
504
- delete baseEntry.socket;
509
+ for (const field of [
510
+ "command", "args", "env", "cwd", "pluginDataDir", "literalEnv", "socket",
511
+ ] as const) {
512
+ delete baseEntry[field];
513
+ }
514
+ } else if (existing && typeof definition.socket === "string") {
515
+ baseEntry = { ...existing };
516
+ for (const field of [
517
+ "command", "args", "env", "cwd", "pluginDataDir", "literalEnv", "url",
518
+ "headers", "requestHeadersCommand", "auth", "bearerToken", "bearerTokenEnv",
519
+ "oauth", "httpTransport",
520
+ ] as const) {
521
+ delete baseEntry[field];
522
+ }
505
523
  }
506
524
  if (existing && typeof definition.url === "string" && definition.url !== existing.url) {
507
525
  if (baseEntry === existing) baseEntry = { ...existing };
@@ -15,7 +15,7 @@ import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates
15
15
  import { isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
16
16
  import { resourceNameToToolName } from "./resource-tools.ts";
17
17
  import { authenticate, supportsOAuth } from "./mcp-auth-flow.ts";
18
- import { formatAuthRequiredMessage, resolveServerUrl, truncateAtWord } from "./utils.ts";
18
+ import { formatAuthRequiredMessage, normalizeToolArguments, resolveServerUrl, truncateAtWord } from "./utils.ts";
19
19
  import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session-recovery.ts";
20
20
  import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
21
21
  import { ensureToolCallApproved } from "./tool-approval.ts";
@@ -429,6 +429,7 @@ export function createDirectToolExecutor(
429
429
  };
430
430
  }
431
431
 
432
+ const normalizedParams = spec.resourceUri ? params : normalizeToolArguments(params);
432
433
  const approval = await ensureToolCallApproved(state, spec.serverName, {
433
434
  name: spec.prefixedName,
434
435
  originalName: spec.originalName,
@@ -437,7 +438,7 @@ export function createDirectToolExecutor(
437
438
  ...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
438
439
  ...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
439
440
  ...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
440
- }, params, ownedSignal, spec.resourceUri ? "resource" : "direct");
441
+ }, normalizedParams, ownedSignal, spec.resourceUri ? "resource" : "direct");
441
442
  if (approval.ok === false) {
442
443
  const denied = approval.reason === "denied";
443
444
  const message = denied
@@ -509,7 +510,7 @@ export function createDirectToolExecutor(
509
510
  ? await maybeStartUiSession(state, {
510
511
  serverName: spec.serverName,
511
512
  toolName: spec.originalName,
512
- toolArgs: params ?? {},
513
+ toolArgs: normalizedParams,
513
514
  uiResourceUri: spec.uiResourceUri!,
514
515
  ...(spec.uiStreamMode !== undefined ? { streamMode: spec.uiStreamMode } : {}),
515
516
  ...(signal ? { signal } : {}),
@@ -527,7 +528,7 @@ export function createDirectToolExecutor(
527
528
  spec.serverName,
528
529
  (conn) => abortable(conn.client.callTool({
529
530
  name: spec.originalName,
530
- arguments: params ?? {},
531
+ arguments: normalizedParams,
531
532
  _meta: uiSession?.requestMeta,
532
533
  }, requestOptions), ownedSignal),
533
534
  );