@herbertgao/pi-extensions 2026.9.0 → 2026.9.2

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 (360) hide show
  1. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  2. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@narumitw/pi-btw/README.md +57 -141
  11. package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
  12. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  13. package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
  14. package/node_modules/@narumitw/pi-btw/package.json +2 -1
  15. package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
  16. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
  17. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
  18. package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
  19. package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
  20. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
  21. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
  22. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  23. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  24. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  25. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  26. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  27. package/node_modules/pi-lens/CHANGELOG.md +1072 -0
  28. package/node_modules/pi-lens/README.md +3 -0
  29. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  30. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  31. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -9
  32. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  33. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
  34. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  35. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  36. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  37. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -9
  38. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
  39. package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -3
  40. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  41. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -6
  42. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  43. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  44. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
  45. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -9
  46. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  47. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +3 -11
  48. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  49. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  50. package/node_modules/pi-lens/dist/clients/cache-observability.js +40 -19
  51. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  52. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -9
  53. package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
  54. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  55. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  56. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  57. package/node_modules/pi-lens/dist/clients/config-core/index.js +50 -0
  58. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  59. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  60. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  61. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +164 -0
  62. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  63. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  64. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  65. package/node_modules/pi-lens/dist/clients/config-core/schema.js +165 -0
  66. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  67. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  68. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  69. package/node_modules/pi-lens/dist/clients/config-schema.js +234 -0
  70. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  71. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  72. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -6
  73. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  74. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  75. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  76. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  77. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  78. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  79. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
  80. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  81. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +15 -5
  82. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
  83. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -44
  84. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +27 -8
  86. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
  87. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
  89. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
  90. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +383 -27
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +1 -2
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +14 -18
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +6 -3
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +1 -2
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +1 -2
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -3
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -67
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  146. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
  147. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -10
  148. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  149. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  150. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  151. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  153. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  154. package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
  155. package/node_modules/pi-lens/dist/clients/file-time.js +7 -2
  156. package/node_modules/pi-lens/dist/clients/file-utils.js +117 -10
  157. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  158. package/node_modules/pi-lens/dist/clients/format-service.js +6 -2
  159. package/node_modules/pi-lens/dist/clients/formatters.js +385 -104
  160. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  161. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +116 -22
  162. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  163. package/node_modules/pi-lens/dist/clients/git-guard.js +6 -1
  164. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  165. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  166. package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
  167. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  168. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  169. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  170. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  171. package/node_modules/pi-lens/dist/clients/installer/index.js +289 -59
  172. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
  173. package/node_modules/pi-lens/dist/clients/instance-reaper.js +110 -236
  174. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  175. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
  176. package/node_modules/pi-lens/dist/clients/language-registry.js +599 -0
  177. package/node_modules/pi-lens/dist/clients/latency-logger.js +165 -9
  178. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +34 -1
  179. package/node_modules/pi-lens/dist/clients/lens-config.js +160 -30
  180. package/node_modules/pi-lens/dist/clients/lens-engine.js +33 -41
  181. package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
  182. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +77 -6
  183. package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -38
  185. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  186. package/node_modules/pi-lens/dist/clients/lsp/client.js +172 -33
  187. package/node_modules/pi-lens/dist/clients/lsp/config.js +484 -158
  188. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  189. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
  190. package/node_modules/pi-lens/dist/clients/lsp/edits.js +18 -5
  191. package/node_modules/pi-lens/dist/clients/lsp/index.js +892 -96
  192. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
  193. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -177
  194. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  195. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  196. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -18
  197. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  198. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  199. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  200. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
  201. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +14 -7
  202. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
  203. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
  204. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  205. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
  206. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  207. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  208. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  209. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  210. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  211. package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
  212. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +3 -5
  213. package/node_modules/pi-lens/dist/clients/module-report.js +21 -46
  214. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  215. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  216. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  217. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -24
  218. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  219. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  220. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +70 -28
  221. package/node_modules/pi-lens/dist/clients/opengrep-config.js +5 -1
  222. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  223. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  224. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  225. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  226. package/node_modules/pi-lens/dist/clients/path-utils.js +451 -15
  227. package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
  228. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  229. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  230. package/node_modules/pi-lens/dist/clients/pipeline.js +79 -18
  231. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  232. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  233. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  234. package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
  235. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
  236. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  237. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  238. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  239. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  240. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  241. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +28 -4
  242. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  243. package/node_modules/pi-lens/dist/clients/project-lens-config.js +438 -122
  244. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
  245. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  246. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  247. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  248. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  249. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  250. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  251. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +16 -5
  252. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  253. package/node_modules/pi-lens/dist/clients/read-guard.js +26 -2
  254. package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
  255. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  256. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +155 -114
  257. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
  258. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  259. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  260. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  261. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  262. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +19 -3
  263. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  264. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  265. package/node_modules/pi-lens/dist/clients/runtime-session.js +184 -32
  266. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +179 -36
  267. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  268. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  269. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  270. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  271. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -12
  272. package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
  273. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  274. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  275. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  277. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
  278. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  279. package/node_modules/pi-lens/dist/clients/sgconfig.js +7 -6
  280. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
  281. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  282. package/node_modules/pi-lens/dist/clients/smells-rollup.js +4 -4
  283. package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
  284. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  285. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  286. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  287. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +244 -0
  288. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  289. package/node_modules/pi-lens/dist/clients/tool-policy.js +92 -22
  290. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  291. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +87 -100
  292. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  293. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  294. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  295. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  296. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
  297. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  298. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -6
  299. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  300. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -9
  301. package/node_modules/pi-lens/dist/clients/word-index-store.js +23 -12
  302. package/node_modules/pi-lens/dist/clients/word-index.js +252 -47
  303. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  304. package/node_modules/pi-lens/dist/clients/zizmor-config.js +4 -1
  305. package/node_modules/pi-lens/dist/index.js +65899 -55340
  306. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  307. package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
  308. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  309. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  310. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  311. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  312. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +96 -25
  313. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +104 -86
  314. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  315. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
  316. package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
  317. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  318. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
  319. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  320. package/node_modules/pi-lens/docs/configuration.md +222 -0
  321. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  322. package/node_modules/pi-lens/docs/features.md +44 -5
  323. package/node_modules/pi-lens/docs/globalconfig.md +20 -0
  324. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  325. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
  326. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  327. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  328. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +33 -0
  329. package/node_modules/pi-lens/docs/pi-lens-subagent.md +49 -0
  330. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  331. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  332. package/node_modules/pi-lens/docs/release-qa-baseline.md +190 -0
  333. package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
  334. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  335. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  336. package/node_modules/pi-lens/docs/word-index.md +1 -1
  337. package/node_modules/pi-lens/package.json +18 -35
  338. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  339. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  340. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  341. package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
  342. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  343. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  344. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  345. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
  346. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  347. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
  348. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  349. package/node_modules/pi-web-access/README.md +62 -15
  350. package/node_modules/pi-web-access/curator-page.ts +3 -1
  351. package/node_modules/pi-web-access/curator-server.ts +5 -1
  352. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  353. package/node_modules/pi-web-access/github-extract.ts +242 -1
  354. package/node_modules/pi-web-access/index.ts +118 -64
  355. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  356. package/node_modules/pi-web-access/package.json +2 -2
  357. package/node_modules/pi-web-access/perplexity.ts +14 -1
  358. package/node_modules/pi-web-access/utils.ts +23 -6
  359. package/node_modules/pi-web-access/xai-search.ts +96 -33
  360. package/package.json +6 -6
@@ -1,10 +1,10 @@
1
1
  import * as path from "node:path";
2
2
  import { isTestMode } from "./env-utils.js";
3
- import { getGlobalPiLensDir } from "./file-utils.js";
3
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
4
4
  import { createNdjsonLogger } from "./ndjson-logger.js";
5
5
  import { getMaxLogSizeMB } from "./log-cleanup.js";
6
6
  import { normalizeLoggedPath } from "./path-utils.js";
7
- const LATENCY_LOG_DIR = getGlobalPiLensDir();
7
+ const LATENCY_LOG_DIR = getGlobalPiLensLogDir();
8
8
  const LATENCY_LOG_FILE = path.join(LATENCY_LOG_DIR, "latency.log");
9
9
  const writer = createNdjsonLogger({
10
10
  filePath: LATENCY_LOG_FILE,
@@ -79,6 +79,11 @@ let recentPhases = [];
79
79
  * usage records. It reports no new work and therefore cannot own last-phase
80
80
  * stall attribution.
81
81
  *
82
+ * #2249: `concurrent_session_bind_rollup` is the same shape as
83
+ * `session_end_bus_rollup`/`path_attribution_verified_rollup` above — a
84
+ * zero-duration session-end summary of records already logged individually,
85
+ * not new work of its own.
86
+ *
82
87
  * #2044: `test_runner_failed_target_state` is a zero-duration decision after a
83
88
  * bounded filesystem probe. The surrounding turn-end test-selection phase owns
84
89
  * any real work, so this row must not replace it in stall attribution.
@@ -101,8 +106,11 @@ const LAST_PHASE_EXCLUDED = new Set([
101
106
  "lsp_scanner_coverage_gap",
102
107
  "lsp_notify_resync_deferred",
103
108
  "lsp_notify_write_late_landed",
109
+ "lsp_notify_stall_cpu_busy",
104
110
  "degradation_ledger",
105
111
  "path_attribution_verified_rollup",
112
+ "concurrent_session_bind_rollup",
113
+ "auxiliary_readiness",
106
114
  ]);
107
115
  /**
108
116
  * The last non-`loop_block` phase logged, or undefined if none yet. Carries its
@@ -147,8 +155,6 @@ export function _recentPhasesStorageLengthForTest() {
147
155
  export function _setRecentPhasesForTest(entries) {
148
156
  recentPhases = entries;
149
157
  }
150
- /** Bound on the closed-bracket ring below — same size discipline as `recentPhases`. */
151
- export const CLOSED_BRACKET_CAP = RECENT_PHASE_CAP;
152
158
  /**
153
159
  * Phases currently executing, keyed by their own start token (#1723 review
154
160
  * round: this replaces an earlier single-slot design that broke two ways.
@@ -172,7 +178,7 @@ export const CLOSED_BRACKET_CAP = RECENT_PHASE_CAP;
172
178
  */
173
179
  const liveBrackets = new Map();
174
180
  /**
175
- * Recently CLOSED brackets, newest first, bounded to `CLOSED_BRACKET_CAP`
181
+ * Recently CLOSED brackets, newest first, bounded to `RECENT_PHASE_CAP`
176
182
  * (#1723 review round, F3 — the decisive finding). `phaseFinished` runs
177
183
  * inside a `finally`, which resumes as a MICROTASK, while the host schedules
178
184
  * `turn_end` as a MACROTASK — and microtasks always fully drain before the
@@ -202,7 +208,7 @@ export function phaseStarted(phase) {
202
208
  }
203
209
  /**
204
210
  * Close a bracket: removes it from `liveBrackets` (one `Map.delete`, O(1))
205
- * and records it on the closed-bracket ring, bounded to `CLOSED_BRACKET_CAP`
211
+ * and records it on the closed-bracket ring, bounded to `RECENT_PHASE_CAP`
206
212
  * with the oldest entry dropped first — see the `closedBrackets` doc comment
207
213
  * above for why a closed history is load-bearing, not just nice-to-have
208
214
  * (#1723 review F3). `Map.delete` reports whether it actually removed
@@ -238,7 +244,7 @@ export function phaseFinished(token) {
238
244
  closedAt: new Date().toISOString(),
239
245
  },
240
246
  ...closedBrackets,
241
- ].slice(0, CLOSED_BRACKET_CAP);
247
+ ].slice(0, RECENT_PHASE_CAP);
242
248
  }
243
249
  /**
244
250
  * The OLDEST phase still executing, if any — the longest-running open
@@ -259,7 +265,7 @@ export function getCurrentPhase() {
259
265
  * A candidate bracket is ignored outright if its OWN lifetime (`elapsedMs`)
260
266
  * is under this fraction of the window's length (#1723 review round 3, N4).
261
267
  * Overlap alone is not enough: the bounded closed-bracket ring can churn a
262
- * real culprit out (busy siblings filling `CLOSED_BRACKET_CAP`), leaving only
268
+ * real culprit out (busy siblings filling `RECENT_PHASE_CAP`), leaving only
263
269
  * a 1ms bracket that happens to have SOME positive overlap with an 18-second
264
270
  * window — reporting it would be a CONFIDENT WRONG ANSWER, worse than no
265
271
  * answer. 5%: a genuine cause's own duration should be a meaningful fraction
@@ -481,7 +487,7 @@ export function getPhaseForWindow(windowStartMs, windowEndMs) {
481
487
  /**
482
488
  * Test-only: the closed-bracket ring's actual storage length, mirroring
483
489
  * `_recentPhasesStorageLengthForTest` above — pins that `phaseFinished`'s
484
- * `.slice(0, CLOSED_BRACKET_CAP)` guard is intact independent of any
490
+ * `.slice(0, RECENT_PHASE_CAP)` guard is intact independent of any
485
491
  * read-side behavior (#1723 review: "ring unbounded" mutation).
486
492
  */
487
493
  export function _closedBracketsStorageLengthForTest() {
@@ -520,6 +526,156 @@ export function resetCurrentPhaseForSession() {
520
526
  liveBrackets.clear();
521
527
  closedBrackets = [];
522
528
  }
529
+ /**
530
+ * Phases already written ONCE for the current session (#2526).
531
+ *
532
+ * Session-scoped telemetry bookkeeping, which is this module's own domain —
533
+ * the same reason `liveBrackets` and `recentPhases` live here rather than in
534
+ * each producer. A producer that is called many times per session but whose
535
+ * record is a SESSION fact (`config_resolved`: `loadLSPConfig` runs at session
536
+ * start, per served root, and on the first edit, all resolving the same
537
+ * config) asks here instead of keeping its own latch, so the "one row per
538
+ * session" a log reader counts against is a property of the logger rather than
539
+ * a convention each producer re-implements.
540
+ */
541
+ const oncePerSessionPhases = new Set();
542
+ /**
543
+ * The identity every once-per-session record of THIS session is stamped with
544
+ * (#2526 review round 2, F2).
545
+ *
546
+ * A line-oriented log reader cannot join two files by adjacency: sessionstart.
547
+ * log's start lines and latency.log's phase rows interleave differently, rotate
548
+ * independently, and (for the warm MCP server) can span days. The first round
549
+ * therefore SUBTRACTED row counts from start-line counts, which silently
550
+ * assumed the two counted the same population — they do not, and the
551
+ * mismatch both masked total silence and manufactured a permanent false
552
+ * deficit. An id makes the join explicit: `handleSessionStart` publishes it
553
+ * beside its expectation, the record carries it, and the analyzer matches.
554
+ *
555
+ * It is minted HERE, beside the claim set it identifies, so "which session is
556
+ * this record from" and "has this session already recorded" can never answer
557
+ * from different sessions.
558
+ *
559
+ * Minted at module load too, not only on re-arm: the warm MCP server resolves
560
+ * config through `ensureReady` without any session boundary of its own, and a
561
+ * record with no id at all would be unjoinable rather than merely unmatched.
562
+ */
563
+ let sessionRecordSeq = 0;
564
+ function mintSessionRecordId() {
565
+ sessionRecordSeq += 1;
566
+ return `${process.pid.toString(36)}-${Date.now().toString(36)}-${sessionRecordSeq.toString(36)}`;
567
+ }
568
+ let sessionRecordId = mintSessionRecordId();
569
+ /**
570
+ * The current session's record identity — an opaque, process-local token.
571
+ *
572
+ * Deliberately NOT the host's session id: that is user-facing conversation
573
+ * identity pi-lens does not otherwise write to disk, and the join only needs
574
+ * "the same session", not "which conversation".
575
+ */
576
+ export function currentSessionRecordId() {
577
+ return sessionRecordId;
578
+ }
579
+ /**
580
+ * The claim key. A phase is claimed per SCOPE (#2526 review round 2, F3):
581
+ * `config_resolved` is a fact about a (session, project root) pair, and the
582
+ * warm MCP server serves N roots from one process — keying on the phase name
583
+ * alone recorded the first root's documents and silently dropped every other
584
+ * root's, so a legacy config in project B could never surface its smell.
585
+ *
586
+ * `scope` is REQUIRED (#2526 review round 3, S3 — narrowed from optional):
587
+ * the one production caller always passes one, and an unscoped claim is the
588
+ * exact shape F3 found unsafe — a phase-only key recording the first caller
589
+ * and silently dropping every later one under a different scope. A future
590
+ * producer with no natural scope can pass a constant string; that is still an
591
+ * explicit choice, not a silently-defaulted one.
592
+ */
593
+ function claimKey(phase, scope) {
594
+ return `${phase}\0${scope}`;
595
+ }
596
+ /**
597
+ * Claim the session's one record for `phase` within `scope`.
598
+ *
599
+ * Returns true exactly once per session and scope; every later call for the
600
+ * same pair returns false until {@link resetOncePerSessionPhases} re-arms it.
601
+ * Callers should claim BEFORE building the payload, so a claim that loses the
602
+ * race costs nothing.
603
+ */
604
+ export function claimPhaseOncePerSession(phase, scope) {
605
+ const key = claimKey(phase, scope);
606
+ if (oncePerSessionPhases.has(key))
607
+ return false;
608
+ oncePerSessionPhases.add(key);
609
+ return true;
610
+ }
611
+ /**
612
+ * Release ONE scope's claim for `phase` — the narrow sibling of
613
+ * {@link releaseOncePerSessionPhase}, which releases every scope of a phase.
614
+ *
615
+ * #2518 review F1. A claim is a promise that this session's record for the
616
+ * (phase, scope) pair has already been written, which holds only while the
617
+ * state that record described is still there. When that state is DROPPED —
618
+ * the session-root registry evicting a root, taking its resolved LSP config
619
+ * with it — the next load for that scope is a genuine SECOND resolution, and
620
+ * its row is the only thing that says what the reloaded config was. So the
621
+ * claim is released with the state it described; a claim outliving its state
622
+ * silences the record for the rest of the session, which is catalog shape 17
623
+ * pointed at a positive-observability record.
624
+ *
625
+ * Bounded by the caller: releases are as frequent as the evictions that cause
626
+ * them, and those are counted (`lsp-session-root-evicted`).
627
+ */
628
+ export function releasePhaseClaim(phase, scope) {
629
+ oncePerSessionPhases.delete(claimKey(phase, scope));
630
+ }
631
+ /**
632
+ * Release every scope's claim for ONE phase, without touching the others
633
+ * (#2526 review round 2, S1).
634
+ *
635
+ * A producer's own `resetForTests` needs its own phase re-armed, not a blanket
636
+ * clear of a shared session-scoped structure: the blanket call reached into
637
+ * claims other producers own and, worse, re-minted the session identity as a
638
+ * side effect of an unrelated module's reset.
639
+ */
640
+ export function releaseOncePerSessionPhase(phase) {
641
+ const prefix = `${phase}\0`;
642
+ for (const key of oncePerSessionPhases) {
643
+ if (key.startsWith(prefix))
644
+ oncePerSessionPhases.delete(key);
645
+ }
646
+ }
647
+ /**
648
+ * Re-arm every once-per-session phase claim for a new session, and mint the
649
+ * new session's record identity.
650
+ *
651
+ * PLACEMENT (#2526 review round 2, F1): this is called from index.ts's
652
+ * `session_start` closure, from BEHIND the #473 concurrent-secondary gate,
653
+ * beside `resetCurrentPhaseForSession` — and NOT from inside
654
+ * `handleSessionStart`. index.ts resolves this session's config in
655
+ * `ensureLSPConfigInitialized` BEFORE it calls the handler, so a re-arm inside
656
+ * the handler fires between the session's own two resolutions: the ensure
657
+ * writes a row, the handler wipes the claim, and the deferred `loadLSPConfig`
658
+ * writes a second row for the same session. The first session of a process got
659
+ * two rows and every later one (whose ensure the process-lifetime
660
+ * `_lspConfigInitializedCwds` memo short-circuits) got one, which is
661
+ * unrecoverable for any per-session count.
662
+ *
663
+ * A latch that is never re-armed silences its record for the rest of the
664
+ * PROCESS (AGENTS.md catalog shape 17), which for a positive-observability
665
+ * record is the exact silence it exists to end — so the re-arm must exist; it
666
+ * just has to sit ahead of the session's first resolution rather than in the
667
+ * middle of them.
668
+ *
669
+ * The warm MCP server (`clients/mcp/session.ts` calls `handleSessionStart`
670
+ * directly, never through index.ts) therefore keeps ONE identity for the life
671
+ * of the process. That is the honest answer for it: a warm server has no
672
+ * session boundary, its "session" is the process, and F3's per-root scoping is
673
+ * what gives it one record per served root.
674
+ */
675
+ export function resetOncePerSessionPhases() {
676
+ oncePerSessionPhases.clear();
677
+ sessionRecordId = mintSessionRecordId();
678
+ }
523
679
  export function logLatency(entry) {
524
680
  const ts = new Date().toISOString();
525
681
  if (entry.type === "phase" &&
@@ -9,8 +9,41 @@
9
9
  */
10
10
  /** The single truncation bound for every ledger field, and for the reasons callers build. */
11
11
  export const LEDGER_FIELD_MAX = 200;
12
+ /**
13
+ * How many retained entries the ledger keeps per kind — and therefore the bound
14
+ * any producer of ledger-bound records sizes itself to.
15
+ *
16
+ * Moved here from `degradation-ledger.ts` (#2426) for the reason this module's
17
+ * doc comment already gives: `config-core/records.ts` bounds its collector to
18
+ * this number, and reading it off the LEDGER made the config core import a sink
19
+ * it is documented not to touch ("Pure: no state, no I/O, no ledger writes").
20
+ * That import also broke three suites that `vi.mock` the ledger wholesale — the
21
+ * exact failure mode this leaf exists to prevent — and closed seven import
22
+ * cycles once the config loaders started resolving through the core.
23
+ */
24
+ export const DEGRADATION_ENTRIES_PER_KIND = 20;
12
25
  export function normalizeForLedger(value) {
13
- return String(value ?? "unknown");
26
+ if (value === null || value === undefined)
27
+ return "unknown";
28
+ // A plain object or array has no `toString` of its own, so `String()`
29
+ // writes "[object Object]" — a ledger row that names nothing. Serialise
30
+ // those; everything else (Error, Date, URL, primitives, class instances)
31
+ // keeps its `String()` form. An object carrying its OWN `toString` goes
32
+ // through `String()` too, so a throwing one still raises into the
33
+ // ledger's corrupted-input failsafe (#2703 r1 F1) instead of being
34
+ // admitted as "{}". Circular input degrades to a fixed marker.
35
+ if (typeof value === "object" && !Object.hasOwn(value, "toString")) {
36
+ const proto = Object.getPrototypeOf(value);
37
+ if (proto === Object.prototype || proto === null || Array.isArray(value)) {
38
+ try {
39
+ return JSON.stringify(value) ?? "unknown";
40
+ }
41
+ catch {
42
+ return "[unserializable object]";
43
+ }
44
+ }
45
+ }
46
+ return String(value);
14
47
  }
15
48
  /** Bound a value to `LEDGER_FIELD_MAX`, marking the elision. */
16
49
  export function truncateForLedger(value) {
@@ -1,41 +1,44 @@
1
- import { logExtension } from "./extension-log.js";
2
- import { notifyUserDegradation } from "./user-notify.js";
3
- import * as fs from "node:fs";
4
- import * as os from "node:os";
1
+ import { resetIgnoredConfigWarnCache, warnIgnoredConfigOnce, } from "./config-warn.js";
2
+ import { errorClassName } from "./error-class.js";
5
3
  import * as path from "node:path";
6
4
  import { assignFlagConfigSection, flagConfigSectionKeys, flagValueFromConfig, getLensFlagSpec, GLOBAL_NON_FLAG_CONFIG_SECTIONS, LENS_FLAGS, readFlagConfigValue, } from "./lens-flag-registry.js";
5
+ import { CANONICAL_GLOBAL_CONFIG_FILE, getPiLensGlobalConfigPath, GLOBAL_CONFIG_LOCATIONS, LEGACY_ROOT_LSP_KEYS, } from "./config-locations.js";
6
+ // Re-exported so this module's own, pre-existing import sites (`./lens-config.js`
7
+ // / `../lens-config.js`) keep working. The function itself now lives in
8
+ // `config-locations.ts` (#2426 review round 3, S1) — see the doc comment there.
9
+ export { getPiLensGlobalConfigPath } from "./config-locations.js";
10
+ import { ignoredRecordCollector, readConfigDocument, reportConfigReadFailure, reportPiLensConfigRecords, resolveOnePiLensConfigDocument, } from "./config-resolve.js";
7
11
  import { findNestedProjectMutationValue, } from "./project-lens-config.js";
8
- export function getPiLensGlobalConfigPath(homeDir = os.homedir()) {
9
- const override = process.env.PI_LENS_CONFIG_PATH;
10
- if (override)
11
- return path.resolve(override);
12
- return path.join(homeDir, ".pi-lens", "config.json");
12
+ /**
13
+ * The canonical global location, looked up rather than constructed, so a change
14
+ * to the shared table cannot leave this loader describing a file that is no
15
+ * longer canonical.
16
+ */
17
+ function globalCanonicalLocation() {
18
+ const location = GLOBAL_CONFIG_LOCATIONS.find((candidate) => candidate.relativePath === CANONICAL_GLOBAL_CONFIG_FILE);
19
+ if (!location) {
20
+ throw new Error(`no canonical global config location named ${CANONICAL_GLOBAL_CONFIG_FILE}`);
21
+ }
22
+ return location;
13
23
  }
14
- const warnedInvalidGlobalConfigs = new Set();
15
24
  /**
16
25
  * Same warn-once-per-(path, reason) contract as project-lens-config.ts's
17
26
  * `warnInvalidConfigOnce` — a malformed global config value is logged once
18
- * and then treated as absent, rather than silently dropped (#792).
27
+ * and then treated as absent, rather than silently dropped (#792). Since #2418
28
+ * the latch, the log line, the durable ledger row, and the stable-coded
29
+ * notification all live in the one shared seam.
19
30
  */
20
- function warnInvalidGlobalConfigOnce(configPath, reason) {
21
- const key = `${configPath}:${reason}`;
22
- if (warnedInvalidGlobalConfigs.has(key))
23
- return;
24
- warnedInvalidGlobalConfigs.add(key);
25
- const message = `ignoring invalid global config ${configPath}: ${reason}`;
26
- logExtension({
31
+ function warnInvalidGlobalConfigOnce(configPath, reason, code) {
32
+ warnIgnoredConfigOnce({
27
33
  subsystem: "lens-config",
28
- level: "warn",
29
- message,
30
- metadata: { configPath, reason },
34
+ file: configPath,
35
+ reason,
36
+ ...(code === undefined ? {} : { code }),
31
37
  });
32
- // HUMAN-audience too: a config the user wrote is being ignored. Routed
33
- // through the host's own render path (#1333), never a raw write.
34
- notifyUserDegradation(`pi-lens: ${message}`);
35
38
  }
36
39
  /** For tests that need to force the warn-once cache to reset between cases. */
37
40
  export function resetGlobalConfigWarnCache() {
38
- warnedInvalidGlobalConfigs.clear();
41
+ resetIgnoredConfigWarnCache("lens-config");
39
42
  }
40
43
  function asConfigObject(value) {
41
44
  return value && typeof value === "object" && !Array.isArray(value)
@@ -43,12 +46,82 @@ function asConfigObject(value) {
43
46
  : undefined;
44
47
  }
45
48
  export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath()) {
49
+ const location = globalCanonicalLocation();
50
+ // #2445: this used to be `JSON.parse(fs.readFileSync(...))` inside a bare
51
+ // `catch { return undefined; }`, so a malformed `~/.pi-lens/config.json`
52
+ // produced ZERO signal of its own — no log line, no ledger row, no
53
+ // notification. The only thing a user saw was the LSP loader's report of the
54
+ // SAME file, mislabelled "ignoring invalid LSP config", because that loader
55
+ // resolves the canonical global too. Both halves are fixed together: the
56
+ // read/parse failure is reported here, under `lens-config`, and
57
+ // `reportConfigReadFailure` derives the subsystem from the DOCUMENT so the
58
+ // LSP loader's report of this file lands under `lens-config` as well and the
59
+ // warn-once latch collapses the two into one honest notice.
60
+ const outcome = readConfigDocument(configPath);
61
+ if (outcome.status === "missing")
62
+ return undefined;
63
+ if (outcome.status === "error") {
64
+ reportConfigReadFailure({
65
+ file: configPath,
66
+ location,
67
+ tier: "global",
68
+ error: outcome.error,
69
+ sourceText: outcome.sourceText,
70
+ });
71
+ return undefined;
72
+ }
73
+ // Every notice this projection composes is BUFFERED and bounded once, at the
74
+ // end, through the SAME `ignoredRecordCollector` the project loader uses
75
+ // (#2426 review round 6, F3; round 7, F2). It used to report each one the
76
+ // moment it was composed, with no collector anywhere in this function, so
77
+ // the unknown-top-level-key scan below — whose count is the number of keys
78
+ // the user typed — put 100 notifications on screen for a 100-key
79
+ // `~/.pi-lens/config.json` while the project loader's identical scan of an
80
+ // identical file produced 19 and a count. Two changelog fragments already
81
+ // claimed one bound for every producer; this is the producer that did not
82
+ // have one.
83
+ //
84
+ // Round 6 gave it that bound by COPYING the project loader's, which left two
85
+ // spellings of one record literal differing only in a tier. Round 7 moved
86
+ // the seam into `config-resolve.ts`, which both loaders already import, and
87
+ // made the tier an argument. This loader never passes a `{ parseError }`:
88
+ // its read/parse failure is reported above by `reportConfigReadFailure`,
89
+ // before the projection starts.
90
+ //
91
+ // The flush is in a `finally` so a throw in the projection still delivers
92
+ // what was composed before it, on top of the whole-config record the catch
93
+ // adds.
94
+ const { note, records: notedRecords } = ignoredRecordCollector(configPath, "global");
46
95
  try {
47
- const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
48
- if (!parsed || typeof parsed !== "object")
96
+ const parsed = outcome.value;
97
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
98
+ // The other half of the same silence: a file that PARSES but is a list
99
+ // or a scalar is not a config either, and was dropped without a word.
100
+ note("top-level value must be an object");
49
101
  return undefined;
50
- const raw = parsed;
51
- const warnInvalid = (reason) => warnInvalidGlobalConfigOnce(configPath, reason);
102
+ }
103
+ // Through the #2425 core (#2426). The canonical global file is resolved as
104
+ // a single `global`-tier source against the canonical schema, so the depth
105
+ // bound, the prototype-key policy and the declared `lsp.*` types apply to
106
+ // `~/.pi-lens/config.json` exactly as they do to `.pi-lens.json`. The
107
+ // field-by-field projection below is unchanged and keeps owning the
108
+ // per-key prose its tests assert on — the core validates the SHAPE, this
109
+ // function still decides what a bad value is called.
110
+ const document = {
111
+ tier: "global",
112
+ file: configPath,
113
+ location,
114
+ value: parsed,
115
+ };
116
+ const resolved = resolveOnePiLensConfigDocument(document);
117
+ // EVERY record this document produced (#2426 review round 3, F1) — not
118
+ // filtered to what this loader "owns". `reportPiLensConfigRecords` derives
119
+ // the reporting subsystem per record; the warn-once latch collapses a
120
+ // duplicate report of the SAME record from the LSP loader's resolution of
121
+ // this same file (it resolves the canonical global too) into one notice.
122
+ reportPiLensConfigRecords(resolved.records);
123
+ const raw = resolved.value;
124
+ const warnInvalid = note;
52
125
  const config = {};
53
126
  for (const spec of LENS_FLAGS) {
54
127
  if (spec.readGlobal)
@@ -60,6 +133,25 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
60
133
  : undefined;
61
134
  if (ignore && ignore.length > 0)
62
135
  config.ignore = ignore;
136
+ const startup = asConfigObject(raw.startup);
137
+ if (startup) {
138
+ const startupConfig = {};
139
+ const mode = startup.mode;
140
+ const scans = asConfigObject(startup.scans);
141
+ if (mode === "quick" || mode === "full" || mode === "minimal")
142
+ startupConfig.mode = mode;
143
+ else if ("mode" in startup)
144
+ warnInvalid('startup.mode must be "quick", "full", or "minimal"');
145
+ if (scans) {
146
+ if (typeof scans.enabled === "boolean") {
147
+ startupConfig.scans = { enabled: scans.enabled };
148
+ }
149
+ else if ("enabled" in scans)
150
+ warnInvalid("startup.scans.enabled must be a boolean");
151
+ }
152
+ if (startupConfig.mode !== undefined || startupConfig.scans !== undefined)
153
+ config.startup = startupConfig;
154
+ }
63
155
  const dispatch = asConfigObject(raw.dispatch);
64
156
  if (dispatch) {
65
157
  const floor = dispatch.runnerTimeoutFloorMs;
@@ -127,9 +219,22 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
127
219
  // (`GLOBAL_NON_FLAG_CONFIG_SECTIONS`, which co-locates `$schema` and the
128
220
  // hand-parsed namespaces beside the registry). Adding a flag needs no
129
221
  // edit here; adding a namespace is a one-line edit in that one constant.
222
+ //
223
+ // `LEGACY_ROOT_LSP_KEYS` joins them for #2426 review round 4, F1. The four
224
+ // legacy root LSP keys are read out of THIS file by the LSP loader and
225
+ // their values are applied, exactly as they are in a project
226
+ // `.pi-lens.json` — where `PROJECT_FOREIGN_CONFIG_NAMESPACES` has
227
+ // tolerated them since #2426. The global scan never got the same
228
+ // treatment, so `~/.pi-lens/config.json` with a root `warmFiles` both
229
+ // honored the setting and called it a typo in the same session. Spread
230
+ // from the registry-derived list rather than restated: `lens-config.ts`
231
+ // already imports `config-locations.ts`, which derives it from
232
+ // `DEPRECATED_CONFIG_SURFACES`, so the accepted set and the removal
233
+ // schedule cannot drift apart and there is no second copy of the names.
130
234
  const knownGlobalConfigKeys = new Set([
131
235
  ...flagConfigSectionKeys(LENS_FLAGS),
132
236
  ...GLOBAL_NON_FLAG_CONFIG_SECTIONS,
237
+ ...LEGACY_ROOT_LSP_KEYS,
133
238
  ]);
134
239
  for (const key of Object.keys(raw)) {
135
240
  if (!knownGlobalConfigKeys.has(key)) {
@@ -138,9 +243,34 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
138
243
  }
139
244
  return config;
140
245
  }
141
- catch {
246
+ catch (error) {
247
+ // #2426 review round 5, S-C. The other half of #2445's silence. The
248
+ // read/parse failure above now reports, but everything AFTER the parse —
249
+ // the resolution through the core and the field-by-field projection —
250
+ // still sat under a bare `catch { return undefined }`, so a throw in any
251
+ // of it dropped the WHOLE global config with no log line, no ledger row
252
+ // and no notification: pi-lens ran on defaults and said nothing, which is
253
+ // the exact defect #2445 was filed for.
254
+ //
255
+ // `PILENS_CFG_0008` ("config resolution failed; whole configuration
256
+ // ignored") rather than `0001`, and the ERROR CLASS only, never its
257
+ // message, which could quote the file — the same rule `resolveConfig`'s
258
+ // own guard follows for the same reason. Round 5 used `0005` here, which
259
+ // is registered as a per-FIELD rejection: a user matching on it would
260
+ // have expected one setting to be missing rather than the whole file
261
+ // (#2426 review round 6, S1). `errorClassName` (#2451) is the ONE
262
+ // implementation of "class name, never the message" — this loader
263
+ // already imports `config-warn.js` for the rest of the seam, so it could
264
+ // call `normalizeParseErrorReason({ classOnly: true })` directly, but
265
+ // calling the same leaf `config-warn.ts` itself delegates to keeps every
266
+ // caller of this guarantee, `config-core/` included, on ONE
267
+ // implementation rather than two that happen to agree today.
268
+ warnInvalidGlobalConfigOnce(configPath, `global config could not be interpreted (${errorClassName(error)}); configuration ignored`, "PILENS_CFG_0008");
142
269
  return undefined;
143
270
  }
271
+ finally {
272
+ reportPiLensConfigRecords(notedRecords());
273
+ }
144
274
  }
145
275
  export function getGlobalIgnorePatterns(configPath) {
146
276
  return loadPiLensGlobalConfig(configPath)?.ignore ?? [];
@@ -18,6 +18,7 @@ import { minimatch } from "./deps/minimatch.js";
18
18
  import { getDiagnosticTracker } from "./diagnostic-tracker.js";
19
19
  import { getLatencyReports, } from "./dispatch/integration.js";
20
20
  import { getResourceFootprint as getResourceFootprintSnapshot, } from "./instance-registry.js";
21
+ import { extensionsForLanguageToken } from "./language-registry.js";
21
22
  import { initLSPConfig } from "./lsp/config.js";
22
23
  import { getLSPService } from "./lsp/index.js";
23
24
  import { acquireWarmWordIndex } from "./mcp/analyze.js";
@@ -27,14 +28,19 @@ import { loadProjectSnapshotWithoutWordIndex } from "./project-snapshot.js";
27
28
  import { getTreeSitterRuntimeStatus, } from "./tree-sitter-shared.js";
28
29
  import { centralityFromReverseDeps, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
29
30
  // --- Facades (re-exported so adapters import only this module) ---------------
30
- export { analyzeFile, } from "./mcp/analyze.js";
31
+ export { analyzeFile } from "./mcp/analyze.js";
31
32
  export { createMcpHost } from "./mcp/host-shim.js";
32
- export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus, requestWarmAnalyze, WARM_TURN_END_SCHEMA_VERSION, } from "./mcp/ipc.js";
33
- export { analyzeFileFresh, canRebuildPiLens, REBUILD_UNAVAILABLE_MESSAGE, resolveRebuildScript, runRebuild, summarizeScan, } from "./mcp/review.js";
33
+ export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus, WARM_TURN_END_SCHEMA_VERSION, } from "./mcp/ipc.js";
34
+ export { analyzeFileFresh, canRebuildPiLens, resolveRebuildScript, runRebuild, summarizeScan, } from "./mcp/review.js";
34
35
  export { acknowledgeTurnEnd, runSessionStart, runTurnEnd, runTurnEndForIpc, } from "./mcp/session.js";
35
36
  export { moduleReport, readEnclosing, readSymbol, renderCompactModuleReport, } from "./module-report.js";
36
37
  export { projectReport, renderCompactProjectReport, } from "./project-report.js";
37
- export { createDefaultHostPorts, } from "./host-ports.js";
38
+ // Effective-config introspection (#2427). A facade rather than a wrapper for
39
+ // the same reason the others are: the computation reaches into the config
40
+ // resolution, the LSP registry and the dispatch plan at once, and putting that
41
+ // composition here would make this file the fourth place that knows how a
42
+ // server is selected. Adapters route to it; nobody re-derives it.
43
+ export { effectiveConfig, isEffectiveFileViewError, } from "./effective-config.js";
38
44
  // --- Query wrappers (own the remaining internal reach-ins) -------------------
39
45
  /** Recent dispatch latency reports (latency.log schema), newest first. */
40
46
  export function recentLatency(limit = 5, fileFilter) {
@@ -163,42 +169,28 @@ export function ensureLspConfig(cwd) {
163
169
  export function resourceFootprint() {
164
170
  return getResourceFootprintSnapshot();
165
171
  }
166
- // Same language identifiers ast_grep_search's `lang` param accepts
167
- // (tools/shared.ts's LANGUAGES) mapped to source file extensions. Duplicated
168
- // here rather than imported — clients/ never reaches into tools/ (see
169
- // AGENTS.md's MCP-mirror layering note) — so this is symbol_search's own small
170
- // copy, scoped to what its `lang` filter needs (extension matching only, no
171
- // AST/LSP concerns).
172
- const SYMBOL_SEARCH_LANG_EXTENSIONS = {
173
- bash: [".sh", ".bash"],
174
- c: [".c", ".h"],
175
- cpp: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"],
176
- csharp: [".cs"],
177
- css: [".css", ".scss", ".less"],
178
- elixir: [".ex", ".exs"],
179
- go: [".go"],
180
- haskell: [".hs", ".lhs"],
181
- html: [".html", ".htm"],
182
- java: [".java"],
183
- javascript: [".js", ".jsx", ".mjs", ".cjs"],
184
- json: [".json", ".jsonc", ".json5"],
185
- kotlin: [".kt", ".kts"],
186
- lua: [".lua"],
187
- nix: [".nix"],
188
- php: [".php"],
189
- python: [".py", ".pyi"],
190
- ruby: [".rb", ".rake", ".gemspec"],
191
- rust: [".rs"],
192
- scala: [".scala", ".sc"],
193
- solidity: [".sol"],
194
- swift: [".swift"],
195
- tsx: [".tsx"],
196
- typescript: [".ts", ".mts", ".cts"],
197
- yaml: [".yaml", ".yml"],
198
- };
199
- function fileMatchesLang(file, lang) {
200
- const exts = SYMBOL_SEARCH_LANG_EXTENSIONS[lang];
201
- if (!exts)
172
+ /**
173
+ * symbol_search's `lang` filter.
174
+ *
175
+ * The extension table this used to consult was a NINTH hand-written language ->
176
+ * extensions map, kept here because `clients/` never reaches into `tools/`
177
+ * (AGENTS.md's MCP-mirror layering note) — and it had drifted from every other
178
+ * one (#2424 review, S2): `css` claimed `.scss`/`.less`, `json` claimed
179
+ * `.jsonc`, `php` omitted its four alias extensions, `ruby` omitted `.ru`, and
180
+ * `solidity` was not a language the registry (or any grammar, or the ast-grep
181
+ * napi matrix) knows at all.
182
+ *
183
+ * The layering constraint was never a reason to duplicate the DATA, only a
184
+ * reason not to import the tool's token list. `extensionsForLanguageToken`
185
+ * resolves the token against the canonical registry instead.
186
+ *
187
+ * Exported so `tests/clients/language-registry-drift.test.ts` guards the SEAM
188
+ * and not just the projection behind it: a future local table reintroduced
189
+ * here would satisfy a registry-only assertion but fail this one.
190
+ */
191
+ export function symbolSearchFileMatchesLang(file, lang) {
192
+ const exts = extensionsForLanguageToken(lang);
193
+ if (exts.length === 0)
202
194
  return false;
203
195
  return exts.includes(path.extname(file).toLowerCase());
204
196
  }
@@ -223,7 +215,7 @@ function buildSymbolSearchFileFilter(cwd, options) {
223
215
  return (file) => {
224
216
  if (paths?.length && !fileMatchesPathGlobs(file, cwd, paths))
225
217
  return false;
226
- if (lang && !fileMatchesLang(file, lang))
218
+ if (lang && !symbolSearchFileMatchesLang(file, lang))
227
219
  return false;
228
220
  return true;
229
221
  };
@@ -2,7 +2,7 @@ import { logExtension } from "./extension-log.js";
2
2
  import { logBusEvent } from "./bus-events-logger.js";
3
3
  import { normalizeFilePath } from "./path-utils.js";
4
4
  import { createLiveBusEmitter, recordStaleBusFailure, resolveLiveBusEmitter, } from "./live-bus-emitter.js";
5
- export const LENS_EVENT_VERSION = 1;
5
+ const LENS_EVENT_VERSION = 1;
6
6
  export const LENS_EVENT_NAMES = {
7
7
  analysisComplete: "pi-lens/analysis-complete",
8
8
  findings: "pi-lens/findings",