@herbertgao/pi-extensions 2026.9.0 → 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 (293) 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/@pi-plugins/fast-mode/README.md +5 -3
  11. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  12. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  13. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  14. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  15. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  16. package/node_modules/pi-lens/README.md +3 -0
  17. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  18. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  19. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  22. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  23. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  24. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  25. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  26. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  27. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  28. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  29. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  30. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  31. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  32. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  33. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  34. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  35. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  36. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  37. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  38. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  39. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  40. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  41. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  42. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  43. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  44. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  45. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  46. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  47. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  48. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  49. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  50. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  51. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  52. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  53. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  54. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  55. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  56. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  58. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  59. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  60. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  63. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  64. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  122. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  123. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  124. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  125. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  126. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  128. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  129. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  130. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  131. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  132. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  133. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  134. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  135. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  136. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  137. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  138. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  139. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  140. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  141. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  142. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  143. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  144. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  145. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  146. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  147. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  148. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  149. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  150. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  151. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  153. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  154. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  155. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  157. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  158. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  159. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  160. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  162. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  163. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  164. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  165. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  167. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  168. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  171. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  172. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  173. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  174. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  175. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  177. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  178. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  179. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  180. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  181. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  182. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  183. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  184. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  185. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  186. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  187. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  188. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  189. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  190. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  191. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  195. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  196. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  197. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  198. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  199. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  200. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  201. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  203. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  204. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  205. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  206. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  207. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  208. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  209. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  210. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  211. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  212. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  213. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  214. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  215. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  216. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  217. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  218. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  219. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  220. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  221. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  222. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  223. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  225. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  226. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  227. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  228. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  229. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  230. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  231. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  232. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  233. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  234. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  241. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  242. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  243. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  244. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  245. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  246. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  247. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  249. package/node_modules/pi-lens/dist/index.js +64335 -54300
  250. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  251. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  252. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  253. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  254. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  255. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  256. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  257. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  258. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  259. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  260. package/node_modules/pi-lens/docs/configuration.md +222 -0
  261. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  262. package/node_modules/pi-lens/docs/features.md +44 -5
  263. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  264. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  265. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  266. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  267. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  268. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  269. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  270. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  271. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  272. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  273. package/node_modules/pi-lens/package.json +15 -23
  274. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  275. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  276. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  277. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  278. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  279. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  280. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  281. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  282. package/node_modules/pi-web-access/README.md +62 -15
  283. package/node_modules/pi-web-access/curator-page.ts +3 -1
  284. package/node_modules/pi-web-access/curator-server.ts +5 -1
  285. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  286. package/node_modules/pi-web-access/github-extract.ts +242 -1
  287. package/node_modules/pi-web-access/index.ts +118 -64
  288. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  289. package/node_modules/pi-web-access/package.json +2 -2
  290. package/node_modules/pi-web-access/perplexity.ts +14 -1
  291. package/node_modules/pi-web-access/utils.ts +23 -6
  292. package/node_modules/pi-web-access/xai-search.ts +96 -33
  293. package/package.json +5 -5
@@ -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
@@ -520,6 +528,156 @@ export function resetCurrentPhaseForSession() {
520
528
  liveBrackets.clear();
521
529
  closedBrackets = [];
522
530
  }
531
+ /**
532
+ * Phases already written ONCE for the current session (#2526).
533
+ *
534
+ * Session-scoped telemetry bookkeeping, which is this module's own domain —
535
+ * the same reason `liveBrackets` and `recentPhases` live here rather than in
536
+ * each producer. A producer that is called many times per session but whose
537
+ * record is a SESSION fact (`config_resolved`: `loadLSPConfig` runs at session
538
+ * start, per served root, and on the first edit, all resolving the same
539
+ * config) asks here instead of keeping its own latch, so the "one row per
540
+ * session" a log reader counts against is a property of the logger rather than
541
+ * a convention each producer re-implements.
542
+ */
543
+ const oncePerSessionPhases = new Set();
544
+ /**
545
+ * The identity every once-per-session record of THIS session is stamped with
546
+ * (#2526 review round 2, F2).
547
+ *
548
+ * A line-oriented log reader cannot join two files by adjacency: sessionstart.
549
+ * log's start lines and latency.log's phase rows interleave differently, rotate
550
+ * independently, and (for the warm MCP server) can span days. The first round
551
+ * therefore SUBTRACTED row counts from start-line counts, which silently
552
+ * assumed the two counted the same population — they do not, and the
553
+ * mismatch both masked total silence and manufactured a permanent false
554
+ * deficit. An id makes the join explicit: `handleSessionStart` publishes it
555
+ * beside its expectation, the record carries it, and the analyzer matches.
556
+ *
557
+ * It is minted HERE, beside the claim set it identifies, so "which session is
558
+ * this record from" and "has this session already recorded" can never answer
559
+ * from different sessions.
560
+ *
561
+ * Minted at module load too, not only on re-arm: the warm MCP server resolves
562
+ * config through `ensureReady` without any session boundary of its own, and a
563
+ * record with no id at all would be unjoinable rather than merely unmatched.
564
+ */
565
+ let sessionRecordSeq = 0;
566
+ function mintSessionRecordId() {
567
+ sessionRecordSeq += 1;
568
+ return `${process.pid.toString(36)}-${Date.now().toString(36)}-${sessionRecordSeq.toString(36)}`;
569
+ }
570
+ let sessionRecordId = mintSessionRecordId();
571
+ /**
572
+ * The current session's record identity — an opaque, process-local token.
573
+ *
574
+ * Deliberately NOT the host's session id: that is user-facing conversation
575
+ * identity pi-lens does not otherwise write to disk, and the join only needs
576
+ * "the same session", not "which conversation".
577
+ */
578
+ export function currentSessionRecordId() {
579
+ return sessionRecordId;
580
+ }
581
+ /**
582
+ * The claim key. A phase is claimed per SCOPE (#2526 review round 2, F3):
583
+ * `config_resolved` is a fact about a (session, project root) pair, and the
584
+ * warm MCP server serves N roots from one process — keying on the phase name
585
+ * alone recorded the first root's documents and silently dropped every other
586
+ * root's, so a legacy config in project B could never surface its smell.
587
+ *
588
+ * `scope` is REQUIRED (#2526 review round 3, S3 — narrowed from optional):
589
+ * the one production caller always passes one, and an unscoped claim is the
590
+ * exact shape F3 found unsafe — a phase-only key recording the first caller
591
+ * and silently dropping every later one under a different scope. A future
592
+ * producer with no natural scope can pass a constant string; that is still an
593
+ * explicit choice, not a silently-defaulted one.
594
+ */
595
+ function claimKey(phase, scope) {
596
+ return `${phase}\0${scope}`;
597
+ }
598
+ /**
599
+ * Claim the session's one record for `phase` within `scope`.
600
+ *
601
+ * Returns true exactly once per session and scope; every later call for the
602
+ * same pair returns false until {@link resetOncePerSessionPhases} re-arms it.
603
+ * Callers should claim BEFORE building the payload, so a claim that loses the
604
+ * race costs nothing.
605
+ */
606
+ export function claimPhaseOncePerSession(phase, scope) {
607
+ const key = claimKey(phase, scope);
608
+ if (oncePerSessionPhases.has(key))
609
+ return false;
610
+ oncePerSessionPhases.add(key);
611
+ return true;
612
+ }
613
+ /**
614
+ * Release ONE scope's claim for `phase` — the narrow sibling of
615
+ * {@link releaseOncePerSessionPhase}, which releases every scope of a phase.
616
+ *
617
+ * #2518 review F1. A claim is a promise that this session's record for the
618
+ * (phase, scope) pair has already been written, which holds only while the
619
+ * state that record described is still there. When that state is DROPPED —
620
+ * the session-root registry evicting a root, taking its resolved LSP config
621
+ * with it — the next load for that scope is a genuine SECOND resolution, and
622
+ * its row is the only thing that says what the reloaded config was. So the
623
+ * claim is released with the state it described; a claim outliving its state
624
+ * silences the record for the rest of the session, which is catalog shape 17
625
+ * pointed at a positive-observability record.
626
+ *
627
+ * Bounded by the caller: releases are as frequent as the evictions that cause
628
+ * them, and those are counted (`lsp-session-root-evicted`).
629
+ */
630
+ export function releasePhaseClaim(phase, scope) {
631
+ oncePerSessionPhases.delete(claimKey(phase, scope));
632
+ }
633
+ /**
634
+ * Release every scope's claim for ONE phase, without touching the others
635
+ * (#2526 review round 2, S1).
636
+ *
637
+ * A producer's own `resetForTests` needs its own phase re-armed, not a blanket
638
+ * clear of a shared session-scoped structure: the blanket call reached into
639
+ * claims other producers own and, worse, re-minted the session identity as a
640
+ * side effect of an unrelated module's reset.
641
+ */
642
+ export function releaseOncePerSessionPhase(phase) {
643
+ const prefix = `${phase}\0`;
644
+ for (const key of oncePerSessionPhases) {
645
+ if (key.startsWith(prefix))
646
+ oncePerSessionPhases.delete(key);
647
+ }
648
+ }
649
+ /**
650
+ * Re-arm every once-per-session phase claim for a new session, and mint the
651
+ * new session's record identity.
652
+ *
653
+ * PLACEMENT (#2526 review round 2, F1): this is called from index.ts's
654
+ * `session_start` closure, from BEHIND the #473 concurrent-secondary gate,
655
+ * beside `resetCurrentPhaseForSession` — and NOT from inside
656
+ * `handleSessionStart`. index.ts resolves this session's config in
657
+ * `ensureLSPConfigInitialized` BEFORE it calls the handler, so a re-arm inside
658
+ * the handler fires between the session's own two resolutions: the ensure
659
+ * writes a row, the handler wipes the claim, and the deferred `loadLSPConfig`
660
+ * writes a second row for the same session. The first session of a process got
661
+ * two rows and every later one (whose ensure the process-lifetime
662
+ * `_lspConfigInitializedCwds` memo short-circuits) got one, which is
663
+ * unrecoverable for any per-session count.
664
+ *
665
+ * A latch that is never re-armed silences its record for the rest of the
666
+ * PROCESS (AGENTS.md catalog shape 17), which for a positive-observability
667
+ * record is the exact silence it exists to end — so the re-arm must exist; it
668
+ * just has to sit ahead of the session's first resolution rather than in the
669
+ * middle of them.
670
+ *
671
+ * The warm MCP server (`clients/mcp/session.ts` calls `handleSessionStart`
672
+ * directly, never through index.ts) therefore keeps ONE identity for the life
673
+ * of the process. That is the honest answer for it: a warm server has no
674
+ * session boundary, its "session" is the process, and F3's per-root scoping is
675
+ * what gives it one record per served root.
676
+ */
677
+ export function resetOncePerSessionPhases() {
678
+ oncePerSessionPhases.clear();
679
+ sessionRecordId = mintSessionRecordId();
680
+ }
523
681
  export function logLatency(entry) {
524
682
  const ts = new Date().toISOString();
525
683
  if (entry.type === "phase" &&
@@ -9,6 +9,19 @@
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
26
  return String(value ?? "unknown");
14
27
  }
@@ -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)
@@ -127,9 +200,22 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
127
200
  // (`GLOBAL_NON_FLAG_CONFIG_SECTIONS`, which co-locates `$schema` and the
128
201
  // hand-parsed namespaces beside the registry). Adding a flag needs no
129
202
  // edit here; adding a namespace is a one-line edit in that one constant.
203
+ //
204
+ // `LEGACY_ROOT_LSP_KEYS` joins them for #2426 review round 4, F1. The four
205
+ // legacy root LSP keys are read out of THIS file by the LSP loader and
206
+ // their values are applied, exactly as they are in a project
207
+ // `.pi-lens.json` — where `PROJECT_FOREIGN_CONFIG_NAMESPACES` has
208
+ // tolerated them since #2426. The global scan never got the same
209
+ // treatment, so `~/.pi-lens/config.json` with a root `warmFiles` both
210
+ // honored the setting and called it a typo in the same session. Spread
211
+ // from the registry-derived list rather than restated: `lens-config.ts`
212
+ // already imports `config-locations.ts`, which derives it from
213
+ // `DEPRECATED_CONFIG_SURFACES`, so the accepted set and the removal
214
+ // schedule cannot drift apart and there is no second copy of the names.
130
215
  const knownGlobalConfigKeys = new Set([
131
216
  ...flagConfigSectionKeys(LENS_FLAGS),
132
217
  ...GLOBAL_NON_FLAG_CONFIG_SECTIONS,
218
+ ...LEGACY_ROOT_LSP_KEYS,
133
219
  ]);
134
220
  for (const key of Object.keys(raw)) {
135
221
  if (!knownGlobalConfigKeys.has(key)) {
@@ -138,9 +224,34 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
138
224
  }
139
225
  return config;
140
226
  }
141
- catch {
227
+ catch (error) {
228
+ // #2426 review round 5, S-C. The other half of #2445's silence. The
229
+ // read/parse failure above now reports, but everything AFTER the parse —
230
+ // the resolution through the core and the field-by-field projection —
231
+ // still sat under a bare `catch { return undefined }`, so a throw in any
232
+ // of it dropped the WHOLE global config with no log line, no ledger row
233
+ // and no notification: pi-lens ran on defaults and said nothing, which is
234
+ // the exact defect #2445 was filed for.
235
+ //
236
+ // `PILENS_CFG_0008` ("config resolution failed; whole configuration
237
+ // ignored") rather than `0001`, and the ERROR CLASS only, never its
238
+ // message, which could quote the file — the same rule `resolveConfig`'s
239
+ // own guard follows for the same reason. Round 5 used `0005` here, which
240
+ // is registered as a per-FIELD rejection: a user matching on it would
241
+ // have expected one setting to be missing rather than the whole file
242
+ // (#2426 review round 6, S1). `errorClassName` (#2451) is the ONE
243
+ // implementation of "class name, never the message" — this loader
244
+ // already imports `config-warn.js` for the rest of the seam, so it could
245
+ // call `normalizeParseErrorReason({ classOnly: true })` directly, but
246
+ // calling the same leaf `config-warn.ts` itself delegates to keeps every
247
+ // caller of this guarantee, `config-core/` included, on ONE
248
+ // implementation rather than two that happen to agree today.
249
+ warnInvalidGlobalConfigOnce(configPath, `global config could not be interpreted (${errorClassName(error)}); configuration ignored`, "PILENS_CFG_0008");
142
250
  return undefined;
143
251
  }
252
+ finally {
253
+ reportPiLensConfigRecords(notedRecords());
254
+ }
144
255
  }
145
256
  export function getGlobalIgnorePatterns(configPath) {
146
257
  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";
@@ -35,6 +36,12 @@ export { acknowledgeTurnEnd, runSessionStart, runTurnEnd, runTurnEndForIpc, } fr
35
36
  export { moduleReport, readEnclosing, readSymbol, renderCompactModuleReport, } from "./module-report.js";
36
37
  export { projectReport, renderCompactProjectReport, } from "./project-report.js";
37
38
  export { createDefaultHostPorts, } from "./host-ports.js";
39
+ // Effective-config introspection (#2427). A facade rather than a wrapper for
40
+ // the same reason the others are: the computation reaches into the config
41
+ // resolution, the LSP registry and the dispatch plan at once, and putting that
42
+ // composition here would make this file the fourth place that knows how a
43
+ // server is selected. Adapters route to it; nobody re-derives it.
44
+ export { effectiveConfig, isEffectiveFileViewError, } from "./effective-config.js";
38
45
  // --- Query wrappers (own the remaining internal reach-ins) -------------------
39
46
  /** Recent dispatch latency reports (latency.log schema), newest first. */
40
47
  export function recentLatency(limit = 5, fileFilter) {
@@ -163,42 +170,28 @@ export function ensureLspConfig(cwd) {
163
170
  export function resourceFootprint() {
164
171
  return getResourceFootprintSnapshot();
165
172
  }
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)
173
+ /**
174
+ * symbol_search's `lang` filter.
175
+ *
176
+ * The extension table this used to consult was a NINTH hand-written language ->
177
+ * extensions map, kept here because `clients/` never reaches into `tools/`
178
+ * (AGENTS.md's MCP-mirror layering note) — and it had drifted from every other
179
+ * one (#2424 review, S2): `css` claimed `.scss`/`.less`, `json` claimed
180
+ * `.jsonc`, `php` omitted its four alias extensions, `ruby` omitted `.ru`, and
181
+ * `solidity` was not a language the registry (or any grammar, or the ast-grep
182
+ * napi matrix) knows at all.
183
+ *
184
+ * The layering constraint was never a reason to duplicate the DATA, only a
185
+ * reason not to import the tool's token list. `extensionsForLanguageToken`
186
+ * resolves the token against the canonical registry instead.
187
+ *
188
+ * Exported so `tests/clients/language-registry-drift.test.ts` guards the SEAM
189
+ * and not just the projection behind it: a future local table reintroduced
190
+ * here would satisfy a registry-only assertion but fail this one.
191
+ */
192
+ export function symbolSearchFileMatchesLang(file, lang) {
193
+ const exts = extensionsForLanguageToken(lang);
194
+ if (exts.length === 0)
202
195
  return false;
203
196
  return exts.includes(path.extname(file).toLowerCase());
204
197
  }
@@ -223,7 +216,7 @@ function buildSymbolSearchFileFilter(cwd, options) {
223
216
  return (file) => {
224
217
  if (paths?.length && !fileMatchesPathGlobs(file, cwd, paths))
225
218
  return false;
226
- if (lang && !fileMatchesLang(file, lang))
219
+ if (lang && !symbolSearchFileMatchesLang(file, lang))
227
220
  return false;
228
221
  return true;
229
222
  };
@@ -17,6 +17,7 @@
17
17
  * `~/.pi-lens/config.json`. Adding a toggle means adding one entry here; there
18
18
  * is no second place to remember.
19
19
  */
20
+ import { DEPRECATED_CONFIG_SURFACES } from "./config-diagnostic-codes.js";
20
21
  export const LENS_FLAGS = [
21
22
  {
22
23
  name: "no-lens",
@@ -217,23 +218,75 @@ export const GLOBAL_NON_FLAG_CONFIG_SECTIONS = [
217
218
  "widget",
218
219
  "$schema",
219
220
  ];
221
+ /**
222
+ * Recognized TOP-LEVEL sections of a project `.pi-lens.json` that are NOT
223
+ * derived from the flag registry — the project loader's own hand-parsed
224
+ * sections (`ignore`, `rules`, `maxProjectFiles`, `reviewGraph`) plus the two
225
+ * namespaces another pi-lens runner reads off `PiLensProjectConfig.raw`
226
+ * (`trivy` in `trivy-client.ts`, `helm.renderValidation.enabled` in the
227
+ * helm-render runner, #1283).
228
+ *
229
+ * Moved here from `project-lens-config.ts` by #2426 so the canonical schema in
230
+ * `config-schema.ts` derives its namespace list from ONE place instead of
231
+ * importing the project loader (which would close an import cycle, since that
232
+ * loader now resolves through the schema).
233
+ */
234
+ export const PROJECT_NON_FLAG_CONFIG_SECTIONS = [
235
+ "ignore",
236
+ "rules",
237
+ "maxProjectFiles",
238
+ "reviewGraph",
239
+ "trivy",
240
+ "helm",
241
+ ];
242
+ /**
243
+ * The four LSP settings a PROJECT config file may carry — at its root during
244
+ * the deprecation window, and under `lsp` canonically. DERIVED from the
245
+ * registry's `kind: "key"` rows so the accepted set and the removal schedule
246
+ * cannot drift apart, and named once because two places below need it.
247
+ */
248
+ const LSP_PROJECT_HONORED_KEYS = DEPRECATED_CONFIG_SURFACES.filter((row) => row.kind === "key").map((row) => row.surface);
220
249
  /**
221
250
  * Foreign top-level namespaces that legitimately appear in a SHARED
222
251
  * `.pi-lens.json` but belong to a DIFFERENT loader: the LSP config loader
223
- * (`clients/lsp/config.ts`) reads `servers` / `serverOverrides` /
224
- * `disabledServers` / `warmFiles` out of the very same file. The project-lens
252
+ * (`clients/lsp/config.ts`) reads the `lsp` namespace — and, for its
253
+ * deprecation window, the legacy ROOT keys `servers` / `serverOverrides` /
254
+ * `disabledServers` / `warmFiles` — out of the very same file. The project-lens
225
255
  * loader must TOLERATE these (never warn "unknown key") — they are not typos,
226
256
  * just another subsystem's namespace. `$schema` is allowed for editor
227
257
  * JSON-schema association. Declared once, here, for the same #883 reason as
228
258
  * {@link GLOBAL_NON_FLAG_CONFIG_SECTIONS}.
259
+ *
260
+ * `lsp` joined this list in #2426: it is the canonical home of the four legacy
261
+ * keys beside it, so a project config that has already been migrated must not
262
+ * be told its `lsp` section is a global-only setting. Its tolerance is
263
+ * SUB-KEY-SCOPED — see {@link PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS}.
229
264
  */
230
265
  export const PROJECT_FOREIGN_CONFIG_NAMESPACES = [
231
- "servers",
232
- "serverOverrides",
233
- "disabledServers",
234
- "warmFiles",
266
+ "lsp",
267
+ // DERIVED, not restated: the four legacy root keys are exactly the
268
+ // `kind: "key"` rows of the deprecation registry, and a hand-copied second
269
+ // list of them would be a place for the accepted set and the removal
270
+ // schedule to drift apart. `config-diagnostic-codes.ts` is a dependency-free
271
+ // data leaf, so importing it keeps this module's own no-cycle property.
272
+ ...LSP_PROJECT_HONORED_KEYS,
235
273
  "$schema",
236
274
  ];
275
+ /**
276
+ * For a foreign namespace whose PROJECT-honored surface is only PART of what
277
+ * the namespace can hold: which sub-keys a `.pi-lens.json` may set.
278
+ *
279
+ * `lsp` is tolerated at the top level because the LSP loader reads it out of
280
+ * this same file — but only four of its keys are project-scoped. `lsp.enabled`
281
+ * is a `scope: "global"` flag (`--no-lsp`), so blanket namespace tolerance made
282
+ * a project-file `lsp.enabled: false` do nothing with no signal at all, where
283
+ * before #2426 the user got the honest "global-only setting" notice
284
+ * (#2426 review round 2, F3). `docs/configuration.md` promises that nothing
285
+ * about a config is ignored silently, so the namespace is tolerated KEY BY KEY
286
+ * and anything else under it is scanned by the same global-only-vs-typo rules a
287
+ * top-level key gets.
288
+ */
289
+ export const PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS = new Map([["lsp", LSP_PROJECT_HONORED_KEYS]]);
237
290
  /**
238
291
  * The distinct TOP-LEVEL section keys a set of flags lives under (the first
239
292
  * dotted segment of each `configKey`). Both config loaders derive their
@@ -22,10 +22,10 @@
22
22
  */
23
23
  import * as fs from "node:fs";
24
24
  import * as path from "node:path";
25
- import { getGlobalPiLensDir } from "./file-utils.js";
25
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
26
26
  import { getRegisteredLogFiles } from "./ndjson-logger.js";
27
27
  import { pathsEqual } from "./path-utils.js";
28
- const LOG_DIR = getGlobalPiLensDir();
28
+ const LOG_DIR = getGlobalPiLensLogDir();
29
29
  const LOGS_SUBDIR = path.join(LOG_DIR, "logs");
30
30
  /**
31
31
  * Every global `.log` pi-lens writes under ~/.pi-lens, resolved dynamically so
@@ -34,7 +34,9 @@
34
34
  * `PromiseDescriptor.budgetMs`.
35
35
  */
36
36
  export async function raceToCompletion(promises, shouldComplete, options = { timeoutMs: 1500 }) {
37
- const results = new Array(promises.length).fill(undefined);
37
+ const results = [];
38
+ results.length = promises.length;
39
+ results.fill(undefined);
38
40
  let graceTimer;
39
41
  let auxGraceTimer;
40
42
  let completed = false;