@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -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
 
@@ -207,6 +243,7 @@ In the configuration examples below, `30000` is illustrative only. If `requestTi
207
243
  | `cwd` | Working directory; supports `${VAR}`, `$env:VAR`, and `~` expansion |
208
244
  | `url` | HTTP endpoint (StreamableHTTP with SSE fallback); supports raw `${VAR}` and `$env:VAR` interpolation, and missing URL variables fail before any request is sent |
209
245
  | `headers` | HTTP headers; supports `${VAR}` and `$env:VAR` interpolation. A value beginning with `!` runs a command when the HTTP server connects or OAuth authenticates; use `!!` for a literal leading `!`. |
246
+ | `requestHeadersCommand` | Trusted executable run for every HTTP request. It receives a versioned JSON envelope containing `method`, `url`, and the exact `bodyBase64` on stdin, and must return a JSON object of headers on stdout. `command`, `args`, and `env` support environment interpolation. Use for caller-bound request signatures; failures stop the request. |
210
247
  | `auth` | `"bearer"` or `"oauth"` |
211
248
  | `oauth.grantType` | `"authorization_code"` (default) or `"client_credentials"` for non-interactive machine auth |
212
249
  | `oauth.clientId` | Pre-registered OAuth client ID. MCP 2026 prefers pre-registered clients or Client ID Metadata Documents; this adapter falls back to Dynamic Client Registration when the ID is omitted and the server supports it. |
@@ -218,6 +255,7 @@ In the configuration examples below, `30000` is illustrative only. If `requestTi
218
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 |
219
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. |
220
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. |
221
259
  | `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, or `"lazy-keep-alive"` |
222
260
  | `idleTimeout` | Minutes before idle disconnect (overrides global) |
223
261
  | `requestTimeoutMs` | Request timeout in milliseconds for live MCP calls (overrides global; if omitted or `<= 0`, the MCP SDK default timeout is used) |
@@ -248,6 +286,8 @@ If an internal authorization server publishes mismatched OAuth metadata and cann
248
286
 
249
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.
250
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
+
251
291
  ### Shared MCP processes with rmcp-mux
252
292
 
253
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:
@@ -292,8 +332,10 @@ You can also pass only the `code` query parameter with `args: { code: "..." }`.
292
332
 
293
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.
294
334
  - **`eager`** — Connect at startup but don't auto-reconnect if the connection drops. No idle timeout by default (set `idleTimeout` explicitly to enable).
295
- - **`keep-alive`** — Connect at startup. Auto-reconnect via health checks. No idle timeout. Use for servers you always need available.
296
- - **`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.
297
339
 
298
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.
299
341
 
@@ -310,6 +352,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
310
352
  "toolResultRendering": "compact",
311
353
  "collapsedResultLines": 1,
312
354
  "notifyOnStartupConnect": true,
355
+ "warnOnLargeDirectTools": true,
313
356
  "hostConfigDiscovery": "off",
314
357
  "approveTools": ["github_delete_*", "notion_update_*"],
315
358
  "oauthDir": ".pi/mcp-oauth",
@@ -340,6 +383,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
340
383
  | `oauthDir` | Legacy OAuth `tokens.json` import directory for this MCP config. Relative paths resolve from the active project cwd. `MCP_OAUTH_DIR` still wins when set. Persistent OAuth credentials are stored in the OS credential store, not this directory. |
341
384
  | `mcpServers.<name>.oauth.authorizationParams` | Extra authorization URL parameters for provider-specific OAuth extensions. Flow-owned parameters such as `client_id`, `redirect_uri`, `scope`, `state`, `code_challenge`, `response_type`, and `resource` cannot be overridden. |
342
385
  | `directTools` | Global default for all servers (default: false). Per-server overrides this. |
386
+ | `warnOnLargeDirectTools` | Show the advisory when 75 or more direct tools resolve (default: `true`). Set to `false` to suppress only this advisory. |
343
387
  | `freezeDirectTools` | Keep direct-tool registration stable after the initial sync so automatic reconnects and list-change notifications do not rebuild the system prompt. Use `mcp({ connect: "server" })` or `/mcp reconnect <server>` to refresh deliberately. Default: false. |
344
388
  | `scriptMode` | Register the MCP-only `mcpScript` plain-JavaScript tool (default: true). Set to `false` to hide it. |
345
389
  | `disableProxyTool` | Hide the `mcp` proxy tool once configured direct tools are fully available from cache. |
@@ -544,7 +588,7 @@ To hide specific tools while still using `directTools: true`, add `excludeTools`
544
588
 
545
589
  `includeTools` and `excludeTools` filter direct tools, proxy search/list/describe, and the `/mcp` panel view.
546
590
 
547
- Each direct tool costs ~150-300 tokens in the system prompt (name + description + schema). Good for targeted sets of 5-20 tools. For servers with 75+ tools, stick with the proxy or pick specific tools with a `string[]`. If 75+ direct tools resolve, the adapter prints a warning but still registers the tools you configured.
591
+ Each direct tool costs ~150-300 tokens in the system prompt (name + description + schema). Good for targeted sets of 5-20 tools. For servers with 75+ tools, stick with the proxy or pick specific tools with a `string[]`. If 75+ direct tools resolve, the adapter prints an advisory but still registers the tools you configured. Set `settings.warnOnLargeDirectTools` to `false` to suppress this advisory.
548
592
 
549
593
  Direct tools register from the metadata cache in the Pi agent dir (`~/.pi/agent/mcp-cache.json` by default, or `$PI_CODING_AGENT_DIR/mcp-cache.json` when set), so no server connections are needed at startup. On the first session after adding `directTools` to a new server, the cache won't exist yet — tools fall back to proxy-only while the cache populates, then the extension hot-loads the refreshed direct tools into the current session. Servers that advertise MCP list-change notifications refresh the current session when their tool or resource list changes. On Pi versions that expose `pi.unregisterTool()`, stale direct tools are removed from the registry during refresh; older Pi versions still deactivate them from the active tool set. To force a refresh: `/mcp reconnect <server>`.
550
594
 
@@ -655,7 +699,7 @@ Prefer `.mcp.json` for project-local shared MCP config. Use `.pi/mcp.json` only
655
699
 
656
700
  `mcp({ connect: "server-name" })` refreshes an already connected server, so new tools, resources, prompts, and instructions can load without restarting Pi.
657
701
 
658
- 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.
659
703
 
660
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.
661
705
 
@@ -695,6 +739,7 @@ Servers that provide usage guidance via the MCP `instructions` field surface it
695
739
  | Command | What it does |
696
740
  |---------|--------------|
697
741
  | `/mcp` | Interactive panel and first-run onboarding surface |
742
+ | `/pi-mcp` | Alias for `/mcp` when the host reserves `/mcp` |
698
743
  | `/mcp setup` | Guided setup for imports, a minimal `.mcp.json`, curated known servers, RepoPrompt quick-add, and config-path inspection |
699
744
  | `/mcp tools` | List all tools |
700
745
  | `/mcp prompts` | List all MCP prompts registered as slash commands |
@@ -722,7 +767,7 @@ Advertised tool `outputSchema` values support JSON Schema draft-07 and 2020-12.
722
767
  - Idle servers disconnect after 10 minutes (configurable), reconnect automatically on next use
723
768
  - npx-based servers resolve to direct binary paths, skipping the ~143 MB npm parent process
724
769
  - MCP server validates arguments, not the adapter
725
- - 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
726
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
727
772
 
728
773
  ## Limitations
@@ -2,10 +2,18 @@ import { readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
4
 
5
+ export function getConfigDirName(): string {
6
+ const configDir = readPiConfig()?.configDir;
7
+ return typeof configDir === "string" && configDir.trim() ? configDir.trim() : ".pi";
8
+ }
9
+
5
10
  export function getAgentDir(): string {
6
- const configured = process.env.PI_CODING_AGENT_DIR?.trim();
11
+ const piConfig = readPiConfig();
12
+ const name = piConfig?.name;
13
+ const appName = typeof name === "string" && name.trim() ? name.trim() : "pi";
14
+ const configured = process.env[`${appName.toUpperCase()}_CODING_AGENT_DIR`]?.trim();
7
15
  if (!configured) {
8
- return join(homedir(), ".pi", "agent");
16
+ return join(homedir(), getConfigDirName(), "agent");
9
17
  }
10
18
  if (configured === "~") {
11
19
  return homedir();
@@ -31,12 +39,12 @@ export function getAgentPath(...segments: string[]): string {
31
39
  *
32
40
  * Falls back to "pi", which is what pi's own APP_NAME resolves to.
33
41
  */
34
- function readPiConfig(): { name?: unknown; clientUri?: unknown } | undefined {
42
+ function readPiConfig(): { name?: unknown; configDir?: unknown; clientUri?: unknown } | undefined {
35
43
  const dir = process.env.PI_PACKAGE_DIR?.trim()
36
44
  if (!dir) return undefined
37
45
  try {
38
46
  const manifest = JSON.parse(readFileSync(join(resolve(dir), "package.json"), "utf8")) as {
39
- piConfig?: { name?: unknown; clientUri?: unknown }
47
+ piConfig?: { name?: unknown; configDir?: unknown; clientUri?: unknown }
40
48
  }
41
49
  return manifest.piConfig
42
50
  } catch {
@@ -14,15 +14,36 @@ function expandHome(input) {
14
14
  return path.resolve(input);
15
15
  }
16
16
 
17
- const AGENT_DIR = process.env.PI_CODING_AGENT_DIR?.trim()
18
- ? expandHome(process.env.PI_CODING_AGENT_DIR.trim())
19
- : path.join(HOME, ".pi", "agent");
17
+ function readPiConfig() {
18
+ const dir = process.env.PI_PACKAGE_DIR?.trim();
19
+ if (!dir) return undefined;
20
+ try {
21
+ return JSON.parse(fs.readFileSync(path.join(path.resolve(dir), "package.json"), "utf8")).piConfig;
22
+ } catch {
23
+ return undefined;
24
+ }
25
+ }
26
+
27
+ function getConfigDirName() {
28
+ const configDir = readPiConfig()?.configDir;
29
+ return typeof configDir === "string" && configDir.trim() ? configDir.trim() : ".pi";
30
+ }
31
+
32
+ function getAgentDir() {
33
+ const piConfig = readPiConfig();
34
+ const appName = typeof piConfig?.name === "string" && piConfig.name.trim() ? piConfig.name.trim() : "pi";
35
+ const configured = process.env[`${appName.toUpperCase()}_CODING_AGENT_DIR`]?.trim();
36
+ if (configured) return expandHome(configured);
37
+ return path.join(HOME, getConfigDirName(), "agent");
38
+ }
39
+
40
+ const AGENT_DIR = getAgentDir();
20
41
  const PI_CONFIG_PATH = path.join(AGENT_DIR, "mcp.json");
21
42
  const GENERIC_GLOBAL_CONFIG_PATH = path.join(HOME, ".config", "mcp", "mcp.json");
22
43
  const AGENTS_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp.json");
23
44
  const AGENTS_NESTED_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp", "mcp.json");
24
45
  const PROJECT_CONFIG_PATH = path.resolve(process.cwd(), ".mcp.json");
25
- const PROJECT_PI_CONFIG_PATH = path.resolve(process.cwd(), ".pi", "mcp.json");
46
+ const PROJECT_PI_CONFIG_PATH = path.resolve(process.cwd(), getConfigDirName(), "mcp.json");
26
47
 
27
48
  const IMPORT_PATHS = {
28
49
  cursor: [path.join(HOME, ".cursor", "mcp.json")],
@@ -52,6 +73,11 @@ function printHelp(log = console.log) {
52
73
  log(" pi-mcp-adapter init Detect host configs and scaffold Pi imports");
53
74
  log(" pi-mcp-adapter init --dry-run");
54
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");
55
81
  }
56
82
 
57
83
  function readJsonFile(filePath) {
@@ -173,7 +199,160 @@ async function runInit(argv, log = console.log) {
173
199
  return 0;
174
200
  }
175
201
 
176
- 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) {
177
356
  const [command, ...rest] = argv;
178
357
 
179
358
  if (!command || command === "help" || command === "--help" || command === "-h") {
@@ -181,6 +360,10 @@ export async function main(argv = process.argv.slice(2), log = console.log, erro
181
360
  return 0;
182
361
  }
183
362
 
363
+ if (command === "token") {
364
+ return runToken(rest, log, error, stdin);
365
+ }
366
+
184
367
  if (command === "install") {
185
368
  error("The custom downloader has been retired.");
186
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