@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.
- package/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- 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.
|
|
296
|
-
- **`lazy-keep-alive`** — Don't connect at startup. Connect on first tool call (like `lazy`). Once spawned, never idle-shut down and
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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(),
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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(),
|
|
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
|
-
|
|
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: (
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
{
|
|
724
|
+
{
|
|
725
|
+
overlay: true,
|
|
726
|
+
overlayOptions: { anchor: "center", width: 82 },
|
|
727
|
+
onHandle: (handle) => { overlayHandle = handle; },
|
|
728
|
+
},
|
|
623
729
|
);
|
|
624
730
|
});
|
|
625
731
|
|