@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
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The shared re-check cadence for a cached "nothing here" verdict — the
3
+ * bound both `file-utils.ts`'s nested `.gitignore`/`.pi-lens.json` matcher
4
+ * (#2159/#2071) and `project-lens-config.ts`'s no-config discovery cache
5
+ * (#2483 round 2) use so a negative result is neither re-statted on every
6
+ * call nor cached for the life of the process.
7
+ *
8
+ * A declared leaf module, the same shape as `ledger-bounds.ts` /
9
+ * `spawn-output-cap.ts`: it imports nothing from `clients/`, because
10
+ * `file-utils.ts` already imports `project-lens-config.ts`, and the
11
+ * `clients/` acyclic-imports rule (`no-client-cycles`,
12
+ * `.dependency-cruiser.cjs`) would reject either of those two files
13
+ * importing a constant that lived inside the other. A shared value neither
14
+ * owns is the only way both stay on the same cadence without one importing
15
+ * the other.
16
+ */
17
+ export const FRESHNESS_CADENCE_MS = 2_000;
@@ -167,18 +167,95 @@ function hasGeneratedArtifactContent(content) {
167
167
  const header = content.slice(0, DEFAULT_HEADER_BYTES);
168
168
  return GENERATED_HEADER_PATTERNS.some((pattern) => pattern.test(header));
169
169
  }
170
- // Memoize the generated-banner verdict per (path, mtimeMs, size, maxBytes).
171
- // The 4 KB header read (openSync + readSync + closeSync) is ~70% of the
172
- // per-file cost in `collectSourceFiles`, and the same files are re-scanned by
173
- // multiple background scanners (todo, project-diagnostics, language-profile)
170
+ /**
171
+ * Content-shape classifier for machine-emitted text (refs #2346).
172
+ *
173
+ * A saved/scraped page, a minified bundle, or an embedded data blob is one
174
+ * giant line or a handful, so mean non-empty line length on already-loaded
175
+ * content is a language-agnostic generated signature. Extensions covered by
176
+ * name patterns can never cover it (`.html` has no `.min.`-style convention),
177
+ * which is the exact gap #2346 hit: a 269 KB scraped page on 38 lines (mean
178
+ * ~7084/line) drew the full auxiliary scanner stack.
179
+ *
180
+ * The threshold is derived from measurement ON THE SHIPPED STATISTIC (the
181
+ * 4096-byte header/prefix mean, not full-content means — the two differ by
182
+ * 3-7x on long-paragraph prose). Measured 2026-08-28 across all 3,331 text
183
+ * files in this repo: the maximum hand-authored prefix mean is 453.2
184
+ * (`docs/analysisall.md`, unwrapped prose paragraphs); the widest
185
+ * hand-authored one-line shape observed anywhere probed is a ~2048-char
186
+ * barrel re-export. The threshold of 2500 sits ~5.5x above the repo's worst
187
+ * prose file and above every observed hand-authored shape, while the #2346
188
+ * evidence file (269 KB / 38 lines) remains far above it. Direction of
189
+ * error is deliberate per #1107: a borderline machine-emitted file that
190
+ * slips through (false KEEP) costs noise once; a hand-authored file
191
+ * silently classified generated (false DROP) vanishes from scans with no
192
+ * signal. Both constants are exported so the tuning claim is checkable and
193
+ * the regression tests pin the margin probes on both sides.
194
+ *
195
+ * Prefix-path quantization: on the 4096-byte header/prefix paths the
196
+ * achievable means are quantized (~4096 for a one-line prefix, ~2048 for
197
+ * two lines), so any threshold in (2048, 4096] behaves as "is the first
198
+ * line longer than 4096 bytes". A machine-emitted file with 2500-4000-char
199
+ * lines is missed on the prefix path even though its full content would
200
+ * classify — the deliberate false-KEEP direction, not a continuous 5.5x
201
+ * margin there. The full-content path (dispatch) has no such quantization.
202
+ *
203
+ * Degenerate cases are guarded: content below the minimum byte length never
204
+ * classifies generated on line shape alone (a 500-byte single-line config
205
+ * file is not machine-emitted evidence), and an empty or whitespace-only
206
+ * file yields a zero mean and stays source.
207
+ */
208
+ const MACHINE_EMITTED_LINE_SHAPE_MIN_CONTENT = 2048;
209
+ export const MACHINE_EMITTED_LINE_SHAPE_MEAN_THRESHOLD = 2500;
210
+ export function classifyMachineEmittedLineShape(content) {
211
+ const contentLength = content.length;
212
+ if (contentLength < MACHINE_EMITTED_LINE_SHAPE_MIN_CONTENT) {
213
+ return { generated: false, meanLineLength: 0 };
214
+ }
215
+ // Single O(contentLength) pass. Deliberately no `split(/\r?\n/)`: a
216
+ // minified blob's single line can be megabytes, and splitting allocates a
217
+ // copy of the whole content. `\r` contributes nothing to line length so a
218
+ // CRLF line is measured by its display width.
219
+ let nonEmptyLineTotal = 0;
220
+ let nonEmptyLineCount = 0;
221
+ let currentLineLength = 0;
222
+ for (let i = 0; i < contentLength; i++) {
223
+ const code = content.charCodeAt(i);
224
+ if (code === 10 /* \n */) {
225
+ if (currentLineLength > 0) {
226
+ nonEmptyLineTotal += currentLineLength;
227
+ nonEmptyLineCount += 1;
228
+ }
229
+ currentLineLength = 0;
230
+ }
231
+ else if (code !== 13 /* \r */) {
232
+ currentLineLength += 1;
233
+ }
234
+ }
235
+ if (currentLineLength > 0) {
236
+ nonEmptyLineTotal += currentLineLength;
237
+ nonEmptyLineCount += 1;
238
+ }
239
+ if (nonEmptyLineCount === 0)
240
+ return { generated: false, meanLineLength: 0 };
241
+ const meanLineLength = nonEmptyLineTotal / nonEmptyLineCount;
242
+ return {
243
+ generated: meanLineLength > MACHINE_EMITTED_LINE_SHAPE_MEAN_THRESHOLD,
244
+ meanLineLength,
245
+ };
246
+ }
247
+ // Memoize the header analysis (banner + line shape) per (path, mtimeMs, size,
248
+ // maxBytes). The 4 KB header read (openSync + readSync + closeSync) is ~70% of
249
+ // the per-file cost in `collectSourceFiles`, and the same files are re-scanned
250
+ // by multiple background scanners (todo, project-diagnostics, language-profile)
174
251
  // every session/turn. Keying on mtime+size makes the memo self-invalidating:
175
252
  // an edited file misses the cache and is re-read. A single stat replaces the
176
253
  // open/read/close on a hit. Bounded to avoid unbounded growth on giant repos.
177
254
  const HEADER_VERDICT_MEMO_CAP = 50_000;
178
- const headerVerdictMemo = new BoundedLruCache(HEADER_VERDICT_MEMO_CAP);
179
- /** Test-only: drop the header-verdict memo so fixtures don't leak across cases. */
255
+ const headerAnalysisMemo = new BoundedLruCache(HEADER_VERDICT_MEMO_CAP);
256
+ /** Test-only: drop the header-analysis memo so fixtures don't leak across cases. */
180
257
  export function _resetGeneratedArtifactCaches() {
181
- headerVerdictMemo.clear();
258
+ headerAnalysisMemo.clear();
182
259
  }
183
260
  function readFileHeader(filePath, maxBytes = DEFAULT_HEADER_BYTES) {
184
261
  let fd;
@@ -202,27 +279,29 @@ function readFileHeader(filePath, maxBytes = DEFAULT_HEADER_BYTES) {
202
279
  }
203
280
  }
204
281
  }
205
- /**
206
- * Memoized form of "does this file's header contain a generated-code banner?".
207
- * Returns `false` when the file cannot be stat'd/read (same fallback as the
208
- * inline read path). Keyed on path + mtime + size so edits invalidate it.
209
- */
210
- function fileHeaderLooksGenerated(filePath, maxBytes) {
282
+ function analyzeFileHeader(filePath, maxBytes) {
211
283
  let key;
212
284
  try {
213
285
  const st = fs.statSync(filePath);
214
286
  key = `${st.mtimeMs}:${st.size}:${maxBytes}:${filePath}`;
215
287
  }
216
288
  catch {
217
- return false;
289
+ return undefined;
218
290
  }
219
- const cached = headerVerdictMemo.get(key);
291
+ const cached = headerAnalysisMemo.get(key);
220
292
  if (cached !== undefined)
221
293
  return cached;
222
294
  const header = readFileHeader(filePath, maxBytes);
223
- const verdict = header !== undefined && hasGeneratedArtifactContent(header);
224
- headerVerdictMemo.set(key, verdict);
225
- return verdict;
295
+ // A header that cannot be read classifies as clean (no banner, no shape
296
+ // signal) and is memoized as such, mirroring the pre-#2346 fallback.
297
+ const analysis = header === undefined
298
+ ? { banner: false, shape: { generated: false, meanLineLength: 0 } }
299
+ : {
300
+ banner: hasGeneratedArtifactContent(header),
301
+ shape: classifyMachineEmittedLineShape(header),
302
+ };
303
+ headerAnalysisMemo.set(key, analysis);
304
+ return analysis;
226
305
  }
227
306
  /**
228
307
  * The full verdict (+ evidence tier) behind {@link isGeneratedOrArtifact}'s
@@ -246,8 +325,7 @@ function fileHeaderLooksGenerated(filePath, maxBytes) {
246
325
  * function and short-circuit on a hit, so by the time this function runs for
247
326
  * those callers evidence (a) has already come back negative. Non-walk
248
327
  * callers with no upstream sibling probe (`file-role.ts`'s content-based
249
- * check, `project-scan-policy.ts`'s single-path `shouldSkipProjectPath`)
250
- * evaluate evidence (b) only — a documented, narrower guarantee than the
328
+ * check) evaluate evidence (b) only — a documented, narrower guarantee than the
251
329
  * walk path's.
252
330
  *
253
331
  * Tradeoff (documented per the issue): when NEITHER piece of evidence
@@ -272,12 +350,28 @@ export function classifyGeneratedOrArtifactDetailed(filePath, options = {}) {
272
350
  if (hasGeneratedArtifactContent(options.content)) {
273
351
  return { verdict: "generated", evidence: "content" };
274
352
  }
353
+ const shape = classifyMachineEmittedLineShape(options.content);
354
+ if (shape.generated) {
355
+ return {
356
+ verdict: "generated",
357
+ evidence: "line-shape",
358
+ lineShapeMean: shape.meanLineLength,
359
+ };
360
+ }
275
361
  return { verdict: weakNameMatch ? "override" : "clean" };
276
362
  }
277
363
  if (options.readContentHeader) {
278
- if (fileHeaderLooksGenerated(filePath, options.maxHeaderBytes ?? DEFAULT_HEADER_BYTES)) {
364
+ const analysis = analyzeFileHeader(filePath, options.maxHeaderBytes ?? DEFAULT_HEADER_BYTES);
365
+ if (analysis?.banner) {
279
366
  return { verdict: "generated", evidence: "content" };
280
367
  }
368
+ if (analysis?.shape.generated) {
369
+ return {
370
+ verdict: "generated",
371
+ evidence: "line-shape",
372
+ lineShapeMean: analysis.shape.meanLineLength,
373
+ };
374
+ }
281
375
  return { verdict: weakNameMatch ? "override" : "clean" };
282
376
  }
283
377
  // No content/header probe was supplied or enabled: there is no cheap way
@@ -292,7 +386,7 @@ export function classifyGeneratedOrArtifactDetailed(filePath, options = {}) {
292
386
  : { verdict: "clean" };
293
387
  }
294
388
  /** Verdict-only convenience wrapper over {@link classifyGeneratedOrArtifactDetailed}. */
295
- export function classifyGeneratedOrArtifact(filePath, options = {}) {
389
+ function classifyGeneratedOrArtifact(filePath, options = {}) {
296
390
  return classifyGeneratedOrArtifactDetailed(filePath, options).verdict;
297
391
  }
298
392
  export function isGeneratedOrArtifact(filePath, options = {}) {
@@ -50,6 +50,7 @@
50
50
  * `GenerationMapOptions.normalizeKey` for the correctness reason the slow
51
51
  * path exists at all.
52
52
  */
53
+ import { BoundedFifoMap } from "./bounded-cache.js";
53
54
  import { incrementDegradationCount } from "./degradation-ledger.js";
54
55
  /**
55
56
  * Names of every generation-carrying store created through this module.
@@ -136,7 +137,7 @@ export function createGenerationMap(name, options = {}) {
136
137
  // Insertion-ordered: the oldest key is evicted first. Re-stamping a key
137
138
  // refreshes its position so a hot cwd is not evicted by a burst of
138
139
  // one-shot ones.
139
- const stamps = new Map();
140
+ const stamps = new BoundedFifoMap(maxKeys);
140
141
  // ONE ticket counter for the whole map. See GenerationMap's doc comment:
141
142
  // per-key counters starting at 0 make eviction and forget() fail OPEN.
142
143
  // 0 is reserved for "this key holds no stamp" and is never issued.
@@ -149,14 +150,8 @@ export function createGenerationMap(name, options = {}) {
149
150
  function issue(key) {
150
151
  nextTicket += 1;
151
152
  stamps.delete(key);
152
- stamps.set(key, nextTicket);
153
- while (stamps.size > maxKeys) {
154
- const oldest = stamps.keys().next().value;
155
- if (oldest === undefined)
156
- break;
157
- stamps.delete(oldest);
158
- invalidations += 1;
159
- }
153
+ const evicted = stamps.set(key, nextTicket);
154
+ invalidations += evicted.length;
160
155
  return nextTicket;
161
156
  }
162
157
  return {
@@ -141,6 +141,11 @@ function canonicalizeGuardCommand(command) {
141
141
  let pendingSpace = false;
142
142
  quote = undefined;
143
143
  for (const ch of result) {
144
+ if (!quote && ch === "\n") {
145
+ collapsed += ch;
146
+ pendingSpace = false;
147
+ continue;
148
+ }
144
149
  if (!quote && /\s/.test(ch)) {
145
150
  pendingSpace = collapsed.length > 0;
146
151
  continue;
@@ -566,7 +571,7 @@ function markCacheUnknown(runtime, reason) {
566
571
  /** Persist a complete, bounded, content-bound guard record. */
567
572
  export function writeGitGuardRecord(cacheManager, runtime, cwd, record) {
568
573
  const capped = capAffectedFiles(Array.isArray(record.affectedFiles) ? record.affectedFiles : [], cwd);
569
- const fileSeqByPath = { ...(record.fileSeqByPath ?? {}) };
574
+ const fileSeqByPath = { ...record.fileSeqByPath };
570
575
  for (const file of capped.files) {
571
576
  const key = guardPathKey(file, cwd);
572
577
  if (fileSeqByPath[key] === undefined) {
@@ -62,4 +62,43 @@ export class GoClient {
62
62
  isGoFile(filePath) {
63
63
  return path.extname(filePath).toLowerCase() === ".go";
64
64
  }
65
+ /** Forget the memoized go path and latched availability verdict — #2455. */
66
+ resetAvailability() {
67
+ this.availability.reset();
68
+ }
69
+ }
70
+ /**
71
+ * The one `GoClient` this process owns (#2455 fix round 4, F2).
72
+ *
73
+ * `GoClient` is not stateless: it wraps a `createToolchainAvailability` latch
74
+ * whose probe-class "missing" verdict never expires (`isLatchingOutcome`), and
75
+ * `resetGoAvailability` below re-arms exactly ONE such latch. Round 2 shipped
76
+ * with two instances — `dispatch/runners/go-vet.ts` built one for the runner
77
+ * and `bootstrap.ts` built another for `BootstrapClients.goClient`, which is
78
+ * the object `handleSessionStart` reads for its "Active tools" line. The
79
+ * session reset cleared the runner's copy and left the session-start copy
80
+ * latched, so a go toolchain installed between sessions stayed invisible on
81
+ * the surface a user actually sees. One instance, in the module that owns the
82
+ * class, is what makes the reset total; `tests/clients/toolchain-client-singleton.test.ts`
83
+ * holds the line.
84
+ */
85
+ export const goClient = new GoClient();
86
+ /**
87
+ * Forget `goClient`'s memoized go path and latched availability verdict —
88
+ * #2455. Same #1496/#1535 shape as `resetZizmorTokenAvailability`; wired into
89
+ * `handleSessionStart` beside it (`clients/runtime-session.ts`) so a go
90
+ * toolchain installed mid-process is observed by the next session instead of
91
+ * staying "missing" for the rest of the process's life.
92
+ *
93
+ * This module — not `go-vet.ts` — because the reset must sit beside the single
94
+ * instance it clears (#2455 fix round 4, F2). It was invisible to the
95
+ * session-state sweep before round 2, but not for the reason an earlier draft
96
+ * of this comment gave (#2455 fix round 3, F4): the sweep skips any file
97
+ * exporting no reset at all, so widening the container predicate to "any class
98
+ * declared in `clients/`" could not have surfaced it. Adding this reset is
99
+ * what made the file visible — the fix preceded the detection, and the
100
+ * pair-with-reset blind spot (MISS 3 in `SWEEP_HEURISTIC_LIMITS`) is unchanged.
101
+ */
102
+ export function resetGoAvailability() {
103
+ goClient.resetAvailability();
65
104
  }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * ktfmt Gradle-plugin style carriage (#2468).
3
+ *
4
+ * The `com.ncorti.ktfmt.gradle` plugin's `ktfmt { }` extension block lets a
5
+ * project select `googleStyle()` or `kotlinLangStyle()` (verified against
6
+ * `KtfmtExtension.kt` in `cortinico/ktfmt-gradle` at
7
+ * `23bdedc8d5d641731a0cf128f1a386d5a127ce4e` — those two are the only style
8
+ * functions it defines today, and both are plain setters over
9
+ * `blockIndent`/`continuationIndent`/`trailingCommaManagementStrategy`, so a
10
+ * body calling both is LAST-CALL-WINS, not first).
11
+ *
12
+ * None of this is read by ktfmt's own CLI from `build.gradle` — verified
13
+ * against `ParsedArgs.kt` in `facebook/ktfmt` (now mirrored at
14
+ * `Kotlin/ktfmt`) at tag v0.63, the exact version `clients/installer/
15
+ * index.ts` pins for pi-lens's managed ktfmt install. That CLI accepts
16
+ * `--google-style` / `--kotlinlang-style` (plus the default `--meta-style`)
17
+ * — so the Gradle-declared choice has to be translated to the matching CLI
18
+ * flag by something on our side; ktfmt itself never bridges `build.gradle`
19
+ * to argv.
20
+ *
21
+ * Reuses the exact lexical pre-pass (`stripGradleCommentsAndStrings` /
22
+ * `namedGradleBlockRanges`) that `clients/tool-policy.ts`'s
23
+ * `getSpotlessKotlinFormatter` already applies to Gradle files, instead of a
24
+ * second hand-rolled Gradle-block parser (#2468 AC) — same
25
+ * single-source-of-truth reuse `clients/cargo-manifest.ts` did for TOML
26
+ * parsing in `clients/formatters.ts`'s rustfmt `--edition` carriage (#2466).
27
+ *
28
+ * ## Project scoping (#2468 review rounds 2 and 3)
29
+ *
30
+ * A Gradle build file configures more than the directory it sits in, and the
31
+ * multi-module layout the plugin is actually used in puts the style in the
32
+ * ROOT build file while each module's own `build.gradle(.kts)` declares only
33
+ * its plugins. Round 1 stopped the ancestor climb at the first directory
34
+ * holding ANY gradle file and matched `ktfmt {` anywhere in it, which was
35
+ * wrong in both directions: a module inherited nothing (the #2468 defect
36
+ * survived for the common layout) while the ROOT's own sources were handed a
37
+ * `subprojects { }`-scoped style that Gradle never applies to them. This
38
+ * module therefore classifies each `ktfmt { }` block by the block ENCLOSING
39
+ * it — the actual brace nesting on the stripped source, not a single
40
+ * hard-coded name test — and climbs past directories that declare no style
41
+ * for the project being formatted:
42
+ *
43
+ * - no enclosing block (a top-level `ktfmt { }`) → the DECLARING directory
44
+ * only. Gradle does not inherit here: the plugin gives each project its own
45
+ * `KtfmtExtension` instance seeded with the plugin conventions (verified at
46
+ * the SHA above), so a module that applies the plugin and calls no style
47
+ * function really does format under ktfmt's default — which is exactly the
48
+ * bare invocation this resolver falls back to. Round 2 spread a top-level
49
+ * block over style-less descendants as a documented heuristic; that
50
+ * manufactured a NEW pi-lens/Gradle disagreement (pi-lens writes
51
+ * `--google-style`, `./gradlew ktfmtCheck` then rejects the file pi-lens
52
+ * just formatted) which the pre-#2468 bare invocation did not have, so it
53
+ * is gone. Aligning with `hasKtfmtConfig`'s wider climb does not justify
54
+ * it: that election answers "is ktfmt the formatter for this file", not
55
+ * "which style"; where Gradle carries no style down, the bare invocation IS
56
+ * the correct carriage of `--meta-style`.
57
+ * - `subprojects { ktfmt { … } }` → DESCENDANT directories only, never the
58
+ * declaring one. `subprojects` configures the children and nothing else.
59
+ * - `allprojects { ktfmt { … } }` → the declaring directory AND descendants.
60
+ * - anything else enclosing it — `configure(subprojects.filter { … }) { }`,
61
+ * `project(":app") { }`, `tasks.register(…) { }`, a convention-plugin
62
+ * wrapper, or more than one nested block — reaches NEITHER scope. pi-lens
63
+ * cannot evaluate a build script, so a scope it cannot compute fails closed
64
+ * to the bare invocation rather than being granted to every project.
65
+ *
66
+ * KNOWN GAP (#2468 review round 3, F3): the first gradle directory found by
67
+ * the climb is treated as the project that OWNS the file. A module directory
68
+ * that is `include(…)`d by `settings.gradle(.kts)` but holds NO build file of
69
+ * its own therefore resolves hop 0 to an ANCESTOR, and asks it for `own`
70
+ * scope when `descendants` is what Gradle would apply. Against a root
71
+ * `subprojects { ktfmt { … } }` this is a fail-safe miss — no flag, the
72
+ * pre-#2468 bare invocation — because `subprojects` only ever fills the
73
+ * `descendants` slot, which hop 0 never reads. But against a root's
74
+ * TOP-LEVEL `ktfmt { }` (`own` scope, by the table above) it is a WRONG
75
+ * flag: hop 0 reads `own` regardless of which project it actually belongs
76
+ * to, so the module is handed the ROOT's own style even though Gradle gives
77
+ * the module its own `KtfmtExtension` and applies no style there. Closing it
78
+ * means reading `settings.gradle`'s `include(…)` list to map a directory to
79
+ * a Gradle project, which this module does not do.
80
+ */
81
+ import * as os from "node:os";
82
+ import * as path from "node:path";
83
+ import { readTextFileOrUndefined } from "./cargo-manifest.js";
84
+ import { findNearestMarkerRoot } from "./path-utils.js";
85
+ import { gradleBlockRanges, stripGradleCommentsAndStrings, } from "./tool-policy.js";
86
+ /** Nearest-first: a Kotlin script build file wins over its Groovy sibling. */
87
+ const KTFMT_GRADLE_ROOT_FILES = [
88
+ "build.gradle.kts",
89
+ "build.gradle",
90
+ "settings.gradle.kts",
91
+ "settings.gradle",
92
+ ];
93
+ /**
94
+ * Ancestor gradle directories consulted before giving up. Each hop costs at
95
+ * most four small reads, and Gradle builds nest a handful of levels at most;
96
+ * the cap keeps a pathological tree (or a symlink cycle that survives
97
+ * `path.dirname`) from turning one format into an unbounded walk. The
98
+ * `homeDir` ceiling inside `findNearestMarkerRoot` normally ends the climb
99
+ * long before this.
100
+ */
101
+ const MAX_GRADLE_ANCESTOR_HOPS = 16;
102
+ /** The CLI flags ktfmt v0.63 actually defines (`ParsedArgs.kt`, verified above). */
103
+ const KTFMT_STYLE_CLI_FLAG = {
104
+ google: "--google-style",
105
+ kotlinlang: "--kotlinlang-style",
106
+ };
107
+ /**
108
+ * Read the style declared by one `ktfmt { }` block body, LAST call wins.
109
+ *
110
+ * `googleStyle()` and `kotlinLangStyle()` both just `.set(...)` the same
111
+ * three extension properties, so in `ktfmt { googleStyle(); kotlinLangStyle() }`
112
+ * the second call is the one whose values survive into the format (verified
113
+ * against `KtfmtExtension.kt`, SHA above). `undefined` means the block
114
+ * declares no recognized style call — including the `dropboxStyle()` that
115
+ * ktfmt-gradle removed in 0.19.0 and ktfmt's CLI never exposed a flag for,
116
+ * which is simply not a style this resolver can carry.
117
+ */
118
+ function styleFromKtfmtBlockBody(body) {
119
+ let style;
120
+ let lastIndex = -1;
121
+ for (const [call, candidate] of [
122
+ [/\bgoogleStyle\s*\(\s*\)/g, "google"],
123
+ [/\bkotlinLangStyle\s*\(\s*\)/g, "kotlinlang"],
124
+ ]) {
125
+ for (const match of body.matchAll(call)) {
126
+ if (match.index > lastIndex) {
127
+ lastIndex = match.index;
128
+ style = candidate;
129
+ }
130
+ }
131
+ }
132
+ return style;
133
+ }
134
+ /**
135
+ * The Gradle blocks whose scope is exactly expressible here. Every other
136
+ * enclosing block — including one we simply have not heard of — is a scope
137
+ * pi-lens cannot evaluate, and is NOT silently promoted to project-wide.
138
+ */
139
+ const SCOPE_BY_ENCLOSING_BLOCK = {
140
+ subprojects: "descendants",
141
+ allprojects: "both",
142
+ };
143
+ /**
144
+ * Classify one `ktfmt { }` block by the block that encloses it.
145
+ *
146
+ * `blocks` is every brace pair in the stripped source, so "enclosing" is
147
+ * literal containment, not a guess from a name test: `range` strictly inside
148
+ * `outer` means `outer.start < range.start` (an enclosing body always opens
149
+ * before the body it contains) and `outer.end >= range.end`.
150
+ *
151
+ * More than one enclosing block (`subprojects { afterEvaluate { ktfmt { } } }`)
152
+ * fails closed too. Composing scopes through an arbitrary intermediate is
153
+ * exactly the kind of build-script evaluation this lexical pass cannot do,
154
+ * and the cost of the conservative answer is the pre-#2468 bare invocation.
155
+ */
156
+ function scopeOfKtfmtBlock(range, blocks) {
157
+ const enclosing = blocks.filter((outer) => outer.start < range.start && outer.end >= range.end);
158
+ if (enclosing.length === 0)
159
+ return "own";
160
+ if (enclosing.length > 1)
161
+ return undefined;
162
+ return SCOPE_BY_ENCLOSING_BLOCK[enclosing[0].name];
163
+ }
164
+ function stylesFromGradleContent(content) {
165
+ const stripped = stripGradleCommentsAndStrings(content);
166
+ const blocks = gradleBlockRanges(stripped);
167
+ const styles = {};
168
+ // Source order, so a later block overwrites an earlier one for the scope
169
+ // it reaches — the same last-call-wins rule that applies inside one body.
170
+ // Each scope is written independently: two blocks that reach DISJOINT sets
171
+ // of projects (a `subprojects { }` one and a top-level one) must not fight
172
+ // over a single slot, or the source order of unrelated scopes decides
173
+ // which project gets the wrong flag.
174
+ for (const range of blocks) {
175
+ if (range.name !== "ktfmt")
176
+ continue;
177
+ const scope = scopeOfKtfmtBlock(range, blocks);
178
+ if (!scope)
179
+ continue;
180
+ const style = styleFromKtfmtBlockBody(stripped.slice(range.start, range.end));
181
+ if (!style)
182
+ continue;
183
+ if (scope !== "descendants")
184
+ styles.own = style;
185
+ if (scope !== "own")
186
+ styles.descendants = style;
187
+ }
188
+ return styles;
189
+ }
190
+ async function stylesForGradleDir(gradleDir) {
191
+ for (const gradleFile of KTFMT_GRADLE_ROOT_FILES) {
192
+ const content = await readTextFileOrUndefined(path.join(gradleDir, gradleFile));
193
+ if (content === undefined)
194
+ continue;
195
+ const styles = stylesFromGradleContent(content);
196
+ if (styles.own || styles.descendants)
197
+ return styles;
198
+ }
199
+ return {};
200
+ }
201
+ /**
202
+ * Resolve the ktfmt CLI style flag (`--google-style` / `--kotlinlang-style`)
203
+ * for the Gradle project that owns `filePath`, so a caller that needs it
204
+ * doesn't have ktfmt silently apply its own default style
205
+ * (`--meta-style`-equivalent) where the project's `ktfmt { }` block picked a
206
+ * different one (#2468 — the same manifest-detected-but-not-carried defect
207
+ * shape #2466 fixed for rustfmt `--edition`).
208
+ *
209
+ * - Finds the nearest directory containing a `build.gradle(.kts)`/
210
+ * `settings.gradle(.kts)` file via the shared `findNearestMarkerRoot`
211
+ * walker (home-ceiling guarded, depth-capped — AGENTS.md
212
+ * walk-confinement; never a private walk-up loop). That first directory is
213
+ * TAKEN to be the project that OWNS the file, so its `own`-scope
214
+ * declaration applies; every further hop is an ancestor, so only its
215
+ * `descendants`-scope declaration does. See the module header's KNOWN GAP
216
+ * note for the `include(…)`-only module directory where that
217
+ * identification is wrong — a missed flag against a `subprojects { }` root,
218
+ * but a WRONG flag (the root's own style) against a root TOP-LEVEL one.
219
+ * - A nested module's OWN style declaration still wins over an ancestor's —
220
+ * the climb only continues past a gradle directory that declares NO style
221
+ * applying to this file, so nearest-wins is unchanged where a nearer
222
+ * declaration exists.
223
+ * - Returns `undefined` on any miss (no gradle file found, no `ktfmt { }`
224
+ * block, or no recognized style call in one): callers fall back to their
225
+ * pre-existing default argv rather than guessing. `dropboxStyle()` — the
226
+ * call ktfmt-gradle removed in 0.19.0, never a ktfmt CLI flag — is just
227
+ * such a miss and never becomes a guessed flag.
228
+ *
229
+ * `homeDir` defaults to `os.homedir()` and exists as a parameter so tests can
230
+ * inject a nearer ceiling and prove the guard actually stops a climb (mirrors
231
+ * `resolveCargoPackageEdition`'s `homeDir` parameter, #2466 review round 2,
232
+ * F5) — production callers never pass it.
233
+ */
234
+ export async function resolveKtfmtGradleStyle(filePath, homeDir = os.homedir()) {
235
+ let searchDir = path.dirname(path.resolve(filePath));
236
+ for (let hop = 0; hop < MAX_GRADLE_ANCESTOR_HOPS; hop += 1) {
237
+ const gradleDir = findNearestMarkerRoot(searchDir, KTFMT_GRADLE_ROOT_FILES, {
238
+ homeDir,
239
+ });
240
+ if (!gradleDir)
241
+ return undefined;
242
+ const styles = await stylesForGradleDir(gradleDir);
243
+ const style = hop === 0 ? styles.own : styles.descendants;
244
+ if (style)
245
+ return KTFMT_STYLE_CLI_FLAG[style];
246
+ const parent = path.dirname(gradleDir);
247
+ if (parent === gradleDir)
248
+ return undefined;
249
+ searchDir = parent;
250
+ }
251
+ return undefined;
252
+ }
@@ -18,7 +18,7 @@ import { getPackageRoot } from "./package-root.js";
18
18
  /** tree-sitter-wasms release the grammars are pulled from. */
19
19
  export const TREE_SITTER_WASMS_VERSION = "0.1.13";
20
20
  /** unpkg mirror of the tree-sitter-wasms artifacts. */
21
- export const GRAMMAR_CDN_BASE = `https://unpkg.com/tree-sitter-wasms@${TREE_SITTER_WASMS_VERSION}/out`;
21
+ const GRAMMAR_CDN_BASE = `https://unpkg.com/tree-sitter-wasms@${TREE_SITTER_WASMS_VERSION}/out`;
22
22
  export const GRAMMAR_SOURCE_OVERRIDES = {
23
23
  "tree-sitter-lua.wasm": {
24
24
  package: "@tree-sitter-grammars/tree-sitter-lua",
@@ -145,7 +145,7 @@ export const BLOCKED_GRAMMARS = {
145
145
  },
146
146
  };
147
147
  /** Runtime signals for the currently-running process. */
148
- export function currentGrammarRuntime() {
148
+ function currentGrammarRuntime() {
149
149
  const m = /^v?(\d+)/.exec(process.versions?.node ?? "");
150
150
  return {
151
151
  nodeMajor: m ? Number(m[1]) : 0,
@@ -173,7 +173,7 @@ export function grammarBlockReason(filename, rt = currentGrammarRuntime()) {
173
173
  * both the download path (validate before writing) and the on-disk path
174
174
  * (`fileHasWasmMagic`, for a file poisoned before this shipped) key off it.
175
175
  */
176
- export const WASM_MAGIC = [0x00, 0x61, 0x73, 0x6d];
176
+ const WASM_MAGIC = [0x00, 0x61, 0x73, 0x6d];
177
177
  /** Do `bytes` start with the wasm magic number? */
178
178
  export function hasWasmMagic(bytes) {
179
179
  if (bytes.length < WASM_MAGIC.length)