@herbertgao/pi-extensions 2026.8.3 → 2026.8.5

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 (408) hide show
  1. package/README.md +45 -4
  2. package/examples/pi-footer.json +275 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  31. package/node_modules/@narumitw/pi-btw/README.md +8 -7
  32. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  33. package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
  34. package/node_modules/pi-footer/CHANGELOG.md +145 -0
  35. package/node_modules/pi-footer/LICENSE +21 -0
  36. package/node_modules/pi-footer/README.md +354 -0
  37. package/node_modules/pi-footer/package.json +68 -0
  38. package/node_modules/pi-footer/src/cache.ts +106 -0
  39. package/node_modules/pi-footer/src/colors.ts +237 -0
  40. package/node_modules/pi-footer/src/config.ts +202 -0
  41. package/node_modules/pi-footer/src/event-widgets.ts +41 -0
  42. package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
  43. package/node_modules/pi-footer/src/git.ts +135 -0
  44. package/node_modules/pi-footer/src/index.ts +268 -0
  45. package/node_modules/pi-footer/src/metrics.ts +141 -0
  46. package/node_modules/pi-footer/src/presets.ts +383 -0
  47. package/node_modules/pi-footer/src/render.ts +114 -0
  48. package/node_modules/pi-footer/src/separators.ts +52 -0
  49. package/node_modules/pi-footer/src/types.ts +122 -0
  50. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
  51. package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
  52. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
  53. package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
  54. package/node_modules/pi-footer/src/ui/events.ts +35 -0
  55. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
  56. package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
  57. package/node_modules/pi-footer/src/ui/fields.ts +252 -0
  58. package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
  59. package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
  60. package/node_modules/pi-footer/src/ui/layout.ts +17 -0
  61. package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
  62. package/node_modules/pi-footer/src/ui/model.ts +64 -0
  63. package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
  64. package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
  65. package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
  66. package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
  67. package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
  68. package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
  69. package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
  70. package/node_modules/pi-footer/src/ui/screen.ts +243 -0
  71. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
  72. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
  73. package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
  74. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
  75. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
  76. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
  77. package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
  78. package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
  79. package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
  80. package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
  81. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
  82. package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
  83. package/node_modules/pi-footer/src/ui/theme.ts +48 -0
  84. package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
  85. package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
  86. package/node_modules/pi-footer/src/ui.ts +66 -0
  87. package/node_modules/pi-footer/src/widgets/context.ts +28 -0
  88. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
  89. package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
  90. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
  91. package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
  92. package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
  93. package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
  94. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
  95. package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
  96. package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
  97. package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
  98. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
  99. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
  100. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
  101. package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
  102. package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
  103. package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
  104. package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
  105. package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
  106. package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
  107. package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
  108. package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
  109. package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
  110. package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
  111. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
  112. package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
  113. package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
  114. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
  115. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
  116. package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
  117. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
  118. package/node_modules/pi-footer/src/widgets/options.ts +122 -0
  119. package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
  120. package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
  121. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
  122. package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
  123. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
  124. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
  125. package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
  126. package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
  127. package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
  128. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
  129. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
  130. package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
  131. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
  132. package/node_modules/pi-footer/src/widgets/store.ts +26 -0
  133. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
  134. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
  135. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
  136. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
  137. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
  138. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
  139. package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
  140. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
  141. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
  142. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
  143. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
  144. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
  145. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
  146. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
  147. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
  148. package/node_modules/pi-footer/src/widgets/types.ts +181 -0
  149. package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
  150. package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
  151. package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
  152. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
  153. package/node_modules/pi-lens/CHANGELOG.md +2440 -954
  154. package/node_modules/pi-lens/README.md +13 -0
  155. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  156. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  157. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  158. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  159. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  160. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  161. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  162. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  163. package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
  164. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  165. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  166. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  167. package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
  168. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  169. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  170. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  171. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  172. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  173. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  174. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  175. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
  177. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  178. package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
  179. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  180. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  181. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  182. package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
  183. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
  184. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  185. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
  186. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  187. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  188. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  189. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  190. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  191. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
  192. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  193. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  194. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  195. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  196. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  197. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  198. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  199. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  201. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  202. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
  204. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  205. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  206. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  219. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  220. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  221. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  222. package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
  223. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  224. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  225. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  226. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  227. package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
  228. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  229. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  230. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  231. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  232. package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
  233. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  234. package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
  235. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
  236. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  237. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  238. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  239. package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
  240. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  241. package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
  242. package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
  243. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  244. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  245. package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
  246. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  247. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  248. package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
  249. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  250. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  251. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
  252. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  253. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
  254. package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
  255. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  256. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
  257. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  258. package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
  259. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  260. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  261. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  262. package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
  263. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  264. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  265. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  266. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
  267. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  268. package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
  269. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  270. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  271. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  272. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  273. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  274. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  275. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  277. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  278. package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
  279. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  280. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  281. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  282. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  283. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  284. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  285. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  286. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  287. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  288. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  289. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  290. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  291. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  292. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  293. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  294. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  295. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  296. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  297. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  298. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  299. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  300. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  301. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  302. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
  303. package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
  304. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
  305. package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
  306. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  307. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
  308. package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
  309. package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
  310. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  311. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  313. package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
  314. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  315. package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
  316. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  317. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  318. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  319. package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
  320. package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
  321. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  322. package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
  323. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  324. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  325. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  326. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  327. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  328. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  329. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  330. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  331. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  332. package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
  333. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  334. package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
  335. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  336. package/node_modules/pi-lens/dist/index.js +49361 -36888
  337. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  338. package/node_modules/pi-lens/dist/mcp/server.js +211 -44
  339. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  340. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
  341. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
  342. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  343. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  344. package/node_modules/pi-lens/docs/agent-guide.md +2 -2
  345. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  346. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  347. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  348. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
  349. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  350. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  351. package/node_modules/pi-lens/docs/fable.md +27 -0
  352. package/node_modules/pi-lens/docs/features.md +31 -2
  353. package/node_modules/pi-lens/docs/globalconfig.md +45 -1
  354. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  355. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  356. package/node_modules/pi-lens/docs/mcp.md +37 -4
  357. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  358. package/node_modules/pi-lens/docs/settings.md +12 -1
  359. package/node_modules/pi-lens/docs/tools.md +4 -1
  360. package/node_modules/pi-lens/docs/usage.md +18 -0
  361. package/node_modules/pi-lens/package.json +10 -10
  362. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  363. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  364. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  365. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  366. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  367. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  368. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  369. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  370. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  371. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  372. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  373. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  374. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
  375. package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
  376. package/node_modules/pi-mcp-adapter/README.md +34 -3
  377. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  378. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  379. package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
  380. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  381. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  382. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  383. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  384. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  385. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  386. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  387. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  388. package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
  389. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  390. package/node_modules/pi-mcp-adapter/package.json +3 -3
  391. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  392. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  393. package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
  394. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  395. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  396. package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
  397. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  398. package/node_modules/pi-mcp-adapter/types.ts +93 -27
  399. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  400. package/package.json +17 -13
  401. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  402. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  403. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  404. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  405. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  406. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  407. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  408. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import type { McpExtensionState } from "./state.ts";
3
- import { isServerDisabled, type McpAdapterOptions, type PromptMetadata, type ToolMetadata } from "./types.ts";
3
+ import { formatToolName, isServerDisabled, resolveToolPrefix, type McpAdapterOptions, type PromptMetadata, type ToolMetadata } from "./types.ts";
4
4
  import { existsSync } from "node:fs";
5
5
  import { cloneMcpConfig, loadMcpConfig } from "./config.ts";
6
6
  import { ConsentManager } from "./consent-manager.ts";
@@ -21,6 +21,7 @@ import {
21
21
  } from "./metadata-cache.ts";
22
22
  import { McpServerManager } from "./server-manager.ts";
23
23
  import { buildToolMetadata, totalToolCount } from "./tool-metadata.ts";
24
+ import { resourceNameToToolName } from "./resource-tools.ts";
24
25
  import { UiResourceHandler } from "./ui-resource-handler.ts";
25
26
  import { formatMcpStatus, openUrl, parallelLimit, sanitizeTerminalText } from "./utils.ts";
26
27
  import { logger } from "./logger.ts";
@@ -242,7 +243,7 @@ export async function initializeMcp(
242
243
 
243
244
  const cachedEntry = cache?.servers?.[name];
244
245
  if (cachedEntry && isServerCacheValid(cachedEntry, definition)) {
245
- const metadata = reconstructToolMetadata(name, cachedEntry, prefix, definition);
246
+ const metadata = reconstructToolMetadata(name, cachedEntry, prefix, definition, config.mcpServers, cache ?? undefined);
246
247
  toolMetadata.set(name, metadata);
247
248
  if (Array.isArray(cachedEntry.resources)) {
248
249
  resourceCounts.set(name, cachedEntry.resources.length);
@@ -288,6 +289,29 @@ export async function initializeMcp(
288
289
  if (initialSignal?.aborted) return state;
289
290
  owner.throwIfInactive();
290
291
 
292
+ const startupKnownMetadata = new Map<string, ToolMetadata[]>();
293
+ for (const { name, definition, connection } of results) {
294
+ if (!connection) continue;
295
+ const effectivePrefix = resolveToolPrefix(definition, prefix);
296
+ const metadata: ToolMetadata[] = [
297
+ ...connection.tools.filter(tool => tool?.name).map(tool => ({
298
+ name: formatToolName(tool.name, name, effectivePrefix),
299
+ originalName: tool.name,
300
+ description: tool.description ?? "",
301
+ })),
302
+ ...(definition.exposeResources !== false ? connection.resources.filter(resource => resource?.name && resource?.uri).map(resource => {
303
+ const originalName = `read_${resourceNameToToolName(resource.name)}`;
304
+ return {
305
+ name: formatToolName(originalName, name, effectivePrefix),
306
+ originalName,
307
+ description: resource.description ?? `Read resource: ${resource.uri}`,
308
+ resourceUri: resource.uri,
309
+ };
310
+ }) : []),
311
+ ];
312
+ startupKnownMetadata.set(name, metadata);
313
+ }
314
+
291
315
  for (const { name, definition, connection, error } of results) {
292
316
  owner.throwIfInactive();
293
317
  if (error || !connection) {
@@ -301,7 +325,7 @@ export async function initializeMcp(
301
325
  continue;
302
326
  }
303
327
 
304
- const { metadata, failedTools } = buildToolMetadata(connection.tools, connection.resources, definition, name, prefix);
328
+ const { metadata, failedTools } = buildToolMetadata(connection.tools, connection.resources, definition, name, prefix, config.mcpServers, startupKnownMetadata, true);
305
329
  toolMetadata.set(name, metadata);
306
330
  resourceCounts.set(name, connection.resources.length);
307
331
  if (!connection.promptDiscoveryFailed) {
@@ -327,7 +351,7 @@ export async function initializeMcp(
327
351
 
328
352
  const connectedCount = results.filter(r => r.connection).length;
329
353
  const failedCount = results.filter(r => r.error).length;
330
- if (ui && connectedCount > 0) {
354
+ if (ui && connectedCount > 0 && config.settings?.notifyOnStartupConnect !== false) {
331
355
  const totalTools = totalToolCount(state);
332
356
  const msg = failedCount > 0
333
357
  ? `MCP: ${connectedCount}/${startupServers.length} servers connected (${totalTools} tools)`
@@ -437,7 +461,7 @@ export function updateServerMetadata(state: McpExtensionState, serverName: strin
437
461
 
438
462
  const prefix = state.config.settings?.toolPrefix ?? "server";
439
463
 
440
- const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix);
464
+ const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix, state.config.mcpServers, state.toolMetadata);
441
465
  state.toolMetadata.set(serverName, metadata);
442
466
  state.resourceCounts?.set(serverName, connection.resources.length);
443
467
  if (!connection.promptDiscoveryFailed) {
@@ -19,7 +19,6 @@ export class McpLifecycleManager {
19
19
  private activeHealthCheck: Promise<void> | undefined;
20
20
  private shutdownPromise: Promise<void> | undefined;
21
21
  private stopped = false;
22
- private healthSignal: AbortSignal | undefined;
23
22
  private removeHealthAbortListener: (() => void) | undefined;
24
23
 
25
24
  constructor(
@@ -58,10 +57,8 @@ export class McpLifecycleManager {
58
57
  const signal = typeof signalOrInterval === "number" ? undefined : signalOrInterval;
59
58
  const intervalMs = typeof signalOrInterval === "number" ? signalOrInterval : maybeIntervalMs;
60
59
  this.stopped = false;
61
- this.healthSignal = signal;
62
60
  if (signal?.aborted) {
63
61
  this.stopped = true;
64
- this.healthSignal = undefined;
65
62
  return;
66
63
  }
67
64
  const stop = () => {
@@ -138,7 +135,6 @@ export class McpLifecycleManager {
138
135
  this.healthCheckInterval = undefined;
139
136
  this.removeHealthAbortListener?.();
140
137
  this.removeHealthAbortListener = undefined;
141
- this.healthSignal = undefined;
142
138
  await this.activeHealthCheck;
143
139
  this.activeHealthCheck = undefined;
144
140
  this.onReconnect = undefined;
@@ -633,7 +633,6 @@ export async function completeAuthFromInput(
633
633
  const signal = combineAbortSignals(runtime.signal, options.signal)
634
634
  throwIfAborted(signal)
635
635
  const key = getPendingAuthKey(serverName, fallbackAuthStorageOptions)
636
- const authStorageOptions = runtimeState.pendingAuths.get(key)?.authStorageOptions ?? fallbackAuthStorageOptions
637
636
  const oauthState = runtimeState.pendingAuthStates.get(key)
638
637
  throwIfAborted(signal)
639
638
  const parsed = parseAuthorizationRedirectInput(input, oauthState)
@@ -899,7 +898,7 @@ export async function getValidToken(
899
898
  * @returns The current auth status
900
899
  */
901
900
  export async function getAuthStatus(serverName: string, options: AuthenticateOptions = {}): Promise<AuthStatus> {
902
- const runtime = getRuntime(options)
901
+ getRuntime(options)
903
902
  const authStorageOptions = options.authStorageOptions ?? {}
904
903
  const hasTokens = await hasStoredTokens(serverName, authStorageOptions)
905
904
  if (!hasTokens) return "not_authenticated"
@@ -37,6 +37,7 @@ const KEYRING_RECOVERY_KEYCTL_ENV = 'PI_MCP_ADAPTER_KEYRING_RECOVERY_KEYCTL';
37
37
  const KEYRING_RECOVERY_NODE_ENV = 'PI_MCP_ADAPTER_KEYRING_RECOVERY_NODE';
38
38
  const KEYRING_RECOVERY_HELPER_ENV = 'PI_MCP_ADAPTER_KEYRING_RECOVERY_HELPER';
39
39
  const TEST_LINUX_KEYRING_RECOVERY_ENV = 'PI_MCP_ADAPTER_TEST_LINUX_KEYRING_RECOVERY';
40
+ const AUTH_CACHE_DISABLED_ENV = 'PI_MCP_ADAPTER_DISABLE_AUTH_CACHE';
40
41
  const KEYRING_RECOVERY_TIMEOUT_MS = 10_000;
41
42
  const AUTH_CHUNK_MANIFEST_KEY = '__piMcpAdapterOAuthChunked';
42
43
 
@@ -148,8 +149,20 @@ interface AuthEntryChunkManifest {
148
149
  let KeyringEntryClass: KeyringEntryConstructor | undefined;
149
150
  const memoryAuthEntries = new Map<string, string>();
150
151
 
152
+ let testAuthSecretStoreReadCount = 0;
153
+ const authEntryCache = new Map<string, AuthEntry | undefined>();
154
+
155
+ function isAuthEntryCacheEnabled(): boolean {
156
+ return process.env[AUTH_CACHE_DISABLED_ENV] !== '1';
157
+ }
158
+
159
+ function cloneAuthEntry(entry: AuthEntry | undefined): AuthEntry | undefined {
160
+ return entry === undefined ? undefined : structuredClone(entry);
161
+ }
162
+
151
163
  const memoryAuthSecretStore: AuthSecretStore = {
152
164
  read(account) {
165
+ testAuthSecretStoreReadCount++;
153
166
  return memoryAuthEntries.get(account);
154
167
  },
155
168
  write(account, payload) {
@@ -175,6 +188,7 @@ const keyringAuthSecretStore: AuthSecretStore = {
175
188
  /** Mimics the Windows Credential Manager per-value ceiling for tests. */
176
189
  const sizeLimitedAuthSecretStore: AuthSecretStore = {
177
190
  read(account) {
191
+ testAuthSecretStoreReadCount++;
178
192
  return memoryAuthEntries.get(account);
179
193
  },
180
194
  write(account, payload) {
@@ -190,6 +204,7 @@ const sizeLimitedAuthSecretStore: AuthSecretStore = {
190
204
 
191
205
  const unavailableAuthSecretStore: AuthSecretStore = {
192
206
  read() {
207
+ testAuthSecretStoreReadCount++;
193
208
  throw new Error('simulated secure credential store unavailable');
194
209
  },
195
210
  write() {
@@ -206,6 +221,7 @@ function createKeyRevokedTestError(): Error {
206
221
 
207
222
  const keyRevokedAuthSecretStore: AuthSecretStore = {
208
223
  read() {
224
+ testAuthSecretStoreReadCount++;
209
225
  throw createKeyRevokedTestError();
210
226
  },
211
227
  write() {
@@ -218,6 +234,16 @@ const keyRevokedAuthSecretStore: AuthSecretStore = {
218
234
 
219
235
  export function resetTestAuthSecretStore(): void {
220
236
  memoryAuthEntries.clear();
237
+ authEntryCache.clear();
238
+ testAuthSecretStoreReadCount = 0;
239
+ }
240
+
241
+ export function resetAuthEntryCache(): void {
242
+ authEntryCache.clear();
243
+ }
244
+
245
+ export function getTestAuthSecretStoreReadCount(): number {
246
+ return testAuthSecretStoreReadCount;
221
247
  }
222
248
 
223
249
  export function getTestAuthSecretStoreEntries(): [string, string][] {
@@ -649,6 +675,19 @@ function writeSecureAuthEntryToStore(store: AuthSecretStore, serverName: string,
649
675
  error,
650
676
  );
651
677
  }
678
+
679
+ publishAuthEntryToCache(serverName, payload);
680
+ }
681
+
682
+ function publishAuthEntryToCache(serverName: string, payload: string): void {
683
+ if (!isAuthEntryCacheEnabled()) return;
684
+ // Cache the same normalized shape a fresh persistent-store read returns.
685
+ const normalized = toAuthEntry(JSON.parse(payload) as unknown);
686
+ if (!normalized) {
687
+ authEntryCache.delete(serverName);
688
+ return;
689
+ }
690
+ authEntryCache.set(serverName, cloneAuthEntry(normalized));
652
691
  }
653
692
 
654
693
  function writeSecureAuthEntry(serverName: string, entry: AuthEntry): void {
@@ -704,12 +743,23 @@ function readAuthEntry(
704
743
  options?: AuthStorageOptions,
705
744
  behavior: { migrateLegacy?: boolean } = {},
706
745
  ): AuthEntry | undefined {
746
+ // Status-only reads deliberately bypass the cache because they do not
747
+ // migrate legacy entries.
748
+ const cacheable = behavior.migrateLegacy !== false && isAuthEntryCacheEnabled();
749
+ if (cacheable && authEntryCache.has(serverName)) {
750
+ return cloneAuthEntry(authEntryCache.get(serverName));
751
+ }
752
+
753
+ let entry: AuthEntry | undefined;
707
754
  try {
708
- return readAuthEntryFromStore(getAuthSecretStore(), serverName, options, behavior);
755
+ entry = readAuthEntryFromStore(getAuthSecretStore(), serverName, options, behavior);
709
756
  } catch (error) {
710
757
  if (!shouldAttemptLinuxKeyringRecovery(error)) throw error;
711
- return readAuthEntryFromStore(linuxKeyringRecoveryAuthSecretStore, serverName, options, behavior);
758
+ entry = readAuthEntryFromStore(linuxKeyringRecoveryAuthSecretStore, serverName, options, behavior);
712
759
  }
760
+
761
+ if (cacheable) authEntryCache.set(serverName, cloneAuthEntry(entry));
762
+ return entry;
713
763
  }
714
764
 
715
765
  /**
@@ -794,9 +844,17 @@ export function removeAuthEntry(serverName: string, options?: AuthStorageOptions
794
844
  if (!shouldAttemptLinuxKeyringRecovery(error)) throw error;
795
845
  removeAuthEntryFromStore(linuxKeyringRecoveryAuthSecretStore, serverName);
796
846
  }
847
+ authEntryCache.delete(serverName);
797
848
  removeLegacyAuthEntry(serverName, options);
798
849
  }
799
850
 
851
+ /**
852
+ * Forget a cached entry so the next ordinary read reloads secure storage.
853
+ */
854
+ export function invalidateAuthEntryCache(serverName: string): void {
855
+ authEntryCache.delete(serverName);
856
+ }
857
+
800
858
  /**
801
859
  * Update tokens for a server.
802
860
  */
@@ -1,11 +1,12 @@
1
1
  import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
2
  import { copyToClipboard } from "@earendil-works/pi-coding-agent";
3
3
  import { createPanelKeys, type PanelKeybindings, type PanelKeys } from "./panel-keys.ts";
4
- import { isServerDisabled, isToolAllowed } from "./types.ts";
4
+ import { getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.ts";
5
5
  import type { McpConfig, McpPanelCallbacks, McpPanelResult, ServerProvenance, ToolPrefix } from "./types.ts";
6
6
  import { resourceNameToToolName } from "./resource-tools.ts";
7
7
  import { sanitizeTerminalText, stripOscSequences } from "./utils.ts";
8
- import type { MetadataCache, ServerCacheEntry, CachedTool } from "./metadata-cache.ts";
8
+ import { isServerCacheValid, type MetadataCache, type ServerCacheEntry, type CachedTool } from "./metadata-cache.ts";
9
+ import { isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
9
10
 
10
11
  interface PanelTheme {
11
12
  border: string;
@@ -170,8 +171,8 @@ class McpPanel {
170
171
  private static readonly INACTIVITY_MS = 60_000;
171
172
 
172
173
  constructor(
173
- config: McpConfig,
174
- cache: MetadataCache | null,
174
+ private config: McpConfig,
175
+ private cache: MetadataCache | null,
175
176
  provenance: Map<string, ServerProvenance>,
176
177
  private callbacks: McpPanelCallbacks,
177
178
  tui: { requestRender(): void },
@@ -187,7 +188,8 @@ class McpPanel {
187
188
  for (const [serverName, definition] of Object.entries(config.mcpServers)) {
188
189
  if (this.authOnly && !callbacks.canAuthenticate(serverName)) continue;
189
190
  const prov = provenance.get(serverName);
190
- const serverCache = cache?.servers?.[serverName];
191
+ const cachedEntry = this.cache?.servers?.[serverName];
192
+ const serverCache = cachedEntry && isServerCacheValid(cachedEntry, definition) ? cachedEntry : undefined;
191
193
 
192
194
  const globalDirect = config.settings?.directTools;
193
195
  let toolFilter: true | string[] | false = false;
@@ -200,7 +202,8 @@ class McpPanel {
200
202
  const tools: ToolState[] = [];
201
203
  if (serverCache && !this.authOnly && !isServerDisabled(definition)) {
202
204
  for (const tool of serverCache.tools ?? []) {
203
- if (!isToolAllowed(tool.name, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
205
+ if (!isUiToolVisibleToModel(tool.uiVisibility)) continue;
206
+ if (!isToolAllowed(tool.name, serverName, this.prefix, definition.includeTools, definition.excludeTools, this.getOtherCurrentCandidates(serverName, definition, serverCache, tool.name))) {
204
207
  continue;
205
208
  }
206
209
 
@@ -216,7 +219,7 @@ class McpPanel {
216
219
  if (definition.exposeResources !== false) {
217
220
  for (const resource of serverCache.resources ?? []) {
218
221
  const baseName = `read_${resourceNameToToolName(resource.name)}`;
219
- if (!isToolAllowed(baseName, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
222
+ if (!isToolAllowed(baseName, serverName, this.prefix, definition.includeTools, definition.excludeTools, this.getOtherCurrentCandidates(serverName, definition, serverCache, baseName))) {
220
223
  continue;
221
224
  }
222
225
 
@@ -563,6 +566,8 @@ class McpPanel {
563
566
  if (server.connectionStatus === "connected") {
564
567
  const entry = this.callbacks.refreshCacheAfterReconnect(server.name);
565
568
  if (entry) {
569
+ this.cache ??= { version: 1, servers: {} };
570
+ this.cache.servers[server.name] = entry;
566
571
  this.rebuildServerTools(server, entry);
567
572
  }
568
573
  server.hasCachedData = true;
@@ -636,13 +641,44 @@ class McpPanel {
636
641
  this.cursorIndex = Math.max(0, Math.min(this.visibleItems.length - 1, this.cursorIndex + delta));
637
642
  }
638
643
 
644
+ private getOtherCurrentCandidates(
645
+ serverName: string,
646
+ definition: McpConfig["mcpServers"][string],
647
+ currentEntry: ServerCacheEntry,
648
+ toolName: string,
649
+ ): Set<string> {
650
+ const candidates = new Set<string>();
651
+ for (const [otherServerName, otherDefinition] of Object.entries(this.config.mcpServers)) {
652
+ if (isServerDisabled(otherDefinition)) continue;
653
+ const cachedEntry = this.cache?.servers?.[otherServerName];
654
+ const entry = otherServerName === serverName
655
+ ? currentEntry
656
+ : cachedEntry && isServerCacheValid(cachedEntry, otherDefinition) ? cachedEntry : undefined;
657
+ if (!entry) continue;
658
+ const otherPrefix = resolveToolPrefix(otherDefinition, this.prefix);
659
+ for (const tool of entry.tools ?? []) {
660
+ if (!isUiToolVisibleToModel(tool.uiVisibility)) continue;
661
+ for (const candidate of getToolNameCandidates(tool.name, otherServerName, otherPrefix, false)) candidates.add(candidate);
662
+ }
663
+ if (otherDefinition.exposeResources !== false) {
664
+ for (const resource of entry.resources ?? []) {
665
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
666
+ for (const candidate of getToolNameCandidates(baseName, otherServerName, otherPrefix, false)) candidates.add(candidate);
667
+ }
668
+ }
669
+ }
670
+ for (const candidate of getToolNameCandidates(toolName, serverName, resolveToolPrefix(definition, this.prefix), false)) candidates.delete(candidate);
671
+ return candidates;
672
+ }
673
+
639
674
  private rebuildServerTools(server: ServerState, entry: ServerCacheEntry): void {
640
675
  const existingState = new Map<string, boolean>();
641
676
  for (const t of server.tools) existingState.set(t.name, t.isDirect);
642
677
 
643
678
  const newTools: ToolState[] = [];
644
679
  for (const tool of entry.tools ?? []) {
645
- if (!isToolAllowed(tool.name, server.name, this.prefix, server.includeTools, server.excludeTools)) {
680
+ if (!isUiToolVisibleToModel(tool.uiVisibility)) continue;
681
+ if (!isToolAllowed(tool.name, server.name, this.prefix, server.includeTools, server.excludeTools, this.getOtherCurrentCandidates(server.name, server, entry, tool.name))) {
646
682
  continue;
647
683
  }
648
684
 
@@ -660,7 +696,7 @@ class McpPanel {
660
696
  if (server.exposeResources) {
661
697
  for (const resource of entry.resources ?? []) {
662
698
  const baseName = `read_${resourceNameToToolName(resource.name)}`;
663
- if (!isToolAllowed(baseName, server.name, this.prefix, server.includeTools, server.excludeTools)) {
699
+ if (!isToolAllowed(baseName, server.name, this.prefix, server.includeTools, server.excludeTools, this.getOtherCurrentCandidates(server.name, server, entry, baseName))) {
664
700
  continue;
665
701
  }
666
702
 
@@ -106,23 +106,25 @@ const capturedConsole = Object.freeze({
106
106
  debug: (...args) => emit(`[console.debug] ${formatWithOptions({ colors: false, depth: 4 }, ...args)}`),
107
107
  });
108
108
 
109
- try {
110
- const context = vm.createContext(Object.assign(Object.create(null), {
111
- tools,
112
- emit,
113
- console: capturedConsole,
114
- }), {
115
- codeGeneration: { strings: false, wasm: false },
116
- name: "mcpScript",
117
- });
118
- const script = new vm.Script(`(async () => {\n${workerData.code}\n})()`, { filename: "mcpScript.js" });
119
- const returnValue = await Promise.resolve(script.runInContext(context));
120
- parentPort.postMessage(returnValue === undefined
121
- ? { type: "done" }
122
- : { type: "done", returnBlock: toContentBlock(returnValue) });
123
- } catch (error) {
124
- parentPort.postMessage({
125
- type: "error",
126
- message: error instanceof Error ? error.message : String(error),
127
- });
128
- }
109
+ void (async () => {
110
+ try {
111
+ const context = vm.createContext(Object.assign(Object.create(null), {
112
+ tools,
113
+ emit,
114
+ console: capturedConsole,
115
+ }), {
116
+ codeGeneration: { strings: false, wasm: false },
117
+ name: "mcpScript",
118
+ });
119
+ const script = new vm.Script(`(async () => {\n${workerData.code}\n})()`, { filename: "mcpScript.js" });
120
+ const returnValue = await Promise.resolve(script.runInContext(context));
121
+ parentPort.postMessage(returnValue === undefined
122
+ ? { type: "done" }
123
+ : { type: "done", returnBlock: toContentBlock(returnValue) });
124
+ } catch (error) {
125
+ parentPort.postMessage({
126
+ type: "error",
127
+ message: error instanceof Error ? error.message : String(error),
128
+ });
129
+ }
130
+ })();
@@ -19,7 +19,7 @@ import type {
19
19
  ToolMetadata,
20
20
  PromptMetadata,
21
21
  } from "./types.ts";
22
- import { formatPromptCommandName, formatToolName, isServerDisabled, isToolAllowed, resolveToolPrefix, type ToolPrefix } from "./types.ts";
22
+ import { formatPromptCommandName, formatToolName, getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix, type ToolPrefix } from "./types.ts";
23
23
  import { resourceNameToToolName } from "./resource-tools.ts";
24
24
  import {
25
25
  extractToolUiStreamMode,
@@ -177,18 +177,41 @@ export function reconstructToolMetadata(
177
177
  serverName: string,
178
178
  entry: ServerCacheEntry,
179
179
  prefix: ToolPrefix,
180
- definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix">
180
+ definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix">,
181
+ configuredServers?: Record<string, ServerEntry>,
182
+ cache?: MetadataCache,
181
183
  ): ToolMetadata[] {
182
184
  const metadata: ToolMetadata[] = [];
183
185
  const seenNames = new Set<string>();
184
186
  const effectivePrefix = resolveToolPrefix(definition, prefix);
187
+ const getOtherCurrentCandidates = (toolName: string): Set<string> | undefined => {
188
+ if (!configuredServers || !cache) return undefined;
189
+ const candidates = new Set<string>();
190
+ for (const [otherServerName, otherDefinition] of Object.entries(configuredServers)) {
191
+ const otherEntry = cache.servers[otherServerName];
192
+ if (!otherEntry || !isServerCacheValid(otherEntry, otherDefinition) || isServerDisabled(otherDefinition)) continue;
193
+ const otherPrefix = resolveToolPrefix(otherDefinition, prefix);
194
+ for (const otherTool of otherEntry.tools ?? []) {
195
+ if (!isUiToolVisibleToModel(otherTool.uiVisibility)) continue;
196
+ for (const candidate of getToolNameCandidates(otherTool.name, otherServerName, otherPrefix, false)) candidates.add(candidate);
197
+ }
198
+ if (otherDefinition.exposeResources !== false) {
199
+ for (const resource of otherEntry.resources ?? []) {
200
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
201
+ for (const candidate of getToolNameCandidates(baseName, otherServerName, otherPrefix, false)) candidates.add(candidate);
202
+ }
203
+ }
204
+ }
205
+ for (const candidate of getToolNameCandidates(toolName, serverName, effectivePrefix, false)) candidates.delete(candidate);
206
+ return candidates;
207
+ };
185
208
 
186
209
  for (const tool of entry.tools ?? []) {
187
210
  if (!tool?.name) continue;
188
211
  if (!isUiToolVisibleToModel(tool.uiVisibility)) {
189
212
  continue;
190
213
  }
191
- if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
214
+ if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(tool.name))) {
192
215
  continue;
193
216
  }
194
217
 
@@ -213,7 +236,7 @@ export function reconstructToolMetadata(
213
236
  for (const resource of entry.resources ?? []) {
214
237
  if (!resource?.name || !resource?.uri) continue;
215
238
  const baseName = `read_${resourceNameToToolName(resource.name)}`;
216
- if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
239
+ if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(baseName))) {
217
240
  continue;
218
241
  }
219
242
 
@@ -1,11 +1,11 @@
1
1
  // npx-resolver.ts - Resolve npx/npm exec binaries to avoid npm parent processes
2
- import { existsSync, readFileSync, realpathSync, readdirSync, statSync, writeFileSync, renameSync, mkdirSync, openSync, readSync, closeSync } from "node:fs";
2
+ import { existsSync, readFileSync, realpathSync, readdirSync, statSync, writeFileSync, renameSync, mkdirSync, openSync, readSync, closeSync, unlinkSync } from "node:fs";
3
3
  import { join, dirname, extname, resolve, sep } from "node:path";
4
4
  import { getAgentPath } from "./agent-dir.ts";
5
5
  import { throwIfAborted } from "./abort.ts";
6
6
  import crossSpawn from "cross-spawn";
7
7
 
8
- const CACHE_VERSION = 1;
8
+ const CACHE_VERSION = 2;
9
9
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
10
10
  const EXACT_PACKAGE_VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/;
11
11
 
@@ -53,7 +53,7 @@ export async function resolveNpxBinary(
53
53
  if (!parsed) return null;
54
54
 
55
55
  const packageSpec = parsePackageSpec(parsed.packageSpec);
56
- const cacheKey = JSON.stringify([command, ...args]);
56
+ const cacheKey = JSON.stringify([command, parsed.packageSpec, parsed.binName ?? ""]);
57
57
  const cache = loadCache();
58
58
  const cached = cache?.entries?.[cacheKey];
59
59
 
@@ -434,41 +434,96 @@ function getNpxCachePath(): string {
434
434
  return getAgentPath("mcp-npx-cache.json");
435
435
  }
436
436
 
437
- function loadCache(): NpxCache | null {
438
- const cachePath = getNpxCachePath();
437
+ function readNpxCachePayload(cachePath: string): unknown | null {
439
438
  if (!existsSync(cachePath)) return null;
440
439
  try {
441
- const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
442
- if (!raw || typeof raw !== "object") return null;
443
- if (raw.version !== CACHE_VERSION) return null;
444
- if (!raw.entries || typeof raw.entries !== "object") return null;
445
- return raw as NpxCache;
440
+ return JSON.parse(readFileSync(cachePath, "utf-8")) as unknown;
446
441
  } catch {
447
442
  return null;
448
443
  }
449
444
  }
450
445
 
451
- function saveCacheEntry(key: string, entry: NpxCacheEntry): void {
452
- const cachePath = getNpxCachePath();
453
- const dir = dirname(cachePath);
454
- mkdirSync(dir, { recursive: true });
446
+ function asRecord(value: unknown): Record<string, unknown> | null {
447
+ return typeof value === "object" && value !== null && !Array.isArray(value)
448
+ ? value as Record<string, unknown>
449
+ : null;
450
+ }
451
+
452
+ function createCacheEntries(): Record<string, NpxCacheEntry> {
453
+ return Object.create(null) as Record<string, NpxCacheEntry>;
454
+ }
455
+
456
+ function toNpxCacheEntry(value: unknown): NpxCacheEntry | null {
457
+ const raw = asRecord(value);
458
+ if (!raw) return null;
459
+ if (typeof raw.resolvedBin !== "string") return null;
460
+ if (typeof raw.resolvedAt !== "number" || !Number.isFinite(raw.resolvedAt)) return null;
461
+ if (typeof raw.isJs !== "boolean") return null;
462
+ if (raw.packageVersion !== undefined && typeof raw.packageVersion !== "string") return null;
463
+ return {
464
+ resolvedBin: raw.resolvedBin,
465
+ resolvedAt: raw.resolvedAt,
466
+ ...(raw.packageVersion !== undefined ? { packageVersion: raw.packageVersion } : {}),
467
+ isJs: raw.isJs,
468
+ };
469
+ }
470
+
471
+ function toNpxCache(value: unknown): NpxCache | null {
472
+ const raw = asRecord(value);
473
+ if (!raw || raw.version !== CACHE_VERSION) return null;
474
+ const rawEntries = asRecord(raw.entries);
475
+ if (!rawEntries) return null;
476
+
477
+ const entries = createCacheEntries();
478
+ for (const [key, rawEntry] of Object.entries(rawEntries)) {
479
+ const entry = toNpxCacheEntry(rawEntry);
480
+ if (entry) entries[key] = entry;
481
+ }
482
+ return { version: CACHE_VERSION, entries };
483
+ }
455
484
 
456
- let merged: NpxCache = { version: CACHE_VERSION, entries: {} };
485
+ function clearLegacyCache(): boolean {
486
+ const cachePath = getNpxCachePath();
487
+ const raw = asRecord(readNpxCachePayload(cachePath));
488
+ if (raw?.version !== 1) return false;
457
489
  try {
458
- if (existsSync(cachePath)) {
459
- const existing = JSON.parse(readFileSync(cachePath, "utf-8")) as NpxCache;
460
- if (existing && existing.version === CACHE_VERSION && existing.entries) {
461
- merged.entries = { ...existing.entries };
462
- }
463
- }
490
+ unlinkSync(cachePath);
464
491
  } catch {
465
- // Ignore parse errors
492
+ try {
493
+ writeFileSync(cachePath, "", "utf-8");
494
+ } catch {
495
+ // Cache cleanup is best effort; resolution must still proceed.
496
+ }
466
497
  }
498
+ return true;
499
+ }
467
500
 
468
- merged.entries[key] = entry;
469
- const tmpPath = `${cachePath}.${process.pid}.tmp`;
470
- writeFileSync(tmpPath, JSON.stringify(merged, null, 2), "utf-8");
471
- renameSync(tmpPath, cachePath);
501
+ clearLegacyCache();
502
+
503
+ function loadCache(): NpxCache | null {
504
+ if (clearLegacyCache()) return null;
505
+
506
+ return toNpxCache(readNpxCachePayload(getNpxCachePath()));
507
+ }
508
+
509
+ function saveCacheEntry(key: string, entry: NpxCacheEntry): void {
510
+ try {
511
+ const cachePath = getNpxCachePath();
512
+ const dir = dirname(cachePath);
513
+ mkdirSync(dir, { recursive: true });
514
+
515
+ const existing = toNpxCache(readNpxCachePayload(cachePath));
516
+ const entries = createCacheEntries();
517
+ if (existing) Object.assign(entries, existing.entries);
518
+ const merged: NpxCache = { version: CACHE_VERSION, entries };
519
+
520
+ merged.entries[key] = entry;
521
+ const tmpPath = `${cachePath}.${process.pid}.tmp`;
522
+ writeFileSync(tmpPath, JSON.stringify(merged, null, 2), "utf-8");
523
+ renameSync(tmpPath, cachePath);
524
+ } catch {
525
+ // Cache writes are best effort; resolution must still proceed.
526
+ }
472
527
  }
473
528
 
474
529
  function safeRealpath(path: string): string {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-adapter",
3
- "version": "2.23.0",
3
+ "version": "2.25.0",
4
4
  "description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
@@ -34,8 +34,8 @@
34
34
  "test": "vitest run",
35
35
  "test:watch": "vitest",
36
36
  "test:coverage": "vitest run --coverage",
37
- "test:oauth": "PI_MCP_ADAPTER_TEST_AUTH_STORE=memory node --import tsx --test --test-concurrency=1 oauth-public-api.test.ts mcp-auth.test.ts mcp-auth-flow.test.ts mcp-callback-server.test.ts mcp-oauth-provider.test.ts",
38
- "test:oauth-provider": "PI_MCP_ADAPTER_TEST_AUTH_STORE=memory node --import tsx --test mcp-oauth-provider.test.ts",
37
+ "test:oauth": "PI_MCP_ADAPTER_TEST_AUTH_STORE=memory PI_MCP_ADAPTER_DISABLE_AUTH_CACHE=1 node --import tsx --test --test-concurrency=1 oauth-public-api.test.ts mcp-auth.test.ts mcp-auth-flow.test.ts mcp-callback-server.test.ts mcp-oauth-provider.test.ts",
38
+ "test:oauth-provider": "PI_MCP_ADAPTER_TEST_AUTH_STORE=memory PI_MCP_ADAPTER_DISABLE_AUTH_CACHE=1 node --import tsx --test mcp-oauth-provider.test.ts",
39
39
  "test:conformance": "bash conformance/run.sh"
40
40
  },
41
41
  "repository": {