@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

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 (389) hide show
  1. package/README.md +5 -5
  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 +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -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 +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  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 +22 -0
  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-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Capture-generation-before-await, check-after — as one primitive (#1754).
3
+ *
4
+ * The shape: a write that follows an `await` must prove the world it captured
5
+ * still exists. A session reset, a cache refresh, or a newer request for the
6
+ * same key can land while the write's producer is in flight, and the late
7
+ * write then publishes an answer the caller no longer wants — or, in the
8
+ * eviction direction, deletes an entry a SUCCESSOR already installed.
9
+ *
10
+ * This module was extracted after the pattern was hand-rolled four times and
11
+ * its absence was a review finding twice:
12
+ *
13
+ * - `clients/dispatch/runners/utils/runner-helpers.ts` held the guarded form
14
+ * at one site and the unguarded form at another IN THE SAME FILE; #1674's
15
+ * F5 round copied the guard by hand to three more write sites.
16
+ * - `clients/lsp/client.ts`'s per-(path, identifier) pull sequences (#1682),
17
+ * whose first guard was vacuous until a review round bound it.
18
+ * - `clients/lsp/workspace-diagnostics-cache.ts`'s per-cwd epochs (#1669),
19
+ * where the epoch guarded `persist()` but not `lookup()`.
20
+ * - `clients/review-graph/builder.ts`'s workspace-cache epoch, the shape the
21
+ * one above was modelled on.
22
+ *
23
+ * Two things every one of those needed and at least one got wrong:
24
+ *
25
+ * 1. The check must run at WRITE time, not only at capture time.
26
+ * 2. The stale branch must be OBSERVABLE. A silently dropped write looks
27
+ * exactly like a write that never happened, which is why the vacuous
28
+ * guards survived review. `guardedWrite` emits one bounded degradation
29
+ * record per (source, subject) with the count of repeats, so a dogfood
30
+ * session can tell "the guard is working" from "the guard never fires".
31
+ *
32
+ * ## Scope and lifetime
33
+ *
34
+ * A `GenerationSource` is a plain counter owned by the RESETTING SEAM — the
35
+ * function that already clears the state the generation protects. This module
36
+ * holds no session state of its own: sources live in the modules that create
37
+ * them, and their reset policy is that module's declaration in
38
+ * `tests/support/session-state-registry.ts`. The only module-level state here
39
+ * is the name registry below, which exists so the #1754 sweep can ask "which
40
+ * stores declare a generation" instead of guessing from identifier names.
41
+ *
42
+ * ## Cost
43
+ *
44
+ * `isCurrent()` on an unkeyed source is a field read and a compare. On a
45
+ * keyed map it is a compare while nothing has been invalidated, and a
46
+ * normalize plus one `Map.get` once something has. The hand-rolled form it
47
+ * replaces normalized on EVERY call, and this repo's normalizer runs
48
+ * `realpathSync.native` on Windows, so an uninterrupted sweep pays that
49
+ * syscall once rather than once per file. See
50
+ * `GenerationMapOptions.normalizeKey` for the correctness reason the slow
51
+ * path exists at all.
52
+ */
53
+ import { incrementDegradationCount } from "./degradation-ledger.js";
54
+ /**
55
+ * Names of every generation-carrying store created through this module.
56
+ *
57
+ * Registration is by CONSTRUCTION: you cannot make a `GenerationSource`
58
+ * without appearing here. `tests/clients/generation-guard-sweep.test.ts`
59
+ * reads this to enforce the ratchet — a file that hand-rolls the pattern
60
+ * instead of declaring it here must carry an explicit exemption reason.
61
+ *
62
+ * Process-lifetime and bounded: names are compile-time literals from module
63
+ * initialization, so this cannot grow with workload.
64
+ */
65
+ const declaredSources = new Set();
66
+ const MAX_DECLARED_SOURCES = 128;
67
+ function declare(name) {
68
+ if (!name.trim()) {
69
+ throw new Error("generation source needs a non-empty name");
70
+ }
71
+ if (declaredSources.size < MAX_DECLARED_SOURCES)
72
+ declaredSources.add(name);
73
+ return name;
74
+ }
75
+ /** Every generation-carrying store name declared this process. */
76
+ export function listDeclaredGenerationSources() {
77
+ return [...declaredSources].sort((a, b) => a.localeCompare(b));
78
+ }
79
+ function recordStaleWrite(sourceName, subject, captured, observed) {
80
+ incrementDegradationCount({
81
+ kind: "generation-guard-stale-write",
82
+ subject: `${sourceName}:${subject}`,
83
+ reason: `write dropped: captured generation ${captured}, current ${observed}`,
84
+ });
85
+ }
86
+ function makeHandle(sourceName, generation, read) {
87
+ return {
88
+ generation,
89
+ isCurrent() {
90
+ return read() === generation;
91
+ },
92
+ guardedWrite(subject, write) {
93
+ const observed = read();
94
+ if (observed !== generation) {
95
+ recordStaleWrite(sourceName, subject, generation, observed);
96
+ return undefined;
97
+ }
98
+ return write();
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * Create a counter for a store invalidated as a whole.
104
+ *
105
+ * `name` appears in the stale-write ledger subject and in the sweep's
106
+ * declaration list, so it must name the STORE, not the module.
107
+ */
108
+ export function createGenerationSource(name) {
109
+ declare(name);
110
+ let generation = 0;
111
+ const read = () => generation;
112
+ return {
113
+ name,
114
+ current: read,
115
+ bump() {
116
+ generation += 1;
117
+ return generation;
118
+ },
119
+ capture() {
120
+ return makeHandle(name, generation, read);
121
+ },
122
+ };
123
+ }
124
+ const DEFAULT_MAX_KEYS = 512;
125
+ /**
126
+ * Create a per-key family of counters.
127
+ *
128
+ * Use this when invalidation is scoped: a cache refresh for ONE cwd must not
129
+ * drop an in-flight write for another, and a newer pull request for one
130
+ * (path, source) pair must not invalidate a different pair's.
131
+ */
132
+ export function createGenerationMap(name, options = {}) {
133
+ declare(name);
134
+ const normalize = options.normalizeKey ?? ((key) => key);
135
+ const maxKeys = Math.max(1, options.maxKeys ?? DEFAULT_MAX_KEYS);
136
+ // Insertion-ordered: the oldest key is evicted first. Re-stamping a key
137
+ // refreshes its position so a hot cwd is not evicted by a burst of
138
+ // one-shot ones.
139
+ const stamps = new Map();
140
+ // ONE ticket counter for the whole map. See GenerationMap's doc comment:
141
+ // per-key counters starting at 0 make eviction and forget() fail OPEN.
142
+ // 0 is reserved for "this key holds no stamp" and is never issued.
143
+ let nextTicket = 0;
144
+ // Counts every mutation that can change what a key's stamp reads: a bump,
145
+ // a forget, an eviction. A handle records this at capture and re-derives
146
+ // its key only when it has moved. See normalizeKey's doc comment.
147
+ let invalidations = 0;
148
+ const read = (key) => stamps.get(key) ?? 0;
149
+ function issue(key) {
150
+ nextTicket += 1;
151
+ stamps.delete(key);
152
+ stamps.set(key, nextTicket);
153
+ while (stamps.size > maxKeys) {
154
+ const oldest = stamps.keys().next().value;
155
+ if (oldest === undefined)
156
+ break;
157
+ stamps.delete(oldest);
158
+ invalidations += 1;
159
+ }
160
+ return nextTicket;
161
+ }
162
+ return {
163
+ name,
164
+ current(key) {
165
+ return read(normalize(key));
166
+ },
167
+ bump(key) {
168
+ invalidations += 1;
169
+ return issue(normalize(key));
170
+ },
171
+ capture(key) {
172
+ const normalized = normalize(key);
173
+ // A first-use capture ISSUES a stamp rather than reading 0. A handle
174
+ // holding 0 would be indistinguishable from one whose key was later
175
+ // forgotten or evicted, which is the fail-open hole this closes.
176
+ const stamp = read(normalized) || issue(normalized);
177
+ const capturedInvalidations = invalidations;
178
+ return makeHandle(`${name}[${normalized}]`, stamp, () => {
179
+ // Fast path: nothing has been invalidated since capture, so no key
180
+ // can have changed stamps and the normalizer need not run again.
181
+ if (invalidations === capturedInvalidations)
182
+ return stamp;
183
+ // Something moved. Re-derive the key from the ORIGINAL string: a
184
+ // normalizer that consults the filesystem can resolve the same
185
+ // input to a different key once the path exists, and a stamp read
186
+ // under the stale spelling would falsely read current.
187
+ return read(normalize(key));
188
+ });
189
+ },
190
+ forget(key) {
191
+ invalidations += 1;
192
+ stamps.delete(normalize(key));
193
+ },
194
+ size() {
195
+ return stamps.size;
196
+ },
197
+ };
198
+ }
199
+ /**
200
+ * Capture the generation, run `fn`, and hand it the handle.
201
+ *
202
+ * Sugar for the common `const handle = source.capture(); ... await ...;
203
+ * handle.guardedWrite(...)` sequence. Use `capture()` directly when the
204
+ * handle must outlive the function that made it — an eviction guard in a
205
+ * `.finally()` is the usual case, and it is what both #1754 migrations do.
206
+ *
207
+ * This ships ahead of its first production caller. Both migrated sites need
208
+ * the handle to outlive its producer, so neither uses this form; it exists
209
+ * because the issue specifies it and because the sites still to migrate
210
+ * (`runtime-coordinator.ts`, `runtime-turn.ts`) write inside the async
211
+ * function that captured. Said plainly rather than left for a reader to
212
+ * discover by grep.
213
+ */
214
+ export async function withGeneration(source, fn) {
215
+ return fn(source.capture());
216
+ }
@@ -1,24 +1,17 @@
1
- import { createHash } from "node:crypto";
2
1
  import * as nodeFs from "node:fs";
3
2
  import * as path from "node:path";
4
3
  import { isPathIgnoredByProject } from "./file-utils.js";
5
4
  import { tokenizeShellCommand } from "./bash-file-access.js";
6
- import { normalizeMapKey } from "./path-utils.js";
7
5
  import { logLatency } from "./latency-logger.js";
8
- const MAX_AFFECTED_FILES = 256;
6
+ import { advisoryFileHash, advisoryPathKey, MAX_ADVISORY_AFFECTED_FILES, snapshotAdvisoryProvenance, } from "./advisory-provenance.js";
9
7
  function resolveGuardPath(filePath, cwd) {
10
8
  return path.resolve(cwd, filePath);
11
9
  }
12
10
  function guardPathKey(filePath, cwd) {
13
- return normalizeMapKey(resolveGuardPath(filePath, cwd));
11
+ return advisoryPathKey(filePath, cwd);
14
12
  }
15
13
  function fileFingerprint(filePath) {
16
- try {
17
- return createHash("sha256").update(nodeFs.readFileSync(filePath)).digest("hex");
18
- }
19
- catch (err) {
20
- return `unreadable:${err.code ?? "unknown"}`;
21
- }
14
+ return advisoryFileHash(filePath);
22
15
  }
23
16
  function currentFileFingerprint(filePath) {
24
17
  try {
@@ -31,19 +24,11 @@ function currentFileFingerprint(filePath) {
31
24
  : `unreadable:${err.code ?? "unknown"}`;
32
25
  }
33
26
  }
34
- function snapshotFileHashes(files, cwd) {
35
- const hashes = {};
36
- for (const file of files) {
37
- const resolved = resolveGuardPath(file, cwd);
38
- hashes[guardPathKey(resolved, cwd)] = currentFileFingerprint(resolved);
39
- }
40
- return hashes;
41
- }
42
27
  function capAffectedFiles(files, cwd) {
43
28
  const unique = [...new Set(files.map((file) => resolveGuardPath(file, cwd)))];
44
29
  return {
45
- files: unique.slice(0, MAX_AFFECTED_FILES),
46
- truncated: unique.length > MAX_AFFECTED_FILES,
30
+ files: unique.slice(0, MAX_ADVISORY_AFFECTED_FILES),
31
+ truncated: unique.length > MAX_ADVISORY_AFFECTED_FILES,
47
32
  };
48
33
  }
49
34
  /** Recovery is safe only when blocker content and its file provenance agree. */
@@ -424,6 +409,17 @@ export function writeGitGuardRecord(cacheManager, runtime, cwd, record) {
424
409
  fileSeqByPath[key] = runtime.getFileSeq(resolveGuardPath(file, cwd));
425
410
  }
426
411
  }
412
+ const currentProvenance = snapshotAdvisoryProvenance({
413
+ cwd,
414
+ runtime,
415
+ generation: 0,
416
+ files: capped.files.map((file) => ({ path: file, role: "affected" })),
417
+ truncated: capped.truncated,
418
+ });
419
+ const fileContentHashes = Object.fromEntries(currentProvenance.files.map((file) => [
420
+ guardPathKey(file.path, cwd),
421
+ file.sha256,
422
+ ]));
427
423
  const data = {
428
424
  ...record,
429
425
  affectedFiles: capped.files,
@@ -432,7 +428,8 @@ export function writeGitGuardRecord(cacheManager, runtime, cwd, record) {
432
428
  blockingFiles: Array.isArray(record.blockingFiles)
433
429
  ? capAffectedFiles(record.blockingFiles, cwd).files
434
430
  : undefined,
435
- fileContentHashes: snapshotFileHashes(capped.files, cwd),
431
+ fileContentHashes,
432
+ provenance: record.provenance ?? currentProvenance,
436
433
  };
437
434
  try {
438
435
  cacheManager.writeCache("turn-end-findings", data, cwd);
@@ -468,6 +465,37 @@ function unknown(cwd, reasonCategory, metadata = {}) {
468
465
  * on tool_result, never tool_call, and therefore does not add disk I/O to the
469
466
  * edit preflight path.
470
467
  */
468
+ /**
469
+ * Retire one file's inline blocker on a confirmed-clean verdict, then bring the
470
+ * commit gate back in line with the map (#1561 F2).
471
+ *
472
+ * Retiring the map entry alone is not enough and claiming otherwise was wrong:
473
+ * the gate reads a LATCH (`gitGuardHasBlockers`) and a PERSISTED record, and
474
+ * `evaluateGitGuard` short-circuits on the latch before it ever looks at the
475
+ * record. A retire that touched neither left the commit blocked and quoted the
476
+ * retired blocker back as the reason.
477
+ *
478
+ * Both are recomputed exactly the way a clean dispatch recomputes them:
479
+ * `updateGitGuardStatus(false, "")` re-derives the latch from the blocker map
480
+ * — it cannot clear a latch another file's live blocker still justifies,
481
+ * because that file's entry is still in the map — and `syncGitGuardRecord`
482
+ * rewrites the persisted record from the same source of truth.
483
+ *
484
+ * Colocated with the gate rather than inlined at the wiring site so the claim
485
+ * is testable without booting the extension.
486
+ *
487
+ * @returns true when an entry was retired (the caller logs it — #1432 Gap 1).
488
+ */
489
+ export function retireInlineBlockerAndResyncGuard(args) {
490
+ const retired = args.runtime.retireInlineBlockerOnConfirmedClean(args.filePath, args.writeIndex, args.coveredSources);
491
+ if (!retired)
492
+ return false;
493
+ args.runtime.updateGitGuardStatus(false, "");
494
+ if (args.lensGuardEnabled) {
495
+ syncGitGuardRecord(args.runtime, args.cacheManager, args.cwd, args.filePath);
496
+ }
497
+ return true;
498
+ }
471
499
  export function syncGitGuardRecord(runtime, cacheManager, cwd, editedFilePath) {
472
500
  const entries = runtime.getInlineBlockersSnapshot?.() ?? [];
473
501
  const inspection = cacheManager.inspectCache("turn-end-findings", cwd);
@@ -28,19 +28,133 @@
28
28
  * (installer entry registered in clients/installer/index.ts) and runs
29
29
  * `gitleaks detect --no-git --report-format json` against the analysis root.
30
30
  *
31
- * Refs: #130
31
+ * Scope policy (#1562): gitleaks is handed a `--config` generated per scan
32
+ * (`writeScopedGitleaksConfig`) that EXTENDS the project's own `.gitleaks.toml`
33
+ * (or gitleaks's built-in defaults, if none) with two additions — an
34
+ * `[allowlist] paths` entry per NARROW secrets-lane scratch tree
35
+ * (`scratch-tree-policy.ts`'s `SECRETS_LANE_SCRATCH_DIR_NAMES` — pi-ecosystem/
36
+ * agent data dirs, `node_modules`, `.git`, generic build caches; deliberately
37
+ * NOT the broader walker-parity `EXCLUDED_DIRS` list knip/jscpd use, which
38
+ * also drops real leak surfaces like `dist/`, `vendor/`, `.vscode/` — see that
39
+ * module's doc for the review-round probe that caught the too-broad first
40
+ * cut) and an `[allowlist] regexes` entry per known placeholder-secret shape
41
+ * (`YOUR_API_KEY`, `<your-key>`, `xxxxxxxx`, …). An allowlist entry is a
42
+ * POST-DETECTION filter, not a walk exclusion — gitleaks still visits every
43
+ * file; the allowlist only decides which raw hits get suppressed from the
44
+ * report. Deliberately NOT `.gitignore`-based — see `scratch-tree-policy.ts`'s
45
+ * module doc for why blanket gitignore-respect is wrong for a secrets scanner
46
+ * (an untracked `.env` with a real credential is exactly the case gitleaks
47
+ * exists to catch, and `.env` is commonly gitignored). `classifyAndFilterFindings`
48
+ * backstops the config with a TS-side re-classification: a finding under the
49
+ * secrets-lane scratch tier is DEMOTED (not deleted) to `severity: "info"`,
50
+ * `semantic: "none"`, `pathStatus: "scratch"` — visible for an audit read,
51
+ * but it can never surface as a blocking "leaked secret" alarm. Every other
52
+ * finding gets `pathStatus` from git's tracked/ignored view.
53
+ *
54
+ * Refs: #130, #1562
32
55
  */
33
56
  import * as fs from "node:fs";
34
57
  import * as os from "node:os";
35
58
  import * as path from "node:path";
36
59
  import { mkdtempSync } from "node:fs";
60
+ import { collectTrackedFiles, collectUntrackedIgnoredIds, } from "./git-tracked-ignore.js";
61
+ import { normalizeEphemeralMapKey, normalizeMapKey } from "./path-utils.js";
37
62
  import { safeSpawnAsync } from "./safe-spawn.js";
38
63
  import { SecurityScanClient } from "./security-scan-client.js";
64
+ import { getSecretsLaneAllowlistPaths, isUnderSecretsLaneScratchTree, } from "./scratch-tree-policy.js";
39
65
  const EMPTY_RESULT = {
40
66
  success: false,
41
67
  findings: [],
42
68
  };
43
69
  const SCAN_TIMEOUT_MS = 120_000;
70
+ // gitleaks discovers its own config at `<source>/.gitleaks.toml` (or `.yaml`/
71
+ // `.yml`) when no `--config` is passed. Kept as a named constant so the
72
+ // generated scoped config (below) can EXTEND whichever of these the project
73
+ // already has, instead of silently replacing the project's own rules.
74
+ const LOCAL_GITLEAKS_CONFIG_NAMES = [
75
+ ".gitleaks.toml",
76
+ ".gitleaks.yaml",
77
+ ".gitleaks.yml",
78
+ ];
79
+ function findLocalGitleaksConfig(cwd) {
80
+ for (const name of LOCAL_GITLEAKS_CONFIG_NAMES) {
81
+ const candidate = path.join(cwd, name);
82
+ try {
83
+ if (fs.existsSync(candidate))
84
+ return candidate;
85
+ }
86
+ catch {
87
+ // non-fatal
88
+ }
89
+ }
90
+ return undefined;
91
+ }
92
+ // The #1562 incident: a curl doc example's `-H "Authorization: Bearer
93
+ // YOUR_API_KEY"` (cached under gitignored scratch, see the secrets-lane
94
+ // exclusion below) read as a leaked secret. These match gitleaks's own
95
+ // `Secret`/`Match` value case-insensitively, catching the canonical
96
+ // placeholder shapes: `YOUR_API_KEY`-style tokens, `<your-key>`-style angle
97
+ // brackets, run-of-`x` filler, and the common changeme/example/dummy family.
98
+ // Scoped to the whole matched value (`^...$`) so a REAL secret that merely
99
+ // contains the substring "key" is never caught by accident.
100
+ //
101
+ // #1562 review-round F3: regex 2's alternation required "key" to be prefixed
102
+ // (`api[-_ ]?key`) — the issue's own literal acceptance example, `<your-key>`
103
+ // (bare "key", no "api" prefix), didn't match. Added a bare `key` alternative;
104
+ // still safe because the match is scoped to the WHOLE bracketed value
105
+ // (`^<...>$`), so a real secret that happens to be wrapped in angle brackets
106
+ // AND merely contain the substring "key" still wouldn't collide with this —
107
+ // only a value that is ENTIRELY `<...key...>`-shaped does.
108
+ export const PLACEHOLDER_SECRET_REGEXES = [
109
+ String.raw `(?i)^(your|my|insert|replace|enter|add)[-_ ]?(the[-_ ]?)?(api[-_ ]?)?(key|token|secret|password|credential)s?$`,
110
+ String.raw `(?i)^<[^<>]*(api[-_ ]?key|token|secret|password|credential|key)[^<>]*>$`,
111
+ String.raw `(?i)^x{3,}$`,
112
+ String.raw `(?i)^(changeme|change[-_]me|example|placeholder|dummy|fake|redacted|xxxxxxxx)$`,
113
+ ];
114
+ /**
115
+ * Build a temp gitleaks config that EXTENDS the project's own config (if any,
116
+ * else gitleaks's built-in defaults) with two additions:
117
+ * 1. `[allowlist] paths` — the NARROW secrets-lane scratch exclusion
118
+ * (`scratch-tree-policy.ts`'s `getSecretsLaneAllowlistPaths`, NOT the
119
+ * broader walker-parity `EXCLUDED_DIRS` list — #1562 review-round F1).
120
+ * 2. `[allowlist] regexes` — the placeholder-secret class above.
121
+ *
122
+ * An `[allowlist]` entry is a POST-DETECTION filter (#1562 review-round F4):
123
+ * gitleaks still visits every file under `--source`; the allowlist decides
124
+ * which of the raw hits it reports, it does not shrink the walk itself.
125
+ *
126
+ * gitleaks's `[extend]` allowlist merge is additive (append, not replace —
127
+ * see gitleaks/config/config.go), so a project's own curated allowlist keeps
128
+ * working unchanged; this only ever ADDS exclusions, never removes one the
129
+ * project opted into.
130
+ *
131
+ * Returns the generated file's path; caller owns cleanup (same temp dir as
132
+ * the report, removed together in `runScan`'s `finally`).
133
+ */
134
+ export function writeScopedGitleaksConfig(outDir, cwd) {
135
+ const localConfig = findLocalGitleaksConfig(cwd);
136
+ const extendLine = localConfig
137
+ ? `path = ${JSON.stringify(localConfig)}`
138
+ : "useDefault = true";
139
+ const pathPatterns = getSecretsLaneAllowlistPaths()
140
+ .map((p) => ` ${JSON.stringify(p)},`)
141
+ .join("\n");
142
+ const regexPatterns = PLACEHOLDER_SECRET_REGEXES.map((r) => ` ${JSON.stringify(r)},`).join("\n");
143
+ const toml = `[extend]
144
+ ${extendLine}
145
+
146
+ [allowlist]
147
+ paths = [
148
+ ${pathPatterns}
149
+ ]
150
+ regexes = [
151
+ ${regexPatterns}
152
+ ]
153
+ `;
154
+ const configPath = path.join(outDir, "gitleaks-scoped-config.toml");
155
+ fs.writeFileSync(configPath, toml, "utf-8");
156
+ return configPath;
157
+ }
44
158
  // --- Detection ---
45
159
  /**
46
160
  * Detect whether the project root has opted in to gitleaks via any of the
@@ -51,12 +165,7 @@ const SCAN_TIMEOUT_MS = 120_000;
51
165
  * the client.
52
166
  */
53
167
  export function hasGitleaksSignal(cwd) {
54
- const candidates = [
55
- ".gitleaks.toml",
56
- ".gitleaks.yaml",
57
- ".gitleaks.yml",
58
- ".gitleaksignore",
59
- ];
168
+ const candidates = [...LOCAL_GITLEAKS_CONFIG_NAMES, ".gitleaksignore"];
60
169
  for (const candidate of candidates) {
61
170
  try {
62
171
  if (fs.existsSync(path.join(cwd, candidate)))
@@ -187,11 +296,14 @@ export class GitleaksClient extends SecurityScanClient {
187
296
  const outDir = mkdtempSync(path.join(os.tmpdir(), "pi-lens-gitleaks-"));
188
297
  const reportPath = path.join(outDir, "gitleaks-report.json");
189
298
  try {
299
+ const configPath = writeScopedGitleaksConfig(outDir, cwd);
190
300
  const result = await safeSpawnAsync(bin, [
191
301
  "detect",
192
302
  "--no-git",
193
303
  "--source",
194
304
  cwd,
305
+ "--config",
306
+ configPath,
195
307
  "--report-format",
196
308
  "json",
197
309
  "--report-path",
@@ -219,7 +331,8 @@ export class GitleaksClient extends SecurityScanClient {
219
331
  summary: (result.stderr ?? "").trim().split("\n")[0] || "no report produced",
220
332
  };
221
333
  }
222
- const findings = parseGitleaksReport(fs.readFileSync(reportPath, "utf-8"));
334
+ const rawFindings = parseGitleaksReport(fs.readFileSync(reportPath, "utf-8"));
335
+ const findings = await classifyAndFilterFindings(rawFindings, cwd);
223
336
  return {
224
337
  success: true,
225
338
  findings,
@@ -243,6 +356,66 @@ export class GitleaksClient extends SecurityScanClient {
243
356
  }
244
357
  }
245
358
  }
359
+ // --- Post-scan classification / filter ---
360
+ /**
361
+ * Backstop for the scoped-config secrets-lane exclusion above, plus the
362
+ * `pathStatus` observability field (#1562).
363
+ *
364
+ * Two things happen here, both defense-in-depth relative to the generated
365
+ * gitleaks config:
366
+ * 1. Any finding still under the NARROW secrets-lane scratch tier
367
+ * (`isUnderSecretsLaneScratchTree` — NOT the broader walker-parity
368
+ * `EXCLUDED_DIRS` list) is DEMOTED, never DROPPED (#1562 review-round
369
+ * F2: the observability criterion requires this value to actually be
370
+ * reachable, so a scratch finding survives with `pathStatus: "scratch"`
371
+ * and the adapter (`gitleaksFindingToProjectDiagnostic`) reports it as
372
+ * `severity: "info"`/`semantic: "none"` — visible for an audit read,
373
+ * but it can never surface as a blocking "leaked secret" alarm). We
374
+ * can't exercise gitleaks's own TOML-regex engine in unit tests, only
375
+ * assert the config we hand it, so this backstop guarantees the demotion
376
+ * even if the generated regex ever mismatches gitleaks's own path
377
+ * normalization.
378
+ * 2. Every OTHER finding gets `pathStatus` set from git's tracked/ignored
379
+ * view, so a future false-positive triage is a log read.
380
+ *
381
+ * `collectTrackedFiles`/`collectUntrackedIgnoredIds` degrade to `undefined`
382
+ * when git itself is unavailable (no repo, spawn failure) — findings just
383
+ * keep `pathStatus: undefined` rather than a guessed value (fail-open,
384
+ * matching `git-tracked-ignore.ts`'s own contract). Exported for unit tests.
385
+ */
386
+ export async function classifyAndFilterFindings(findings, cwd) {
387
+ if (findings.length === 0)
388
+ return findings;
389
+ const [trackedIds, untrackedIgnoredIds] = await Promise.all([
390
+ collectTrackedFiles(cwd),
391
+ collectUntrackedIgnoredIds(cwd),
392
+ ]);
393
+ const classified = [];
394
+ for (const finding of findings) {
395
+ if (isUnderSecretsLaneScratchTree(finding.file)) {
396
+ classified.push({ ...finding, pathStatus: "scratch" });
397
+ continue;
398
+ }
399
+ const absPath = path.isAbsolute(finding.file)
400
+ ? finding.file
401
+ : path.resolve(cwd, finding.file);
402
+ let pathStatus;
403
+ if (trackedIds?.has(normalizeEphemeralMapKey(absPath))) {
404
+ pathStatus = "tracked";
405
+ }
406
+ else if (untrackedIgnoredIds?.has(normalizeMapKey(absPath))) {
407
+ pathStatus = "ignored";
408
+ }
409
+ else if (trackedIds !== undefined || untrackedIgnoredIds !== undefined) {
410
+ // At least one of the two git-backed sets resolved, so the absence
411
+ // from both is a real answer ("untracked, not gitignored") rather
412
+ // than an unresolved probe.
413
+ pathStatus = "untracked";
414
+ }
415
+ classified.push(pathStatus ? { ...finding, pathStatus } : finding);
416
+ }
417
+ return classified;
418
+ }
246
419
  // --- Parser ---
247
420
  /**
248
421
  * Map gitleaks's JSON report (a flat array of finding objects) to our
@@ -7,15 +7,16 @@
7
7
  * Docs: https://pkg.go.dev/golang.org/x/tools/gopls
8
8
  */
9
9
  import { createSubsystemLogger } from "./extension-log.js";
10
- import * as fs from "node:fs";
11
10
  import * as path from "node:path";
12
- import { safeSpawnAsync } from "./safe-spawn.js";
11
+ import { createToolchainAvailability, } from "./dispatch/runners/utils/toolchain-availability.js";
13
12
  // --- Common install paths ---
14
13
  const GO_WINDOWS_PATHS = [
15
14
  "C:\\Program Files\\Go\\bin\\go.exe",
16
15
  "C:\\Go\\bin\\go.exe",
17
16
  "go.exe", // PATH
18
17
  ];
18
+ /** Budget for the PATH candidate's `go version` probe, ms. */
19
+ const PROBE_TIMEOUT_MS = 3_000;
19
20
  const GO_UNIX_PATHS = [
20
21
  "/usr/local/go/bin/go",
21
22
  "/usr/bin/go",
@@ -23,58 +24,39 @@ const GO_UNIX_PATHS = [
23
24
  ];
24
25
  // --- Client ---
25
26
  export class GoClient {
26
- goAvailable = null;
27
- goPath = null;
27
+ /**
28
+ * Availability lifecycle, behind the shared transient-aware latch (#1476).
29
+ * The PATH candidate is resolved by spawning `go version` with a 3 s budget,
30
+ * so a host stall could latch "no Go toolchain" for the whole session and
31
+ * silently disable every Go diagnostic until restart.
32
+ */
33
+ availability;
28
34
  log;
29
35
  constructor(verbose = false) {
30
36
  this.log = verbose
31
37
  ? createSubsystemLogger("go")
32
38
  : () => { };
39
+ this.availability = createToolchainAvailability({
40
+ tool: "go",
41
+ label: "Go",
42
+ windowsPaths: GO_WINDOWS_PATHS,
43
+ unixPaths: GO_UNIX_PATHS,
44
+ probeArgs: ["version"],
45
+ budgetMs: PROBE_TIMEOUT_MS,
46
+ log: (msg) => this.log(msg),
47
+ });
33
48
  }
34
49
  /**
35
50
  * Find go executable path (async — probes PATH candidates off the event loop).
36
51
  */
37
52
  async findGoPathAsync() {
38
- if (this.goPath)
39
- return this.goPath;
40
- const paths = process.platform === "win32" ? GO_WINDOWS_PATHS : GO_UNIX_PATHS;
41
- for (const p of paths) {
42
- try {
43
- if (p.includes("\\") || p.includes("/")) {
44
- // Absolute path - check if exists
45
- if (fs.existsSync(p)) {
46
- this.goPath = p;
47
- return p;
48
- }
49
- }
50
- else {
51
- // Relative (PATH) - try running it
52
- const result = await safeSpawnAsync(p, ["version"], {
53
- timeout: 3000,
54
- });
55
- if (!result.error && result.status === 0) {
56
- this.goPath = p;
57
- return p;
58
- }
59
- }
60
- }
61
- catch (err) {
62
- void err;
63
- }
64
- }
65
- return null;
53
+ return this.availability.findPath();
66
54
  }
67
55
  /**
68
56
  * Check if Go is installed (cached)
69
57
  */
70
58
  async isGoAvailableAsync() {
71
- if (this.goAvailable !== null)
72
- return this.goAvailable;
73
- this.goAvailable = (await this.findGoPathAsync()) !== null;
74
- if (this.goAvailable) {
75
- this.log(`Go found: ${this.goPath}`);
76
- }
77
- return this.goAvailable;
59
+ return this.availability.isAvailable();
78
60
  }
79
61
  /**
80
62
  * Check if a file is a Go file