@herbertgao/pi-extensions 2026.8.15 → 2026.9.1

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 (351) hide show
  1. package/README.md +4 -1
  2. package/THIRD_PARTY_NOTICES.md +26 -0
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  4. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  5. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  6. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  7. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  11. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +20 -0
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +46 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +6 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +5 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +34 -30
  18. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  19. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  20. package/node_modules/@narumitw/pi-btw/README.md +5 -1
  21. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -14
  22. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  23. package/node_modules/@narumitw/pi-btw/package.json +4 -4
  24. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +158 -7
  25. package/node_modules/@narumitw/pi-btw/src/text.ts +18 -0
  26. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +5 -16
  27. package/node_modules/@narumitw/pi-caffeinate/LICENSE +21 -0
  28. package/node_modules/@narumitw/pi-caffeinate/README.md +224 -0
  29. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +1112 -0
  30. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +7 -0
  31. package/node_modules/@narumitw/pi-caffeinate/package.json +53 -0
  32. package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +811 -0
  33. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +153 -0
  34. package/node_modules/@narumitw/pi-caffeinate/src/index.ts +1 -0
  35. package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +43 -0
  36. package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +167 -0
  37. package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +202 -0
  38. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  39. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  40. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  41. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  42. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  43. package/node_modules/@tifan/pi-handoff/README.md +6 -3
  44. package/node_modules/@tifan/pi-handoff/package.json +1 -1
  45. package/node_modules/@tifan/pi-handoff/src/index.ts +8 -3
  46. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  47. package/node_modules/pi-lens/README.md +3 -0
  48. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  49. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  50. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  51. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  52. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  53. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  54. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  55. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  56. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  57. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  58. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  59. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  60. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  61. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  62. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  63. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  64. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  65. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  66. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  67. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  68. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  69. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  70. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  71. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  72. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  73. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  74. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  75. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  76. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  77. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  78. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  79. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  80. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  81. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  82. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  83. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  84. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  85. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  86. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  87. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  88. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  89. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  90. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  91. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  92. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  93. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  94. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  95. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  96. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  146. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  147. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  148. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  149. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  150. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  151. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  152. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  153. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  154. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  155. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  156. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  157. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  158. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  159. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  160. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  161. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  162. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  163. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  164. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  165. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  166. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  167. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  168. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  169. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  170. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  171. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  172. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  173. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  174. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  175. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  176. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  177. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  178. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  179. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  180. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  181. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  182. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  183. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  184. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  185. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  186. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  187. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  188. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  189. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  190. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  191. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  192. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  193. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  194. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  195. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  196. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  197. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  198. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  199. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  200. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  201. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  202. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  203. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  204. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  205. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  206. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  207. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  208. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  209. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  210. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  211. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  212. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  213. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  214. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  215. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  216. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  217. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  218. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  219. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  220. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  221. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  222. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  223. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  224. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  225. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  226. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  227. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  228. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  229. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  230. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  231. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  232. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  233. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  234. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  235. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  236. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  237. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  238. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  239. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  240. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  241. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  242. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  243. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  244. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  245. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  246. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  247. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  248. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  249. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  250. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  251. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  252. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  253. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  254. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  255. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  256. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  257. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  258. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  259. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  260. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  261. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  262. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  263. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  264. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  265. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  266. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  267. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  268. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  269. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  270. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  271. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  272. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  273. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  274. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  275. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  276. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  277. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  278. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  279. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  280. package/node_modules/pi-lens/dist/index.js +64335 -54300
  281. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  282. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  283. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  284. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  285. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  286. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  287. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  288. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  289. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  290. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  291. package/node_modules/pi-lens/docs/configuration.md +222 -0
  292. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  293. package/node_modules/pi-lens/docs/features.md +44 -5
  294. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  295. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  296. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  297. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  298. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  299. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  300. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  301. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  302. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  303. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  304. package/node_modules/pi-lens/package.json +15 -23
  305. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  306. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  307. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  308. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  309. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  310. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  311. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  312. package/node_modules/pi-mcp-adapter/CHANGELOG.md +27 -0
  313. package/node_modules/pi-mcp-adapter/README.md +13 -13
  314. package/node_modules/pi-mcp-adapter/commands.ts +58 -21
  315. package/node_modules/pi-mcp-adapter/config.ts +18 -4
  316. package/node_modules/pi-mcp-adapter/direct-tools.ts +26 -6
  317. package/node_modules/pi-mcp-adapter/dist/config.d.ts +4 -0
  318. package/node_modules/pi-mcp-adapter/dist/config.js +13 -4
  319. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  320. package/node_modules/pi-mcp-adapter/dist/types.d.ts +11 -3
  321. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  322. package/node_modules/pi-mcp-adapter/error-signal.ts +15 -4
  323. package/node_modules/pi-mcp-adapter/errors.ts +141 -0
  324. package/node_modules/pi-mcp-adapter/host-html-template.ts +58 -5
  325. package/node_modules/pi-mcp-adapter/index.ts +2 -3
  326. package/node_modules/pi-mcp-adapter/init.ts +5 -0
  327. package/node_modules/pi-mcp-adapter/mcp-panel.ts +30 -9
  328. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +66 -28
  329. package/node_modules/pi-mcp-adapter/mcp-status.ts +2 -0
  330. package/node_modules/pi-mcp-adapter/package.json +2 -1
  331. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -13
  332. package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +217 -0
  333. package/node_modules/pi-mcp-adapter/server-manager.ts +337 -6
  334. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +1 -0
  335. package/node_modules/pi-mcp-adapter/types.ts +18 -3
  336. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +18 -2
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +179 -54
  338. package/node_modules/pi-mcp-adapter/ui-session.ts +28 -8
  339. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  340. package/node_modules/pi-web-access/README.md +62 -15
  341. package/node_modules/pi-web-access/curator-page.ts +3 -1
  342. package/node_modules/pi-web-access/curator-server.ts +5 -1
  343. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  344. package/node_modules/pi-web-access/github-extract.ts +242 -1
  345. package/node_modules/pi-web-access/index.ts +118 -64
  346. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  347. package/node_modules/pi-web-access/package.json +2 -2
  348. package/node_modules/pi-web-access/perplexity.ts +14 -1
  349. package/node_modules/pi-web-access/utils.ts +23 -6
  350. package/node_modules/pi-web-access/xai-search.ts +96 -33
  351. package/package.json +15 -11
@@ -221,6 +221,35 @@ function createState(files) {
221
221
  progress: [],
222
222
  },
223
223
  },
224
+ // #2526: the config stack's positive-observability rows. `resolved`
225
+ // counts `config_resolved` phase records; `legacyWithoutRecords` counts
226
+ // the rows that carry a deprecated document but produced no
227
+ // migration/notice record — the deprecation machinery gone silent.
228
+ //
229
+ // Round 2, F2: the session->row relation is a JOIN on the session id both
230
+ // sides carry, not a subtraction of two counts.
231
+ //
232
+ // Round 3, S1: the session-side half of the join is no longer a
233
+ // PREDICTION. `pendingSessions` holds every session id that published a
234
+ // `config_resolution_pending` mark — written by `loadLSPConfig` itself,
235
+ // at the instant a resolution is actually attempted, never by a
236
+ // `runtime-session.ts` handler guessing from `no-lsp`/subagent/warm-attach
237
+ // flags ahead of time. A session that never reaches that call (quick or
238
+ // minimal mode's second-and-later session in a process, for the same
239
+ // root) never gets a mark, so it is silently excluded rather than
240
+ // counted against. `resolvedSessions` holds every session id a
241
+ // resolution actually reported, from EITHER sink (the latency row or the
242
+ // sessionstart line — one call writes both, so this is one fact
243
+ // surviving an independent rotation of either log, not a second source
244
+ // of truth).
245
+ config: {
246
+ resolved: 0,
247
+ pendingSessions: new Map(),
248
+ resolvedSessions: new Set(),
249
+ legacyDocuments: 0,
250
+ legacyWithoutRecords: 0,
251
+ examples: [],
252
+ },
224
253
  diagnostics: {
225
254
  bySeverity: counter(),
226
255
  byTool: counter(),
@@ -351,7 +380,44 @@ async function analyzeLatency(files, state) {
351
380
  } else if (entry.type === "phase") {
352
381
  const phase = entry.phase ?? "unknown";
353
382
  state.latency.phaseCounts.inc(phase);
354
- if (/_timeout$/.test(phase)) state.latency.phaseTimeouts.inc(phase);
383
+ if (phase.endsWith("_timeout")) state.latency.phaseTimeouts.inc(phase);
384
+ if (phase === "config_resolved") {
385
+ // #2526. Counted here rather than derived from phaseCounts so the
386
+ // legacy/record cross-check reads the same row it counts.
387
+ state.config.resolved += 1;
388
+ const md = entry.metadata ?? {};
389
+ // #2526 R2 F2 / #2552 R4: the join key is (session, root), not
390
+ // session alone — see `configJoinKey`'s doc comment. `entry.filePath`
391
+ // is this row's root (the same `configResolutionKey(cwd)` value the
392
+ // pending mark's `root=` carries); a row from a build that predates
393
+ // the session id carries none, and simply joins to nothing.
394
+ if (
395
+ typeof md.sessionId === "string" &&
396
+ md.sessionId.length > 0 &&
397
+ typeof entry.filePath === "string" &&
398
+ entry.filePath.length > 0
399
+ ) {
400
+ state.config.resolvedSessions.add(
401
+ configJoinKey(md.sessionId, entry.filePath),
402
+ );
403
+ }
404
+ const documents = Array.isArray(md.documents) ? md.documents : [];
405
+ const legacy = documents.filter((doc) => doc?.legacy === true);
406
+ state.config.legacyDocuments += legacy.length;
407
+ // A deprecated document that produced NO record means the
408
+ // migration notices went silent — the user is on a removal
409
+ // schedule and is never told. Zero records with zero legacy
410
+ // documents is the correct canonical-only answer, not a smell.
411
+ if (legacy.length > 0 && Number(md.recordCount ?? 0) === 0) {
412
+ state.config.legacyWithoutRecords += 1;
413
+ pushTop(
414
+ state.config.examples,
415
+ summarizeConfigResolved(entry, "legacy document with 0 records"),
416
+ limit * 3,
417
+ byDuration,
418
+ );
419
+ }
420
+ }
355
421
  if (
356
422
  phase === "total" &&
357
423
  (entry.durationMs ?? 0) >= thresholds.totalSlowMs
@@ -636,6 +702,40 @@ async function analyzeSessionStart(files, state) {
636
702
  trackProject(state, cwd);
637
703
  }
638
704
 
705
+ // #2526 R3 S1: this session's config-resolution PENDING mark, published
706
+ // by `loadLSPConfig` itself at the instant a resolution is actually
707
+ // attempted — never a start-line prediction from `no-lsp`/subagent/
708
+ // warm-attach flags. A session that never calls `loadLSPConfig` (quick
709
+ // or minimal mode's second-and-later session in a process, for the
710
+ // same root) never writes this line, and is correctly absent from the
711
+ // join rather than counted against.
712
+ //
713
+ // #2552 R4: `root=` is REQUIRED in the match — a warm MCP process keeps
714
+ // one session id for its whole life but marks once per served root
715
+ // (`configJoinKey`'s doc comment), so a line missing it cannot be
716
+ // attributed to a specific root and is left out of the join rather than
717
+ // guessed into a wrong one.
718
+ const pending =
719
+ /session_start config_resolution_pending session=(\S+) root=(.*)$/.exec(
720
+ message,
721
+ );
722
+ if (pending) {
723
+ const [, sessionId, root] = pending;
724
+ state.config.pendingSessions.set(configJoinKey(sessionId, root), {
725
+ ts: iso(ts),
726
+ sessionId,
727
+ root,
728
+ });
729
+ }
730
+ // The loader's own line is the second half of the join, so a rotated
731
+ // latency.log cannot manufacture a deficit on its own.
732
+ const resolvedSession =
733
+ /config resolved .*\bsession=(\S+) root=(.*)$/.exec(message);
734
+ if (resolvedSession) {
735
+ const [, sessionId, root] = resolvedSession;
736
+ state.config.resolvedSessions.add(configJoinKey(sessionId, root));
737
+ }
738
+
639
739
  const total = /session_start total:\s*(\d+)ms/.exec(message);
640
740
  if (total && Number(total[1]) >= thresholds.startupSlowMs) {
641
741
  state.smellTotals.inc("slow-session-start");
@@ -899,6 +999,23 @@ function iso(date) {
899
999
  return date?.toISOString?.() ?? "unknown";
900
1000
  }
901
1001
 
1002
+ /**
1003
+ * The config-resolution join key (#2552 review round 4). A warm MCP process
1004
+ * keeps ONE session id for its entire life but calls `loadLSPConfig` once per
1005
+ * SERVED ROOT (#2526 review round 2, F3's premise, reintroduced one layer up
1006
+ * here) — joining on session id alone let one root's row silently clear every
1007
+ * OTHER root's deficit under the same session id. `root` is whatever string
1008
+ * the producer already wrote (the pending mark's `root=`, or the row's own
1009
+ * `filePath` — both come from the SAME `configResolutionKey(cwd)` call in
1010
+ * `clients/lsp/config.ts`, which folds separators, canonicalizes, then folds
1011
+ * again — `normalizeFilePath(path.resolve(normalizeFilePath(cwd)))`, in that
1012
+ * order (#2518 review F6; resolving first breaks POSIX backslash names), so they compare equal without this script re-deriving any
1013
+ * path normalization of its own).
1014
+ */
1015
+ function configJoinKey(sessionId, root) {
1016
+ return `${sessionId} ${root}`;
1017
+ }
1018
+
902
1019
  function counter() {
903
1020
  const map = new Map();
904
1021
  return {
@@ -985,6 +1102,33 @@ function summarizeLatency(entry) {
985
1102
  };
986
1103
  }
987
1104
 
1105
+ /**
1106
+ * A `config_resolved` row (#2526), summarised for a smell example.
1107
+ *
1108
+ * Deliberately NOT `summarizeLatency`: that helper's metadata whitelist would
1109
+ * drop every field this row carries, so the example would print a phase name
1110
+ * and nothing a reader could act on.
1111
+ */
1112
+ function summarizeConfigResolved(entry, note) {
1113
+ const md = entry.metadata ?? {};
1114
+ const documents = Array.isArray(md.documents) ? md.documents : [];
1115
+ return {
1116
+ ts: entry.ts,
1117
+ durationMs: entry.durationMs,
1118
+ phase: entry.phase,
1119
+ project: projectOf(entry.filePath),
1120
+ note,
1121
+ documents: documents
1122
+ .map(
1123
+ (doc) =>
1124
+ `${doc?.tier ?? "?"}:${doc?.file ?? "?"}${doc?.legacy ? " (legacy)" : ""}`,
1125
+ )
1126
+ .slice(0, 8),
1127
+ recordCount: md.recordCount,
1128
+ deniedServers: md.deniedServers,
1129
+ };
1130
+ }
1131
+
988
1132
  function summarizeWorkspaceSweep(entry) {
989
1133
  const md = entry.metadata ?? {};
990
1134
  const bits = [];
@@ -1193,6 +1337,43 @@ function buildReport(state) {
1193
1337
  `session_start background tasks >= ${thresholds.backgroundSlowMs}ms`,
1194
1338
  state.session.slowTasks.slice(0, limit),
1195
1339
  );
1340
+ // #2526: config resolution has to prove it HAPPENED. Two shapes, one smell:
1341
+ //
1342
+ // (a) a session that has a `config_resolution_pending` mark and produced no
1343
+ // `config_resolved` row. Round 3, S1: the pending mark is written by
1344
+ // `loadLSPConfig` itself, at the instant a resolution is actually
1345
+ // attempted — never predicted from a handler's own flags ahead of time.
1346
+ // Round 2, F2 established the JOIN itself: on the session id both sides
1347
+ // carry, never a subtraction of two counts (subtracting counted quick
1348
+ // sessions' rows against full sessions' start lines — different
1349
+ // populations — which both masked total silence and charged
1350
+ // `--no-lsp`/subagent sessions with a permanent deficit they could never
1351
+ // clear).
1352
+ // Round 4, #2552 review: the join is on (session, root), not session
1353
+ // alone — a warm MCP process keeps one session id for its whole life but
1354
+ // marks once per SERVED ROOT (`configJoinKey`'s doc comment), so one
1355
+ // root's row must not clear another root's deficit under the same id.
1356
+ // (b) a legacy document present with zero records — the deprecation
1357
+ // machinery went silent while the user is on a removal schedule.
1358
+ const pendingConfigResolution = [...state.config.pendingSessions.entries()];
1359
+ const unresolvedSessions = pendingConfigResolution.filter(
1360
+ ([key]) => !state.config.resolvedSessions.has(key),
1361
+ );
1362
+ for (const [, value] of unresolvedSessions.slice(0, limit * 3)) {
1363
+ state.config.examples.push({
1364
+ ts: value.ts,
1365
+ session: value.sessionId,
1366
+ root: value.root,
1367
+ note: "session had a config_resolution_pending mark and produced no config_resolved row",
1368
+ });
1369
+ }
1370
+ addSmell(
1371
+ smells,
1372
+ "config-resolution",
1373
+ unresolvedSessions.length + state.config.legacyWithoutRecords,
1374
+ `Sessions with a config_resolution_pending mark but no config_resolved row (${unresolvedSessions.length} of ${pendingConfigResolution.length}) or a legacy config document that produced no migration record (${state.config.legacyWithoutRecords})`,
1375
+ state.config.examples.slice(0, limit),
1376
+ );
1196
1377
  addSmell(
1197
1378
  smells,
1198
1379
  "cascade-fallbacks",
@@ -1352,6 +1533,16 @@ function buildReport(state) {
1352
1533
  rotations: state.session.rotations.toJSON(),
1353
1534
  errors: state.session.errors.slice(0, limit),
1354
1535
  },
1536
+ // #2526: the positive-observability counters behind the
1537
+ // `config-resolution` smell, so a reader can see WHY it fired (or that
1538
+ // it correctly did not) without re-deriving the deficit.
1539
+ config: {
1540
+ resolved: state.config.resolved,
1541
+ sessionsPendingResolution: pendingConfigResolution.length,
1542
+ sessionsWithoutResolution: unresolvedSessions.length,
1543
+ legacyDocuments: state.config.legacyDocuments,
1544
+ legacyWithoutRecords: state.config.legacyWithoutRecords,
1545
+ },
1355
1546
  actionable: {
1356
1547
  events: state.actionable.events.toJSON(),
1357
1548
  reports: state.actionable.reports,
@@ -17,7 +17,9 @@
17
17
  * Force-imports the modules whose TOP-LEVEL bare imports are the documented
18
18
  * failure points, plus the bare specifiers directly, and checks the two
19
19
  * build-script-provided assets (ast-grep CLI binary + tree-sitter grammars)
20
- * that pnpm/bun skip by default. It runs no model and needs no credentials.
20
+ * that pnpm/bun skip by default. It also resolves the `pi.skills` manifest
21
+ * entries the way pi does, from the installed package root (#2587). It runs no
22
+ * model and needs no credentials.
21
23
  *
22
24
  * WHAT IT NO LONGER COVERS (#1926)
23
25
  * pi supplies `typebox` and `@earendil-works/pi-tui` from its own runtime, so
@@ -47,6 +49,7 @@ import { createRequire } from "node:module";
47
49
  import * as path from "node:path";
48
50
  import { fileURLToPath } from "node:url";
49
51
  import { HOST_PROVIDED_PACKAGES } from "./lib/host-provided-deps.mjs";
52
+ import { collectSkillEntryPaths } from "./lib/skills-predicate.mjs";
50
53
 
51
54
  const here = path.dirname(fileURLToPath(import.meta.url));
52
55
  const pkgRoot = path.resolve(here, "..");
@@ -242,6 +245,56 @@ try {
242
245
  }
243
246
  record("tree-sitter grammars", "asset", hasCoreGrammar, grammarDetail);
244
247
 
248
+ // --- 4. pi.skills manifest resolution, AS INSTALLED (#2587) ----------------
249
+ // Guards the recurrence of #2587: a `pi.skills` entry that resolves outside the
250
+ // installed package, so pi registers none of the shipped skills. This probe runs
251
+ // from the INSTALLED package (`require.resolve("pi-lens/scripts/…")`), so it sees
252
+ // the real layout — hoisted node_modules, pnpm's symlink store, bun, yarn — which
253
+ // is exactly where `../../skills` escaped and where a static check on the source
254
+ // tree cannot look. Replicates pi's own resolver, `PackageManager#collectFiles\
255
+ // FromManifestEntries` in `@earendil-works/pi-coding-agent`
256
+ // `dist/core/package-manager.js`: a non-glob entry is `resolve(packageRoot, entry)`
257
+ // (verified identical in 0.78.1 / 0.84.1 / 0.85.1).
258
+ //
259
+ // #2626 review round 2 (F2): the "does this dir have a skill" walk used to be
260
+ // a hand-rolled `countSkillFiles` here AND a second, differently-wrong one in
261
+ // `clients/skills-resolver.ts`. Both now derive from the ONE structural
262
+ // predicate in `./lib/skills-predicate.mjs`, which replicates pi's actual
263
+ // `loadSkillsFromDirInternal` walk (see that module's header for the exact
264
+ // discovery rules and the two documented scope gaps).
265
+ {
266
+ const pkgJson = JSON.parse(
267
+ fs.readFileSync(path.join(pkgRoot, "package.json"), "utf8"),
268
+ );
269
+ const entries = pkgJson.pi?.skills ?? [];
270
+ if (entries.length === 0) {
271
+ record("pi.skills declared", "manifest", false, "pi.skills is empty");
272
+ }
273
+ for (const entry of entries) {
274
+ const label = `pi.skills "${entry}"`;
275
+ // pi globs entries containing * or ? through a different branch; this
276
+ // probe only replicates the non-glob one.
277
+ if (/[*?]/.test(entry)) {
278
+ record(label, "manifest", true, "glob entry — not probed");
279
+ continue;
280
+ }
281
+ const resolved = path.resolve(pkgRoot, entry);
282
+ if (resolved !== pkgRoot && !resolved.startsWith(pkgRoot + path.sep)) {
283
+ record(label, "manifest", false, `escapes the package: ${resolved}`);
284
+ continue;
285
+ }
286
+ const found = collectSkillEntryPaths(resolved).length;
287
+ record(
288
+ label,
289
+ "manifest",
290
+ found > 0,
291
+ found > 0
292
+ ? `${found} SKILL.md under ${resolved}`
293
+ : `no SKILL.md under ${resolved}`,
294
+ );
295
+ }
296
+ }
297
+
245
298
  // --- Report ----------------------------------------------------------------
246
299
  const pad = Math.max(...results.map((r) => r.name.length));
247
300
  let hardFail = 0;
@@ -249,7 +302,10 @@ let softFail = 0;
249
302
  for (const r of results) {
250
303
  const soft = (r.kind === "asset" || r.kind === "tool") && allowSoft;
251
304
  const status = r.ok ? "PASS" : soft ? "WARN" : "FAIL";
252
- if (!r.ok) soft ? softFail++ : hardFail++;
305
+ if (!r.ok) {
306
+ if (soft) softFail++;
307
+ else hardFail++;
308
+ }
253
309
  console.log(
254
310
  ` [${status}] ${r.name.padEnd(pad)} ${r.detail ? "— " + r.detail : ""}`,
255
311
  );
@@ -0,0 +1,129 @@
1
+ // The ONE structural replica of pi's own skill-loading walk (#2626 review
2
+ // round 2, F1/F2).
3
+ //
4
+ // Two independent detectors used to answer "does this directory tree hold a
5
+ // skill pi would load" with two DIFFERENT, hand-rolled heuristics that both
6
+ // diverged from pi's real resolver:
7
+ // - `clients/skills-resolver.ts` (a `SKILL.md` one level down only, no
8
+ // recursion, no root `.md` acceptance, no symlink-follow, no dot/
9
+ // node_modules skip)
10
+ // - `scripts/install-selftest.mjs`'s `countSkillFiles` (recursed
11
+ // UNBOUNDED — would double-count a `SKILL.md` sitting inside another
12
+ // skill's own resource subdirectory — with no dot/node_modules skip, no
13
+ // root `.md` acceptance, no symlink handling)
14
+ // Neither matched `loadSkillsFromDirInternal` in
15
+ // `@earendil-works/pi-coding-agent` `dist/core/skills.js` (mirrored by
16
+ // `collectSkillEntries` in `dist/core/package-manager.js`, byte-identical
17
+ // walk, confirmed against the 0.85.1 tarball). This module is the fold: BOTH
18
+ // consumers now derive from `collectSkillEntryPaths` below.
19
+ //
20
+ // Discovery rules, replicated from `loadSkillsFromDirInternal` (verified
21
+ // against the extracted 0.85.1 `skills.js`):
22
+ // 1. If `dir` directly contains a (non-symlink-broken) `SKILL.md`, `dir` IS
23
+ // one skill root — return immediately, do NOT look at `dir`'s other
24
+ // entries or recurse into its subdirectories (a `SKILL.md` inside a
25
+ // skill's own reference/ subfolder is the skill's OWN resource, not a
26
+ // second skill).
27
+ // 2. Otherwise, skip dot-prefixed entries and `node_modules`; recurse into
28
+ // every remaining subdirectory (same two rules, one level down); at the
29
+ // OUTERMOST call only (`isRoot`), a loose `*.md` file directly in `dir`
30
+ // also counts (pi's `includeRootFiles`, true only for the top call).
31
+ // 3. A symlink is followed via `statSync` (broken symlink = skip) for BOTH
32
+ // the `SKILL.md` check and the directory/file classification in step 2.
33
+ //
34
+ // Deliberately NOT replicated (out of #2626's scope — packaging/path
35
+ // resolution, not skill-content validation):
36
+ // - `.gitignore`/`.ignore`/`.fdignore` matching inside the skills tree.
37
+ // - frontmatter parsing: pi additionally requires a non-empty `description`
38
+ // field before a discovered file becomes a loaded skill. A `SKILL.md`
39
+ // file that exists but fails THAT validation is a content-authoring
40
+ // defect a maintainer would see in normal use, not a silent zero.
41
+ // Both gaps mean this predicate can be a bit more OPTIMISTIC than pi's true
42
+ // count in a pathological case (an ignored or description-less file still
43
+ // counts as "found" here); it is never more PESSIMISTIC — every real skill
44
+ // pi loads is found by this walk too — which is the direction #2626 cares
45
+ // about (never claim "no skills" when pi would load one).
46
+
47
+ import * as fs from "node:fs";
48
+ import * as path from "node:path";
49
+
50
+ /**
51
+ * Symlink-aware entry classification, mirroring `loadSkillsFromDirInternal`'s
52
+ * own `entry.isSymbolicLink() ? statSync(fullPath) : entry`.
53
+ * @param {string} fullPath
54
+ * @param {import("node:fs").Dirent} entry
55
+ * @returns {"file" | "dir" | "other"}
56
+ */
57
+ export function resolvedEntryKind(fullPath, entry) {
58
+ if (entry.isSymbolicLink()) {
59
+ try {
60
+ const stats = fs.statSync(fullPath);
61
+ if (stats.isDirectory()) return "dir";
62
+ if (stats.isFile()) return "file";
63
+ return "other";
64
+ } catch {
65
+ return "other"; // broken symlink
66
+ }
67
+ }
68
+ if (entry.isDirectory()) return "dir";
69
+ if (entry.isFile()) return "file";
70
+ return "other";
71
+ }
72
+
73
+ /**
74
+ * The structural walk over an ALREADY-READ directory listing — split out
75
+ * from `collectSkillEntryPaths` so a caller that already has `entries` (a
76
+ * top-level ENOENT/EACCES distinction, say) is not forced into a second
77
+ * `readdirSync`.
78
+ * @param {string} dir
79
+ * @param {import("node:fs").Dirent[]} entries
80
+ * @param {boolean} isRoot
81
+ * @returns {string[]} absolute paths of every `SKILL.md` (or, at the root,
82
+ * loose `.md`) file pi's own resolver would treat as a skill entry point.
83
+ */
84
+ export function scanEntriesForSkills(dir, entries, isRoot) {
85
+ for (const entry of entries) {
86
+ if (
87
+ entry.name === "SKILL.md" &&
88
+ resolvedEntryKind(path.join(dir, entry.name), entry) === "file"
89
+ ) {
90
+ return [path.join(dir, entry.name)];
91
+ }
92
+ }
93
+ const found = [];
94
+ for (const entry of entries) {
95
+ if (entry.name.startsWith(".") || entry.name === "node_modules") {
96
+ continue;
97
+ }
98
+ const fullPath = path.join(dir, entry.name);
99
+ const kind = resolvedEntryKind(fullPath, entry);
100
+ if (kind === "dir") {
101
+ found.push(...collectSkillEntryPaths(fullPath, false));
102
+ continue;
103
+ }
104
+ if (isRoot && kind === "file" && entry.name.endsWith(".md")) {
105
+ found.push(fullPath);
106
+ }
107
+ }
108
+ return found;
109
+ }
110
+
111
+ /**
112
+ * Every skill entry point under `dir`, recursively, per pi's discovery
113
+ * rules. Fail-closed: a `readdirSync` error at `dir` (absent, unreadable) or
114
+ * any recursed-into subdirectory returns `[]` for that branch rather than
115
+ * throwing — matching `loadSkillsFromDirInternal`'s own bare `try { … }
116
+ * catch {}` per directory.
117
+ * @param {string} dir
118
+ * @param {boolean} [isRoot]
119
+ * @returns {string[]}
120
+ */
121
+ export function collectSkillEntryPaths(dir, isRoot = true) {
122
+ let entries;
123
+ try {
124
+ entries = fs.readdirSync(dir, { withFileTypes: true });
125
+ } catch {
126
+ return [];
127
+ }
128
+ return scanEntriesForSkills(dir, entries, isRoot);
129
+ }
@@ -61,7 +61,9 @@ function handle(line) {
61
61
  if (m.type === "response" && m.command === "get_commands") {
62
62
  const cmds = (m.data && m.data.commands) || [];
63
63
  const lens = cmds.filter(
64
- (c) => /^lens-/.test(c.name) || String(c.path || "").includes("pi-lens"),
64
+ (c) =>
65
+ String(c.name || "").startsWith("lens-") ||
66
+ String(c.path || "").includes("pi-lens"),
65
67
  );
66
68
  console.log(
67
69
  `total commands: ${cmds.length}; pi-lens commands: ${lens.map((c) => c.name).join(", ") || "(none)"}`,
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.32.1] - 2026-09-01
11
+
12
+ ### Fixed
13
+ - The published package now includes the updated public helper build artifacts for the `2.32.0` config and MCP Apps changes.
14
+
15
+ ## [2.32.0] - 2026-09-01
16
+
17
+ ### Highlights
18
+ - You can now enable and disable MCP servers directly from the `/mcp` panel.
19
+ - `/mcp setup` is clearer about where new shared servers will be saved.
20
+ - MCP App views that use browser storage now render reliably without exposing host session access.
21
+ - Long-running sessions handle MCP 2026 input flows, catalog updates, and UI resource refreshes more reliably.
22
+ - OAuth reuse and per-server status messages are less confusing.
23
+
24
+ ### Added
25
+ - The `/mcp` panel now supports enabling and disabling servers in place with `ctrl+d` on a server row. Saving persists the `disabled` flag to the project Pi layer and reloads the session, matching `/mcp disable` and `/mcp enable`. Thanks to [@ericykim](https://github.com/ericykim) for PR #479.
26
+
27
+ ### Changed
28
+ - `/mcp setup` now lets you choose project `.mcp.json` or global `~/.config/mcp/mcp.json` as the write target for new shared MCP servers, while keeping Pi-owned files and compatibility inputs in the advanced flow. The bundled `mcp-scripting` skill is manual-only by default. Thanks to [@w-winter](https://github.com/w-winter) for #477.
29
+
30
+ ### Fixed
31
+ - MCP 2026 multi-round input flows now work more reliably across proxy, direct, resource, and UI-resource calls, with clearer no-UI errors and cancellation cleanup.
32
+ - MCP 2026-07-28 catalog listens now recover from dropped listens, refresh quietly when catalogs change, and notify open UIs when resources update. (#468)
33
+ - MCP App views now load through a separate loopback sandbox proxy origin so storage APIs work without exposing host session capabilities. Thanks to [@drewbitt](https://github.com/drewbitt) for #480.
34
+ - Implicit OAuth now reuses URL-bound stored credentials while preserving anonymous fallback. Thanks to [@wilt00](https://github.com/wilt00) for #471.
35
+ - Per-server proxy lists now distinguish cached lazy tools from servers that need authentication while preserving active failure backoff. Thanks to [@inattendu](https://github.com/inattendu) for PR #474.
36
+
10
37
  ## [2.31.0] - 2026-08-28
11
38
 
12
39
  ### Highlights
@@ -32,9 +32,9 @@ The adapter reads standard MCP files automatically. No extra setup needed if you
32
32
 
33
33
  | You already have... | What happens |
34
34
  |---------------------|--------------|
35
- | `.mcp.json` or `~/.config/mcp/mcp.json` | Pi uses it immediately. The first time you open `/mcp`, you'll see a short heads-up explaining which file Pi detected and that Pi only writes adapter-specific overrides to its own files. |
35
+ | `.mcp.json` or `~/.config/mcp/mcp.json` | Pi uses it immediately. Use `.mcp.json` for project/team sharing and `~/.config/mcp/mcp.json` for all projects. The first time you open `/mcp`, you'll see a short heads-up explaining which file Pi detected and that Pi only writes adapter-specific overrides to its own files. |
36
36
  | Host-specific configs (Cursor, Claude Code, Codex, etc.) but no standard MCP files | Run `/mcp setup` to adopt those host configs into Pi. The setup flow shows exactly what it found, lets you pick which ones to import, and previews the exact file changes before writing. |
37
- | Nothing configured yet | Run `/mcp setup` to scaffold a minimal `.mcp.json`, add a curated known server, quick-add RepoPrompt, or inspect what the adapter discovered on your machine. |
37
+ | Nothing configured yet | Run `/mcp setup`, choose project `.mcp.json` or global `~/.config/mcp/mcp.json`, then scaffold a minimal config, add a curated known server, quick-add RepoPrompt, or inspect what the adapter discovered on your machine. |
38
38
 
39
39
  If you prefer the terminal, you can also run `pi-mcp-adapter init` after install to scan for host-specific configs and add missing compatibility imports to the Pi agent dir (`~/.pi/agent/mcp.json` by default, or `$PI_CODING_AGENT_DIR/mcp.json` when set).
40
40
 
@@ -53,16 +53,16 @@ Preferred project config: `.mcp.json`
53
53
  }
54
54
  ```
55
55
 
56
- Preferred user-global shared config: `~/.config/mcp/mcp.json`. Pi also reads the tool-agnostic global paths `~/.agents/mcp.json` and `~/.agents/mcp/mcp.json`.
56
+ Preferred user-global shared config: `~/.config/mcp/mcp.json` (for all projects). Pi also reads the tool-agnostic global paths `~/.agents/mcp.json` and `~/.agents/mcp/mcp.json` as compatibility inputs.
57
57
 
58
- Pi also reads Pi-owned override files for settings and host-specific compatibility:
58
+ Pi-owned files are not additional normal setup choices. They hold Pi-specific settings, compatibility imports, and adapter-only overrides:
59
59
 
60
60
  - `<Pi agent dir>/mcp.json` — Pi global override (`~/.pi/agent/mcp.json` by default)
61
61
  - `.pi/mcp.json` — Pi project override
62
62
 
63
- Host-specific configs are detected and shown by `/mcp setup` and `pi-mcp-adapter init`, but they are not loaded automatically. The normal `/mcp` panel does not scan host-specific files when `settings.hostConfigDiscovery` is `"off"`. To explicitly opt in to host-config fallback discovery, set `settings.hostConfigDiscovery` to `"on"` or run `pi-mcp-adapter init --discover-host-configs`. The default is `"off"`; `"prompt"` is available for integrations that want detection without activation. Host configs are lower precedence than every shared and Pi-owned source, and `/mcp setup` continues to offer explicit import adoption. Discovery reports source paths, provenance, and same-name conflicts; it never writes to external host files or silently launches commands from them.
63
+ Host-specific configs are detected and shown by `/mcp setup` and `pi-mcp-adapter init`, but they are compatibility inputs rather than normal setup paths and are not loaded automatically. The normal `/mcp` panel does not scan host-specific files when `settings.hostConfigDiscovery` is `"off"`. To explicitly opt in to host-config fallback discovery, set `settings.hostConfigDiscovery` to `"on"` or run `pi-mcp-adapter init --discover-host-configs`. The default is `"off"`; `"prompt"` is available for integrations that want detection without activation. Host configs are lower precedence than every shared and Pi-owned source, and `/mcp setup` continues to offer explicit import adoption. Discovery reports source paths, provenance, and same-name conflicts; it never writes to external host files or silently launches commands from them.
64
64
 
65
- Precedence is:
65
+ Precedence is (later entries win):
66
66
 
67
67
  1. `~/.config/mcp/mcp.json`
68
68
  2. `~/.agents/mcp.json`
@@ -302,7 +302,7 @@ Use `"auto"` to probe for MCP 2026-07-28 and conservatively fall back to the cla
302
302
 
303
303
  Use `"2026-07-28"` to pin that revision. Pinning has no legacy or SSE fallback and fails if the server does not offer the requested version.
304
304
 
305
- The stable SDK handles era-specific request envelopes, result decoding, list-changed subscriptions, cancellation, and multi-round-trip sampling/elicitation. The adapter keeps strict OAuth issuer validation in every mode. Adapter-level roots support, standard MCP logging presentation, and configuration/UI for protocol cache hints are not yet implemented.
305
+ The stable SDK handles era-specific request envelopes, result decoding, list-changed subscriptions, cancellation, and multi-round-trip sampling/elicitation. The SDK's embedded-input progress callback does not expose the originating tool or resource identity, so the adapter cannot maintain a durable per-tool waiting status row; interactive sessions keep the existing input dialog visible, and proxy calls show request progress when UI is available. The adapter keeps strict OAuth issuer validation in every mode. Adapter-level roots support, standard MCP logging presentation, and configuration/UI for protocol cache hints are not yet implemented.
306
306
 
307
307
  For pre-registered browser OAuth clients, set `oauth.redirectUri` to the exact callback registered with the provider, for example `"http://localhost:3118/callback"`. Dynamic clients normally omit it and use a lazy OS-assigned localhost callback port. A configured `https://` callback runs in manual mode because the adapter cannot receive a callback on another host. After authorization, copy the full callback URL from the browser address bar and paste it into `/mcp-auth` or `mcp({ action: "auth-complete", ... })`.
308
308
 
@@ -412,7 +412,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
412
412
  | `strictDirectToolArguments` | Validate direct-tool inputs against their advertised schemas and recover one JSON string layer for object and array properties (default: false). |
413
413
  | `directToolResultDetails` | Direct-tool result details: `"lean"` (default) or `"bounded"` to retain the guarded raw MCP result. |
414
414
  | `warnOnLargeDirectTools` | Show the advisory when 75 or more direct tools resolve (default: `true`). Set to `false` to suppress only this advisory. |
415
- | `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. |
415
+ | `freezeDirectTools` | Keep direct-tool registration stable after the initial sync so metadata updates and explicit reconnects do not rebuild the system prompt. Proxy/search/cache metadata still refreshes. Default: false. |
416
416
  | `scriptMode` | Register the MCP-only `mcpScript` plain-JavaScript tool (default: true). Set to `false` to hide it. |
417
417
  | `disableProxyTool` | Hide the `mcp` proxy tool once configured direct tools are fully available from cache. |
418
418
  | `autoAuth` | Auto-run OAuth on `connect`/tool calls when a server needs auth, then retry once (default: false). |
@@ -484,7 +484,7 @@ Set `"outputGuard": false` — or the env kill switch `MCP_OUTPUT_GUARD=0` — t
484
484
 
485
485
  For multi-call MCP work, write ordinary JavaScript: discover, inspect, call, loop, filter, chain, or fan out, then return one result. Run that code with the default-on `mcpScript` tool. For a single MCP call, search, describe, status check, or auth action, use `mcp` instead. Set `settings.scriptMode` to `false` to hide the scripting tool.
486
486
 
487
- The bundled `mcp-scripting` skill is a separate Pi package resource. To hide that skill while keeping the adapter extension installed, replace the package entry in Pi settings with the object form and disable package skills:
487
+ The bundled `mcp-scripting` skill is a separate Pi package resource and is manual-only by default, so its description is not added to the model's automatic skill context. Use `/skill:mcp-scripting` when you want its detailed workflow. To remove the manual skill command as well while keeping the adapter extension and `mcpScript` tool installed, replace the package entry in Pi settings with the object form and disable package skills:
488
488
 
489
489
  ```json
490
490
  {
@@ -624,13 +624,13 @@ Models sometimes encode an object or array argument as a JSON string. Set `setti
624
624
 
625
625
  Set `settings.directToolResultDetails` to `"bounded"` when an extension needs structured MCP result fields in Pi's direct-tool result details. The same output guard limits apply. Small leading structured fields stay available, while large fields receive bounded summaries and the complete guarded result follows the output guard's spill-file policy. The default `"lean"` mode keeps the existing server and tool metadata only.
626
626
 
627
- If prompt-cache stability matters more than automatic direct-tool hot-loading, set `settings.freezeDirectTools` to `true`. The initial direct-tool sync still runs, but later automatic reconnects, lazy-connects, and list-change notifications keep the registered tool surface unchanged. Deliberate refreshes through `mcp({ connect: "server" })` or `/mcp reconnect <server>` still update direct tools.
627
+ If prompt-cache stability matters more than direct-tool hot-loading, set `settings.freezeDirectTools` to `true`. The initial direct-tool sync still runs, but later metadata updates and explicit reconnects keep the registered tool surface unchanged while proxy/search/cache metadata refreshes normally.
628
628
 
629
629
  When you change direct-tool toggles in `/mcp`, the extension updates direct tool registration in the current session. Broader setup writes from `/mcp setup` still use Pi's normal reload flow because they can add or restructure MCP config files.
630
630
 
631
- **Interactive configuration:** Run `/mcp` to open an interactive panel showing all servers with connection status, tools, and direct/proxy toggles. You can reconnect servers and toggle tools between direct and proxy from the same overlay. For OAuth, press Enter on a server that needs auth or `ctrl+a` on any OAuth server. The Save action defaults to `ctrl+s` and can be remapped with the `mcp.panel.save` keybinding.
631
+ **Interactive configuration:** Run `/mcp` to open an interactive panel showing all servers with connection status, tools, and direct/proxy toggles. You can reconnect servers, toggle tools between direct and proxy, and enable or disable servers (`ctrl+d`) from the same overlay. For OAuth, press Enter on a server that needs auth or `ctrl+a` on any OAuth server. The Save action defaults to `ctrl+s` and can be remapped with the `mcp.panel.save` keybinding.
632
632
 
633
- **Guided first-run setup:** Run `/mcp setup` to inspect detected shared MCP files, adopt compatibility imports from other hosts, open discovered config paths, preview exact before/after file diffs for writes, scaffold a minimal project `.mcp.json`, add a curated known server (DeepWiki, Context7, Notion, GitHub, or Chrome DevTools), or quick-add RepoPrompt into a standard/shared MCP file.
633
+ **Guided first-run setup:** Run `/mcp setup` to choose the normal write target for new shared servers — project `.mcp.json` or global `~/.config/mcp/mcp.json` — inspect detected shared MCP files, adopt compatibility imports from other hosts, open discovered config paths, preview exact before/after file diffs for writes, scaffold a minimal selected config, add a curated known server (DeepWiki, Context7, Notion, GitHub, or Chrome DevTools), or quick-add RepoPrompt into a standard/shared MCP file.
634
634
 
635
635
  **Subagent integration:** If you use the subagent extension, agents can request direct MCP tools in their frontmatter with `mcp:server-name` syntax. See the subagent README for details.
636
636
 
@@ -712,7 +712,7 @@ Supported compatibility imports: `cursor`, `claude-code`, `claude-desktop`, `ope
712
712
 
713
713
  ### Project Config
714
714
 
715
- Prefer `.mcp.json` for project-local shared MCP config. Use `.pi/mcp.json` only when you need a Pi-specific project override. Project files override both user-global shared MCP config and Pi global overrides.
715
+ Prefer `.mcp.json` for project-local shared MCP config and `~/.config/mcp/mcp.json` for user-global shared MCP config. Use `.pi/mcp.json` only when you need a Pi-specific project override. Project files override both user-global shared MCP config and Pi global overrides.
716
716
 
717
717
  ## Usage
718
718