@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,424 @@
1
+ /**
2
+ * `pi-hashline-edit-pro` anchor resolution (#2423 review round 1, finding F1).
3
+ *
4
+ * ## Why this module exists
5
+ *
6
+ * `pi-hashline-edit-pro` addresses lines by a bare THREE-CHARACTER base62
7
+ * anchor — `"aB3"`, never `"42"`. Its schema says so in as many words
8
+ * (`src/payload-contract.ts`: *"Bare 3-char anchor only (e.g. \"aB3\")"*), and
9
+ * its parser accepts exactly `trimmed.length === ANCHOR_LEN && ALPH_RE.test()`
10
+ * (`src/hashline/parse.ts`). The first cut of the shape adapter in
11
+ * `clients/mutating-tool.ts` read those fields as DECIMAL line numbers, so
12
+ * against the real extension every anchor was "malformed" and the adapter
13
+ * blocked a working edit tool.
14
+ *
15
+ * This module reproduces the extension's own line-hash function so pi-lens can
16
+ * map an anchor back to a line, and does it from the extension's source rather
17
+ * than from a guess. Everything below is a port of
18
+ * `pi-hashline-edit-pro@8f027ca1c2a0796ccf1c15bd5a0d12db5d9fb9f0`
19
+ * (github.com/YuGiMob/pi-hashline-edit-pro, branch `master`):
20
+ *
21
+ * - `src/hashline/alphabet.ts` — `HASH_LEN = 3`, the 62-character alphabet
22
+ * `A-Za-z0-9`.
23
+ * - `src/hashline/hash.ts` — `hashSource` (strip `\r`, `trimEnd`, truncate to
24
+ * `MAX_HASH_SOURCE_BYTES = 500` UTF-8 bytes on a code-point boundary),
25
+ * `baseIdx = (xxh32(source) >>> 14) % 62**3`, and open-addressed collision
26
+ * probing with stride `62**2 + 62 + 1` over a used-index bitset.
27
+ * - `src/hashline/hasher.ts` — `xxh32` is `xxhash-wasm`'s `h32`, i.e. standard
28
+ * XXH32 with seed 0 over the UTF-8 bytes. Reimplemented here in ~40 lines of
29
+ * integer arithmetic rather than taking a runtime dependency (pi installs
30
+ * pi-lens with `npm install --omit=dev`, so a hashing dep would ship to every
31
+ * user for one adapter).
32
+ * - `src/hashline/resolve.ts` — `resAnchorFromMap`: an anchor resolves ONLY
33
+ * when it matches exactly one line. Zero matches is `not_found`, two or more
34
+ * is `ambiguous`, and the extension refuses both rather than guessing.
35
+ *
36
+ * `tests/clients/hashline-anchor.test.ts` pins the port against a vector table
37
+ * generated by RUNNING the upstream code with the real `xxhash-wasm`, so a
38
+ * drift in either direction is a red test rather than a silent misattribution.
39
+ *
40
+ * ## Why resolution is gated on UNIQUE CONTENT (review round 3, finding F1) —
41
+ * and why the gate is NOT a correctness proof (review round 4, finding F1)
42
+ *
43
+ * The live extension does not recompute hashes on every read. It persists a
44
+ * per-file snapshot (`src/hash-store.ts`) and, after every edit, carries a
45
+ * surviving line's old anchor forward (`mapStableHashes` in
46
+ * `src/hashline/hash.ts`) before persisting the result — so the anchors the
47
+ * agent sends back are STORE-CARRIED, not freshly computed. pi-lens has no
48
+ * access to that store and can only recompute with `_lineHashesPure`.
49
+ *
50
+ * Where a line's canonical content is unique in the file, the store's anchor
51
+ * for it and pi-lens's own recomputed anchor for it USUALLY agree — that is
52
+ * the case the gate below exists to hold. Where content is NOT unique — `}`,
53
+ * a blank line, `continue;`, a bare comment-close line — the anchor comes
54
+ * from probing, whose result depends on every line before it, so an edit
55
+ * anywhere upstream repermutes those anchors among the identical lines. A
56
+ * store-carried anchor then still matches EXACTLY ONE line under
57
+ * recomputation: a confident, unique, WRONG answer.
58
+ *
59
+ * Gating on "the matched line's content is unique" narrows that failure a
60
+ * long way, but it is not a proof, and review round 4 measured the two
61
+ * mechanisms that survive it:
62
+ *
63
+ * (a) The gate is ONE-SIDED: it checks the MATCHED line's content, never the
64
+ * anchor's own provenance. A probe-derived anchor that truly belongs to
65
+ * some duplicate-content line elsewhere can, by construction of the
66
+ * probe stride, land on the same anchor VALUE as a completely unrelated
67
+ * unique-content line's own base index. Measured case: a file with 25
68
+ * occurrences of a bare block-comment-close line — the store-carried
69
+ * anchor for one of them resolved to an unrelated unique-content line
70
+ * 1532 lines away, and the gate answered it, because that OTHER line's
71
+ * content is (correctly) unique. A unique answer is not evidence the
72
+ * anchor was ever meant for it.
73
+ * (b) Content uniqueness does not imply base-index uniqueness. `baseIdx =
74
+ * (xxh32(source) >>> 14) % 238328` has only 238,328 slots; over enough
75
+ * DISTINCT line contents in a real file, two different contents collide
76
+ * on the same base index and one of them is probed to a different slot
77
+ * — the identical open-addressing mechanism as a literal duplicate, just
78
+ * between lines whose content is each unique. Measured across 220 files:
79
+ * 194 of 74,386 unique-content lines carry a probe-derived anchor, not
80
+ * their own content's base index.
81
+ *
82
+ * So `resolveHashlineAnchor` answers when the matched line's canonical
83
+ * content occurs exactly once in the file — a gate that removes nearly all
84
+ * of the literal-duplicate case and neither (a) nor (b). Measured over 220
85
+ * files and five edit shapes (~550,000 anchors asked): roughly 2 anchors
86
+ * resolved to the wrong line, against ~67% of anchors answered at all. That
87
+ * residual is ACCEPTED (review round 4 orchestrator decision), not fixed
88
+ * here: closing it needs either the extension's own store, which pi-lens
89
+ * does not have, or refusing every anchor within probing distance of a
90
+ * collision, which costs far more coverage than the ~2-in-550,000 residual
91
+ * is worth. Everything the gate refuses is `content_not_unique`: unresolved,
92
+ * which is a report and never a block. pi-lens's uncertainty about a
93
+ * third-party tool's addressing is not evidence that the agent's edit is
94
+ * wrong. An unresolved anchor degrades to "mutation recognized, lines
95
+ * unknown" — the same state a host `edit` with an unknown schema reaches on
96
+ * master — so the file still gets its turn-state entry (the whole file as
97
+ * its modified range), its deferred format and its change-log receipt.
98
+ *
99
+ * This costs coverage: roughly a third of the lines in a typical TypeScript
100
+ * file are blank or a bare brace. Trading those for an almost-always-correct
101
+ * answer on the rest is the deliberate choice — a wrong line range is worse
102
+ * than no line range on average, because the guard acts on it, but "gated on
103
+ * unique content" is a reduction of the wrong-line rate, not its elimination.
104
+ */
105
+ import * as nodeFs from "node:fs";
106
+ import { BoundedFifoMap } from "./bounded-cache.js";
107
+ /** `HASH_LEN` in `src/hashline/alphabet.ts`. */
108
+ const HASHLINE_ANCHOR_LENGTH = 3;
109
+ /** `ALPH` in `src/hashline/alphabet.ts`. Order is load-bearing: it is the digit set. */
110
+ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
111
+ /** `HASH_SPACE` — 62**3 = 238328 addressable anchors. */
112
+ const HASH_SPACE = ALPHABET.length ** HASHLINE_ANCHOR_LENGTH;
113
+ /** `HASH_PROBE_STRIDE` — 62**2 + 62 + 1, the open-addressing step. */
114
+ const HASH_PROBE_STRIDE = ALPHABET.length ** 2 + ALPHABET.length + 1;
115
+ /** `MAX_HASH_SOURCE_BYTES` in `src/constants.ts`. */
116
+ const MAX_HASH_SOURCE_BYTES = 500;
117
+ const BITSET_WORDS = Math.ceil(HASH_SPACE / 32);
118
+ const ANCHOR_TOKEN_RE = /^[A-Za-z0-9]{3}$/;
119
+ // --- XXH32 (seed 0), the algorithm behind `xxhash-wasm`'s `h32` -------------
120
+ const XXH_PRIME1 = 0x9e3779b1;
121
+ const XXH_PRIME2 = 0x85ebca77;
122
+ const XXH_PRIME3 = 0xc2b2ae3d;
123
+ const XXH_PRIME4 = 0x27d4eb2f;
124
+ const XXH_PRIME5 = 0x165667b1;
125
+ function rotl32(value, bits) {
126
+ return (value << bits) | (value >>> (32 - bits));
127
+ }
128
+ function xxhRound(acc, input) {
129
+ return (Math.imul(rotl32((acc + Math.imul(input, XXH_PRIME2)) | 0, 13), XXH_PRIME1) | 0);
130
+ }
131
+ /**
132
+ * Standard XXH32 with seed 0 over `bytes`. Returns an unsigned 32-bit integer,
133
+ * matching `xxh32()` in `src/hashline/hasher.ts`.
134
+ */
135
+ export function xxh32Bytes(bytes) {
136
+ const length = bytes.length;
137
+ let index = 0;
138
+ let hash;
139
+ if (length >= 16) {
140
+ const limit = length - 16;
141
+ let v1 = (XXH_PRIME1 + XXH_PRIME2) | 0;
142
+ let v2 = XXH_PRIME2 | 0;
143
+ let v3 = 0;
144
+ let v4 = -XXH_PRIME1 | 0;
145
+ do {
146
+ v1 = xxhRound(v1, readU32(bytes, index));
147
+ v2 = xxhRound(v2, readU32(bytes, index + 4));
148
+ v3 = xxhRound(v3, readU32(bytes, index + 8));
149
+ v4 = xxhRound(v4, readU32(bytes, index + 12));
150
+ index += 16;
151
+ } while (index <= limit);
152
+ hash =
153
+ (rotl32(v1, 1) + rotl32(v2, 7) + rotl32(v3, 12) + rotl32(v4, 18)) | 0;
154
+ }
155
+ else {
156
+ hash = XXH_PRIME5 | 0;
157
+ }
158
+ hash = (hash + length) | 0;
159
+ while (index + 4 <= length) {
160
+ hash =
161
+ Math.imul(rotl32((hash + Math.imul(readU32(bytes, index), XXH_PRIME3)) | 0, 17), XXH_PRIME4) | 0;
162
+ index += 4;
163
+ }
164
+ while (index < length) {
165
+ hash =
166
+ Math.imul(rotl32((hash + Math.imul(bytes[index], XXH_PRIME5)) | 0, 11), XXH_PRIME1) | 0;
167
+ index += 1;
168
+ }
169
+ hash = (hash ^ (hash >>> 15)) | 0;
170
+ hash = Math.imul(hash, XXH_PRIME2) | 0;
171
+ hash = (hash ^ (hash >>> 13)) | 0;
172
+ hash = Math.imul(hash, XXH_PRIME3) | 0;
173
+ hash = (hash ^ (hash >>> 16)) | 0;
174
+ return hash >>> 0;
175
+ }
176
+ function readU32(bytes, offset) {
177
+ return (bytes[offset] |
178
+ (bytes[offset + 1] << 8) |
179
+ (bytes[offset + 2] << 16) |
180
+ (bytes[offset + 3] << 24) |
181
+ 0);
182
+ }
183
+ // --- The line-hash function -------------------------------------------------
184
+ /** `canon` in `src/hashline/hash.ts`. */
185
+ function canon(line) {
186
+ return line.replace(/\r/g, "").trimEnd();
187
+ }
188
+ /** `splitLines` in `src/utils.ts`. An empty file is ONE empty line. */
189
+ function splitLines(text) {
190
+ if (text.length === 0)
191
+ return [""];
192
+ const lines = text.split("\n");
193
+ return text.endsWith("\n") ? lines.slice(0, -1) : lines;
194
+ }
195
+ /**
196
+ * `hashSource` in `src/hashline/hash.ts`, as bytes.
197
+ *
198
+ * Upstream truncates the STRING to 500 UTF-8 bytes by iterating code points
199
+ * (`truncateToBytes` in `src/utils.ts`) and then hashes its UTF-8 encoding.
200
+ * Cutting the encoded buffer back to the last non-continuation byte is the
201
+ * same operation, because a UTF-8 code point occupies contiguous bytes.
202
+ */
203
+ function hashSourceBytes(line) {
204
+ const bytes = Buffer.from(canon(line), "utf8");
205
+ if (bytes.length <= MAX_HASH_SOURCE_BYTES)
206
+ return bytes;
207
+ let cut = MAX_HASH_SOURCE_BYTES;
208
+ while (cut > 0 && (bytes[cut] & 0xc0) === 0x80)
209
+ cut -= 1;
210
+ return bytes.subarray(0, cut);
211
+ }
212
+ /**
213
+ * The same `hashSource` value as a string — the key upstream itself compares
214
+ * lines by (`newByContent` / `removedByContent` in `mapStableHashes`). Two
215
+ * lines with the same key are indistinguishable to BOTH implementations, which
216
+ * is exactly the condition the uniqueness gate tests. Decoding the truncated
217
+ * buffer is lossless because the cut lands on a code-point boundary.
218
+ */
219
+ function hashSourceKey(line) {
220
+ return Buffer.from(hashSourceBytes(line)).toString("utf8");
221
+ }
222
+ function indexToAnchor(index) {
223
+ let out = "";
224
+ let remaining = index;
225
+ for (let digit = 0; digit < HASHLINE_ANCHOR_LENGTH; digit += 1) {
226
+ out = ALPHABET[remaining % ALPHABET.length] + out;
227
+ remaining = Math.floor(remaining / ALPHABET.length);
228
+ }
229
+ return out;
230
+ }
231
+ function getBit(bits, index) {
232
+ return ((bits[index >>> 5] >>> (index & 31)) & 1) !== 0;
233
+ }
234
+ function setBit(bits, index) {
235
+ bits[index >>> 5] |= 1 << (index & 31);
236
+ }
237
+ function nextZeroBit(bits, start) {
238
+ let index = start % HASH_SPACE;
239
+ for (let step = 0; step < HASH_SPACE; step += 1) {
240
+ if (!getBit(bits, index))
241
+ return index;
242
+ index += HASH_PROBE_STRIDE;
243
+ if (index >= HASH_SPACE)
244
+ index -= HASH_SPACE;
245
+ }
246
+ // Upstream throws `[E_FILE_TOO_LARGE]` here. pi-lens is an observer, so it
247
+ // reports "cannot address this file" instead of failing the caller.
248
+ return -1;
249
+ }
250
+ /**
251
+ * `_lineHashesPure` in `src/hashline/hash.ts`: one anchor per line of
252
+ * ALREADY-NORMALIZED content (BOM stripped, CRLF folded to LF).
253
+ *
254
+ * Returns `undefined` for a file with more lines than the anchor space can
255
+ * address, which is where upstream raises `[E_FILE_TOO_LARGE]`.
256
+ */
257
+ export function computeHashlineAnchors(content) {
258
+ const lines = splitLines(content);
259
+ if (lines.length > HASH_SPACE)
260
+ return undefined;
261
+ const anchors = [];
262
+ anchors.length = lines.length;
263
+ const used = new Uint32Array(BITSET_WORDS);
264
+ let hint = 0;
265
+ const sourceCache = new Map();
266
+ for (let i = 0; i < lines.length; i += 1) {
267
+ const line = lines[i];
268
+ let baseIndex = sourceCache.get(line);
269
+ if (baseIndex === undefined) {
270
+ baseIndex = (xxh32Bytes(hashSourceBytes(line)) >>> 14) % HASH_SPACE;
271
+ sourceCache.set(line, baseIndex);
272
+ }
273
+ if (!getBit(used, baseIndex)) {
274
+ setBit(used, baseIndex);
275
+ hint = baseIndex + HASH_PROBE_STRIDE;
276
+ anchors[i] = indexToAnchor(baseIndex);
277
+ continue;
278
+ }
279
+ const probed = nextZeroBit(used, hint);
280
+ if (probed < 0)
281
+ return undefined;
282
+ setBit(used, probed);
283
+ hint = probed + HASH_PROBE_STRIDE;
284
+ anchors[i] = indexToAnchor(probed);
285
+ }
286
+ return anchors;
287
+ }
288
+ // --- Anchor tokens ----------------------------------------------------------
289
+ /**
290
+ * The anchor token an edit-pro request carries, or `undefined` when the value
291
+ * is not one. Mirrors `parseRef` in `src/hashline/parse.ts`: trim, then require
292
+ * exactly three characters from the alphabet. A decimal line number is NOT an
293
+ * anchor.
294
+ */
295
+ export function normalizeHashlineAnchorToken(value) {
296
+ if (typeof value !== "string")
297
+ return undefined;
298
+ const trimmed = value.trim();
299
+ return ANCHOR_TOKEN_RE.test(trimmed) ? trimmed : undefined;
300
+ }
301
+ /**
302
+ * Files larger than this are not anchor-resolved. The extension itself caps at
303
+ * `MAX_HASH_LINES` (62**3 lines); this byte cap keeps the synchronous read on
304
+ * the tool_call path bounded regardless of line length.
305
+ */
306
+ const MAX_ANCHOR_FILE_BYTES = 4 * 1024 * 1024;
307
+ /**
308
+ * Bounded memo. `classifyMutatingTool` runs several times per tool call and the
309
+ * same file is asked about repeatedly; without this every ask re-reads and
310
+ * re-hashes the file. Keyed by path and invalidated by mtime/size, so an edit
311
+ * between two asks is USUALLY not answered from the stale index.
312
+ *
313
+ * "Usually" (review round 4, finding F5): mtime/size alone is not sufficient.
314
+ * A same-size rewrite that lands within one filesystem mtime tick (mtime
315
+ * resolution is coarser than a millisecond on some platforms/filesystems)
316
+ * changes neither key, so the memo can serve stale content for a file that
317
+ * genuinely changed. The memo only ever needs to survive WITHIN one tool
318
+ * call — the repeated-ask case above — so `dropHashlineAnchorMemo` is called
319
+ * at the `tool_call` boundary in `runtime-tool-call.ts` (`handleToolCallImpl`
320
+ * entry) to bound its lifetime to that, rather than trusting mtime/size
321
+ * across calls.
322
+ */
323
+ const ANCHOR_INDEX_CACHE_LIMIT = 8;
324
+ const ANCHOR_INDEX_CACHE = new BoundedFifoMap(ANCHOR_INDEX_CACHE_LIMIT);
325
+ /**
326
+ * Drop the memo. Called at the `tool_call` boundary in production
327
+ * (`runtime-tool-call.ts`) so a stale mtime/size match from a PRIOR tool call
328
+ * can never leak into this one (review round 4, finding F5), and as a test
329
+ * seam so a fixture rewrite is never answered from cache.
330
+ */
331
+ export function dropHashlineAnchorMemo() {
332
+ ANCHOR_INDEX_CACHE.clear();
333
+ }
334
+ function buildAnchorIndex(filePath) {
335
+ let stats;
336
+ try {
337
+ stats = nodeFs.statSync(filePath);
338
+ }
339
+ catch {
340
+ return { failure: "file_unreadable" };
341
+ }
342
+ if (!stats.isFile())
343
+ return { failure: "file_unreadable" };
344
+ if (stats.size > MAX_ANCHOR_FILE_BYTES)
345
+ return { failure: "file_too_large" };
346
+ const cached = ANCHOR_INDEX_CACHE.get(filePath);
347
+ if (cached &&
348
+ cached.mtimeMs === stats.mtimeMs &&
349
+ cached.size === stats.size) {
350
+ return { index: cached.index, contentUnique: cached.contentUnique };
351
+ }
352
+ let raw;
353
+ try {
354
+ raw = nodeFs.readFileSync(filePath, "utf8");
355
+ }
356
+ catch {
357
+ return { failure: "file_unreadable" };
358
+ }
359
+ // `readNormFile` in the extension strips the BOM and folds line endings to
360
+ // LF before hashing, so the same normalization has to happen here.
361
+ const normalized = (raw.startsWith("") ? raw.slice(1) : raw)
362
+ .replace(/\r\n/g, "\n")
363
+ .replace(/\r/g, "\n");
364
+ const anchors = computeHashlineAnchors(normalized);
365
+ if (anchors === undefined)
366
+ return { failure: "file_too_large" };
367
+ const index = new Map();
368
+ for (let i = 0; i < anchors.length; i += 1) {
369
+ index.set(anchors[i], i + 1);
370
+ }
371
+ // Content multiplicity, keyed exactly as upstream keys it. A line whose key
372
+ // repeats is unanswerable: see the module header.
373
+ const lines = splitLines(normalized);
374
+ const keys = [];
375
+ keys.length = lines.length;
376
+ const keyCounts = new Map();
377
+ const keyCache = new Map();
378
+ for (let i = 0; i < lines.length; i += 1) {
379
+ const line = lines[i];
380
+ let key = keyCache.get(line);
381
+ if (key === undefined) {
382
+ key = hashSourceKey(line);
383
+ keyCache.set(line, key);
384
+ }
385
+ keys[i] = key;
386
+ keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
387
+ }
388
+ const contentUnique = keys.map((key) => keyCounts.get(key) === 1);
389
+ ANCHOR_INDEX_CACHE.set(filePath, {
390
+ mtimeMs: stats.mtimeMs,
391
+ size: stats.size,
392
+ index,
393
+ contentUnique,
394
+ });
395
+ return { index, contentUnique };
396
+ }
397
+ /**
398
+ * Resolve one anchor token against the file's CURRENT content.
399
+ *
400
+ * TWO conditions, both required (review round 3, finding F1):
401
+ *
402
+ * 1. The anchor matches a line of the recomputed table — `resAnchorFromMap` in
403
+ * `src/hashline/resolve.ts` requires that much.
404
+ * 2. That line's canonical content occurs exactly once in the file. Without
405
+ * this, a store-carried anchor for one `}` resolves confidently to a
406
+ * DIFFERENT `}`, and the wrong range reaches the read guard. The module
407
+ * header has the measurement — including the residual this narrows but
408
+ * does not close (review round 4, finding F1).
409
+ */
410
+ export function resolveHashlineAnchor(filePath, anchor) {
411
+ const token = normalizeHashlineAnchorToken(anchor);
412
+ if (token === undefined)
413
+ return { failure: "not_an_anchor" };
414
+ const built = buildAnchorIndex(filePath);
415
+ if ("failure" in built)
416
+ return { failure: built.failure };
417
+ const line = built.index.get(token);
418
+ if (line === undefined)
419
+ return { failure: "anchor_not_found" };
420
+ if (built.contentUnique[line - 1] !== true) {
421
+ return { failure: "content_not_unique" };
422
+ }
423
+ return { line };
424
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The per-hook wall-clock budgets, in ONE table (#2523).
3
+ *
4
+ * ## What a budget here means
5
+ *
6
+ * The maintainer contract behind #2523: only the write/edit `tool_result`
7
+ * path may block pi's host loop. `session_start`, `turn_end`, `agent_end`,
8
+ * `agent_settled` and read-only `tool_result` must either finish inside a
9
+ * small bound or deliver their work OFF-hook (`clients/deferred-lsp-work.ts`,
10
+ * `publishActionableWarningsReport` #2509, the test-runner `agent_settled`
11
+ * delivery #2366, the cascade deferred settle #450) — deliver, never drop.
12
+ *
13
+ * A number here is the TOTAL wall time the hook may spend, not a per-await
14
+ * allowance. `0` means the hook may not await anything at all: whatever it
15
+ * does must be synchronous bookkeeping or fire-and-forget with its own
16
+ * bound.
17
+ *
18
+ * The numbers are the contract's, not a measurement: they are the ceilings
19
+ * the maintainer set for what the host may be made to wait for. #2523's
20
+ * sweep measured today's reality against them — `turn_end` p50 3687 ms /
21
+ * p90 14246 ms against a 3000 ms budget, `agent_end` p90 10043 ms against
22
+ * 1000 ms — which is what makes this table a target rather than a
23
+ * description.
24
+ *
25
+ * ## Why a table, and why it is not wired yet
26
+ *
27
+ * Slice 1 of #2523 ships the enforcement PRIMITIVE (`bounded()` in
28
+ * `clients/deadline-utils.ts`) and the guard that makes a new unbounded hook
29
+ * await impossible to add (`tests/config/hook-await-bounds.test.ts`). It
30
+ * deliberately changes NO hook's behavior. This table exists now so the
31
+ * guard's exemption table can say which hook each unbounded await belongs to
32
+ * — a claim that is checked, since every exemption's `hook` field must be a
33
+ * key of {@link HOOK_WALL_BUDGET_MS}. Slice 2 (AC3-AC8) wires it into the
34
+ * outer `bounded()` around each registered handler.
35
+ *
36
+ * #1978's phase-budget watchdog is the general form of this idea ("every
37
+ * recorded duration gets a declared budget"). When it lands it must CONSUME
38
+ * this table for the hook family rather than declare a second set of hook
39
+ * numbers — AGENTS.md's single-source-of-truth rule, and #2523 AC7 says so
40
+ * explicitly.
41
+ */
42
+ /**
43
+ * Total wall-clock budget per hook, in milliseconds. `0` = may not await.
44
+ *
45
+ * `session_start` is the one VISIBLE budget: 5 s of startup is work the user
46
+ * is already watching a progress line for. Everything else is time the user
47
+ * did not ask to spend.
48
+ */
49
+ export const HOOK_WALL_BUDGET_MS = Object.freeze({
50
+ /** Visible startup work; the user is watching a progress line. */
51
+ session_start: 5000,
52
+ /** Pure bookkeeping — nothing here may await (#2523 contract). */
53
+ turn_start: 0,
54
+ /** Measured p50 3687 ms / p90 14246 ms today. The gap IS the issue. */
55
+ turn_end: 3000,
56
+ /** Measured p90 10043 ms today, from one unbounded pipeline re-entry. */
57
+ agent_end: 1000,
58
+ /** The designated place for settled-time sweeps; the widest non-edit budget. */
59
+ agent_settled: 10000,
60
+ /** Read/Grep/Glob/Bash: must never await analyzer bootstrap (AC5). */
61
+ tool_result_read_only: 500,
62
+ /** The ONLY path the contract lets block the host, and only this long. */
63
+ tool_result_edit: 10000,
64
+ /** Teardown: spawning or awaiting here aborts libuv (#234). */
65
+ session_shutdown: 0,
66
+ /** Synchronous message contribution; the host is blocked on the answer. */
67
+ context: 0,
68
+ /** Fork bookkeeping; the session is being replaced under it. */
69
+ session_before_fork: 0,
70
+ });
71
+ /** Every declared hook family, for sweeps that must enumerate them. */
72
+ export const HOOK_BUDGET_KEYS = Object.keys(HOOK_WALL_BUDGET_MS);
73
+ /** Type guard for a string that names a declared hook family. */
74
+ export function isHookBudgetKey(value) {
75
+ return Object.hasOwn(HOOK_WALL_BUDGET_MS, value);
76
+ }
@@ -5,7 +5,10 @@
5
5
  *
6
6
  * SOURCE OF TRUTH: `@earendil-works/pi-coding-agent`
7
7
  * `dist/core/tools/edit-diff.js` -- `normalizeForFuzzyMatch`, `normalizeToLF`,
8
- * `detectLineEnding`, `restoreLineEndings`, `stripBom`.
8
+ * `detectLineEnding`, `restoreLineEndings`. As of 0.85.1 the BOM
9
+ * split/strip primitive (`stripBom`'s underlying `splitBom`) moved to
10
+ * `dist/utils/text.js`, imported by edit-diff.js rather than inlined there
11
+ * -- see the sync test's two-source read.
9
12
  *
10
13
  * The host SDK is a *type-only* dependency at runtime (pi installs extensions
11
14
  * with `npm install --omit=dev`, so the SDK is not in `node_modules`). These
@@ -25,7 +28,7 @@
25
28
  * floor in package.json and re-confirm the sync test when the host changes its
26
29
  * normalization ladder.
27
30
  */
28
- export const HOST_EDIT_DIFF_SDK_FLOOR = "0.79.9";
31
+ export const HOST_EDIT_DIFF_SDK_FLOOR = "0.85.1";
29
32
  // Host normalizeForFuzzyMatch code-point sets (edit-diff.js). Exported so the
30
33
  // sync test can assert the host source still encodes exactly these.
31
34
  /** U+2018 U+2019 U+201A U+201B -> ' */
@@ -34,6 +34,6 @@ export function createDefaultHostPorts(overrides = {}) {
34
34
  // annotated rather than inferred.
35
35
  return Object.fromEntries(Object.entries(defaults).map(([group, value]) => [
36
36
  group,
37
- { ...value, ...(overrides[group] ?? {}) },
37
+ { ...value, ...overrides[group] },
38
38
  ]));
39
39
  }