@herbertgao/pi-extensions 2026.8.6 → 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 (343) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  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/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -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 +1 -0
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -10
  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 +2 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  49. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  50. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  51. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  52. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  53. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  54. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  55. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  56. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  57. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  58. package/node_modules/pi-lens/CHANGELOG.md +909 -0
  59. package/node_modules/pi-lens/README.md +11 -3
  60. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  61. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  62. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  63. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  64. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  65. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  66. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  67. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  68. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  69. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  70. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  71. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  72. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  73. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  74. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  75. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  76. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  77. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  78. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  79. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  80. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  81. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  82. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  83. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  84. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  86. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  87. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  122. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  123. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  124. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  125. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  126. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  127. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  128. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  129. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  130. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  131. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  134. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  135. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  136. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  137. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  138. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  139. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  140. package/node_modules/pi-lens/dist/clients/jscpd-client.js +82 -6
  141. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  142. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  143. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  144. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  145. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  146. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  147. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  148. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +102 -7
  149. package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
  150. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  151. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  152. package/node_modules/pi-lens/dist/clients/lsp/index.js +2274 -147
  153. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  154. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  155. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  156. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  157. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  158. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  159. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  160. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  161. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  163. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  164. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  165. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  166. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  167. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  168. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  169. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  170. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  171. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  172. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  173. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  174. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  175. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  176. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  177. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  178. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  179. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  180. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  181. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  182. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  183. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  185. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  186. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  187. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  188. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  189. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  190. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +308 -45
  191. package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -9
  192. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  193. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  194. package/node_modules/pi-lens/dist/clients/runtime-turn.js +575 -67
  195. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  196. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  197. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  198. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  199. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  200. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  201. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  202. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  203. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  204. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  205. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  210. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  211. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  212. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  213. package/node_modules/pi-lens/dist/index.js +67738 -55739
  214. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
  215. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  216. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  217. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  218. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  219. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  220. package/node_modules/pi-lens/docs/features.md +19 -5
  221. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  222. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  223. package/node_modules/pi-lens/docs/mcp.md +2 -2
  224. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  225. package/node_modules/pi-lens/docs/settings.md +19 -1
  226. package/node_modules/pi-lens/docs/usage.md +1 -0
  227. package/node_modules/pi-lens/package.json +10 -6
  228. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  237. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  238. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  239. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  240. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  241. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  252. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  253. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  254. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  255. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  256. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  257. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  258. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  288. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  289. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  290. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  291. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  292. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  293. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  294. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  295. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  296. package/node_modules/pi-mcp-adapter/README.md +47 -5
  297. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  298. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  299. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  300. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  301. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  302. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  303. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  304. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  305. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  306. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  307. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  308. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  309. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  310. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  311. package/node_modules/pi-mcp-adapter/package.json +3 -1
  312. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  313. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  314. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  315. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  316. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  317. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  318. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  319. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  320. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  321. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  322. package/node_modules/pi-web-access/CHANGELOG.md +23 -0
  323. package/node_modules/pi-web-access/README.md +27 -9
  324. package/node_modules/pi-web-access/anysearch.ts +4 -2
  325. package/node_modules/pi-web-access/brave.ts +16 -4
  326. package/node_modules/pi-web-access/curator-page.ts +6 -3
  327. package/node_modules/pi-web-access/curator-server.ts +4 -1
  328. package/node_modules/pi-web-access/exa.ts +18 -7
  329. package/node_modules/pi-web-access/extract.ts +28 -3
  330. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  331. package/node_modules/pi-web-access/index.ts +41 -57
  332. package/node_modules/pi-web-access/package.json +1 -1
  333. package/node_modules/pi-web-access/page-query.ts +4 -2
  334. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  335. package/node_modules/pi-web-access/parallel.ts +2 -2
  336. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  337. package/node_modules/pi-web-access/serper.ts +211 -0
  338. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  339. package/node_modules/pi-web-access/summary-review.ts +44 -22
  340. package/node_modules/pi-web-access/tavily.ts +16 -4
  341. package/node_modules/pi-web-access/utils.ts +87 -0
  342. package/node_modules/pi-web-access/valyu.ts +199 -0
  343. package/package.json +13 -13
@@ -763,6 +763,21 @@ function ensureUtf8ConsoleCodePageOnce() {
763
763
  export function resetUtf8ConsoleCodePageStateForTests() {
764
764
  utf8ConsoleCodePageApplied = false;
765
765
  }
766
+ // #1656: post-exit pipe-idle wait constants. Node's "close" event only fires
767
+ // once every stdio fd referencing the child has been released — a
768
+ // daemonized descendant that inherited our stdout/stderr pipe (the Windows
769
+ // no-job-object case) can hold that fd open forever, hanging "close" even
770
+ // though the child we spawned is long dead. A fixed post-exit deadline
771
+ // avoids that hang but truncates a legitimately still-streaming child. The
772
+ // idle-grace construction (adopted from pi's `waitForChildProcess`) does
773
+ // neither: after "exit", wait for the pipes to go quiet — no data for
774
+ // EXIT_PIPE_IDLE_GRACE_MS, re-armed on every chunk — so a quiet inherited
775
+ // handle still releases after one grace window while an actively-writing
776
+ // descendant keeps being read. EXIT_PIPE_IDLE_MAX_WAIT_MS caps the total
777
+ // extra wait so a pathological, never-quiet descendant can't extend the
778
+ // caller's overall budget unboundedly.
779
+ const EXIT_PIPE_IDLE_GRACE_MS = 100;
780
+ const EXIT_PIPE_IDLE_MAX_WAIT_MS = 2000;
766
781
  // ============================================================================
767
782
  // ASYNC VERSION (Recommended - Non-blocking)
768
783
  // ============================================================================
@@ -825,7 +840,18 @@ export async function safeSpawnAsync(command, args, options) {
825
840
  let aborted = false;
826
841
  let killed = false;
827
842
  let outputTruncated = false;
828
- let spawnErrored = false;
843
+ // #1651 review: a single boolean the close/error handlers both check
844
+ // AND set, so whichever one decides the outcome first wins outright —
845
+ // the DECISION itself is shape-based (see the `close` handler below),
846
+ // this flag only stops the loser from resolving a second time.
847
+ let resolved = false;
848
+ // The most recent 'error' Error, captured the instant the event fires
849
+ // regardless of whether that handler goes on to resolve. The `close`
850
+ // handler's shape-based branch (code === null || code < 0) reads this
851
+ // when it decides FIRST, so a genuinely informative ENOENT/EACCES/etc.
852
+ // Error is used when it happens to already be available, instead of
853
+ // always falling back to a generic synthesized one.
854
+ let pendingSpawnError;
829
855
  // #1109: the non-Windows SIGTERM→SIGKILL escalation timer (armed in
830
856
  // killTree below). Stored per-call (never shared) so the close/error
831
857
  // handlers can clear it if the child exits before it fires — same
@@ -841,6 +867,11 @@ export async function safeSpawnAsync(command, args, options) {
841
867
  // any `await`, so a timer firing during the close handler's `await
842
868
  // killPromise` still observes the flag correctly).
843
869
  let closed = false;
870
+ // #1656: set only while the post-exit idle-pipe wait is active (see
871
+ // waitForPipeIdle below); the stdout/stderr "data" handlers call this to
872
+ // re-arm the grace timer on every chunk. `undefined` before exit / after
873
+ // the wait settles, so the calls below are no-ops outside that window.
874
+ let rearmIdleGrace;
844
875
  const maxOutputBytes = options?.maxOutputBytes !== undefined &&
845
876
  Number.isFinite(options.maxOutputBytes) &&
846
877
  options.maxOutputBytes > 0
@@ -996,7 +1027,7 @@ export async function safeSpawnAsync(command, args, options) {
996
1027
  lifetimeState.pids.add(child.pid);
997
1028
  }
998
1029
  // #620: bracket this spawn's lifetime with a short-interval CPU/RSS poll
999
- // (started right here, stopped in the "close" handler below) so transient
1030
+ // (started right here, stopped in finalize() below) so transient
1000
1031
  // analyzer children (jscpd, knip, madge, gitleaks, etc.) — which live too
1001
1032
  // briefly for heartbeat-cadence sampling to reliably catch — still get a
1002
1033
  // peak/average resource reading. `startSpawnUsageSampler` itself is
@@ -1052,7 +1083,7 @@ export async function safeSpawnAsync(command, args, options) {
1052
1083
  }
1053
1084
  };
1054
1085
  abortSignal?.addEventListener("abort", onAbort, { once: true });
1055
- // Output-cap kills are awaited by the close handler below, just like
1086
+ // Output-cap kills are awaited by finalize() below, just like
1056
1087
  // timeout/abort kills. This keeps a noisy CLI from continuing in the
1057
1088
  // background after its retained output has been bounded.
1058
1089
  let killPromise;
@@ -1068,10 +1099,28 @@ export async function safeSpawnAsync(command, args, options) {
1068
1099
  child.stdout?.on("data", (data) => {
1069
1100
  stdout = appendOutput(stdout, data);
1070
1101
  stopForOutputLimit();
1102
+ rearmIdleGrace?.();
1071
1103
  });
1072
1104
  child.stderr?.on("data", (data) => {
1073
1105
  stderr = appendOutput(stderr, data);
1074
1106
  stopForOutputLimit();
1107
+ rearmIdleGrace?.();
1108
+ });
1109
+ // #1673 review round 3 (F2): registered HERE, at spawn time, alongside
1110
+ // the other handlers above — not inside `waitForPipeIdle`. Real Node
1111
+ // emits `close` a fraction of a millisecond after `exit` (both fire in
1112
+ // the same "process exit" turn, well before any microtask), so a
1113
+ // listener attached inside `waitForPipeIdle` — which `finalize` only
1114
+ // reaches after `await killPromise`, at least one microtask later — is
1115
+ // always attaching to an event that has already happened. That's why
1116
+ // the round-2 `child.on("close", finish)` placed inside
1117
+ // `waitForPipeIdle` never actually caught a real spawn's `close` early:
1118
+ // every real call still paid the full grace window. Capturing the
1119
+ // boolean here, before any await, means `waitForPipeIdle` can check
1120
+ // "did close already happen" synchronously the moment it runs.
1121
+ let closeSeen = false;
1122
+ child.on("close", () => {
1123
+ closeSeen = true;
1075
1124
  });
1076
1125
  // Timeout handling - KILL the process, don't just abandon it
1077
1126
  const timeoutId = setTimeout(() => {
@@ -1105,23 +1154,144 @@ export async function safeSpawnAsync(command, args, options) {
1105
1154
  }
1106
1155
  return summary;
1107
1156
  };
1157
+ // #1656: wait for stdout/stderr to fall idle after exit instead of
1158
+ // waiting for Node's "close" event (see the constants' doc comment
1159
+ // above). Resolves immediately if the child has no pipes to drain.
1160
+ //
1161
+ // These timers stay REF'D, matching `timeoutId`/`escalationTimer`
1162
+ // elsewhere in this function: the caller's own settlement depends on
1163
+ // them firing, so unref'ing would let a short-lived host process (a
1164
+ // one-shot CLI whose only other in-flight work was this spawn) exit
1165
+ // the event loop before `finish` ever runs — silently abandoning this
1166
+ // promise instead of merely leaking a handle. Both paths to `finish`
1167
+ // clear both timers, so nothing outlives settlement either way.
1168
+ const waitForPipeIdle = () => {
1169
+ if (!child.stdout && !child.stderr)
1170
+ return Promise.resolve();
1171
+ // #1673 review round 3 (F2): `close` fires a fraction of a
1172
+ // millisecond after `exit` for a real Node child — well before
1173
+ // `finalize`'s `await killPromise` ever yields, i.e. before this
1174
+ // function is even called. By the time we get here, `closeSeen`
1175
+ // (set by the listener registered at spawn time, above) already
1176
+ // reflects reality for the overwhelming majority of real spawns:
1177
+ // nothing is holding this child's stdio open, so don't pay out the
1178
+ // 100ms grace window for a signal that already arrived.
1179
+ if (closeSeen)
1180
+ return Promise.resolve();
1181
+ return new Promise((finishIdleWait) => {
1182
+ let settled = false;
1183
+ let graceTimer;
1184
+ const capTimer = setTimeout(() => {
1185
+ finish();
1186
+ }, EXIT_PIPE_IDLE_MAX_WAIT_MS);
1187
+ const finish = () => {
1188
+ if (settled)
1189
+ return;
1190
+ settled = true;
1191
+ rearmIdleGrace = undefined;
1192
+ if (graceTimer)
1193
+ clearTimeout(graceTimer);
1194
+ clearTimeout(capTimer);
1195
+ // Optional chaining, not a bare call: `child` is a real Node
1196
+ // ChildProcess (always an EventEmitter, always has this) in
1197
+ // production, but several existing test doubles across the
1198
+ // suite fake stdio-bearing children as plain objects with only
1199
+ // the handful of methods (`on`, `kill`, ...) their scenario
1200
+ // needs — no `removeListener`. Skipping cleanup on those is
1201
+ // harmless (the fake is torn down with the test either way);
1202
+ // crashing here from inside a timer callback is not: it would
1203
+ // throw before `finishIdleWait()` runs, hanging every caller's
1204
+ // `safeSpawnAsync` promise (#1673 review round 2 caught this
1205
+ // live in tests/clients/installer/version-drift.test.ts).
1206
+ child.removeListener?.("close", finish);
1207
+ finishIdleWait();
1208
+ };
1209
+ // The `closeSeen` fast-path above catches the common case (close
1210
+ // already happened by the time we get here). This listener is
1211
+ // the fallback for the rare case where `close` genuinely lands
1212
+ // DURING the wait — e.g. a child whose stdio takes a beat
1213
+ // longer than usual to release, but still well under the grace
1214
+ // window. `close` is a stronger signal than "no data for
1215
+ // EXIT_PIPE_IDLE_GRACE_MS", so finishing on it here still beats
1216
+ // waiting out the timer. The anti-hang property is untouched
1217
+ // either way: a daemonized descendant holding the pipe open is
1218
+ // EXACTLY the case where `close` never fires at all, so the
1219
+ // grace/cap timers still bound that case as before.
1220
+ child.on("close", finish);
1221
+ rearmIdleGrace = () => {
1222
+ if (graceTimer)
1223
+ clearTimeout(graceTimer);
1224
+ graceTimer = setTimeout(finish, EXIT_PIPE_IDLE_GRACE_MS);
1225
+ };
1226
+ rearmIdleGrace();
1227
+ });
1228
+ };
1108
1229
  // Process completion
1109
- child.on("close", async (code, signal) => {
1110
- if (spawnErrored)
1230
+ //
1231
+ // #1656 x #1651/#1670: finalize off BOTH `exit` and `close`, not just
1232
+ // one — each covers a shape the other misses.
1233
+ // - `exit` is what unblocks the #1656 hang: a daemonized descendant
1234
+ // that inherited our stdout/stderr pipe can hold that fd open
1235
+ // forever, so `close` (which waits for every fd referencing the
1236
+ // child to release) never fires even though the child we spawned
1237
+ // is long dead. `exit` always fires once the child itself exits,
1238
+ // independent of who still holds its pipes.
1239
+ // - `close` is still wired because a child that never actually
1240
+ // launched (bad binary, EACCES, ...) gets NO `exit` event at all —
1241
+ // Node only emits `error` then `close(code<0, null)` for that
1242
+ // shape (#1651/#1670's shape guard below decides from exactly
1243
+ // this). `exit`-only would make that guard unreachable.
1244
+ // Whichever fires first drives `finalize`; `settled` (distinct from
1245
+ // `resolved`, which `finalize` itself owns) stops the second event
1246
+ // from dispatching a redundant, concurrently-racing `finalize` call —
1247
+ // double-invoking it would double-run `waitForPipeIdle` and
1248
+ // `finishResourceUsage` against the same child.
1249
+ let settled = false;
1250
+ const onChildSettle = (code, signal) => {
1251
+ if (settled || resolved)
1111
1252
  return;
1253
+ settled = true;
1112
1254
  closed = true;
1113
1255
  clearTimeout(timeoutId);
1114
1256
  abortSignal?.removeEventListener("abort", onAbort);
1115
1257
  if (child.pid)
1116
1258
  lifetimeState.pids.delete(child.pid);
1259
+ void finalize(code, signal);
1260
+ };
1261
+ const finalize = async (code, signal) => {
1262
+ // Belt, not the decision: whichever path settles `resolved` FIRST
1263
+ // wins. A healthy run's outcome must never be discarded by a late,
1264
+ // unrelated 'error' (e.g. a post-exit kill() that itself failed
1265
+ // with EPERM, #1651 review F4) — so this only bails when something
1266
+ // else has ALREADY resolved, never on `error` merely having fired.
1117
1267
  await killPromise;
1268
+ if (resolved)
1269
+ return;
1118
1270
  // #1109: the child has exited — if killTree armed the non-Windows
1119
1271
  // SIGTERM→SIGKILL escalation timer and it hasn't fired yet, clear it
1120
1272
  // so it doesn't linger as a ref'd handle after this promise resolves.
1121
1273
  if (escalationTimer)
1122
1274
  clearTimeout(escalationTimer);
1275
+ // #1673 review F1: latch the verdict as DECIDED here, before the
1276
+ // idle-pipe wait — not after it. `code`/`signal`/`timedOut`/`aborted`
1277
+ // are already fixed by this point (they don't change during the
1278
+ // wait), so nothing below depends on staying unresolved across it.
1279
+ // Leaving `resolved` false across the wait would reopen the exact
1280
+ // race #1651 review F4 closed: a late, unrelated 'error' (e.g. a
1281
+ // post-exit kill() failing with EPERM) landing inside the
1282
+ // idle-grace window (bounded, but up to EXIT_PIPE_IDLE_MAX_WAIT_MS)
1283
+ // would steal an already-decided healthy verdict. Pre-#1656 that
1284
+ // race window was one microtask — the gap between `close` firing
1285
+ // and `resolve()` being called synchronously in the same handler.
1286
+ // This closes the window back down to that same shape instead of
1287
+ // widening it to the whole wait.
1288
+ resolved = true;
1289
+ await waitForPipeIdle();
1123
1290
  const resourceUsage = finishResourceUsage();
1124
1291
  const outputInfo = outputTruncated ? { outputTruncated: true } : {};
1292
+ // #1816: surface the signal name as a field on every path where one
1293
+ // exists, so callers can NAME it instead of scraping `error.message`.
1294
+ const signalInfo = signal ? { signal } : {};
1125
1295
  if (timedOut) {
1126
1296
  const cause = new Error(`Process timed out after ${timeout}ms (killed with ${signal || "SIGTERM"})`);
1127
1297
  resolve({
@@ -1130,6 +1300,7 @@ export async function safeSpawnAsync(command, args, options) {
1130
1300
  status: null,
1131
1301
  error: cause,
1132
1302
  failure: "timeout",
1303
+ ...signalInfo,
1133
1304
  spawnFailure: new SpawnFailureError("timeout", cause.message, cause),
1134
1305
  ...outputInfo,
1135
1306
  resourceUsage,
@@ -1143,6 +1314,7 @@ export async function safeSpawnAsync(command, args, options) {
1143
1314
  status: null,
1144
1315
  error: cause,
1145
1316
  failure: "aborted",
1317
+ ...signalInfo,
1146
1318
  spawnFailure: new SpawnFailureError("killed", cause.message, cause),
1147
1319
  ...outputInfo,
1148
1320
  resourceUsage,
@@ -1156,17 +1328,58 @@ export async function safeSpawnAsync(command, args, options) {
1156
1328
  status: null,
1157
1329
  error: cause,
1158
1330
  failure: "signal",
1331
+ ...signalInfo,
1159
1332
  spawnFailure: new SpawnFailureError("killed", cause.message, cause),
1160
1333
  ...outputInfo,
1161
1334
  resourceUsage,
1162
1335
  });
1163
1336
  }
1337
+ else if (code === null || code < 0) {
1338
+ // #1651 review F3: a real completed process never exits with a
1339
+ // null or negative code (Node reports the negated libuv/errno for
1340
+ // a child that never launched — e.g. ENOENT: -2 on Linux, -4058
1341
+ // on Windows). This decides from that SHAPE alone, independent of
1342
+ // whether/when 'error' has fired — Node's docs leave the
1343
+ // 'error'/'close' ordering for a failed spawn unspecified, and a
1344
+ // same-microtask re-check (the #1651 first-round fix) still loses
1345
+ // if 'error' lands a tick later via setImmediate/nextTick.
1346
+ // Reuse `pendingSpawnError` when 'error' already landed (a more
1347
+ // informative Error — ENOENT vs EACCES vs other); otherwise
1348
+ // synthesize an ENOENT-shaped one so a genuinely missing tool is
1349
+ // never read as a clean, answered run.
1350
+ const cause = pendingSpawnError ?? synthesizeEnoentError(command);
1351
+ const spawnFailure = await classifySpawnFailure(cause, {
1352
+ command,
1353
+ cwd: options?.cwd,
1354
+ });
1355
+ resolve({
1356
+ stdout,
1357
+ stderr,
1358
+ status: code,
1359
+ error: cause,
1360
+ failure: "spawn",
1361
+ spawnFailure,
1362
+ ...outputInfo,
1363
+ resourceUsage,
1364
+ });
1365
+ }
1164
1366
  else {
1165
1367
  resolve({ stdout, stderr, status: code, ...outputInfo, resourceUsage });
1166
1368
  }
1167
- });
1369
+ };
1370
+ child.on("exit", onChildSettle);
1371
+ child.on("close", onChildSettle);
1168
1372
  child.on("error", (err) => {
1169
- spawnErrored = true;
1373
+ // Capture unconditionally — even when `close` already resolved, this
1374
+ // keeps the most recent Error available for anyone reading state
1375
+ // mid-flight, and costs nothing (the assignment, not a resolve).
1376
+ pendingSpawnError = err;
1377
+ // A completed run's own verdict must never be downgraded by an
1378
+ // unrelated LATE error (#1651 review F4 — e.g. a post-exit kill()
1379
+ // failing with EPERM after `close(0, null)` already answered).
1380
+ if (resolved)
1381
+ return;
1382
+ resolved = true;
1170
1383
  closed = true;
1171
1384
  clearTimeout(timeoutId);
1172
1385
  abortSignal?.removeEventListener("abort", onAbort);
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Single source of truth for "scratch/cache tree" exclusion, shared by every
3
+ * runner that hands a DIRECTORY to an external binary and lets that binary do
4
+ * its own tree walk.
5
+ *
6
+ * pi-lens's own in-process walkers (`source-filter.ts`/`source-walker.ts`,
7
+ * `getProjectIgnoreMatcher`) already route every entry through
8
+ * `isExcludedDirName`/`EXCLUDED_DIRS` (`file-utils.ts`) — that's how knip,
9
+ * jscpd, and the tree-sitter project scan skip `.pi/`, `node_modules/`, etc.
10
+ * But a runner that shells out with `cwd`/`--source <dir>` and lets the
11
+ * EXTERNAL tool walk (gitleaks `detect --source`, `trivy fs`, `opengrep scan
12
+ * <dir>`) bypasses that walker entirely — the tool sees the raw tree,
13
+ * including pi-ecosystem scratch/data directories that were never meant to be
14
+ * scan targets (#1562: `.pi/greedysearch-sources/` — a gitignored web-research
15
+ * cache — served a `YOUR_API_KEY` doc-example placeholder to the agent as a
16
+ * "leaked secret").
17
+ *
18
+ * This module derives each such runner's native exclude syntax from the SAME
19
+ * `EXCLUDED_DIRS` list `file-utils.ts` already uses for the in-process walk,
20
+ * rather than hand-maintaining a parallel per-tool list (the single-source-of-
21
+ * truth rule that #883 encodes — a hand-copied list is a defect in itself:
22
+ * `dead-code-client.ts`'s `VULTURE_EXCLUDES` had drifted from `EXCLUDED_DIRS`
23
+ * before this fix, e.g. missing `.pi`/`.claude`/`.next`).
24
+ *
25
+ * Deliberately NOT `.gitignore`-based: a secrets scanner (gitleaks, trivy's
26
+ * `secret` scanner) must still see an untracked `.env` with a real credential
27
+ * — `.env` is commonly gitignored, so blanket "respect .gitignore" would hide
28
+ * exactly the file classes these scanners exist to catch (#1562 design note).
29
+ * `EXCLUDED_DIRS` is a narrower, deliberate list of scratch/cache/vendor
30
+ * directory NAMES (`.pi`, `.claude`, `node_modules`, `dist`, …), not a
31
+ * gitignore pattern set.
32
+ *
33
+ * TWO exclusion tiers, not one (#1562 review-round F1) — `EXCLUDED_DIRS`
34
+ * itself conflates two different questions:
35
+ * - "don't bother INDEXING/LINTING/DEDUPING this" (the walker-parity
36
+ * exports below, `getScratchTreeGlobPatterns`/`getScratchTreeDirNames`/
37
+ * `getScratchTreeFnmatchPatterns`) — correctly broad: build output
38
+ * (`dist`, `build`, `out`, `target`, `coverage`), vendored source
39
+ * (`vendor`, `third_party`, `vendors`), and editor config (`.vscode`)
40
+ * are all noise for knip/jscpd/vulture.
41
+ * - "don't bother looking for SECRETS here" — a much narrower question.
42
+ * The first cut of this fix reused the walker-parity list for gitleaks's
43
+ * secrets-lane exclusion too, and a review probe caught it silently
44
+ * dropping real AWS-shaped secrets seeded in `dist/`, `vendor/`,
45
+ * `.vscode/`, `build/`, `out/`, `target/`, `third_party/`,
46
+ * `third-party/`, `coverage/` (0/9 kept, unlogged) — a bundler can bake
47
+ * a real key into `dist/`, `.vscode/launch.json` commonly carries a
48
+ * committed debug token, and a vendored tree can carry an upstream
49
+ * leak. `SECRETS_LANE_SCRATCH_DIR_NAMES`/`getSecretsLaneAllowlistPaths`/
50
+ * `isUnderSecretsLaneScratchTree` are the narrow tier gitleaks (or any
51
+ * future gitleaks-shaped secret scanner) must use instead — pi-ecosystem/
52
+ * agent data dirs, `node_modules`, `.git`, and generic build/package-
53
+ * manager CACHES only; every real-leak-surface directory above stays
54
+ * fully in scope.
55
+ */
56
+ import { EXCLUDED_DIRS, getExcludedDirGlobs } from "./file-utils.js";
57
+ /** Directory-name entries only — drops glob entries (e.g. `*.dSYM`) that a
58
+ * bare directory-name/regex exclude can't express. */
59
+ function literalExcludedDirNames() {
60
+ return EXCLUDED_DIRS.filter((name) => !name.includes("*") && !name.includes("?"));
61
+ }
62
+ /**
63
+ * `**\/name/**` glob patterns for tools that take doublestar globs
64
+ * (trivy `--skip-dirs`, opengrep/semgrep `--exclude`). Reuses
65
+ * `file-utils.ts`'s own glob derivation so both walkers and shell-out runners
66
+ * stay byte-for-byte in sync.
67
+ */
68
+ export function getScratchTreeGlobPatterns() {
69
+ return getExcludedDirGlobs();
70
+ }
71
+ function escapeRegExp(literal) {
72
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
73
+ }
74
+ /** Bare directory names, for tools (opengrep/semgrep `--exclude`) that treat a
75
+ * slash-free pattern as "this directory name, anywhere in the tree". */
76
+ export function getScratchTreeDirNames() {
77
+ return literalExcludedDirNames();
78
+ }
79
+ /**
80
+ * Fnmatch-style glob patterns (`star/name/star`, not doublestar) for
81
+ * vulture's `--exclude` — the format `dead-code-client.ts` needs.
82
+ */
83
+ export function getScratchTreeFnmatchPatterns() {
84
+ return literalExcludedDirNames().map((name) => `*/${name}/*`);
85
+ }
86
+ // --- Secrets-lane tier (#1562 review-round F1) ---
87
+ //
88
+ // Deliberately curated, NOT `EXCLUDED_DIRS`-derived: every name here must be
89
+ // individually justified as "never intentionally holds a first-party
90
+ // credential", not merely "not worth linting". See the module doc above for
91
+ // the walker-parity-vs-secrets-lane distinction and the 9-path probe that
92
+ // caught the original (too-broad) version of this fix.
93
+ export const SECRETS_LANE_SCRATCH_DIR_NAMES = [
94
+ // pi-ecosystem / coding-agent data + cache directories.
95
+ ".pi",
96
+ ".pi-lens",
97
+ ".claude",
98
+ ".codex",
99
+ ".agents",
100
+ ".worktrees",
101
+ ".rescue",
102
+ ".gstack",
103
+ ".superpowers",
104
+ ".guardrails",
105
+ ".playwright-cli",
106
+ ".playwright-mcp",
107
+ // git's own internal object storage — binary/compressed, not readable
108
+ // source; a byte-regex scan of it produces garbage, not real findings.
109
+ ".git",
110
+ // Vendored/installed npm packages — third-party noise, not a first-party
111
+ // leak surface (unlike `vendor`/`third_party`, which are vendored SOURCE
112
+ // a project can carry an upstream leak in and stays in scope).
113
+ "node_modules",
114
+ // Generic build/package-manager CACHES — regenerated from source on every
115
+ // build/install, never a place a human commits a credential on purpose.
116
+ // Deliberately excludes build OUTPUT (`dist`/`build`/`out`/`target`/
117
+ // `coverage`) — a bundler/compiler CAN bake a real secret into output.
118
+ ".turbo",
119
+ ".cache",
120
+ ".parcel-cache",
121
+ ".svelte-kit",
122
+ ".nuxt",
123
+ ".yarn",
124
+ ".pnpm-store",
125
+ ".gradle",
126
+ ".next",
127
+ ".ruff_cache",
128
+ ".tox",
129
+ ".pytest_cache",
130
+ "venv",
131
+ ".venv",
132
+ "__pycache__",
133
+ ];
134
+ /**
135
+ * Regex patterns for gitleaks's `[allowlist] paths` config array, scoped to
136
+ * the narrow secrets-lane tier above (NOT the walker-parity list — see the
137
+ * module doc). Matched against the finding's relative path.
138
+ *
139
+ * #1562 review-round F4: gitleaks's `File` field preserves the OS-native
140
+ * separator from Go's directory walk — on Windows that's `\`, not `/` — so
141
+ * an anchor of only `(?:^|/)` never matches there. Both separators are
142
+ * accepted in the anchor and in the "rest of path" tail.
143
+ */
144
+ export function getSecretsLaneAllowlistPaths() {
145
+ return SECRETS_LANE_SCRATCH_DIR_NAMES.map((name) => {
146
+ const escaped = escapeRegExp(name);
147
+ return `(?:^|[/\\\\])${escaped}(?:[/\\\\].*)?$`;
148
+ });
149
+ }
150
+ /**
151
+ * True when any path segment of `relPath` (forward- or back-slash separated)
152
+ * is a secrets-lane scratch dir name. Used for the TS-side post-filter that
153
+ * backstops the generated gitleaks config — belt-and-suspenders because we
154
+ * can't exercise gitleaks's own TOML-regex engine in unit tests, only assert
155
+ * the config we hand it.
156
+ */
157
+ export function isUnderSecretsLaneScratchTree(relPath) {
158
+ const names = new Set(SECRETS_LANE_SCRATCH_DIR_NAMES.map((n) => n.toLowerCase()));
159
+ const segments = relPath.split(/[/\\]/).filter(Boolean);
160
+ return segments.some((segment) => names.has(segment.toLowerCase()));
161
+ }
@@ -4,15 +4,31 @@
4
4
  *
5
5
  * Search → edit is a common flow: the agent finds where something must change
6
6
  * (ast_grep_search / lsp_navigation / grep), then edits exactly those lines.
7
- * Those lines were genuinely shown, so they count as read — but ONLY those lines
8
- * (plus a small context margin), never the whole file, so editing an unseen
9
- * region is still guarded.
7
+ * Those lines were genuinely shown, so they count as read — but ONLY the lines
8
+ * the search actually DELIVERED, never the whole file and never a blanket
9
+ * margin, so editing an unseen region is still guarded (#1904).
10
10
  */
11
11
  import * as nodeFs from "node:fs";
12
12
  import * as path from "node:path";
13
13
  import { isPathIgnoredByProject } from "./file-utils.js";
14
14
  import { isExternalOrVendorFile } from "./path-utils.js";
15
- const DEFAULT_CONTEXT_MARGIN = 2;
15
+ /** Clamp a delivered-context count to a usable non-negative integer. */
16
+ function normalizeMargin(value) {
17
+ if (value === undefined || !Number.isFinite(value))
18
+ return undefined;
19
+ return Math.max(0, Math.floor(value));
20
+ }
21
+ /**
22
+ * #1904 item 2: this was 2, which credited every search hit as a 5-line read
23
+ * even though most searches print ONE line. The guard then let edits pass
24
+ * against lines the model never saw. Credit only delivered lines; callers that
25
+ * DID deliver context pass `contextBefore`/`contextAfter` per location.
26
+ *
27
+ * Note this is not the only slack in the system: `readCoversRange` in
28
+ * read-guard.ts still widens every read record by `config.contextLines` when it
29
+ * tests coverage, so a bare hit remains editable within that configured band.
30
+ */
31
+ const DEFAULT_CONTEXT_MARGIN = 0;
16
32
  /**
17
33
  * Record each shown location as a read (± context margin). Resolves project-
18
34
  * relative paths, skips external/vendor/ignored/non-existent files, and dedupes
@@ -39,11 +55,18 @@ export function registerSearchReads(readGuard, locations, opts) {
39
55
  catch {
40
56
  continue;
41
57
  }
42
- const start = Math.max(1, Math.floor(loc.startLine) - margin);
58
+ const marginBefore = normalizeMargin(loc.contextBefore) ?? margin;
59
+ const marginAfter = normalizeMargin(loc.contextAfter) ?? margin;
60
+ const creditReason = marginBefore === 0 && marginAfter === 0
61
+ ? "match-lines-only"
62
+ : loc.contextBefore !== undefined || loc.contextAfter !== undefined
63
+ ? "delivered-context-flags"
64
+ : "caller-margin";
65
+ const start = Math.max(1, Math.floor(loc.startLine) - marginBefore);
43
66
  const endLine = Number.isFinite(loc.endLine)
44
67
  ? loc.endLine
45
68
  : loc.startLine;
46
- const end = Math.max(start, Math.floor(endLine) + margin);
69
+ const end = Math.max(start, Math.floor(endLine) + marginAfter);
47
70
  const limit = end - start + 1;
48
71
  const key = `${abs}:${start}:${limit}`;
49
72
  if (seen.has(key))
@@ -56,6 +79,11 @@ export function registerSearchReads(readGuard, locations, opts) {
56
79
  effectiveOffset: start,
57
80
  effectiveLimit: limit,
58
81
  expandedByLsp: false,
82
+ searchCredit: {
83
+ marginBefore,
84
+ marginAfter,
85
+ reason: creditReason,
86
+ },
59
87
  turnIndex: opts.turnIndex,
60
88
  writeIndex: opts.writeIndex,
61
89
  timestamp: Date.now(),