@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  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 +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -178,6 +178,10 @@ export function collectTrackedFiles(cwd) {
178
178
  // this file's module doc for why this diverges from `collectUntrackedIgnoredIds`.
179
179
  const key = normalizeEphemeralMapKey(cwd);
180
180
  const now = Date.now();
181
+ for (const [snapshotKey, entry] of _trackedCache) {
182
+ if (now - entry.fetchedAtMs >= CACHE_TTL_MS)
183
+ _trackedSnapshot.delete(snapshotKey);
184
+ }
181
185
  const cached = _trackedCache.get(key);
182
186
  if (cached && now - cached.fetchedAtMs < CACHE_TTL_MS)
183
187
  return cached.promise;
@@ -6,15 +6,17 @@
6
6
  * Requires: gopls (go install golang.org/x/tools/gopls@latest)
7
7
  * Docs: https://pkg.go.dev/golang.org/x/tools/gopls
8
8
  */
9
- import * as fs from "node:fs";
9
+ import { createSubsystemLogger } from "./extension-log.js";
10
10
  import * as path from "node:path";
11
- import { safeSpawnAsync } from "./safe-spawn.js";
11
+ import { createToolchainAvailability, } from "./dispatch/runners/utils/toolchain-availability.js";
12
12
  // --- Common install paths ---
13
13
  const GO_WINDOWS_PATHS = [
14
14
  "C:\\Program Files\\Go\\bin\\go.exe",
15
15
  "C:\\Go\\bin\\go.exe",
16
16
  "go.exe", // PATH
17
17
  ];
18
+ /** Budget for the PATH candidate's `go version` probe, ms. */
19
+ const PROBE_TIMEOUT_MS = 3_000;
18
20
  const GO_UNIX_PATHS = [
19
21
  "/usr/local/go/bin/go",
20
22
  "/usr/bin/go",
@@ -22,58 +24,39 @@ const GO_UNIX_PATHS = [
22
24
  ];
23
25
  // --- Client ---
24
26
  export class GoClient {
25
- goAvailable = null;
26
- 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;
27
34
  log;
28
35
  constructor(verbose = false) {
29
36
  this.log = verbose
30
- ? (msg) => console.error(`[go] ${msg}`)
37
+ ? createSubsystemLogger("go")
31
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
+ });
32
48
  }
33
49
  /**
34
50
  * Find go executable path (async — probes PATH candidates off the event loop).
35
51
  */
36
52
  async findGoPathAsync() {
37
- if (this.goPath)
38
- return this.goPath;
39
- const paths = process.platform === "win32" ? GO_WINDOWS_PATHS : GO_UNIX_PATHS;
40
- for (const p of paths) {
41
- try {
42
- if (p.includes("\\") || p.includes("/")) {
43
- // Absolute path - check if exists
44
- if (fs.existsSync(p)) {
45
- this.goPath = p;
46
- return p;
47
- }
48
- }
49
- else {
50
- // Relative (PATH) - try running it
51
- const result = await safeSpawnAsync(p, ["version"], {
52
- timeout: 3000,
53
- });
54
- if (!result.error && result.status === 0) {
55
- this.goPath = p;
56
- return p;
57
- }
58
- }
59
- }
60
- catch (err) {
61
- void err;
62
- }
63
- }
64
- return null;
53
+ return this.availability.findPath();
65
54
  }
66
55
  /**
67
56
  * Check if Go is installed (cached)
68
57
  */
69
58
  async isGoAvailableAsync() {
70
- if (this.goAvailable !== null)
71
- return this.goAvailable;
72
- this.goAvailable = (await this.findGoPathAsync()) !== null;
73
- if (this.goAvailable) {
74
- this.log(`Go found: ${this.goPath}`);
75
- }
76
- return this.goAvailable;
59
+ return this.availability.isAvailable();
77
60
  }
78
61
  /**
79
62
  * Check if a file is a Go file
@@ -20,12 +20,16 @@ import * as fs from "node:fs";
20
20
  import * as os from "node:os";
21
21
  import * as path from "node:path";
22
22
  import { safeSpawnAsync } from "./safe-spawn.js";
23
+ import { assertInstallAllowed } from "./project-trust.js";
23
24
  import { SecurityScanClient } from "./security-scan-client.js";
25
+ import { classifyProbeFailure, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
24
26
  const EMPTY_RESULT = {
25
27
  success: false,
26
28
  findings: [],
27
29
  };
28
30
  const SCAN_TIMEOUT_MS = 120_000;
31
+ /** Budget for the `go install` auto-install, ms. */
32
+ const INSTALL_TIMEOUT_MS = 60_000;
29
33
  // --- Client ---
30
34
  export class GovulncheckClient extends SecurityScanClient {
31
35
  constructor(verbose = false) {
@@ -53,33 +57,122 @@ export class GovulncheckClient extends SecurityScanClient {
53
57
  this.available = true;
54
58
  return true;
55
59
  }
60
+ if (this.probeWasTransient()) {
61
+ // #1467: a timed-out/killed probe says nothing about whether
62
+ // govulncheck is on PATH. Latching `false` here disabled it for the
63
+ // life of the process; a `go install` here would be a heavyweight
64
+ // reaction to a host hiccup. Record an expiring verdict, retry later.
65
+ this.log("govulncheck probe timed out; retrying later (not installing)");
66
+ this.markTransientlyUnavailable(this.lastProbeCause ?? "probe-timeout");
67
+ return false;
68
+ }
69
+ if (!assertInstallAllowed("govulncheck go install")) {
70
+ // Deliberately NOT latching `available = false` (#1350 delta review):
71
+ // trust denial is policy, not tool absence -- a later trust grant
72
+ // (re-adopted at turn_start) must be able to retry the install, and
73
+ // the cached false in ensureAvailable() would make denial permanent.
74
+ return false;
75
+ }
56
76
  // Not on PATH — auto-install via `go install`. This is safe to assume
57
77
  // here because the only path reaching `ensureAvailable()` is the
58
78
  // session_start task gated on `hasGoModule(analysisRoot)`; if the
59
79
  // project has a go.mod the user has the Go toolchain by definition.
60
80
  // Same shape as rust-clippy / cargo: lean on the language's own
61
81
  // install mechanism rather than adding a new installer strategy.
62
- const goOnPath = await safeSpawnAsync("go", ["version"], {
63
- timeout: 5000,
64
- });
82
+ const goProbeStartedAt = Date.now();
83
+ const goSampler = startHostStallSampler();
84
+ let goOnPath;
85
+ let goHostStallMs;
86
+ try {
87
+ goOnPath = await safeSpawnAsync("go", ["version"], {
88
+ timeout: 5000,
89
+ });
90
+ }
91
+ finally {
92
+ goHostStallMs = goSampler.stop();
93
+ }
65
94
  if (goOnPath.error || goOnPath.status !== 0) {
95
+ const { outcome, cause } = classifyProbeFailure(goOnPath, {
96
+ hostStallMs: goHostStallMs,
97
+ });
98
+ if (outcome === "transient") {
99
+ this.log("`go version` probe timed out; retrying govulncheck later");
100
+ const retryAfterMs = this.markTransientlyUnavailable(cause);
101
+ logAvailabilityDecision({
102
+ tool: "govulncheck",
103
+ verdict: "unavailable",
104
+ outcome,
105
+ cause,
106
+ elapsedMs: Date.now() - goProbeStartedAt,
107
+ latched: false,
108
+ hostStallMs: goHostStallMs,
109
+ ...(retryAfterMs > 0 && { retryAfterMs }),
110
+ budgetMs: 5000,
111
+ });
112
+ return false;
113
+ }
66
114
  this.log("go binary not on PATH — cannot auto-install govulncheck");
67
115
  this.available = false;
68
116
  return false;
69
117
  }
70
118
  this.log("govulncheck not found, attempting auto-install via go install");
71
- const install = await safeSpawnAsync("go", ["install", "golang.org/x/vuln/cmd/govulncheck@latest"], { timeout: 60_000 });
119
+ const installStartedAt = Date.now();
120
+ const installSampler = startHostStallSampler();
121
+ let install;
122
+ let installHostStallMs;
123
+ try {
124
+ install = await safeSpawnAsync("go", ["install", "golang.org/x/vuln/cmd/govulncheck@latest"], { timeout: INSTALL_TIMEOUT_MS });
125
+ }
126
+ finally {
127
+ installHostStallMs = installSampler.stop();
128
+ }
72
129
  if (install.error || install.status !== 0) {
73
130
  this.log(`govulncheck auto-install failed: ${(install.stderr ?? "").slice(0, 200)}`);
131
+ // #1476: `go install` runs against a 60 s budget over the network. A
132
+ // timed-out or killed install says nothing about whether govulncheck
133
+ // can ever be installed here, so it must not latch the durable
134
+ // "tool is not installed" verdict the way a real install refusal does.
135
+ // A non-zero exit (module not found, compile error) still latches:
136
+ // that IS evidence about this machine, and retrying it every turn
137
+ // would be a `go install` storm.
138
+ const { outcome, cause } = classifyProbeFailure(install, {
139
+ hostStallMs: installHostStallMs,
140
+ });
141
+ if (outcome === "transient") {
142
+ const retryAfterMs = this.markTransientlyUnavailable(cause);
143
+ // One record per decision, so an install that keeps timing out is
144
+ // readable in latency.log the same day (#1467's forensic trail).
145
+ logAvailabilityDecision({
146
+ tool: "govulncheck",
147
+ verdict: "unavailable",
148
+ outcome,
149
+ cause,
150
+ elapsedMs: Date.now() - installStartedAt,
151
+ latched: false,
152
+ hostStallMs: installHostStallMs,
153
+ ...(retryAfterMs > 0 && { retryAfterMs }),
154
+ budgetMs: INSTALL_TIMEOUT_MS,
155
+ });
156
+ return false;
157
+ }
74
158
  this.available = false;
75
159
  return false;
76
160
  }
77
161
  // `go install` writes to `$GOBIN` or `$GOPATH/bin`. The user may not
78
162
  // have that on `$PATH`. Re-probe by name (works when it is on PATH)
79
163
  // then fall back to the canonical bin dirs.
80
- const reprobe = await safeSpawnAsync("govulncheck", ["-version"], {
81
- timeout: 5000,
82
- });
164
+ const reprobeStartedAt = Date.now();
165
+ const reprobeSampler = startHostStallSampler();
166
+ let reprobe;
167
+ let reprobeHostStallMs;
168
+ try {
169
+ reprobe = await safeSpawnAsync("govulncheck", ["-version"], {
170
+ timeout: 5000,
171
+ });
172
+ }
173
+ finally {
174
+ reprobeHostStallMs = reprobeSampler.stop();
175
+ }
83
176
  if (!reprobe.error && reprobe.status === 0) {
84
177
  this.log("govulncheck auto-installed and found on PATH");
85
178
  this.available = true;
@@ -110,6 +203,30 @@ export class GovulncheckClient extends SecurityScanClient {
110
203
  // fall through to next candidate
111
204
  }
112
205
  }
206
+ // The install SUCCEEDED, so the tool is on this machine somewhere. If the
207
+ // re-probe merely timed out, latching "not installed" would be the #1467
208
+ // mistake one step later in the same function (#1476).
209
+ const { outcome, cause } = classifyProbeFailure(reprobe, {
210
+ hostStallMs: reprobeHostStallMs,
211
+ });
212
+ if (outcome === "transient") {
213
+ this.log("govulncheck installed but the re-probe timed out; retrying later");
214
+ const retryAfterMs = this.markTransientlyUnavailable(cause);
215
+ logAvailabilityDecision({
216
+ tool: "govulncheck",
217
+ verdict: "unavailable",
218
+ outcome,
219
+ cause,
220
+ // The re-probe's own wall time. A hard-coded 0 here was the #1474
221
+ // defect verbatim: a duration field that measures nothing.
222
+ elapsedMs: Date.now() - reprobeStartedAt,
223
+ latched: false,
224
+ hostStallMs: reprobeHostStallMs,
225
+ ...(retryAfterMs > 0 && { retryAfterMs }),
226
+ budgetMs: 5000,
227
+ });
228
+ return false;
229
+ }
113
230
  this.log("govulncheck auto-install succeeded but binary not locatable — check $GOBIN / $GOPATH");
114
231
  this.available = false;
115
232
  return false;
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import * as fs from "node:fs";
13
13
  import * as path from "node:path";
14
+ import { writeFileAtomic } from "./atomic-write.js";
14
15
  /** tree-sitter-wasms release the grammars are pulled from. */
15
16
  export const TREE_SITTER_WASMS_VERSION = "0.1.13";
16
17
  /** unpkg mirror of the tree-sitter-wasms artifacts. */
@@ -110,11 +111,18 @@ export function grammarBlockReason(filename, rt = currentGrammarRuntime()) {
110
111
  return block?.blocked(rt) ? block.reason : null;
111
112
  }
112
113
  /**
113
- * Fetch one grammar wasm into `destDir` (atomic via a temp file). Returns true
114
+ * Fetch one grammar wasm into `destDir`, staged and renamed via
115
+ * `atomic-write.ts` so a reader never sees a half-written wasm. Returns true
114
116
  * on success. Never throws — a failed fetch (offline, 4xx) degrades to "grammar
115
117
  * unavailable" so callers can decide how to handle it. A grammar that crashes the
116
118
  * runtime is protected at LOAD time (BLOCKED_GRAMMARS / grammarBlockReason), not
117
119
  * by refusing to download it.
120
+ *
121
+ * #1217: the staging name used to be hand-rolled `.${filename}.${pid}.tmp` —
122
+ * per-process, so two concurrent fetches of one grammar shared a staging inode
123
+ * (#1205). `tree-sitter-client.ts` de-dupes in-flight fetches per grammar file,
124
+ * which made that hard to reach through the runtime path but did not cover
125
+ * direct callers.
118
126
  */
119
127
  export async function downloadGrammar(destDir, filename) {
120
128
  try {
@@ -123,9 +131,9 @@ export async function downloadGrammar(destDir, filename) {
123
131
  if (!res.ok)
124
132
  throw new Error(`HTTP ${res.status}`);
125
133
  const data = Buffer.from(await res.arrayBuffer());
126
- const tmp = path.join(destDir, `.${filename}.${process.pid}.tmp`);
127
- fs.writeFileSync(tmp, data);
128
- fs.renameSync(tmp, path.join(destDir, filename));
134
+ // bestEffort: false — a swallowed write failure would return true for a
135
+ // grammar that never landed.
136
+ writeFileAtomic(path.join(destDir, filename), data, { bestEffort: false });
129
137
  return true;
130
138
  }
131
139
  catch {
@@ -4,10 +4,12 @@ import { Readable } from "node:stream";
4
4
  import { pipeline } from "node:stream/promises";
5
5
  import { parentPort } from "node:worker_threads";
6
6
  import { createGzip } from "node:zlib";
7
+ import { stagePathFor } from "./atomic-write-staging.js";
7
8
  /**
8
9
  * Shared worker-thread body-persist core (#958, single source of truth #883):
9
- * `JSON.stringify` → chunked `createGzip` pipeline → `<stagePath>.tmp-<pid>` →
10
- * atomic rename to `stagePath`, returning byte/timing metrics. Both
10
+ * `JSON.stringify` → chunked `createGzip` pipeline → a per-call staging file
11
+ * from `atomic-write.ts`'s {@link stagePathFor} → atomic rename to
12
+ * `stagePath`, returning byte/timing metrics. Both
11
13
  * `clients/review-graph/persist-worker.ts` and
12
14
  * `clients/project-snapshot-persist-worker.ts` call this so the streamed-gzip
13
15
  * write lives in exactly one place; each worker's `parentPort` wiring owns its
@@ -19,9 +21,25 @@ import { createGzip } from "node:zlib";
19
21
  * On failure the partial `.tmp` is removed and the error is rethrown for the
20
22
  * caller to record; the `stagePath` itself is only ever created by the atomic
21
23
  * rename, so a crash mid-write can never leave a torn stage file behind.
24
+ *
25
+ * That crash-safety is NOT the same as concurrency isolation, and this
26
+ * docstring used to conflate the two (#1217). The staging name is what keeps
27
+ * two concurrent calls on one `stagePath` from writing into a shared inode,
28
+ * and it has to come from `stagePathFor` rather than a local
29
+ * `${stagePath}.tmp-${process.pid}`: this module runs on worker threads, which
30
+ * share `process.pid` with the main thread and with each other, so pid alone
31
+ * isolates nothing here (#1205's per-pid tear, verbatim). Callers normally
32
+ * pass a per-generation `stagePath`, which makes the overlap rare — a retry, a
33
+ * re-queued persist, a repeated generation, or a future non-generational
34
+ * caller makes it exact. Beyond tear-freedom this offers nothing: two calls on
35
+ * one `stagePath` are still unordered and last-rename-wins on POSIX — on
36
+ * Windows, MoveFileEx's REPLACE_EXISTING does not serialize concurrent replaces
37
+ * of the same destination, so one rename can fail with EPERM instead, which
38
+ * the caller must treat as a persist failure (both persist workers fall back
39
+ * to a main-thread rewrite on an error result).
22
40
  */
23
41
  export async function writeGzipStageFile(data, stagePath, testDelayMs) {
24
- const tmpPath = `${stagePath}.tmp-${process.pid}`;
42
+ const tmpPath = stagePathFor(stagePath);
25
43
  try {
26
44
  if (testDelayMs) {
27
45
  await new Promise((resolve) => setTimeout(resolve, testDelayMs));
@@ -0,0 +1,31 @@
1
+ /** Host-neutral capabilities available to the pi-lens engine (#1358 S2). */
2
+ /**
3
+ * Headless/test implementation. Its feature-detection defaults deliberately
4
+ * match the pre-ports absent-host paths: unknown trust/mode, fail-open spawn,
5
+ * and no-op delivery surfaces.
6
+ */
7
+ export function createDefaultHostPorts(overrides = {}) {
8
+ const unknownMode = () => "unknown";
9
+ const defaults = {
10
+ notify: { user: () => { } },
11
+ trust: { isProjectTrusted: () => "unknown" },
12
+ mode: {
13
+ current: unknownMode,
14
+ supportsTuiWidget: () => true,
15
+ suppressesUserNotify: () => false,
16
+ },
17
+ log: { extension: () => { }, debug: () => { }, sink: () => () => { } },
18
+ emit: { bus: () => { } },
19
+ status: { set: () => { } },
20
+ spawn: { abortSignal: () => undefined, isAllowed: () => true },
21
+ render: { invalidate: () => { } },
22
+ session: { id: () => undefined },
23
+ workspace: { cwd: () => undefined, projectRoot: () => undefined },
24
+ flags: { get: () => undefined },
25
+ tools: { has: async () => false, getActive: () => [], setActive: () => { } },
26
+ };
27
+ return Object.fromEntries(Object.entries(defaults).map(([group, value]) => [
28
+ group,
29
+ { ...value, ...(overrides[group] ?? {}) },
30
+ ]));
31
+ }