@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
@@ -8,11 +8,14 @@
8
8
  * Requires: npm install -D knip
9
9
  * Docs: https://knip.dev/
10
10
  */
11
+ import { createSubsystemLogger } from "./extension-log.js";
11
12
  import * as fs from "node:fs";
12
13
  import * as path from "node:path";
13
14
  import { getProjectDataDir } from "./file-utils.js";
14
15
  import { findNearestMarkerRoot } from "./path-utils.js";
15
16
  import { safeSpawnAsync } from "./safe-spawn.js";
17
+ import { createAvailabilityChecker, findManagedNodeToolBinary, getManagedToolEnvironment, resolveAvailableOrInstall, } from "./dispatch/runners/utils/runner-helpers.js";
18
+ import { createAvailabilityLatch, describeUnavailability, } from "./dispatch/runners/utils/availability-policy.js";
16
19
  const EMPTY_RESULT = {
17
20
  success: false,
18
21
  issues: [],
@@ -56,7 +59,18 @@ export function readOverridePinnedPackageNames(targetDir) {
56
59
  }
57
60
  // --- Client ---
58
61
  export class KnipClient {
59
- knipAvailable = null;
62
+ knipAvailability = createAvailabilityChecker("knip", ".cmd", ["--version"], {
63
+ environment: (cwd) => getManagedToolEnvironment("knip", cwd),
64
+ unclassifiedFailureOutcome: "missing",
65
+ fastPath: () => findManagedNodeToolBinary("knip"),
66
+ });
67
+ /**
68
+ * Client-side memo. Only a DURABLE verdict is latched — a transient probe
69
+ * failure expires, so an installed knip becomes available again without a
70
+ * host restart (#1467).
71
+ */
72
+ availabilityLatch = createAvailabilityLatch();
73
+ knipCommand = "knip";
60
74
  ensureInFlight = null;
61
75
  log;
62
76
  /**
@@ -75,7 +89,7 @@ export class KnipClient {
75
89
  inFlight = new Map();
76
90
  constructor(verbose = false) {
77
91
  this.log = verbose
78
- ? (msg) => console.error(`[knip] ${msg}`)
92
+ ? createSubsystemLogger("knip")
79
93
  : () => { };
80
94
  }
81
95
  /**
@@ -97,12 +111,17 @@ export class KnipClient {
97
111
  return findNearestMarkerRoot(startDir, ["package.json", "knip.json", "knip.ts", "knip.config.js", "knip.config.ts"], { boundaries: [".git", ".hg", ".svn"], homeDir: homeDirOverride });
98
112
  }
99
113
  /**
100
- * Check if knip CLI is available, auto-install if not
114
+ * Check if knip CLI is available, auto-install if not.
115
+ *
116
+ * The memo returns `null` when the last verdict was transient and its
117
+ * cooldown has expired, which re-enters the probe. That is the difference
118
+ * between "knip is missing" (a fact worth caching) and "the probe timed out"
119
+ * (a moment worth retrying).
101
120
  */
102
121
  async ensureAvailable() {
103
- // Fast path: already checked
104
- if (this.knipAvailable !== null)
105
- return this.knipAvailable;
122
+ const memo = this.availabilityLatch.read();
123
+ if (memo !== null)
124
+ return memo;
106
125
  if (this.ensureInFlight)
107
126
  return this.ensureInFlight;
108
127
  this.ensureInFlight = this.doEnsureAvailable();
@@ -114,27 +133,40 @@ export class KnipClient {
114
133
  }
115
134
  }
116
135
  async doEnsureAvailable() {
117
- // Check if available in PATH (fast)
118
- const pathResult = await safeSpawnAsync("knip", ["--version"], {
119
- timeout: 5000,
120
- });
121
- if (!pathResult.error && pathResult.status === 0) {
122
- this.knipAvailable = true;
123
- this.log("Knip found in PATH");
136
+ const cwd = process.cwd();
137
+ const resolved = await resolveAvailableOrInstall(this.knipAvailability, "knip", cwd);
138
+ if (resolved !== null) {
139
+ this.knipCommand = resolved;
140
+ this.availabilityLatch.noteAvailable();
124
141
  return true;
125
142
  }
126
- // Auto-install via pi-lens installer
127
- this.log("Knip not found, attempting auto-install...");
128
- const { ensureTool } = await import("./installer/index.js");
129
- const installedPath = await ensureTool("knip");
130
- if (installedPath) {
131
- this.knipAvailable = true;
132
- this.log(`Knip auto-installed: ${installedPath}`);
133
- return true;
134
- }
135
- this.knipAvailable = false;
143
+ const verdict = this.knipAvailability.getVerdict(cwd);
144
+ this.availabilityLatch.noteUnavailable(verdict.outcome ?? "missing", verdict.cause ?? "not-found");
136
145
  return false;
137
146
  }
147
+ /**
148
+ * The single place a knip-unavailable result is worded. A transient probe
149
+ * failure must never be reported as "install knip" — knip is on disk.
150
+ */
151
+ unavailableResult() {
152
+ const verdict = this.knipAvailability.getVerdict(process.cwd());
153
+ const transient = verdict.outcome === "transient";
154
+ const retryAfterMs = verdict.retryAtMs
155
+ ? Math.max(0, verdict.retryAtMs - Date.now())
156
+ : undefined;
157
+ return {
158
+ ...EMPTY_RESULT,
159
+ failureKind: transient ? "unavailable-transient" : "unavailable-missing",
160
+ summary: describeUnavailability({
161
+ tool: "Knip",
162
+ installHint: "npm install -D knip",
163
+ outcome: verdict.outcome,
164
+ cause: verdict.cause,
165
+ elapsedMs: verdict.elapsedMs,
166
+ retryAfterMs,
167
+ }),
168
+ };
169
+ }
138
170
  /**
139
171
  * Run knip analysis on the project.
140
172
  *
@@ -160,10 +192,7 @@ export class KnipClient {
160
192
  };
161
193
  }
162
194
  if (!(await this.ensureAvailable())) {
163
- return {
164
- ...EMPTY_RESULT,
165
- summary: "Knip not available. Install with: npm install -D knip",
166
- };
195
+ return this.unavailableResult();
167
196
  }
168
197
  const key = path.resolve(targetDir);
169
198
  const existing = this.inFlight.get(key);
@@ -213,10 +242,10 @@ export class KnipClient {
213
242
  "--cache-location",
214
243
  cacheLocation,
215
244
  ];
216
- const result = await safeSpawnAsync("knip", args, {
245
+ const result = await safeSpawnAsync(this.knipCommand, args, {
217
246
  timeout: ANALYSIS_TIMEOUT_MS,
218
247
  cwd: targetDir,
219
- env: await this.getKnipEnvironment(targetDir),
248
+ env: await getManagedToolEnvironment("knip", targetDir),
220
249
  });
221
250
  if (result.error) {
222
251
  this.log(`Analysis error: ${result.error.message}`);
@@ -271,19 +300,6 @@ export class KnipClient {
271
300
  ? result
272
301
  : { ...result, issues, unusedDeps };
273
302
  }
274
- async getKnipEnvironment(targetDir) {
275
- const { getToolEnvironment } = await import("./installer/index.js");
276
- const env = await getToolEnvironment();
277
- const separator = process.platform === "win32" ? ";" : ":";
278
- const currentPath = env.PATH || env.Path || process.env.PATH || "";
279
- const localBin = path.join(targetDir, "node_modules", ".bin");
280
- const augmentedPath = `${localBin}${separator}${currentPath}`;
281
- return {
282
- ...env,
283
- PATH: augmentedPath,
284
- ...(process.platform === "win32" ? { Path: augmentedPath } : {}),
285
- };
286
- }
287
303
  /**
288
304
  * Find unused exports in a specific file
289
305
  */
@@ -56,11 +56,13 @@ export const LANGUAGE_POLICY = {
56
56
  lua: { lspCapable: true },
57
57
  zig: { lspCapable: true },
58
58
  haskell: { lspCapable: true },
59
+ "helm-template": { lspCapable: false },
59
60
  elixir: { lspCapable: true },
60
61
  gleam: { lspCapable: true },
61
62
  ocaml: { lspCapable: true },
62
63
  clojure: { lspCapable: true },
63
64
  terraform: { lspCapable: true },
65
+ terragrunt: { lspCapable: false },
64
66
  nix: { lspCapable: true },
65
67
  toml: { lspCapable: true, startup: { defaults: ["taplo"] } },
66
68
  };
@@ -117,7 +119,7 @@ const PRIMARY_DISPATCH_GROUPS = {
117
119
  },
118
120
  yaml: {
119
121
  mode: "all",
120
- runnerIds: ["lsp", "yamllint", "trivy-config"],
122
+ runnerIds: ["lsp", "yamllint", "trivy-config", "helm-lint"],
121
123
  filterKinds: ["yaml"],
122
124
  },
123
125
  sql: {
@@ -179,6 +181,11 @@ const PRIMARY_DISPATCH_GROUPS = {
179
181
  lua: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["lua"] },
180
182
  zig: { mode: "all", runnerIds: ["lsp", "zig-check"], filterKinds: ["zig"] },
181
183
  haskell: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["haskell"] },
184
+ "helm-template": {
185
+ mode: "all",
186
+ runnerIds: ["helm-lint"],
187
+ filterKinds: ["helm-template"],
188
+ },
182
189
  elixir: {
183
190
  mode: "all",
184
191
  runnerIds: ["lsp", "elixir-check", "credo"],
@@ -193,9 +200,14 @@ const PRIMARY_DISPATCH_GROUPS = {
193
200
  clojure: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["clojure"] },
194
201
  terraform: {
195
202
  mode: "all",
196
- runnerIds: ["lsp", "tflint"],
203
+ runnerIds: ["lsp", "tflint", "trivy-config"],
197
204
  filterKinds: ["terraform"],
198
205
  },
206
+ terragrunt: {
207
+ mode: "all",
208
+ runnerIds: ["terragrunt"],
209
+ filterKinds: ["terragrunt"],
210
+ },
199
211
  nix: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["nix"] },
200
212
  toml: {
201
213
  mode: "all",
@@ -1,12 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { CODE_KINDS, detectFileKind, DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, } from "./file-kinds.js";
3
+ import { CODE_KINDS, detectFileKind, DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, TERRAGRUNT_FILENAMES, } from "./file-kinds.js";
4
4
  import { getProjectIgnoreMatcher } from "./file-utils.js";
5
- import { direntsHaveMarkerGlobMatch } from "./path-utils.js";
5
+ import { direntsHaveMarkerGlobMatch, normalizeMapKey } from "./path-utils.js";
6
6
  import { LANGUAGE_POLICY, } from "./language-policy.js";
7
7
  import { getSourceFiles } from "./scan-utils.js";
8
8
  import { readDirEntriesSafe, shouldRecurseIntoDir } from "./source-walker.js";
9
9
  import { findNearestDirWithAnyBasename } from "./workspace-topology.js";
10
+ import { BoundedLruCache } from "./bounded-cache.js";
10
11
  /** Every registered kind participates in project-language detection (#894). */
11
12
  export const SUPPORTED_FILE_KINDS = Object.keys(KIND_EXTENSIONS);
12
13
  const PROJECT_MARKERS_BY_KIND = {
@@ -36,6 +37,7 @@ const PROJECT_MARKERS_BY_KIND = {
36
37
  elixir: ["mix.exs"],
37
38
  gleam: ["gleam.toml"],
38
39
  terraform: [".terraform.lock.hcl"],
40
+ terragrunt: TERRAGRUNT_FILENAMES,
39
41
  nix: ["flake.nix"],
40
42
  toml: ["pyproject.toml", "Cargo.toml", "taplo.toml"],
41
43
  csharp: DOTNET_CSHARP_ROOT_MARKERS,
@@ -80,6 +82,7 @@ const ROOT_MARKERS_BY_KIND = {
80
82
  elixir: ["mix.exs"],
81
83
  gleam: ["gleam.toml"],
82
84
  terraform: [".terraform.lock.hcl"],
85
+ terragrunt: TERRAGRUNT_FILENAMES,
83
86
  nix: ["flake.nix"],
84
87
  toml: ["pyproject.toml", "Cargo.toml", "taplo.toml"],
85
88
  csharp: DOTNET_CSHARP_ROOT_MARKERS,
@@ -101,16 +104,16 @@ function hasProjectMarker(projectRoot, marker) {
101
104
  // must not pollute the no-arg cache. The synchronous getSourceFiles() call
102
105
  // inside this function does the same expensive ignoreMatcher-driven walk
103
106
  // as resolveStartupScanContext, so the same memo strategy applies.
104
- const languageProfileCache = new Map();
107
+ const languageProfileCache = new BoundedLruCache(32);
105
108
  export function detectProjectLanguageProfile(projectRoot, sourceFiles) {
106
109
  if (sourceFiles === undefined) {
107
- const cached = languageProfileCache.get(projectRoot);
110
+ const cached = languageProfileCache.get(normalizeMapKey(projectRoot));
108
111
  if (cached)
109
112
  return cached;
110
113
  }
111
114
  const result = computeProjectLanguageProfile(projectRoot, sourceFiles);
112
115
  if (sourceFiles === undefined) {
113
- languageProfileCache.set(projectRoot, result);
116
+ languageProfileCache.set(normalizeMapKey(projectRoot), result);
114
117
  }
115
118
  return result;
116
119
  }
@@ -297,6 +300,9 @@ export async function collectSourceFilesForWarmup(rootDir, maxFiles = MAX_WARMUP
297
300
  break walk;
298
301
  }
299
302
  }
303
+ // Each iteration performs only bounded dirent/ext/ignore checks. Keep the
304
+ // count cadence: the cost does not scale with the corpus item size, and
305
+ // this walk's existing matched-file ceiling bounds the total work.
300
306
  if (++processedSinceYield % yieldEvery === 0) {
301
307
  // See countSourceFilesWithinLimitAsync for why setImmediate.
302
308
  await new Promise((resolve) => setImmediate(resolve));
@@ -306,7 +312,7 @@ export async function collectSourceFilesForWarmup(rootDir, maxFiles = MAX_WARMUP
306
312
  return [...codeOut, ...nonCodeOut];
307
313
  }
308
314
  export async function detectProjectLanguageProfileAsync(projectRoot) {
309
- const cached = languageProfileCache.get(projectRoot);
315
+ const cached = languageProfileCache.get(normalizeMapKey(projectRoot));
310
316
  if (cached)
311
317
  return cached;
312
318
  const files = await collectSourceFilesForWarmup(projectRoot);
@@ -315,6 +321,6 @@ export async function detectProjectLanguageProfileAsync(projectRoot) {
315
321
  // probe (`existsSync` for package.json / pyproject.toml / etc.) which
316
322
  // is constant-time and cheap.
317
323
  const result = detectProjectLanguageProfile(projectRoot, files);
318
- languageProfileCache.set(projectRoot, result);
324
+ languageProfileCache.set(normalizeMapKey(projectRoot), result);
319
325
  return result;
320
326
  }
@@ -9,11 +9,82 @@ const writer = createNdjsonLogger({
9
9
  filePath: LATENCY_LOG_FILE,
10
10
  maxBytes: getMaxLogSizeMB() * 1024 * 1024,
11
11
  });
12
+ /**
13
+ * Most recent non-`loop_block` phase seen by `logLatency`, for cheap block
14
+ * attribution (#1122 / #1123 item 1): the event-loop-block probe fires at
15
+ * turn_end and cannot see *what* stalled the loop, so it stamps the last phase
16
+ * that ran as a starting point for root-causing a genuine block. Tracked before
17
+ * the test-mode guard so it is deterministic and unit-testable.
18
+ */
19
+ let lastPhase;
20
+ /**
21
+ * Phases excluded from `lastPhase` attribution alongside `loop_block`.
22
+ * #1412 L3: `lsp_typescript_project_identity` is the classic-TS first-open
23
+ * project-identity probe (tsserver-sync.ts) — a detached, best-effort
24
+ * telemetry sample fired on every first didOpen, not genuine work. Letting it
25
+ * win `lastPhase` would overwrite the real stall attribution for a
26
+ * loop_block that happens to land right after a first open.
27
+ * #1432 review: `advisory_provenance_decision`,
28
+ * `authoritative_content_attachment_decision`, and
29
+ * `agent_end_deferred_mutation_drain` are the same shape — zero-duration
30
+ * decision telemetry, not genuine work — so they get the same exclusion.
31
+ * `agent_end_deferred_mutation_requeue` (S2d gap 4's per-requeue record) and
32
+ * `session_end_bus_rollup` (S2d gap 5's session-end rollup) are new
33
+ * zero-duration siblings of the same shape, added alongside them.
34
+ *
35
+ * `lsp_aux_wait_outcome` (#1458) is DIFFERENT from every entry above: its
36
+ * `durationMs` is a REAL bounded wait (the post-primary auxiliary grace, up
37
+ * to a few seconds), not zero-duration decision telemetry. It is still
38
+ * excluded because it is a WAIT-OUTCOME RECORD written after the aux wait
39
+ * already completed, not the stall itself — that wait ran inside the
40
+ * `lsp_touch_file`/diagnostics phase surrounding it, so letting this summary
41
+ * row win `lastPhase` would misattribute a `loop_block` to the record instead
42
+ * of to whatever phase is actually stalled when the block fires.
43
+ * #1453: `tool_set_mutation` records an active-tool-set rewrite (zero-duration
44
+ * bookkeeping, and it fires during session_start where a real stall must stay
45
+ * attributed to the work around it), so it is excluded for the same reason.
46
+ * #1467: `availability_decision` records a tool-probe verdict. Its `durationMs`
47
+ * is the child's own probe time (often zero for a fast path or a cached
48
+ * decision) and the record is bookkeeping ABOUT a probe, not host work — a
49
+ * loop_block landing next to one must stay attributed to whatever really
50
+ * stalled the loop, which is frequently the very thing that expired the probe.
51
+ *
52
+ * #1461 slice 1: `finding_dead_path_drop` is the same shape — the record a
53
+ * delivery seam writes when it drops findings whose cited path no longer
54
+ * exists.
55
+ */
56
+ const LAST_PHASE_EXCLUDED = new Set([
57
+ "loop_block",
58
+ "lsp_typescript_project_identity",
59
+ "advisory_provenance_decision",
60
+ "authoritative_content_attachment_decision",
61
+ "agent_end_deferred_mutation_drain",
62
+ "agent_end_deferred_mutation_requeue",
63
+ "session_end_bus_rollup",
64
+ "lsp_aux_wait_outcome",
65
+ "tool_set_mutation",
66
+ "availability_decision",
67
+ "finding_dead_path_drop",
68
+ ]);
69
+ /**
70
+ * The last non-`loop_block` phase logged, or undefined if none yet. Carries its
71
+ * own `ts` so a consumer can gauge staleness: it is intentionally NOT cleared at
72
+ * turn/window boundaries, so on a turn that logged no phase of its own it may
73
+ * point at a prior turn's phase — compare `ts` against the block time before
74
+ * trusting it as the cause (it is a breadcrumb, not proof).
75
+ */
76
+ export function getLastLoggedPhase() {
77
+ return lastPhase;
78
+ }
12
79
  export function logLatency(entry) {
80
+ const ts = new Date().toISOString();
81
+ if (entry.type === "phase" && entry.phase && !LAST_PHASE_EXCLUDED.has(entry.phase)) {
82
+ lastPhase = { phase: entry.phase, ts };
83
+ }
13
84
  if (isTestMode()) {
14
85
  return;
15
86
  }
16
- writer.log({ ...entry, ts: new Date().toISOString(), pid: process.pid });
87
+ writer.log({ ...entry, ts, pid: process.pid });
17
88
  }
18
89
  export function getLatencyLogPath() {
19
90
  return LATENCY_LOG_FILE;
@@ -1,3 +1,5 @@
1
+ import { logExtension } from "./extension-log.js";
2
+ import { notifyUserDegradation } from "./user-notify.js";
1
3
  import * as fs from "node:fs";
2
4
  import * as os from "node:os";
3
5
  import * as path from "node:path";
@@ -20,7 +22,16 @@ function warnInvalidGlobalConfigOnce(configPath, reason) {
20
22
  if (warnedInvalidGlobalConfigs.has(key))
21
23
  return;
22
24
  warnedInvalidGlobalConfigs.add(key);
23
- console.error(`[pi-lens] ignoring invalid global config ${configPath}: ${reason}`);
25
+ const message = `ignoring invalid global config ${configPath}: ${reason}`;
26
+ logExtension({
27
+ subsystem: "lens-config",
28
+ level: "warn",
29
+ message,
30
+ metadata: { configPath, reason },
31
+ });
32
+ // HUMAN-audience too: a config the user wrote is being ignored. Routed
33
+ // through the host's own render path (#1333), never a raw write.
34
+ notifyUserDegradation(`pi-lens: ${message}`);
24
35
  }
25
36
  /** For tests that need to force the warn-once cache to reset between cases. */
26
37
  export function resetGlobalConfigWarnCache() {
@@ -13,27 +13,28 @@
13
13
  * adds thin wrappers over the remaining internal reach-ins (latency, project
14
14
  * scan, LSP status, diagnostic stats, LSP config).
15
15
  */
16
+ import * as path from "node:path";
17
+ import { minimatch } from "./deps/minimatch.js";
16
18
  import { getDiagnosticTracker } from "./diagnostic-tracker.js";
17
19
  import { getLatencyReports, } from "./dispatch/integration.js";
18
20
  import { getResourceFootprint as getResourceFootprintSnapshot, } from "./instance-registry.js";
19
21
  import { initLSPConfig } from "./lsp/config.js";
20
22
  import { getLSPService } from "./lsp/index.js";
21
- import { getOrLoadWarmWordIndex } from "./mcp/analyze.js";
23
+ import { acquireWarmWordIndex } from "./mcp/analyze.js";
24
+ import { normalizeMapKey } from "./path-utils.js";
22
25
  import { scanProjectDiagnostics } from "./project-diagnostics/scanner.js";
26
+ import { loadProjectSnapshotWithoutWordIndex } from "./project-snapshot.js";
23
27
  import { getTreeSitterRuntimeStatus, } from "./tree-sitter-shared.js";
24
- import * as path from "node:path";
25
- import { minimatch } from "./deps/minimatch.js";
26
- import { normalizeMapKey } from "./path-utils.js";
27
- import { loadProjectSnapshot } from "./project-snapshot.js";
28
- import { centralityFromReverseDeps, deserializeWordIndex, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
28
+ import { centralityFromReverseDeps, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
29
29
  // --- Facades (re-exported so adapters import only this module) ---------------
30
30
  export { analyzeFile, } from "./mcp/analyze.js";
31
31
  export { createMcpHost } from "./mcp/host-shim.js";
32
- export { ipcPathForCwd, requestWarmAnalyze, } from "./mcp/ipc.js";
32
+ export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus, requestWarmAnalyze, WARM_TURN_END_SCHEMA_VERSION, } from "./mcp/ipc.js";
33
33
  export { analyzeFileFresh, canRebuildPiLens, REBUILD_UNAVAILABLE_MESSAGE, resolveRebuildScript, runRebuild, summarizeScan, } from "./mcp/review.js";
34
- export { runSessionStart, runTurnEnd, } from "./mcp/session.js";
34
+ export { acknowledgeTurnEnd, runSessionStart, runTurnEnd, runTurnEndForIpc, } from "./mcp/session.js";
35
35
  export { moduleReport, readEnclosing, readSymbol, renderCompactModuleReport, } from "./module-report.js";
36
36
  export { projectReport, renderCompactProjectReport, } from "./project-report.js";
37
+ export { createDefaultHostPorts, } from "./host-ports.js";
37
38
  // --- Query wrappers (own the remaining internal reach-ins) -------------------
38
39
  /** Recent dispatch latency reports (latency.log schema), newest first. */
39
40
  export function recentLatency(limit = 5, fileFilter) {
@@ -44,9 +45,21 @@ export function recentLatency(limit = 5, fileFilter) {
44
45
  }
45
46
  return reports.slice(-limit).reverse();
46
47
  }
47
- /** Cheap project-wide scan (tree-sitter + fact rules). */
48
- export function projectScan(cwd, maxFiles) {
49
- return scanProjectDiagnostics({ cwd, tier: "cheap", maxFiles });
48
+ /**
49
+ * Cheap project-wide scan (tree-sitter + fact rules).
50
+ *
51
+ * `includeGenerated` (#1107 phase 2 review): scan WITHOUT the generated/
52
+ * artifact NAME-heuristic filter — the actionable opt-out
53
+ * `generatedSkipNotice` below points a user at. Default `false` (existing
54
+ * filtering behavior unchanged).
55
+ */
56
+ export function projectScan(cwd, maxFiles, includeGenerated) {
57
+ return scanProjectDiagnostics({
58
+ cwd,
59
+ tier: "cheap",
60
+ maxFiles,
61
+ includeGenerated,
62
+ });
50
63
  }
51
64
  /**
52
65
  * #784: `scanTruncated` (#760) reaches this seam intact but, until now, no
@@ -66,6 +79,53 @@ export function scanTruncationNotice(snapshot) {
66
79
  return (`⚠ Scan truncated at ${snapshot.filesScanned} file(s) — results are partial; ` +
67
80
  "raise maxProjectFiles in .pi-lens.json to scan fully.");
68
81
  }
82
+ /**
83
+ * #1107 phase 2: `generatedNameOnlySkips`/`generatedDirSkips` reach this seam
84
+ * via `ProjectDiagnosticsSnapshot` but need explicit rendering, same pattern
85
+ * as `scanTruncationNotice` above — otherwise a user has no way to learn the
86
+ * generated-name heuristic silently excluded files/directories short of
87
+ * reading `latency.log`'s `source_walk_skip_summary` line directly.
88
+ *
89
+ * #1107 phase 2 review round 2 (P1, empirically proven): this deliberately
90
+ * keys off `generatedNameOnlySkips`, NOT the raw `generatedFileSkips` total.
91
+ * `generatedFileSkips` counts every file the generated/artifact heuristic
92
+ * skipped, including STRONG evidence (lockfiles, declaration files, minified/
93
+ * bundle/chunk output — expected on nearly every real repo) and
94
+ * content-CONFIRMED WEAK matches (the escape hatch checked and a genuine
95
+ * generated-code header was present). Keying the notice off that total meant
96
+ * a repo with nothing more unusual than a `package-lock.json` and an ambient
97
+ * `.d.ts` showed "2 file(s) excluded" on literally every scan forever — the
98
+ * signal drowned itself on day one. `generatedNameOnlySkips` is the narrower,
99
+ * genuinely at-risk residual: a WEAK name match trusted with NO corroborating
100
+ * evidence check at all (only reachable when a caller opts out of the header
101
+ * probe — source-filter.ts's default project-walk path always enables it).
102
+ * Post-escape-hatch, this should be rare-to-zero on the default path, which
103
+ * is exactly what makes the notice meaningful again when it DOES fire.
104
+ * `generatedDirSkips` stays in the trigger condition unchanged: directory-
105
+ * level pruning has no escape hatch at all (the walk never enumerates a
106
+ * pruned directory's contents to check them), so it is always a genuine
107
+ * "real files may be hiding in here, unverified" signal, not a
108
+ * false-positive-prone one — see `generatedDirSkips`'s doc on
109
+ * `SourceCollectionResult`.
110
+ *
111
+ * Returns `undefined` when neither counter is set (nothing to append).
112
+ */
113
+ export function generatedSkipNotice(snapshot) {
114
+ const files = snapshot.generatedNameOnlySkips ?? 0;
115
+ const dirs = snapshot.generatedDirSkips ?? 0;
116
+ if (files === 0 && dirs === 0)
117
+ return undefined;
118
+ const parts = [];
119
+ if (files > 0)
120
+ parts.push(`${files} file(s)`);
121
+ if (dirs > 0)
122
+ parts.push(`${dirs} director${dirs === 1 ? "y" : "ies"}`);
123
+ return (`ℹ ${parts.join(" and ")} excluded by generated-name heuristics with no ` +
124
+ "confirming evidence — a real hand-written file/directory whose name " +
125
+ "looks generated (e.g. `gen.ts`, `generated/`) can be excluded too; " +
126
+ "pass includeGenerated: true to pilens_project_scan / lens_diagnostics " +
127
+ "to scan without this filter if you suspect a false skip.");
128
+ }
69
129
  /** Process-wide tree-sitter health for host status surfaces. */
70
130
  export function treeSitterRuntimeStatus() {
71
131
  return getTreeSitterRuntimeStatus();
@@ -213,7 +273,7 @@ function toSymbolSearchHit(result) {
213
273
  * cwd, never blocking this call) so a retry shortly after succeeds (#348
214
274
  * decision 3).
215
275
  *
216
- * #536 rider: prefers the warm in-memory index (`getOrLoadWarmWordIndex`,
276
+ * #536 rider: prefers the warm in-memory index (`acquireWarmWordIndex`,
217
277
  * clients/mcp/analyze.ts) over a fresh disk read when one exists for this
218
278
  * cwd — a warm `pilens_analyze` call updates that live copy synchronously but
219
279
  * persists it to disk on a debounce (default 1500ms), so without this a query
@@ -239,9 +299,11 @@ function toSymbolSearchHit(result) {
239
299
  * nothing on this hot path.
240
300
  */
241
301
  export async function symbolSearch(query, cwd, limit = 20, options = {}) {
242
- const snapshot = loadProjectSnapshot(cwd);
243
- const index = getOrLoadWarmWordIndex(cwd) ?? deserializeWordIndex(snapshot?.wordIndex);
302
+ const warmLease = acquireWarmWordIndex(cwd);
303
+ const snapshot = loadProjectSnapshotWithoutWordIndex(cwd);
304
+ const index = warmLease.index;
244
305
  if (!index) {
306
+ warmLease.release();
245
307
  const priorStatus = getWordIndexBuildStatus(cwd);
246
308
  const status = priorStatus?.state === "refused"
247
309
  ? priorStatus
@@ -266,9 +328,16 @@ export async function symbolSearch(query, cwd, limit = 20, options = {}) {
266
328
  }
267
329
  // Boost well-connected files using the snapshot's reverse-dependency
268
330
  // (importedBy) counts; snapshot keys are normalized, index keys are raw.
269
- const centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path.resolve(file)));
270
- const fileFilter = buildSymbolSearchFileFilter(cwd, options);
271
- const results = searchWordIndex(index, query, { limit, centrality, fileFilter });
331
+ let centrality;
332
+ let results;
333
+ try {
334
+ centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path.resolve(file)));
335
+ const fileFilter = buildSymbolSearchFileFilter(cwd, options);
336
+ results = searchWordIndex(index, query, { limit, centrality, fileFilter });
337
+ }
338
+ finally {
339
+ warmLease.release();
340
+ }
272
341
  const hits = results.map(toSymbolSearchHit);
273
342
  const { getCachedReviewGraph } = await import("./review-graph/builder.js");
274
343
  const graph = getCachedReviewGraph(cwd);
@@ -1,12 +1,17 @@
1
+ import { logExtension } from "./extension-log.js";
2
+ import { logBusEvent } from "./bus-events-logger.js";
3
+ import { normalizeFilePath } from "./path-utils.js";
4
+ import { createLiveBusEmitter, recordStaleBusFailure, resolveLiveBusEmitter, } from "./live-bus-emitter.js";
1
5
  export const LENS_EVENT_VERSION = 1;
2
6
  export const LENS_EVENT_NAMES = {
3
7
  analysisComplete: "pi-lens/analysis-complete",
4
8
  findings: "pi-lens/findings",
5
9
  turnFindings: "pi-lens/turn-findings",
6
10
  };
7
- let lensEventBus;
8
- export function initLensEvents(pi) {
9
- lensEventBus = pi.events;
11
+ const liveEmitter = createLiveBusEmitter();
12
+ /** Keep deferred deliveries on the live extension activation. */
13
+ export function initLensEventsGetter(getter) {
14
+ liveEmitter.wireGetter(getter);
10
15
  }
11
16
  function truncateText(value, maxChars) {
12
17
  if (value === undefined)
@@ -26,17 +31,66 @@ function normalizeDiagnostic(diagnostic) {
26
31
  function normalizeDiagnostics(diagnostics) {
27
32
  return diagnostics.map(normalizeDiagnostic);
28
33
  }
34
+ // Dropped-emit observability (#1128 review): drops are legitimate (no bus
35
+ // during startup/replacement windows) but must not be invisible. One line per
36
+ // process — enough to notice a permanently-missing bus without spamming.
37
+ let _droppedEmitLogged = false;
38
+ // Emit-failure gating (H1, #1415 review): occurrence-scoped, matching every
39
+ // other producer's `hasLoggedFailure` guard (see clients/bus-publish.ts) and
40
+ // AGENTS.md's "one bus-stale degradation per occurrence" invariant. A success
41
+ // re-arms it, so a later failure records a fresh degradation rather than
42
+ // being silently absorbed by an earlier one.
43
+ let hasLoggedFailure = false;
44
+ /** Test-only: reset module state between test files. */
45
+ export function _resetForTests() {
46
+ liveEmitter.reset();
47
+ _droppedEmitLogged = false;
48
+ hasLoggedFailure = false;
49
+ }
29
50
  function emitLensEvent(eventName, payload) {
30
- const emit = lensEventBus?.emit;
31
- if (!emit)
32
- return;
33
51
  setImmediate(() => {
52
+ const rawCwd = payload.cwd ?? process.cwd();
53
+ // M1: normalizeFilePath is a sync realpathSync.native on Windows — build
54
+ // the resolveLiveBusEmitter log entry lazily so the ready-path (the
55
+ // common case) never pays it. Only the stale-session branch needs it.
56
+ const resolution = resolveLiveBusEmitter(liveEmitter, () => ({
57
+ event: eventName,
58
+ cwd: normalizeFilePath(rawCwd),
59
+ }));
60
+ if (resolution.outcome === "stale-session")
61
+ return;
62
+ if (resolution.outcome === "unwired") {
63
+ if (!_droppedEmitLogged) {
64
+ _droppedEmitLogged = true;
65
+ logExtension({
66
+ subsystem: "lens-events",
67
+ level: "warn",
68
+ message: `lens event dropped (no live bus): ${eventName} — further drops logged once per process`,
69
+ metadata: { eventName },
70
+ });
71
+ }
72
+ return;
73
+ }
74
+ const cwd = normalizeFilePath(rawCwd);
34
75
  try {
35
- emit.call(lensEventBus, eventName, payload);
76
+ resolution.emit(eventName, payload);
77
+ hasLoggedFailure = false;
78
+ logBusEvent({ event: eventName, outcome: "emitted", cwd });
36
79
  }
37
- catch {
80
+ catch (err) {
81
+ logBusEvent({
82
+ event: eventName,
83
+ outcome: "emit_failed",
84
+ cwd,
85
+ error: String(err),
86
+ ctxSource: resolution.ctxSource,
87
+ });
88
+ if (!hasLoggedFailure) {
89
+ hasLoggedFailure = true;
90
+ recordStaleBusFailure(eventName, err);
91
+ }
38
92
  // Inter-extension events are observational. A listener must never break
39
- // the pi-lens hook path or delay agent progress with error handling noise.
93
+ // the pi-lens hook path or delay agent progress.
40
94
  }
41
95
  });
42
96
  }