@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,175 @@
1
+ /**
2
+ * #1618: a full workspace sweep (`LSPService.runWorkspaceDiagnostics`, the
3
+ * engine behind `lens_diagnostics mode=full`) grants itself a wall-clock
4
+ * ceiling (`FULL_SCAN_WALL_CLOCK_MS`, see {@link getFullScanWallClockMs})
5
+ * that can outlive the detached LSP idle-reset timer armed by
6
+ * `clients/runtime-turn.ts`'s `handleTurnEnd` on a file-less turn. Nothing
7
+ * previously stopped that timer from firing mid-sweep and destroying the
8
+ * very service the sweep was actively touching — every file the sweep had
9
+ * not yet reached then failed with zero trace and got mislabeled as budget
10
+ * exhaustion.
11
+ *
12
+ * This module is the shared hold: `runWorkspaceDiagnostics` acquires it for
13
+ * its own lifetime (try/finally, so a throw or an aborted sweep still
14
+ * releases it) and the idle-reset timer consults it before firing. Holds are
15
+ * tracked as TOKENS (acquire-time-stamped), not a bare counter — two
16
+ * overlapping `lens_diagnostics mode=full` calls (or a sweep started while
17
+ * another is still finishing) must each be free to release independently
18
+ * without the other's hold being dropped early.
19
+ *
20
+ * A leaked hold that never re-arms idle reset is the inverse defect (state
21
+ * that must re-arm cannot hide behind a stuck guard), so this module screens
22
+ * against it on TWO axes, matching the repo's own session-boundary +
23
+ * bounded-lifetime conventions:
24
+ * 1. `clearWorkspaceSweepHoldForSessionStart` — `resetLSPService({reason:
25
+ * "session_start"})` clears every held token unconditionally. A session
26
+ * boundary means no in-flight sweep from a PRIOR generation is still
27
+ * meaningful to wait on.
28
+ * 2. `reapStaleHolds` (internal, called from every liveness check) —
29
+ * force-releases any token older than {@link getWorkspaceSweepMaxHoldAgeMs},
30
+ * derived from the sweep's own wall-clock ceiling plus a safety margin.
31
+ * A real sweep is either done or self-aborted well within that window
32
+ * (`tools/lens-diagnostics.ts` races `FULL_SCAN_WALL_CLOCK_MS`), so a
33
+ * token still held past it can only be a bug — never a legitimate long
34
+ * sweep — and gets force-released with its own distinct log record.
35
+ *
36
+ * `getFullScanWallClockMs` also lives here (rather than duplicated between
37
+ * `tools/lens-diagnostics.ts` and `clients/runtime-turn.ts`) so the idle-reset
38
+ * delay on every path (base AND the subagent/budget-shortened paths) can be
39
+ * DERIVED from the sweep's own ceiling instead of the constants drifting
40
+ * apart again (#1618 acceptance criterion 6).
41
+ */
42
+ import { logLatency } from "../latency-logger.js";
43
+ /** Extra headroom beyond the sweep's own wall-clock ceiling before an
44
+ * idle-reset delay (or the max-hold-age failsafe) trusts that a real sweep
45
+ * must be done. Single source for both derivations below AND for every
46
+ * idle-reset path `clients/runtime-turn.ts` derives (base, subagent, and
47
+ * budget-shortened) — never a second independently-tunable literal. */
48
+ export const SWEEP_IDLE_SAFETY_MARGIN_MS = 60_000;
49
+ let holds = new Map(); // holdId -> acquiredAt (Date.now())
50
+ let nextHoldId = 1;
51
+ let idleWaiters = [];
52
+ function flushIdleWaitersIfEmpty() {
53
+ if (holds.size > 0)
54
+ return;
55
+ if (idleWaiters.length === 0)
56
+ return;
57
+ const waiters = idleWaiters;
58
+ idleWaiters = [];
59
+ for (const waiter of waiters) {
60
+ try {
61
+ waiter();
62
+ }
63
+ catch {
64
+ // Best-effort — a waiter failure must never propagate into the
65
+ // releasing/reaping call stack.
66
+ }
67
+ }
68
+ }
69
+ /**
70
+ * A held token past this age can only be a bug (leaked release, a sweep that
71
+ * hung outside its own abort signal, …) — a legitimate sweep is done or
72
+ * self-aborted well before it. Derived, not a separate literal, so it can't
73
+ * drift out of sync with `getFullScanWallClockMs` the way the idle-reset
74
+ * timer and the sweep's own ceiling once did.
75
+ */
76
+ export function getWorkspaceSweepMaxHoldAgeMs() {
77
+ return getFullScanWallClockMs() + SWEEP_IDLE_SAFETY_MARGIN_MS;
78
+ }
79
+ function reapStaleHolds() {
80
+ if (holds.size === 0)
81
+ return;
82
+ const maxAgeMs = getWorkspaceSweepMaxHoldAgeMs();
83
+ const now = Date.now();
84
+ for (const [holdId, acquiredAt] of holds) {
85
+ const ageMs = now - acquiredAt;
86
+ if (ageMs <= maxAgeMs)
87
+ continue;
88
+ holds.delete(holdId);
89
+ logLatency({
90
+ type: "phase",
91
+ phase: "lsp_workspace_sweep_hold_force_released",
92
+ filePath: "",
93
+ durationMs: ageMs,
94
+ metadata: { holdId, maxHoldAgeMs: maxAgeMs },
95
+ });
96
+ }
97
+ flushIdleWaitersIfEmpty();
98
+ }
99
+ /**
100
+ * Acquire the hold for one `runWorkspaceDiagnostics` call. Returns a release
101
+ * function; callers MUST call it from a `finally` block so an overlapping or
102
+ * throwing sweep still releases its hold. Idempotent — calling the returned
103
+ * function more than once past the first call, or after the hold was already
104
+ * force-released (session reset / max-age reap), is a no-op.
105
+ */
106
+ export function acquireWorkspaceSweepHold() {
107
+ const holdId = nextHoldId++;
108
+ holds.set(holdId, Date.now());
109
+ return () => {
110
+ if (!holds.delete(holdId))
111
+ return;
112
+ flushIdleWaitersIfEmpty();
113
+ };
114
+ }
115
+ /** True while at least one `runWorkspaceDiagnostics` sweep is in flight.
116
+ * Reaps stale holds first, so a leaked/hung hold can't permanently read as
117
+ * "active" past its own max age. */
118
+ export function isWorkspaceSweepActive() {
119
+ reapStaleHolds();
120
+ return holds.size > 0;
121
+ }
122
+ /**
123
+ * Run `cb` once the hold count next returns to zero. Runs synchronously and
124
+ * immediately when no sweep is active right now — the common case, and the
125
+ * one every non-sweep idle-reset arm/fire takes.
126
+ */
127
+ export function runWhenWorkspaceSweepIdle(cb) {
128
+ reapStaleHolds();
129
+ if (holds.size === 0) {
130
+ cb();
131
+ return;
132
+ }
133
+ idleWaiters.push(cb);
134
+ }
135
+ /**
136
+ * `resetLSPService({reason: "session_start"})` calls this: a session
137
+ * boundary means no hold from a PRIOR generation is still meaningful to wait
138
+ * on, so every held token is cleared unconditionally (state that must
139
+ * re-arm at session_start cannot hide behind a leaked/stuck guard from the
140
+ * generation before it). Only logs when there was actually something to
141
+ * clear — the common case (no sweep in flight at session boundary) stays
142
+ * silent.
143
+ */
144
+ export function clearWorkspaceSweepHoldForSessionStart() {
145
+ if (holds.size === 0)
146
+ return;
147
+ logLatency({
148
+ type: "phase",
149
+ phase: "lsp_workspace_sweep_hold_session_reset",
150
+ filePath: "",
151
+ durationMs: 0,
152
+ metadata: { clearedHolds: holds.size },
153
+ });
154
+ holds.clear();
155
+ flushIdleWaitersIfEmpty();
156
+ }
157
+ /** Test-only: reset the module-scope hold state between tests. */
158
+ export function _resetWorkspaceSweepHoldForTests() {
159
+ holds = new Map();
160
+ idleWaiters = [];
161
+ nextHoldId = 1;
162
+ }
163
+ /**
164
+ * Wall-clock ceiling for one `lens_diagnostics mode=full` scan
165
+ * (`runWorkspaceDiagnostics` plus its concurrent project-runner/analyzer
166
+ * work). Single source for `tools/lens-diagnostics.ts` (the sweep caller),
167
+ * `clients/runtime-turn.ts` (every idle-reset path, whose delay must outlive
168
+ * it), and this module's own max-hold-age failsafe — lazy env read, never
169
+ * memoized, matching this codebase's house style so a test can flip the
170
+ * override mid-run.
171
+ */
172
+ export function getFullScanWallClockMs() {
173
+ const raw = Number(process.env.PI_LENS_LENS_DIAGNOSTICS_FULL_TIMEOUT_MS);
174
+ return Number.isFinite(raw) && raw > 0 ? raw : 300_000; // 5 min default
175
+ }
@@ -1,7 +1,8 @@
1
1
  /** Shared lazy LSP service seam (#1394). */
2
- let lspPromise;
2
+ import { createLazyImport } from "./lazy-import.js";
3
+ const lazyLsp = createLazyImport(() => import("./lsp/index.js"));
3
4
  export function warmLspService() {
4
- return (lspPromise ??= import("./lsp/index.js"));
5
+ return lazyLsp.get();
5
6
  }
6
7
  export function loadLspService() {
7
8
  return warmLspService();
@@ -32,7 +32,12 @@ import { handleTurnEnd } from "../runtime-turn.js";
32
32
  import { createMcpHost } from "./host-shim.js";
33
33
  let contextPromise;
34
34
  let resolvedRuntime;
35
- /** Lazily build (once) the persistent session context shared across MCP calls. */
35
+ /**
36
+ * Lazily build (once) the persistent session context shared across MCP
37
+ * calls. A rejected build (e.g. `loadBootstrapClients` hitting a transient
38
+ * fs error) must not poison every later MCP call for the rest of the
39
+ * process — evict the memo on rejection so the next call retries (#1570).
40
+ */
36
41
  export function getMcpSessionContext() {
37
42
  contextPromise ??= (async () => {
38
43
  const context = {
@@ -43,7 +48,10 @@ export function getMcpSessionContext() {
43
48
  };
44
49
  resolvedRuntime = context.runtime;
45
50
  return context;
46
- })();
51
+ })().catch((err) => {
52
+ contextPromise = undefined;
53
+ throw err;
54
+ });
47
55
  return contextPromise;
48
56
  }
49
57
  /** Synchronous peek at the session runtime, for construction sites that cannot
@@ -162,6 +162,14 @@ async function extractFile(absPath, languageId, content) {
162
162
  let callbacks = [];
163
163
  let callbackError;
164
164
  try {
165
+ // SAFETY: `ModuleReportNode` is this module's own structural
166
+ // subset of a tree-sitter node: `type`, `text`, `children`,
167
+ // `parent`, `isNamed`, and the two positions. A real node
168
+ // implements all of it, but the two types are declared in
169
+ // unrelated packages so TypeScript sees no relation. The cast
170
+ // asserts only that structural subset; widen
171
+ // `ModuleReportNode` past what tree-sitter nodes provide and
172
+ // it stops holding.
165
173
  callbacks = extractCallbacks(tree.rootNode, owners, languageId, warnings);
166
174
  }
167
175
  catch (error) {
@@ -1330,10 +1338,19 @@ export async function moduleReport(file, cwd, options) {
1330
1338
  let graph;
1331
1339
  let graphFileCap;
1332
1340
  let callGraphIdentity;
1341
+ // #1961 review F4: the blind read now serves a snapshot stamped at a
1342
+ // different commit. project_report says so in its trust notes; this report
1343
+ // reads through the SAME accessor, so without this it would present the
1344
+ // other branch's importers and call edges as current, under a builtAt that
1345
+ // looks fresh. Computed per call, never cached.
1346
+ let revisionDriftNote;
1333
1347
  try {
1334
- const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, } = await import("./review-graph/builder.js");
1348
+ const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote, } = await import("./review-graph/builder.js");
1335
1349
  graph = getCachedReviewGraph(cwd);
1336
1350
  graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
1351
+ const drift = graph ? getReviewGraphRevisionDrift(cwd) : undefined;
1352
+ if (drift)
1353
+ revisionDriftNote = formatReviewGraphRevisionDriftNote(drift);
1337
1354
  callGraphIdentity = graph
1338
1355
  ? (() => {
1339
1356
  const identity = getReviewGraphCacheIdentity(cwd, graph);
@@ -1366,6 +1383,10 @@ export async function moduleReport(file, cwd, options) {
1366
1383
  const api = topLevel.filter((entry) => entry.exported);
1367
1384
  const internal = topLevel.filter((entry) => !entry.exported);
1368
1385
  const warnings = [...(extractionWarnings ?? [])];
1386
+ // Every graph-derived section below (who-uses-this, blast radius, call
1387
+ // edges) came from the served snapshot, so the caveat belongs with them.
1388
+ if (revisionDriftNote)
1389
+ warnings.push(revisionDriftNote);
1369
1390
  if (callbackError) {
1370
1391
  warnings.push(`Failed to extract callbacks: ${callbackError}`);
1371
1392
  logLatency({
@@ -131,6 +131,35 @@ export function getRegisteredLogFiles() {
131
131
  export function _resetRegisteredLogFilesForTest() {
132
132
  registeredLogFiles.clear();
133
133
  }
134
+ /**
135
+ * Snapshot of unrecovered write losses, one entry per sink that has any
136
+ * (#1970). Pure in-memory read — no I/O, so a caller (`degradation-ledger.ts`)
137
+ * can fold this into a durable ledger entry without this module ever writing
138
+ * a record about its own failure through the sink that is failing. See
139
+ * `NdjsonWriterState.writeFailures` for the recursion-hazard rationale.
140
+ */
141
+ export function getSinkWriteFailures() {
142
+ if (!ndjsonGlobalState)
143
+ return [];
144
+ const result = [];
145
+ for (const state of ndjsonGlobalState.writers.values()) {
146
+ if (state.writeFailures > 0) {
147
+ result.push({ file: state.file, droppedCount: state.writeFailures });
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ /**
153
+ * Session-boundary reset (catalog shape 17: a process-lifetime latch must
154
+ * re-arm at session_start). Wired into `resetDegradationLedger()` so both
155
+ * reset together; also used directly by tests.
156
+ */
157
+ export function resetSinkWriteFailures() {
158
+ if (!ndjsonGlobalState)
159
+ return;
160
+ for (const state of ndjsonGlobalState.writers.values())
161
+ state.writeFailures = 0;
162
+ }
134
163
  function requireCurrentGlobalState() {
135
164
  if (ndjsonGlobalState)
136
165
  return ndjsonGlobalState;
@@ -159,17 +188,46 @@ function assertCompatibleWriterOptions(existing, maxBytes, backupPath) {
159
188
  throw new Error(`createNdjsonLogger: incompatible options for shared path ${existing.file}; ` +
160
189
  `the first writer's maxBytes/backupPath must be reused`);
161
190
  }
191
+ function writeQueueItemSync(state, item) {
192
+ if (item.kind === "truncate") {
193
+ fs.writeFileSync(state.file, "");
194
+ }
195
+ else {
196
+ rotateIfNeeded(state);
197
+ fs.appendFileSync(state.file, item.line);
198
+ }
199
+ }
200
+ /**
201
+ * The pi-analyze #15 shape (#1970): a write that throws — including the
202
+ * `ERR_STREAM_DESTROYED` a torn-down sink produces — gets one reopen-and-
203
+ * retry before it counts as a loss. There is no persistent handle to close
204
+ * here (every write already opens, writes, and closes in one call), so
205
+ * "reopen" means dropping the cached `ensuredDir` assumption and
206
+ * re-verifying the parent directory before the retry — the one piece of
207
+ * cross-write state this module holds that a destroyed sink could have
208
+ * invalidated. An unrecovered write is counted, never thrown or retried a
209
+ * second time (`applyQueueItemSync`, `applyQueueItemAsync`, and the batched
210
+ * write in `drainLoop` skip DIFFERENT amounts of work per queue item, so
211
+ * each keeps its own copy of this two-step shape rather than sharing one
212
+ * generic retry wrapper across sync/async).
213
+ */
162
214
  function applyQueueItemSync(state, item) {
163
215
  ensureDirSync(state);
164
- runBestEffort(() => {
165
- if (item.kind === "truncate") {
166
- fs.writeFileSync(state.file, "");
167
- }
168
- else {
169
- rotateIfNeeded(state);
170
- fs.appendFileSync(state.file, item.line);
171
- }
172
- });
216
+ try {
217
+ writeQueueItemSync(state, item);
218
+ return;
219
+ }
220
+ catch {
221
+ // fall through to the one reopen-and-retry
222
+ }
223
+ state.ensuredDir = false;
224
+ ensureDirSync(state);
225
+ try {
226
+ writeQueueItemSync(state, item);
227
+ }
228
+ catch {
229
+ state.writeFailures += 1;
230
+ }
173
231
  }
174
232
  function flushStateSync(state) {
175
233
  // Drain the in-memory queue synchronously — safe at process exit.
@@ -208,6 +266,9 @@ function createWriterState(file, maxBytes, backupPath) {
208
266
  assertCompatibleWriterOptions(existing, maxBytes, backupPath);
209
267
  if (!exitFlushers.has(existing.exitFlusher))
210
268
  registerWriter(existing);
269
+ // A state adopted from a pre-#1970 module graph predates this field.
270
+ if (typeof existing.writeFailures !== "number")
271
+ existing.writeFailures = 0;
211
272
  return existing;
212
273
  }
213
274
  const state = {};
@@ -220,6 +281,7 @@ function createWriterState(file, maxBytes, backupPath) {
220
281
  state.syncRepairItems = null;
221
282
  state.ensuredDir = false;
222
283
  state.exitFlusher = () => flushStateSync(state);
284
+ state.writeFailures = 0;
223
285
  globalState.writers.set(file, state);
224
286
  registerWriter(state);
225
287
  return state;
@@ -258,23 +320,36 @@ function rotateIfNeeded(state) {
258
320
  // no file yet, or rename raced — nothing to rotate
259
321
  }
260
322
  }
323
+ async function writeQueueItemAsync(state, item) {
324
+ if (item.kind === "truncate") {
325
+ await fs.promises.writeFile(state.file, "");
326
+ }
327
+ else {
328
+ // Rotation is deliberately synchronous here. This function is only
329
+ // reached from the already-deferred drain, and keeping stat/rm/rename
330
+ // in one synchronous section prevents flushSync from racing a late
331
+ // async rename after it has written new data.
332
+ rotateIfNeeded(state);
333
+ await fs.promises.appendFile(state.file, item.line);
334
+ }
335
+ }
336
+ /** See `applyQueueItemSync`'s doc comment for the reopen-and-retry-once shape. */
261
337
  async function applyQueueItemAsync(state, item) {
262
338
  await ensureDirAsync(state);
263
339
  try {
264
- if (item.kind === "truncate") {
265
- await fs.promises.writeFile(state.file, "");
266
- }
267
- else {
268
- // Rotation is deliberately synchronous here. This function is only
269
- // reached from the already-deferred drain, and keeping stat/rm/rename
270
- // in one synchronous section prevents flushSync from racing a late
271
- // async rename after it has written new data.
272
- rotateIfNeeded(state);
273
- await fs.promises.appendFile(state.file, item.line);
274
- }
340
+ await writeQueueItemAsync(state, item);
341
+ return;
275
342
  }
276
343
  catch {
277
- // telemetry is best-effort
344
+ // fall through to the one reopen-and-retry
345
+ }
346
+ state.ensuredDir = false;
347
+ await ensureDirAsync(state);
348
+ try {
349
+ await writeQueueItemAsync(state, item);
350
+ }
351
+ catch {
352
+ state.writeFailures += 1;
278
353
  }
279
354
  }
280
355
  async function drainLoop(state) {
@@ -292,7 +367,7 @@ async function drainLoop(state) {
292
367
  ? [item]
293
368
  : state.queue.slice(0, pendingEnd);
294
369
  state.inFlightBatch = pending;
295
- try {
370
+ const writeBatch = async () => {
296
371
  if (item.kind === "truncate") {
297
372
  await fs.promises.writeFile(state.file, "");
298
373
  }
@@ -305,9 +380,25 @@ async function drainLoop(state) {
305
380
  .map((queued) => queued.line)
306
381
  .join(""));
307
382
  }
383
+ };
384
+ try {
385
+ await writeBatch();
308
386
  }
309
387
  catch {
310
- // telemetry is best-effort
388
+ // Reopen-and-retry once (#1970, pi-analyze #15 shape): a destroyed
389
+ // sink (ERR_STREAM_DESTROYED) or a directory that vanished mid-session
390
+ // gets exactly one recovery attempt before the batch counts as a loss.
391
+ state.ensuredDir = false;
392
+ await ensureDirAsync(state);
393
+ try {
394
+ await writeBatch();
395
+ }
396
+ catch {
397
+ // Unrecovered: count every line in this batch as dropped. Purely
398
+ // in-memory — see `writeFailures`'s doc comment for why this must
399
+ // never itself attempt a durable write through this same sink.
400
+ state.writeFailures += pending.length;
401
+ }
311
402
  }
312
403
  for (const written of pending) {
313
404
  // flushSync may have drained this prefix while the append is in
@@ -42,13 +42,19 @@
42
42
  * So `opengrepResultToProjectDiagnostics` deliberately applies NO suppression
43
43
  * filtering of its own — doing so would be redundant at best.
44
44
  *
45
- * Refs: #584, #111 (opengrep adoption), #387 (workspace-sweep serialization)
45
+ * Scope note (#1562 class fix): the CLI scan is handed `--exclude` per shared
46
+ * scratch/cache tree (`scratch-tree-policy.ts`) so a directory that isn't
47
+ * gitignored (opengrep's own default exclusion) still doesn't reach the
48
+ * agent as a finding — same `EXCLUDED_DIRS`-derived list gitleaks/trivy use.
49
+ *
50
+ * Refs: #584, #111 (opengrep adoption), #387 (workspace-sweep serialization), #1562
46
51
  */
47
52
  import * as fs from "node:fs";
48
53
  import * as os from "node:os";
49
54
  import * as path from "node:path";
50
55
  import { mkdtempSync } from "node:fs";
51
56
  import { resolveOpengrepConfig } from "./opengrep-config.js";
57
+ import { getScratchTreeDirNames } from "./scratch-tree-policy.js";
52
58
  import { safeSpawnAsync } from "./safe-spawn.js";
53
59
  import { SecurityScanClient } from "./security-scan-client.js";
54
60
  const EMPTY_RESULT = {
@@ -116,6 +122,14 @@ export class OpengrepClient extends SecurityScanClient {
116
122
  "--no-error",
117
123
  "--quiet",
118
124
  "--disable-version-check",
125
+ // #1562 class fix: opengrep's own `.gitignore` respect covers the
126
+ // common case (scratch trees are usually gitignored), but not a
127
+ // scratch/cache tree that ISN'T (e.g. an un-gitignored worktree
128
+ // cache) — `--exclude` is semgrep-compatible, so a slash-free
129
+ // pattern matches that directory name anywhere in the tree,
130
+ // independent of gitignore. Same `EXCLUDED_DIRS`-derived list
131
+ // gitleaks/trivy use, so the three scanners can't drift apart.
132
+ ...getScratchTreeDirNames().flatMap((name) => ["--exclude", name]),
119
133
  cwd,
120
134
  ], { cwd, timeout: SCAN_TIMEOUT_MS });
121
135
  if (result.error) {