@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

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 (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Turn-boundary freshness gate for cached inline blockers (#1631).
3
+ *
4
+ * An inline blocker recorded for a file F is a verdict about F *and everything F
5
+ * imports*. The existing invalidation paths all key on F alone — a later dispatch
6
+ * of the SAME path returning no blockers, the path ceasing to exist (#1245), and a
7
+ * fresh confirmed-clean verdict (#1561/#1573). When only a DEPENDENCY of F changes,
8
+ * none of those events fire, so F's stale verdict re-served at every turn end for
9
+ * the rest of the session. The live instances in #1631 are exactly that shape: the
10
+ * missing export was added to the dependency (one via a bash script, so the
11
+ * dependency was never dispatched at all — even the same-path event was unreachable
12
+ * by construction).
13
+ *
14
+ * The gate here is a READ-time freshness sweep, not the #1561 dependency-axis
15
+ * invalidation (which stays blocked on a maintainer design decision). Before a cached
16
+ * blocking finding is re-served at turn end, stat the file and its forward imports.
17
+ * The file's own import list comes from the parse layer — no reverse-dependency index
18
+ * is needed, sidestepping the tests-free-index blocker that holds #1561's remainder.
19
+ *
20
+ * Drifted entries are DEMOTED, not dropped (#1419 precedent, as applied by sibling
21
+ * issue #1622): a surviving file whose content drifted gets re-served with a
22
+ * `[stale — re-run to confirm]` marker and out of the authoritative blocker channel,
23
+ * rather than being silently deleted or re-asserted at full authority. The gate never
24
+ * re-pulls LSP verdicts on its own — a drifted entry is marked stale and left for an
25
+ * explicit re-run or the next dispatch to resolve. That keeps the sweep bounded and
26
+ * free of the document-resync hazard the issue calls out (re-querying an LSP whose
27
+ * in-memory dependency document is itself stale would regenerate the stale verdict).
28
+ *
29
+ * Scope (#1631 review F4): an inline blocker is not always LSP-only —
30
+ * `dispatcher.ts` builds it from every `semantic === "blocking"` diagnostic across
31
+ * all runners, so an eslint, biome-check, or ast-grep security-rule (hardcoded
32
+ * secret, CVE) finding can land here too. Only a language-server verdict is
33
+ * actually invalidated by an IMPORT changing; an ast-grep secret match doesn't stop
34
+ * being true because a file it imports was edited. The sweep therefore demotes an
35
+ * entry only when its recorded `sources` (`InlineBlockerRecord.sources`) are ALL
36
+ * `"lsp"` — a mixed or non-LSP-sourced blocker is left fully authoritative, the same
37
+ * fail-closed shape `retireInlineBlockerOnConfirmedClean` already uses for
38
+ * provenance it can't yet reason about.
39
+ *
40
+ * Resolution boundary (#1631 review F8): `extractForwardImportPaths` parses static
41
+ * import/require syntax via tree-sitter. It does not, and cannot, resolve a
42
+ * dependency reached only through a dynamic `import()` or `require()` behind a
43
+ * runtime condition — that edge is invisible to this gate, so a blocker whose only
44
+ * stale dependency arrives that way survives a replay. This is an honest boundary,
45
+ * not a silently-closed one.
46
+ */
47
+ import * as fs from "node:fs";
48
+ import { normalizeEphemeralMapKey } from "./path-utils.js";
49
+ import { resolveImportToFiles } from "./review-graph/import-resolvers.js";
50
+ import { getSharedTreeSitterClient, resolveTreeSitterLanguage, } from "./tree-sitter-shared.js";
51
+ import { TreeSitterSymbolExtractor } from "./tree-sitter-symbol-extractor.js";
52
+ /**
53
+ * Upper bound on the number of forward imports stat-checked for a single blocker.
54
+ * Blocking entries are rare, but one file can import many modules; the sweep must
55
+ * stay bounded so it cannot inflate a turn end.
56
+ */
57
+ const MAX_DRIFT_CHECK_IMPORTS = 128;
58
+ // Per-language extractor cache, mirroring review-graph/builder.ts and
59
+ // module-report.ts. Memoize failures too: a grammar that fails to load once is not
60
+ // re-probed for every blocker of that language within the process.
61
+ const extractorCache = new Map();
62
+ function getExtractor(languageId) {
63
+ let cached = extractorCache.get(languageId);
64
+ if (!cached) {
65
+ cached = (async () => {
66
+ const client = getSharedTreeSitterClient();
67
+ if (!client)
68
+ return null;
69
+ const extractor = new TreeSitterSymbolExtractor(languageId, client);
70
+ const ok = await extractor.init();
71
+ return ok ? extractor : null;
72
+ })().catch(() => null);
73
+ extractorCache.set(languageId, cached);
74
+ }
75
+ return cached;
76
+ }
77
+ /**
78
+ * #1631 review F6: memoized forward-import RESOLUTION (the parse step, not the
79
+ * per-dependency mtime stats — those must always be read fresh so drift is never
80
+ * missed). Reviewer measurement: ~450ms/turn at 8 blockers × 300 imports
81
+ * unmemoized, ~435ms per `mode=all` call, because both the turn-end sweep and the
82
+ * `mode=all` gate independently re-parse the SAME consumer files' import lists.
83
+ *
84
+ * Keyed on (path, own mtime) — a cache hit is safe by construction: if the file's
85
+ * mtime hasn't moved, its parsed import LIST cannot have changed (the imports
86
+ * themselves may still have drifted; that's what the mtime stats after this cache
87
+ * check are for). Scoped PER TURN, not process-lifetime (the latch screen this
88
+ * class of state must pass): `noteImportResolutionMemoTurn` clears it whenever the
89
+ * `RuntimeCoordinator`'s `turnIndex` advances, so a resolver injected for one test
90
+ * or a project whose import graph is edited mid-session is never served a result
91
+ * from a stale earlier turn. When no turn index is available (mode=all called
92
+ * outside a tracked turn, or a test with no runtime), the cache is left alone —
93
+ * still correct because of the mtime key, just not turn-bounded in that case.
94
+ */
95
+ const importResolutionMemo = new Map();
96
+ let importResolutionMemoTurnIndex;
97
+ function noteImportResolutionMemoTurn(turnIndex) {
98
+ if (turnIndex === undefined)
99
+ return;
100
+ if (importResolutionMemoTurnIndex !== turnIndex) {
101
+ importResolutionMemo.clear();
102
+ importResolutionMemoTurnIndex = turnIndex;
103
+ }
104
+ }
105
+ async function resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports) {
106
+ const sig = await statSignature(filePath);
107
+ if (sig === undefined) {
108
+ // Can't key reliably (deleted/unreadable) — resolve uncached rather than
109
+ // risk serving a memo entry for content that may no longer exist.
110
+ try {
111
+ return await resolveForwardImports(cwd, filePath);
112
+ }
113
+ catch {
114
+ return [];
115
+ }
116
+ }
117
+ const cached = importResolutionMemo.get(filePath);
118
+ // mtime alone collides for writes within the same timestamp tick (~1ms);
119
+ // size disambiguates the shrink-then-restore case (#1641's measurement).
120
+ if (cached && cached.mtimeMs === sig.mtimeMs && cached.size === sig.size)
121
+ return cached.imports;
122
+ let imports;
123
+ try {
124
+ imports = await resolveForwardImports(cwd, filePath);
125
+ }
126
+ catch {
127
+ imports = [];
128
+ }
129
+ importResolutionMemo.set(filePath, { mtimeMs: sig.mtimeMs, size: sig.size, imports });
130
+ return imports;
131
+ }
132
+ /** Test-only: clear the extractor and import-resolution memos. */
133
+ export function _resetBlockerFreshnessForTests() {
134
+ extractorCache.clear();
135
+ importResolutionMemo.clear();
136
+ importResolutionMemoTurnIndex = undefined;
137
+ }
138
+ /**
139
+ * Production forward-import resolver. Parses `filePath` with its tree-sitter grammar
140
+ * and resolves every extracted import source to in-project files. Degrades to an
141
+ * empty list (never throws) when the grammar, client, or file is unavailable — the
142
+ * sweep then falls back to checking only the blocker file's own drift.
143
+ */
144
+ export async function extractForwardImportPaths(cwd, filePath) {
145
+ const languageId = resolveTreeSitterLanguage(filePath);
146
+ if (!languageId)
147
+ return [];
148
+ const client = getSharedTreeSitterClient();
149
+ if (!client)
150
+ return [];
151
+ const extractor = await getExtractor(languageId);
152
+ if (!extractor)
153
+ return [];
154
+ let content;
155
+ try {
156
+ content = fs.readFileSync(filePath, "utf-8");
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ const outcome = await client.withParsedTree(filePath, languageId, content, (tree) => extractor.extract(tree, filePath, content).imports);
162
+ if (!outcome.parsed)
163
+ return [];
164
+ const resolved = new Set();
165
+ for (const ref of outcome.value) {
166
+ for (const target of resolveImportToFiles(cwd, filePath, languageId, ref.source)) {
167
+ resolved.add(target);
168
+ }
169
+ }
170
+ return [...resolved];
171
+ }
172
+ async function statSignature(filePath) {
173
+ try {
174
+ const stat = await fs.promises.stat(filePath);
175
+ return { mtimeMs: stat.mtimeMs, size: stat.size };
176
+ }
177
+ catch {
178
+ return undefined;
179
+ }
180
+ }
181
+ async function statMtimeMs(filePath) {
182
+ try {
183
+ const stat = await fs.promises.stat(filePath);
184
+ return stat.mtimeMs;
185
+ }
186
+ catch {
187
+ return undefined;
188
+ }
189
+ }
190
+ /**
191
+ * Resolve `filePath`'s forward imports and stat each, returning the imports that
192
+ * exist together with their mtime. Shared by the inline-blocker sweep and the
193
+ * widget-store dependency reconcile so both gates weigh drift identically. A path
194
+ * that cannot be stat'ed (deleted/unreadable) is omitted — a deleted dependency is
195
+ * not a content drift this gate reports.
196
+ */
197
+ export async function collectForwardImportMtimes(cwd, filePath, resolveForwardImports = extractForwardImportPaths, turnIndex) {
198
+ noteImportResolutionMemoTurn(turnIndex);
199
+ let imports = [];
200
+ try {
201
+ imports = await resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports);
202
+ }
203
+ catch {
204
+ imports = [];
205
+ }
206
+ const truncated = imports.length > MAX_DRIFT_CHECK_IMPORTS;
207
+ const out = [];
208
+ for (const dep of imports.slice(0, MAX_DRIFT_CHECK_IMPORTS)) {
209
+ const mtimeMs = await statMtimeMs(dep);
210
+ if (mtimeMs !== undefined)
211
+ out.push({ path: dep, mtimeMs });
212
+ }
213
+ return { mtimes: out, truncated };
214
+ }
215
+ /**
216
+ * Detect drift for one blocker entry. Returns the set of paths (the file itself plus
217
+ * any forward import) whose mtime is strictly newer than the verdict baseline. A
218
+ * missing/unstat-able path contributes nothing here — a DELETED blocker file is
219
+ * already dropped by `reconcileInlineBlockers`, and a deleted dependency is not a
220
+ * content drift this gate is responsible for.
221
+ *
222
+ * +50ms tolerance. The verdict baseline is `Date.now()`, but on Windows a file's
223
+ * mtime can LEAD `Date.now()` by a measurable margin — not just sub-millisecond
224
+ * rounding. Reviewer measurement across 200 writes on Windows: 184/200 mtimes read
225
+ * ahead of the immediately-following `Date.now()`, by up to ~11.4ms, the same host
226
+ * skew #1491/#1498 hit for install-probe timestamps. A +1ms tolerance (the
227
+ * convention borrowed from `reconcileStaleWidgetFiles` /
228
+ * `reconcileProjectDiagnosticsSnapshot`, both same-process single-writer paths
229
+ * without this skew) demoted a blocker recorded immediately after its own file
230
+ * write with zero real drift — 42 false demotions in 50 runs, and two of this
231
+ * gate's own tests went host-dependent (red 3/6 runs on Windows, green 6/6 at
232
+ * +50ms). +50ms comfortably clears the measured skew while staying far below the
233
+ * gap between genuinely distinct edits.
234
+ */
235
+ export const MTIME_DRIFT_TOLERANCE_MS = 50;
236
+ async function detectDrift(cwd, filePath, recordedAtMs, resolveForwardImports, turnIndex) {
237
+ const drifted = [];
238
+ const ownMtimeMs = await statMtimeMs(filePath);
239
+ if (ownMtimeMs !== undefined && ownMtimeMs > recordedAtMs + MTIME_DRIFT_TOLERANCE_MS) {
240
+ drifted.push(filePath);
241
+ }
242
+ const { mtimes, truncated } = await collectForwardImportMtimes(cwd, filePath, resolveForwardImports, turnIndex);
243
+ for (const { path, mtimeMs } of mtimes) {
244
+ if (mtimeMs > recordedAtMs + MTIME_DRIFT_TOLERANCE_MS)
245
+ drifted.push(path);
246
+ }
247
+ return { drifted, truncated };
248
+ }
249
+ /**
250
+ * Whether a population entry is eligible for the drift check at all — the same
251
+ * three gates the main sweep loop applies (not already stale, has a timestamp
252
+ * baseline, all-`"lsp"` sources). Factored out so the #1790 review F5 dedup
253
+ * decision below (chain vs. separate row) asks the IDENTICAL question the main
254
+ * loop will ask, rather than a second hand-written approximation of it that
255
+ * could drift from the real gates.
256
+ */
257
+ function isEligibleForDriftCheck(entry) {
258
+ if (entry.stale)
259
+ return false;
260
+ if (entry.recordedAtMs === undefined)
261
+ return false;
262
+ return (entry.sources !== undefined &&
263
+ entry.sources.length > 0 &&
264
+ entry.sources.every((source) => source === "lsp"));
265
+ }
266
+ /**
267
+ * Freshness sweep over every blocking row the widget currently serves — the cached
268
+ * inline blockers (`RuntimeCoordinator`) AND, since #1790, any widget-store row
269
+ * reached only through a cache-served replay (`options.additionalEntries`, injected
270
+ * by the call site to avoid an import cycle with `widget-state.ts`). Called at turn
271
+ * end before the blockers are re-served. Entries whose own file or forward imports
272
+ * drifted since the verdict are demoted via `demote`; the turn-end renderer then
273
+ * serves them out of the advisory channel with a `[stale — re-run to confirm]`
274
+ * marker instead of as an authoritative blocker.
275
+ *
276
+ * #1790: a file present in BOTH stores — the common case, since a live dispatch
277
+ * writes an inline blocker (`runtime-tool-result.ts`) AND a widget-store record
278
+ * (`pipeline.ts`) for the SAME verdict — is drift-checked ONCE, via its
279
+ * inline-blocker entry, so the sweep's one drift check never runs twice over the
280
+ * same file WHEN that inline entry is itself eligible for the check. But
281
+ * `markInlineBlockerStale` only ever touches `RuntimeCoordinator`'s map, never
282
+ * the widget store; on drift for a duplicated path, the widget demote is
283
+ * CHAINED onto the inline entry's `demote` (both stores write) rather than the
284
+ * widget row being silently dropped — the reviewer's F1 probe caught an earlier
285
+ * revision that discarded it: `revalidated:1` while the widget's own
286
+ * `isBlocking` for the file still read true, the exact ghost #1790 exists to
287
+ * kill.
288
+ *
289
+ * #1790 review F5: chaining is conditional on the inline entry actually being
290
+ * ELIGIBLE for the drift check (`isEligibleForDriftCheck` — not already stale,
291
+ * timestamped, all-LSP sources). An ineligible inline entry never reaches
292
+ * `demote()` in the loop below, so chaining onto one is ALSO a silent drop —
293
+ * just one store removed from F1's. The widget row's eligibility belongs to the
294
+ * WIDGET STORE, not to whatever inline entry happens to share its file path; an
295
+ * ineligible duplicate therefore gets its own separate population row instead
296
+ * of being chained, so a file can legitimately count twice (once per store) when
297
+ * the two stores disagree on eligibility.
298
+ *
299
+ * Never throws: any internal failure leaves the entry untouched (existing re-serve
300
+ * behavior) rather than failing the turn end.
301
+ */
302
+ export async function sweepInlineBlockerFreshness(runtime, cwd, options) {
303
+ const counts = {
304
+ total: 0,
305
+ kept: 0,
306
+ revalidated: 0,
307
+ alreadyStale: 0,
308
+ truncatedImports: 0,
309
+ };
310
+ const resolveForwardImports = options?.resolveForwardImports ?? extractForwardImportPaths;
311
+ let turnIndex;
312
+ try {
313
+ turnIndex = runtime.turnIndex;
314
+ }
315
+ catch {
316
+ turnIndex = undefined;
317
+ }
318
+ let inlineEntries;
319
+ try {
320
+ inlineEntries = runtime.getInlineBlockersSnapshot();
321
+ }
322
+ catch {
323
+ return counts;
324
+ }
325
+ const population = inlineEntries.map((entry) => ({
326
+ filePath: entry.filePath,
327
+ stale: entry.stale ?? false,
328
+ recordedAtMs: entry.recordedAtMs,
329
+ sources: entry.sources,
330
+ demote: () => runtime.markInlineBlockerStale(entry.filePath, "dependency-drift"),
331
+ }));
332
+ // #1790 review F2: dedup key is `normalizeEphemeralMapKey`, not
333
+ // `normalizeMapKey` — the latter realpaths a live file and walks up the
334
+ // directory tree for a missing one (measured ~313µs per deleted path, exactly
335
+ // the population this sweep processes) on a turn-end hot path. The widget
336
+ // store this population is reconciled against keys on
337
+ // `normalizeEphemeralMapKey` too (see `widget-state.ts`'s module doc on why
338
+ // `normalizeMapKey` is wrong for this in-process, single-walk kind of key),
339
+ // so this also keeps the dedup consistent with the store it is deduping
340
+ // against, not just cheaper.
341
+ const inlineByKey = new Map();
342
+ for (const entry of population) {
343
+ inlineByKey.set(normalizeEphemeralMapKey(entry.filePath), entry);
344
+ }
345
+ for (const extra of options?.additionalEntries ?? []) {
346
+ const key = normalizeEphemeralMapKey(extra.filePath);
347
+ const inlineEntry = inlineByKey.get(key);
348
+ // #1790 review F5: chaining onto an INELIGIBLE inline entry is a silent
349
+ // drop, not a merge. The main loop below short-circuits BEFORE ever
350
+ // calling `demote()` for an already-stale entry (the one-way
351
+ // dependency-drift latch — a forever-ghost once it fires once), an
352
+ // unstamped legacy record, or a non-LSP/mixed-sources entry (e.g. an
353
+ // unrelated ast-grep finding on the same file). Any of those swallows a
354
+ // chained widget demote even though the widget row is pure-LSP by
355
+ // construction (`getWidgetBlockingFilesForSweep` only emits LSP-sourced
356
+ // rows) and carries its OWN baseline. Eligibility belongs to the STORE
357
+ // the row came from, not the file path two stores happen to share — so
358
+ // only chain when the inline entry would itself reach `demote()`;
359
+ // otherwise give the widget row its own population entry so its own
360
+ // gates and its own drift check decide its own fate.
361
+ if (inlineEntry && isEligibleForDriftCheck(inlineEntry)) {
362
+ // #1790 review F1: a duplicated path is counted and drift-checked ONCE
363
+ // (via the inline entry above), but BOTH stores must record the
364
+ // verdict — `markInlineBlockerStale` only ever touches
365
+ // `RuntimeCoordinator`'s map, so without this chain the widget store's
366
+ // own diagnostic for this file stays fully blocking (`isBlocking` true)
367
+ // even after the inline entry demotes. Chain, don't replace: either
368
+ // store's own untouched failure path must not suppress the other's
369
+ // write.
370
+ const demoteInline = inlineEntry.demote;
371
+ const demoteWidget = extra.demote;
372
+ inlineEntry.demote = () => {
373
+ const inlineChanged = demoteInline();
374
+ const widgetChanged = demoteWidget();
375
+ return inlineChanged || widgetChanged;
376
+ };
377
+ continue;
378
+ }
379
+ population.push({
380
+ filePath: extra.filePath,
381
+ stale: false,
382
+ recordedAtMs: extra.recordedAtMs,
383
+ sources: ["lsp"],
384
+ demote: extra.demote,
385
+ });
386
+ }
387
+ counts.total = population.length;
388
+ for (const entry of population) {
389
+ try {
390
+ if (entry.stale) {
391
+ counts.alreadyStale += 1;
392
+ continue;
393
+ }
394
+ // No timestamp baseline (legacy/unstamped record) — we cannot order disk
395
+ // mtimes against the verdict, so leave it untouched (fail toward the
396
+ // existing behavior rather than fabricating a drift signal).
397
+ if (entry.recordedAtMs === undefined) {
398
+ counts.kept += 1;
399
+ continue;
400
+ }
401
+ // #1631 review F4: fail-closed on non-LSP (or unknown) provenance. Import
402
+ // drift only invalidates a language-server verdict; a mixed or non-`"lsp"`
403
+ // `sources` list is kept at full authority rather than demoted.
404
+ const isLspSourced = entry.sources !== undefined &&
405
+ entry.sources.length > 0 &&
406
+ entry.sources.every((source) => source === "lsp");
407
+ if (!isLspSourced) {
408
+ counts.kept += 1;
409
+ continue;
410
+ }
411
+ const { drifted, truncated } = await detectDrift(cwd, entry.filePath, entry.recordedAtMs, resolveForwardImports, turnIndex);
412
+ if (truncated)
413
+ counts.truncatedImports += 1;
414
+ if (drifted.length > 0) {
415
+ entry.demote();
416
+ counts.revalidated += 1;
417
+ }
418
+ else {
419
+ counts.kept += 1;
420
+ }
421
+ }
422
+ catch {
423
+ // Per-entry failure: keep the entry as-is.
424
+ counts.kept += 1;
425
+ }
426
+ }
427
+ return counts;
428
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Past-EOF validity gate for the turn-end inline-blocker store (#1641
3
+ * remainder).
4
+ *
5
+ * #1664 wired `demotePastEofDiagnostics` into every surface that renders a
6
+ * `WidgetDiagnostic` — widget-state, `lens_diagnostics` (`mode=all`/`full`),
7
+ * and the TUI blocking-render loop. It deliberately deferred one surface:
8
+ * `RuntimeCoordinator`'s `_pendingInlineBlockers` map (the "Unresolved from
9
+ * this turn" blocker re-served at `turn_end`, `clients/runtime-turn.ts`).
10
+ * That store's records (`InlineBlockerRecord`) never carried a `WidgetDiagnostic`
11
+ * — just a rendered `summary` string — so the shared gate had nothing to run
12
+ * against. #1633 was meant to add the structured line field and never did;
13
+ * it merged with only the dependency-drift gate (`blocker-freshness.ts`)
14
+ * wired in. This module is that missing half.
15
+ *
16
+ * Design choice (#1641 remainder, decided here): carry a STRUCTURED `lines`
17
+ * field on `InlineBlockerRecord`, captured at write time from the same
18
+ * `Diagnostic[]` the summary text was rendered from
19
+ * (`dispatchResult.blockers` in `pipeline.ts`), rather than regex-parsing the
20
+ * cited line back out of the rendered summary prose. Re-deriving a number
21
+ * that was already in hand one call earlier — by parsing a string built for
22
+ * human display, not machine reading — is exactly the re-derivation-vs-
23
+ * correlation screen's failure shape: any future change to the summary's
24
+ * wording (a different prefix, a renamed tool label) would silently break
25
+ * the parse with no type error. Threading the number through is one field on
26
+ * an existing interface and a few lines at each hop.
27
+ *
28
+ * Composition with #1631's dependency-drift gate (`blocker-freshness.ts`,
29
+ * also swept at `turn_end`): each gate demotes with its own
30
+ * `InlineBlockerRecord.staleReason` and only re-derives (heals) entries
31
+ * carrying its OWN reason — `setInlineBlockerPastEofStale` refuses to touch a
32
+ * `"dependency-drift"` demotion, and this gate skips a record already
33
+ * demoted for that reason before it even computes a verdict. The two gates
34
+ * therefore layer rather than race: whichever ran first "wins" the record for
35
+ * this turn, and the other's condition is picked up again once the first
36
+ * heals or a fresh dispatch/retire clears the record outright.
37
+ *
38
+ * RE-ARMS, never latches (mirrors `demotePastEofDiagnostics`): the verdict is
39
+ * recomputed from the file's CURRENT line count on every turn end, so a
40
+ * transient shrink-then-restore un-demotes on its own next turn without any
41
+ * session-lifetime flag to reset.
42
+ */
43
+ import { demotePastEofDiagnostics, resyncDocumentOnPastEof, } from "./diagnostic-line-freshness.js";
44
+ /**
45
+ * Past-EOF sweep over the cached inline blockers. Called at turn end
46
+ * alongside (and, per the module doc, ordered before) the dependency-drift
47
+ * sweep — a cheap `statSync` per file is worth paying first so the pricier
48
+ * import-parsing sweep can skip anything this gate already took out of the
49
+ * authoritative channel.
50
+ *
51
+ * Never throws: a per-entry failure leaves that entry untouched rather than
52
+ * failing the turn end.
53
+ */
54
+ export function sweepInlineBlockerPastEof(runtime, cwd, options) {
55
+ const counts = {
56
+ total: 0,
57
+ checked: 0,
58
+ demoted: 0,
59
+ deadLinesByPath: new Map(),
60
+ };
61
+ const resync = options?.resync ?? resyncDocumentOnPastEof;
62
+ let entries;
63
+ try {
64
+ entries = runtime.getInlineBlockersSnapshot();
65
+ }
66
+ catch {
67
+ return counts;
68
+ }
69
+ counts.total = entries.length;
70
+ for (const entry of entries) {
71
+ try {
72
+ if (!entry.lines || entry.lines.length === 0)
73
+ continue;
74
+ // Not this gate's demotion to heal or re-assert (#1641 composition
75
+ // rule — see module doc).
76
+ if (entry.stale && entry.staleReason !== "past-eof")
77
+ continue;
78
+ counts.checked += 1;
79
+ // Reuse the shared gate's per-line verdict machinery: feed it one
80
+ // synthetic "diagnostic" per cited line, sharing this record's
81
+ // current stale/staleReason so it can detect the rising/falling
82
+ // edge itself instead of this module re-implementing that logic.
83
+ const probes = entry.lines.map((line) => ({
84
+ line,
85
+ stale: entry.stale,
86
+ staleReason: entry.staleReason,
87
+ }));
88
+ // `demotePastEofDiagnostics` already logs (`diagnostic_past_eof`) and
89
+ // fires `resync` exactly once per call on a rising edge — reusing it
90
+ // here (rather than re-implementing that bookkeeping) keeps this
91
+ // gate's telemetry and resync-on-demotion behavior identical to the
92
+ // widget/lens surfaces #1664 already wired.
93
+ const { diagnostics } = demotePastEofDiagnostics({
94
+ store: "inline-blocker",
95
+ cwd,
96
+ filePath: entry.filePath,
97
+ diagnostics: probes,
98
+ lineCountCache: options?.lineCountCache,
99
+ resync,
100
+ });
101
+ // The record cites any of its lines being past EOF as a whole-record
102
+ // demotion — one summary string, not one flag per line.
103
+ const isPastEof = diagnostics.some((d) => d.stale);
104
+ if (isPastEof) {
105
+ // #1944: record WHICH cited lines died, before the transition check
106
+ // below short-circuits. A record demoted on an earlier turn does not
107
+ // transition, yet its dead lines are exactly what the delivery
108
+ // surface must annotate and retire on.
109
+ counts.deadLinesByPath.set(entry.filePath, diagnostics
110
+ .filter((d) => d.stale)
111
+ .map((d) => d.line)
112
+ .filter((line) => typeof line === "number"));
113
+ }
114
+ const transitioned = runtime.setInlineBlockerPastEofStale(entry.filePath, isPastEof);
115
+ if (!transitioned)
116
+ continue;
117
+ // Only the rising edge is countable here — see `BlockerPastEofCounts`
118
+ // for why the falling edge cannot occur on this store anymore. The
119
+ // call below still un-demotes, so the STORE stays honest even though
120
+ // nothing can observe the transition.
121
+ if (isPastEof)
122
+ counts.demoted += 1;
123
+ }
124
+ catch {
125
+ // Per-entry failure: leave the entry as-is.
126
+ }
127
+ }
128
+ return counts;
129
+ }
@@ -47,6 +47,14 @@ async function logBootstrapFailures(failures) {
47
47
  // the per-analyzer lines above already named the failures
48
48
  }
49
49
  }
50
+ /**
51
+ * Every per-client load below is individually fail-soft (`load`/`loadList`
52
+ * degrade to a stub instead of throwing), so this promise is not expected to
53
+ * reject in practice. It is still memoized with eviction-on-rejection —
54
+ * consistent with the other lazy-import memos (#1570) — so a genuinely
55
+ * unexpected throw (e.g. `logBootstrapFailures`) cannot latch a permanently
56
+ * rejected bootstrap for the rest of the process.
57
+ */
50
58
  export function loadBootstrapClients() {
51
59
  bootstrapPromise ??= (async () => {
52
60
  const failures = [];
@@ -112,6 +120,9 @@ export function loadBootstrapClients() {
112
120
  agentBehaviorClient,
113
121
  deadCodeClients,
114
122
  };
115
- })();
123
+ })().catch((err) => {
124
+ bootstrapPromise = null;
125
+ throw err;
126
+ });
116
127
  return bootstrapPromise;
117
128
  }
@@ -57,9 +57,18 @@ async function releaseQuarantineLock(lockPath, token) {
57
57
  }
58
58
  }
59
59
  function quarantineOwnerIsStale(owner, staleMs) {
60
- return Number.isInteger(owner.pid) && owner.pid > 0 &&
61
- Number.isFinite(owner.createdAt) &&
62
- (!ownerPidIsLive(owner.pid) || Date.now() - owner.createdAt > staleMs);
60
+ // #1816: the two staleness signals are INDEPENDENT, and the original
61
+ // conjunction made the dead-PID one unreachable. An `owner.json` with a
62
+ // valid PID but a missing or non-numeric `createdAt` (an older writer, a
63
+ // half-written file, a hand-edited one) short-circuited on the
64
+ // `Number.isFinite` guard, so a dead owner never reclaimed and the lock
65
+ // stayed poisoned for the life of the directory. This is
66
+ // `installer/index.ts:173`'s predicate: a dead PID reclaims regardless of
67
+ // `createdAt`, and an aged lock reclaims regardless of what the PID says.
68
+ const pidUsable = Number.isInteger(owner.pid) && owner.pid > 0;
69
+ if (pidUsable && !ownerPidIsLive(owner.pid))
70
+ return true;
71
+ return (Number.isFinite(owner.createdAt) && Date.now() - owner.createdAt > staleMs);
63
72
  }
64
73
  async function reclaimQuarantineLock(lockPath, staleMs) {
65
74
  const quarantined = quarantinePath(lockPath, `reclaim-${Date.now()}-${randomUUID()}`);