@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
@@ -127,10 +127,6 @@ export function _exitFlushersForTest() {
127
127
  export function getRegisteredLogFiles() {
128
128
  return registeredLogFiles;
129
129
  }
130
- /** Test-only reset — each test file gets a clean registry (see ndjson-logger.test.ts). */
131
- export function _resetRegisteredLogFilesForTest() {
132
- registeredLogFiles.clear();
133
- }
134
130
  /**
135
131
  * Snapshot of unrecovered write losses, one entry per sink that has any
136
132
  * (#1970). Pure in-memory read — no I/O, so a caller (`degradation-ledger.ts`)
@@ -160,6 +156,41 @@ export function resetSinkWriteFailures() {
160
156
  for (const state of ndjsonGlobalState.writers.values())
161
157
  state.writeFailures = 0;
162
158
  }
159
+ /**
160
+ * Snapshot of mid-session rotations, one entry per sink that has rotated
161
+ * at least once (#2505). Pure in-memory read — no I/O — so a caller
162
+ * (`degradation-ledger.ts`) can fold this into a durable ledger entry the
163
+ * same way it folds `getSinkWriteFailures()`, without this module importing
164
+ * the ledger back (see `NdjsonWriterState.rotationCount`'s doc comment).
165
+ */
166
+ export function getSinkRotations() {
167
+ if (!ndjsonGlobalState)
168
+ return [];
169
+ const result = [];
170
+ for (const state of ndjsonGlobalState.writers.values()) {
171
+ if (state.rotationCount > 0 || state.rotationFailures > 0) {
172
+ result.push({
173
+ file: state.file,
174
+ rotationCount: state.rotationCount,
175
+ failureCount: state.rotationFailures,
176
+ });
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+ /**
182
+ * Session-boundary reset (catalog shape 17), same shape as
183
+ * `resetSinkWriteFailures`. Wired into `resetDegradationLedger()`.
184
+ */
185
+ export function resetSinkRotations() {
186
+ if (!ndjsonGlobalState)
187
+ return;
188
+ for (const state of ndjsonGlobalState.writers.values()) {
189
+ state.rotationCount = 0;
190
+ state.rotationFailures = 0;
191
+ state.rotationRetryAfterMs = 0;
192
+ }
193
+ }
163
194
  function requireCurrentGlobalState() {
164
195
  if (ndjsonGlobalState)
165
196
  return ndjsonGlobalState;
@@ -191,10 +222,14 @@ function assertCompatibleWriterOptions(existing, maxBytes, backupPath) {
191
222
  function writeQueueItemSync(state, item) {
192
223
  if (item.kind === "truncate") {
193
224
  fs.writeFileSync(state.file, "");
225
+ noteTruncated(state);
194
226
  }
195
227
  else {
196
- rotateIfNeeded(state);
197
- fs.appendFileSync(state.file, item.line);
228
+ // One line, so the batch cap has nothing to cap: the plan is only
229
+ // ever "rotate first if this line no longer fits".
230
+ const plan = planBoundedWrite(state, [item.line]);
231
+ fs.appendFileSync(state.file, plan.joined);
232
+ noteWriteSucceeded(state, plan.bytes);
198
233
  }
199
234
  }
200
235
  /**
@@ -269,6 +304,20 @@ function createWriterState(file, maxBytes, backupPath) {
269
304
  // A state adopted from a pre-#1970 module graph predates this field.
270
305
  if (typeof existing.writeFailures !== "number")
271
306
  existing.writeFailures = 0;
307
+ // A state adopted from a pre-#2505 module graph predates these fields.
308
+ // (`knownSize` itself needs no adoption shim: its type is
309
+ // `number | undefined`, so a value that is neither is unreachable —
310
+ // #2515 review S2.)
311
+ if (typeof existing.writesSinceStatSync !== "number")
312
+ existing.writesSinceStatSync = 0;
313
+ if (typeof existing.lastStatSyncMs !== "number")
314
+ existing.lastStatSyncMs = 0;
315
+ if (typeof existing.rotationCount !== "number")
316
+ existing.rotationCount = 0;
317
+ if (typeof existing.rotationFailures !== "number")
318
+ existing.rotationFailures = 0;
319
+ if (typeof existing.rotationRetryAfterMs !== "number")
320
+ existing.rotationRetryAfterMs = 0;
272
321
  return existing;
273
322
  }
274
323
  const state = {};
@@ -282,6 +331,12 @@ function createWriterState(file, maxBytes, backupPath) {
282
331
  state.ensuredDir = false;
283
332
  state.exitFlusher = () => flushStateSync(state);
284
333
  state.writeFailures = 0;
334
+ state.knownSize = undefined;
335
+ state.writesSinceStatSync = 0;
336
+ state.lastStatSyncMs = 0;
337
+ state.rotationCount = 0;
338
+ state.rotationFailures = 0;
339
+ state.rotationRetryAfterMs = 0;
285
340
  globalState.writers.set(file, state);
286
341
  registerWriter(state);
287
342
  return state;
@@ -305,32 +360,191 @@ async function ensureDirAsync(state) {
305
360
  // telemetry is best-effort
306
361
  }
307
362
  }
308
- function rotateIfNeeded(state) {
309
- if (state.maxBytes === undefined)
363
+ // Rotation-check cadence (#2505): the real `fs.statSync` reconciliation below
364
+ // is throttled to at most once per this many writes OR this many elapsed ms,
365
+ // whichever comes first — never once per individual write. Between
366
+ // reconciliations, `state.knownSize` (updated cheaply, no I/O, after every
367
+ // successful write) is trusted. A short window in both axes bounds how far a
368
+ // concurrent writer to the same global log (another pi-lens process sharing
369
+ // ~/.pi-lens) can drift `knownSize` from the real file before the next
370
+ // reconciliation self-heals it.
371
+ const ROTATION_STAT_RESYNC_WRITES = 25;
372
+ const ROTATION_STAT_RESYNC_MS = 2_000;
373
+ /** Reconcile `state.knownSize` with a real stat, gated by the cadence above. */
374
+ function syncKnownSizeIfDue(state, now) {
375
+ const due = state.knownSize === undefined ||
376
+ state.writesSinceStatSync >= ROTATION_STAT_RESYNC_WRITES ||
377
+ now - state.lastStatSyncMs >= ROTATION_STAT_RESYNC_MS;
378
+ if (!due)
310
379
  return;
380
+ state.writesSinceStatSync = 0;
381
+ state.lastStatSyncMs = now;
382
+ try {
383
+ state.knownSize = fs.statSync(state.file).size;
384
+ }
385
+ catch {
386
+ // No file yet (first write) or a transient stat error — treat as
387
+ // empty; the next successful write/rotate re-establishes the truth.
388
+ state.knownSize = 0;
389
+ }
390
+ }
391
+ /**
392
+ * A rotation attempt that FAILS (an unwritable backup path, or the Windows
393
+ * sharing violation another process holding the file open produces) must not
394
+ * be re-attempted by every subsequent write: that is a syscall storm that
395
+ * never succeeds. After a failure the next attempt is deferred by this many
396
+ * ms. The sink keeps writing during the window — dropping lines would be a
397
+ * worse trade than an oversized file — and the failure is reported through
398
+ * `getSinkRotations()` as `log-sink-rotate-failed`, so an unbounded log is
399
+ * visible in `pilens_health` instead of silent (#2505 review F2).
400
+ */
401
+ const ROTATION_FAILURE_BACKOFF_MS = 10_000;
402
+ /**
403
+ * Move the active file aside, and report whether that actually happened.
404
+ *
405
+ * The in-memory size check that gets us here is deliberately cheap and
406
+ * therefore fallible: another process writing the SAME global log (a pi
407
+ * session and the warm MCP server share one global read-guard.log) moves the
408
+ * real file under us, so a cached size can be stale in either direction.
409
+ * Believing a stale-HIGH size is destructive, not merely wasteful — it
410
+ * force-removes the other process backup and renames that process freshly
411
+ * rotated (tiny) file on top of it, losing real log data. So the decision is
412
+ * CONFIRMED against a real stat here, on the rare path where a rotation is
413
+ * believed due, instead of on the hot path where every write would pay for
414
+ * it (#2505 review F4).
415
+ */
416
+ function rotateNow(state, incomingBytes, now) {
417
+ if (state.maxBytes === undefined)
418
+ return false;
419
+ if (now < state.rotationRetryAfterMs)
420
+ return false;
421
+ let actualSize;
422
+ try {
423
+ actualSize = fs.statSync(state.file).size;
424
+ }
425
+ catch {
426
+ // No file yet — nothing to rotate, and the next write creates it.
427
+ state.knownSize = 0;
428
+ state.writesSinceStatSync = 0;
429
+ state.lastStatSyncMs = now;
430
+ return false;
431
+ }
432
+ state.knownSize = actualSize;
433
+ state.writesSinceStatSync = 0;
434
+ state.lastStatSyncMs = now;
435
+ // The real file disagrees with the cached belief: nothing to do. This is
436
+ // the cross-process case above, and declining here is exactly what
437
+ // preserves the other process backup.
438
+ if (actualSize === 0)
439
+ return false;
440
+ if (actualSize + incomingBytes < state.maxBytes)
441
+ return false;
442
+ const backup = state.backupPath ?? `${state.file}.1`;
311
443
  try {
312
- const size = fs.statSync(state.file).size;
313
- if (size < state.maxBytes)
314
- return;
315
- const backup = state.backupPath ?? `${state.file}.1`;
316
444
  runBestEffort(() => fs.rmSync(backup, { force: true }));
317
445
  fs.renameSync(state.file, backup);
318
446
  }
319
447
  catch {
320
- // no file yet, or rename raced — nothing to rotate
448
+ // The rename failed and the file is still there at `actualSize` (which
449
+ // `knownSize` now holds — no guessing, the stat above just confirmed
450
+ // it). Counted rather than swallowed so `log-sink-rotate-failed` can
451
+ // surface an unbounded sink, and backed off so the next write does not
452
+ // re-run the same failing syscall.
453
+ state.rotationFailures += 1;
454
+ state.rotationRetryAfterMs = now + ROTATION_FAILURE_BACKOFF_MS;
455
+ return false;
456
+ }
457
+ state.knownSize = 0;
458
+ state.rotationCount += 1;
459
+ state.rotationRetryAfterMs = 0;
460
+ return true;
461
+ }
462
+ function takeAll(lines) {
463
+ const joined = lines.join("");
464
+ return { count: lines.length, joined, bytes: Buffer.byteLength(joined) };
465
+ }
466
+ /**
467
+ * Decide what this append writes, rotating first when the file already on
468
+ * disk leaves no room. Two things have to hold, and the pre-#2505 write path
469
+ * did neither:
470
+ *
471
+ * 1. The bound is checked against the size this write PRODUCES, not only
472
+ * the size already on disk — otherwise the write that crosses the bound
473
+ * is always allowed through and only the NEXT one rotates (one write
474
+ * late), which on a long-lived or idle-tailed session (the warm MCP
475
+ * server never re-runs the session-start sweep in `log-cleanup.ts`) may
476
+ * be a long time or never.
477
+ * 2. The batch ITSELF is capped at the bound. Rotation only ever moves
478
+ * PRE-EXISTING content aside, so an oversized batch written whole into
479
+ * an empty (or just-rotated) file lands past the bound with nothing left
480
+ * to rotate away: a 500-line drain against a 1KB bound landed at ~107KB,
481
+ * 106x over, with zero rotations. Capping makes the drain loop come back
482
+ * around for the remainder instead.
483
+ *
484
+ * A single NDJSON line is atomic and cannot be split, so at least one line
485
+ * is always taken: the enforced invariant is "the bound plus at most one
486
+ * line", never "the bound plus an unbounded batch".
487
+ */
488
+ function planBoundedWrite(state, lines) {
489
+ if (state.maxBytes === undefined || lines.length === 0)
490
+ return takeAll(lines);
491
+ const now = Date.now();
492
+ syncKnownSizeIfDue(state, now);
493
+ state.writesSinceStatSync += 1;
494
+ const firstBytes = Buffer.byteLength(lines[0] ?? "");
495
+ let known = state.knownSize ?? 0;
496
+ if (known > 0 && known + firstBytes >= state.maxBytes) {
497
+ rotateNow(state, firstBytes, now);
498
+ known = state.knownSize ?? 0;
499
+ if (known > 0 && known + firstBytes >= state.maxBytes) {
500
+ // Rotation is unavailable (a failing rename, or its backoff window).
501
+ // Capping cannot bound a file that cannot be rotated — it would only
502
+ // shred each drain into one append per line for the rest of the
503
+ // window. Write the whole batch; the reason the bound is not holding
504
+ // is reported through `log-sink-rotate-failed`.
505
+ return takeAll(lines);
506
+ }
321
507
  }
508
+ let bytes = 0;
509
+ let count = 0;
510
+ for (const line of lines) {
511
+ const lineBytes = Buffer.byteLength(line);
512
+ if (count > 0 && known + bytes + lineBytes >= state.maxBytes)
513
+ break;
514
+ bytes += lineBytes;
515
+ count += 1;
516
+ }
517
+ if (count === lines.length)
518
+ return takeAll(lines);
519
+ return { count, joined: lines.slice(0, count).join(""), bytes };
520
+ }
521
+ /** Cheap in-memory bookkeeping after a write actually lands (#2505). */
522
+ function noteWriteSucceeded(state, bytes) {
523
+ if (state.maxBytes === undefined)
524
+ return;
525
+ state.knownSize = (state.knownSize ?? 0) + bytes;
526
+ }
527
+ /** The file is now definitively empty — no guessing needed (#2505). */
528
+ function noteTruncated(state) {
529
+ if (state.maxBytes === undefined)
530
+ return;
531
+ state.knownSize = 0;
532
+ state.writesSinceStatSync = 0;
533
+ state.lastStatSyncMs = Date.now();
322
534
  }
323
535
  async function writeQueueItemAsync(state, item) {
324
536
  if (item.kind === "truncate") {
325
537
  await fs.promises.writeFile(state.file, "");
538
+ noteTruncated(state);
326
539
  }
327
540
  else {
328
541
  // Rotation is deliberately synchronous here. This function is only
329
542
  // reached from the already-deferred drain, and keeping stat/rm/rename
330
543
  // in one synchronous section prevents flushSync from racing a late
331
544
  // async rename after it has written new data.
332
- rotateIfNeeded(state);
333
- await fs.promises.appendFile(state.file, item.line);
545
+ const plan = planBoundedWrite(state, [item.line]);
546
+ await fs.promises.appendFile(state.file, plan.joined);
547
+ noteWriteSucceeded(state, plan.bytes);
334
548
  }
335
549
  }
336
550
  /** See `applyQueueItemSync`'s doc comment for the reopen-and-retry-once shape. */
@@ -363,20 +577,29 @@ async function drainLoop(state) {
363
577
  ? state.queue.findIndex((queued) => queued.kind === "truncate")
364
578
  : 0;
365
579
  const pendingEnd = truncateIndex === -1 ? state.queue.length : truncateIndex;
366
- const pending = item.kind === "truncate" ? [item] : state.queue.slice(0, pendingEnd);
580
+ const offered = item.kind === "truncate" ? [item] : state.queue.slice(0, pendingEnd);
581
+ // Rotation and the batch cap are decided synchronously, BEFORE this
582
+ // batch is published as in-flight: rotation stays inside the already-
583
+ // deferred drain (only the append remains async), so no awaited
584
+ // rotation step can run after flushSync has written to the active file.
585
+ const plan = item.kind === "truncate"
586
+ ? null
587
+ : planBoundedWrite(state, offered.map((queued) => queued.line));
588
+ // Only the lines the plan actually takes belong to THIS write. A batch
589
+ // that would itself cross `maxBytes` is cut at the bound and the loop
590
+ // comes back around for the remainder (#2505) — writing it whole would
591
+ // sail past a bound that rotation cannot recover, because rotation only
592
+ // moves pre-existing content aside.
593
+ const pending = plan === null ? offered : offered.slice(0, plan.count);
367
594
  state.inFlightBatch = pending;
368
595
  const writeBatch = async () => {
369
- if (item.kind === "truncate") {
596
+ if (plan === null) {
370
597
  await fs.promises.writeFile(state.file, "");
598
+ noteTruncated(state);
371
599
  }
372
600
  else {
373
- // Rotation is kept synchronous inside the deferred drain. The
374
- // append remains async, but no awaited rotation step can run after
375
- // flushSync has written to the active file.
376
- rotateIfNeeded(state);
377
- await fs.promises.appendFile(state.file, pending
378
- .map((queued) => queued.line)
379
- .join(""));
601
+ await fs.promises.appendFile(state.file, plan.joined);
602
+ noteWriteSucceeded(state, plan.bytes);
380
603
  }
381
604
  };
382
605
  try {
@@ -0,0 +1,101 @@
1
+ /**
2
+ * What pi-lens ALREADY tracks, and where an observed change is replayed to
3
+ * (#2430).
4
+ *
5
+ * The observational net has to answer one question in three places — the
6
+ * `tool_call` pre-snapshot, the `tool_result` diff, and the `agent_settled`
7
+ * sweep — and the answer must be the same set every time, or a file drifts in
8
+ * and out of coverage depending on which seam asked. This module is that single
9
+ * source: the read-guard's read/write set, the widget's diagnostic files, and
10
+ * the documents open on any live language server rooted at the workspace.
11
+ *
12
+ * There is deliberately NO workspace walk here. The whole point of #2430 item 3
13
+ * is that the turn-boundary net costs a bounded number of stats over files
14
+ * pi-lens has already seen; a walk would make the settle boundary pay for the
15
+ * project's size on every run.
16
+ *
17
+ * The replay recorder is the mutation bridge. Nothing in this module writes
18
+ * bookkeeping itself: an observed change goes through exactly the seam an
19
+ * in-process producer uses, so the read-guard stamp, the `turn-state.json`
20
+ * entry, the attributed change-log receipt and the deferred format at
21
+ * `agent_settled` are the SAME code path a `write` takes.
22
+ */
23
+ import { getLSPService } from "./lsp/index.js";
24
+ import { getMutationBridge } from "./mutation-bridge.js";
25
+ import { getFileDiagnosticSummaries } from "./widget-state.js";
26
+ /**
27
+ * Files pi-lens has read, written, diagnosed, or opened on a language server.
28
+ *
29
+ * Order is deliberate and the cap is applied ACROSS the whole union: the
30
+ * read-guard set is the most direct evidence the agent is working on a file,
31
+ * so it is added first and a truncating cap never drops it in favour of a
32
+ * cascade neighbour some server happened to open.
33
+ */
34
+ export function collectTrackedPaths(args) {
35
+ const tracked = new Set();
36
+ const add = (paths) => {
37
+ for (const filePath of paths) {
38
+ if (tracked.size >= args.limit)
39
+ return;
40
+ if (typeof filePath === "string" && filePath.length > 0)
41
+ tracked.add(filePath);
42
+ }
43
+ };
44
+ try {
45
+ add(args.readGuard?.getTrackedPaths?.() ?? []);
46
+ }
47
+ catch {
48
+ // A guard mid-eviction contributes nothing; the sweep stays advisory.
49
+ }
50
+ try {
51
+ add(getFileDiagnosticSummaries().map((summary) => summary.filePath));
52
+ }
53
+ catch {
54
+ // Widget state is display state; it must never break the net.
55
+ }
56
+ if (args.cwd) {
57
+ try {
58
+ add(getLSPService().getOpenDocumentPaths(args.cwd));
59
+ }
60
+ catch {
61
+ // No pool, or a client torn down mid-enumeration.
62
+ }
63
+ }
64
+ return [...tracked];
65
+ }
66
+ /**
67
+ * Read-guard stored per-line hashes for a file, newest record first.
68
+ *
69
+ * This is the "content diff against the read-guard's stored content" #2430
70
+ * asks for: #505 already keeps an FNV-1a hash per line for every read, so a
71
+ * post-mutation re-hash names exactly the lines that changed without pi-lens
72
+ * having to have kept the bytes.
73
+ */
74
+ export function storedLineHashesFor(readGuard, filePath) {
75
+ try {
76
+ const history = readGuard?.getReadHistory?.(filePath) ?? [];
77
+ for (let index = history.length - 1; index >= 0; index -= 1) {
78
+ const hashes = history[index]?.lineHashes;
79
+ if (hashes && Object.keys(hashes).length > 0)
80
+ return hashes;
81
+ }
82
+ }
83
+ catch {
84
+ // No history is the same answer as no hashes: derive no ranges.
85
+ }
86
+ return undefined;
87
+ }
88
+ /**
89
+ * Replay an observed change through the mutation bridge.
90
+ *
91
+ * Returns `false` when the bridge is not mounted (pi-lens not activated, or a
92
+ * unit test that never registered one) or when the bridge itself declined the
93
+ * entry — out of scope, ignored, or a bookkeeping failure. A `false` is a real
94
+ * answer the caller acts on: it does NOT count as evidence the tool was clean.
95
+ */
96
+ export function replayThroughMutationBridge(entry) {
97
+ const bridge = getMutationBridge();
98
+ if (!bridge)
99
+ return false;
100
+ return bridge.recordMutation(entry);
101
+ }