@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
@@ -24,13 +24,15 @@ import * as path from "node:path";
24
24
  import { fileURLToPath } from "node:url";
25
25
  import { AstGrepClient } from "../clients/ast-grep-client.js";
26
26
  import { CacheManager } from "../clients/cache-manager.js";
27
- import { computeBuildStamp, STALE_SERVED_BY_FRESH, STALE_WARN_ONLY, StalenessGate, stalenessCheckEnabled, } from "./build-staleness.js";
28
- import { analyzeFile, analyzeFileFresh, canRebuildPiLens, createMcpHost, diagnosticStats, ensureLspConfig, ipcPathForCwd, lspStatus, renderLspBrokenStatusLines, moduleReport, projectReport, projectScan, readEnclosing, readSymbol, recentLatency, renderCompactModuleReport, renderCompactProjectReport, resolveRebuildScript, resourceFootprint, runRebuild, runSessionStart, runTurnEnd, scanTruncationNotice, summarizeScan, symbolSearch, treeSitterRuntimeStatus, } from "../clients/lens-engine.js";
27
+ import { getDegradationSummary, renderDegradationLines, } from "../clients/degradation-ledger.js";
28
+ import { acknowledgeTurnEnd, analyzeFile, analyzeFileFresh, canRebuildPiLens, createMcpHost, createWarmIpcLineReader, createWarmIpcRequestQueue, diagnosticStats, ensureLspConfig, generatedSkipNotice, ipcPathForCwd, lspStatus, moduleReport, projectReport, projectScan, readEnclosing, readSymbol, readTurnEndStatus, recentLatency, renderCompactModuleReport, renderCompactProjectReport, renderLspBrokenStatusLines, resolveRebuildScript, resourceFootprint, runRebuild, runSessionStart, runTurnEnd, runTurnEndForIpc, scanTruncationNotice, summarizeScan, symbolSearch, treeSitterRuntimeStatus, WARM_TURN_END_SCHEMA_VERSION, } from "../clients/lens-engine.js";
29
29
  import { createAstGrepReplaceTool } from "../tools/ast-grep-replace.js";
30
30
  import { createAstGrepSearchTool } from "../tools/ast-grep-search.js";
31
31
  import { createLensDiagnosticsTool } from "../tools/lens-diagnostics.js";
32
+ import { peekMcpSessionRuntime } from "../clients/mcp/session.js";
32
33
  import { createLspDiagnosticsTool } from "../tools/lsp-diagnostics.js";
33
34
  import { createLspNavigationTool } from "../tools/lsp-navigation.js";
35
+ import { computeBuildStamp, STALE_SERVED_BY_FRESH, STALE_WARN_ONLY, StalenessGate, stalenessCheckEnabled, } from "./build-staleness.js";
34
36
  // Any stray stdout write corrupts the JSON-RPC stream; force it onto stderr.
35
37
  console.log = (...args) => {
36
38
  console.error(...args);
@@ -191,6 +193,32 @@ function getAutoSessionStatus() {
191
193
  // runs in THIS warm process (LSP-complete) instead of a cold hook process.
192
194
  // Responses go over the socket — never stdout — so the MCP stream is untouched.
193
195
  const IPC_PATH = ipcPathForCwd(DEFAULT_CWD);
196
+ /**
197
+ * #1273: the turn-end route runs the TARGET directory's configured test runner
198
+ * and spins up an LSP for it, from a client-supplied `cwd`. Every legitimate
199
+ * client derived the socket path from that same cwd, so a workspace check costs
200
+ * nothing legitimate — but without it, anything that can reach this endpoint
201
+ * (same-uid processes, a permissive-umask shared host, a Windows named pipe's
202
+ * more generous default DACL) can make the server execute an arbitrary
203
+ * directory's test suite. "Lint one file" and "run this directory's test suite"
204
+ * are different blast radii, so the guard is on turn-end and its ack.
205
+ */
206
+ function rejectForeignTurnCwd(route, cwd) {
207
+ const message = `${route} cwd outside this server's workspace: ${path.resolve(cwd)} not within ${DEFAULT_CWD}`;
208
+ console.error(`[pi-lens-mcp] rejected ${message}`);
209
+ return message;
210
+ }
211
+ function isWithinServerWorkspace(cwd) {
212
+ const target = path.resolve(cwd);
213
+ // `path.relative` applies the platform's own path semantics (including
214
+ // win32 case-insensitivity), which is what "inside the workspace" means
215
+ // here — do NOT hand-roll a case fold. An empty result means `target`
216
+ // IS `DEFAULT_CWD` (win32 can reach that via a pure case/drive-letter
217
+ // difference, since path.relative there is case-insensitive) and must
218
+ // be accepted, not rejected — do NOT reintroduce a `rel !== ""` guard.
219
+ const rel = path.relative(DEFAULT_CWD, target);
220
+ return !rel.startsWith("..") && !path.isAbsolute(rel);
221
+ }
194
222
  function startIpcServer() {
195
223
  // POSIX: a stale socket file blocks listen; remove it first. (Named pipes on
196
224
  // Windows don't need this.)
@@ -202,33 +230,77 @@ function startIpcServer() {
202
230
  // no stale socket — fine
203
231
  }
204
232
  }
233
+ const requestQueue = createWarmIpcRequestQueue();
205
234
  const ipc = net.createServer((socket) => {
206
235
  socket.setEncoding("utf8");
207
- let buffer = "";
208
- socket.on("data", (chunk) => {
209
- buffer += chunk;
210
- const newline = buffer.indexOf("\n");
211
- if (newline === -1)
212
- return;
213
- const line = buffer.slice(0, newline);
214
- void (async () => {
236
+ // One-shot per connection (#1219): clients write exactly one request and
237
+ // read one reply, so a handler that kept re-reading the same buffered
238
+ // line re-dispatched the request on stray bytes. The reader consumes the
239
+ // line and ignores anything after it.
240
+ socket.on("data", createWarmIpcLineReader((line) => {
241
+ void requestQueue.enqueue(async () => {
215
242
  try {
216
- // #535: the PostToolUse hook bin (mcp/analyze-cli.ts) already treats
217
- // ANY error response as "no usable warm server" and falls back to
218
- // its own cold, load-fresh-from-disk analysis path — so on a stale
219
- // warm build, replying with an error IS the fresh-fork behavior for
220
- // this channel, for free. No separate fresh-fork plumbing needed
221
- // here: the client-side fallback already loads current code.
222
243
  if (isWarmBuildStale()) {
223
- console.error("[pi-lens-mcp] warm analyze: build stale, replying error so the hook falls back cold");
224
- socket.end(`${JSON.stringify({ error: "warm build stale — falling back to cold analysis" })}\n`);
244
+ console.error("[pi-lens-mcp] warm request: build stale, replying with an error");
245
+ socket.end(`${JSON.stringify({ error: "warm build stale" })}\n`);
246
+ return;
247
+ }
248
+ const parsed = JSON.parse(line);
249
+ if (parsed.route === "turn-end-ack") {
250
+ if (parsed.version !== WARM_TURN_END_SCHEMA_VERSION ||
251
+ typeof parsed.deliveryId !==
252
+ "string") {
253
+ socket.end(`${JSON.stringify({ error: `turn-end ack schema ${parsed.version} != ${WARM_TURN_END_SCHEMA_VERSION}` })}\n`);
254
+ return;
255
+ }
256
+ const turnCwd = parsed.cwd ?? DEFAULT_CWD;
257
+ if (!isWithinServerWorkspace(turnCwd)) {
258
+ socket.end(`${JSON.stringify({ error: rejectForeignTurnCwd("turn-end-ack", turnCwd) })}\n`);
259
+ return;
260
+ }
261
+ const acknowledged = acknowledgeTurnEnd(turnCwd, parsed.deliveryId);
262
+ socket.end(`${JSON.stringify({
263
+ result: {
264
+ route: "turn-end-ack",
265
+ version: WARM_TURN_END_SCHEMA_VERSION,
266
+ acknowledged,
267
+ },
268
+ })}\n`);
269
+ return;
270
+ }
271
+ if (parsed.route === "turn-end") {
272
+ if (parsed.version !== WARM_TURN_END_SCHEMA_VERSION) {
273
+ socket.end(`${JSON.stringify({ error: `turn-end schema ${parsed.version} != ${WARM_TURN_END_SCHEMA_VERSION}` })}\n`);
274
+ return;
275
+ }
276
+ const turnCwd = parsed.cwd ?? DEFAULT_CWD;
277
+ if (!isWithinServerWorkspace(turnCwd)) {
278
+ socket.end(`${JSON.stringify({ error: rejectForeignTurnCwd("turn-end", turnCwd) })}\n`);
279
+ return;
280
+ }
281
+ // #1274: a client that already gave up (hook timeout, killed
282
+ // Claude Code) leaves a destroyed socket. Starting a heavy
283
+ // pass whose reply nobody can read is pure waste; the findings
284
+ // stay in the cache for the next Stop either way.
285
+ if (socket.destroyed) {
286
+ console.error(`[pi-lens-mcp] warm turn-end: client gone before the pass started (${turnCwd})`);
287
+ return;
288
+ }
289
+ console.error(`[pi-lens-mcp] warm turn-end: ${turnCwd}`);
290
+ await ensureReady(turnCwd);
291
+ const delivery = await runTurnEndForIpc(turnCwd);
292
+ const result = {
293
+ route: "turn-end",
294
+ version: WARM_TURN_END_SCHEMA_VERSION,
295
+ turnEnd: delivery.outcome.turnEnd,
296
+ tests: delivery.outcome.tests,
297
+ deliveryId: delivery.deliveryId,
298
+ };
299
+ socket.end(`${JSON.stringify({ result })}\n`);
225
300
  return;
226
301
  }
227
- const req = JSON.parse(line);
302
+ const req = parsed;
228
303
  console.error(`[pi-lens-mcp] warm analyze: ${req.file}`);
229
- // Warm = full LSP + an edit-detection path (register turn-state) +
230
- // review-graph maintenance (#536 — this is an in-process, long-lived
231
- // path, unlike the ephemeral `fresh` worker).
232
304
  const result = await analyzeFile(req.file, req.cwd, {
233
305
  registerTurnState: true,
234
306
  updateGraph: true,
@@ -238,8 +310,8 @@ function startIpcServer() {
238
310
  catch (err) {
239
311
  socket.end(`${JSON.stringify({ error: String(err) })}\n`);
240
312
  }
241
- })();
242
- });
313
+ });
314
+ }));
243
315
  socket.on("error", () => socket.destroy());
244
316
  });
245
317
  ipc.on("error", (err) => {
@@ -340,7 +412,12 @@ const cacheManager = new CacheManager();
340
412
  // nothing — a no-op dressed as a fix, not a real parity gap. The 4th arg is left
341
413
  // at its default (`async () => {}`, already a no-op) rather than importing and
342
414
  // wiring a flush with nothing to flush.
343
- const lensDiagnosticsTool = createLensDiagnosticsTool(cacheManager, () => DEFAULT_CWD);
415
+ const lensDiagnosticsTool = createLensDiagnosticsTool(cacheManager, () => DEFAULT_CWD, undefined, undefined, undefined, undefined,
416
+ // #1413 surface parity: validate cached test-runner findings against the
417
+ // same session identity the in-process path uses. Resolved lazily — until
418
+ // an MCP session context exists there is no session to compare against and
419
+ // validation skips the check, which is the honest classification.
420
+ () => peekMcpSessionRuntime());
344
421
  const astGrepClient = new AstGrepClient();
345
422
  const astGrepSearchTool = createAstGrepSearchTool(astGrepClient);
346
423
  const astGrepReplaceTool = createAstGrepReplaceTool(astGrepClient);
@@ -395,7 +472,7 @@ const ALL_TOOLS = [
395
472
  },
396
473
  flags: {
397
474
  type: "object",
398
- description: "Optional pi-lens flag overrides for this run, e.g. {\"no-lsp\": true} to bench the non-LSP path.",
475
+ description: 'Optional pi-lens flag overrides for this run, e.g. {"no-lsp": true} to bench the non-LSP path.',
399
476
  },
400
477
  },
401
478
  required: ["file"],
@@ -415,8 +492,14 @@ const ALL_TOOLS = [
415
492
  inputSchema: {
416
493
  type: "object",
417
494
  properties: {
418
- limit: { type: "number", description: "Max reports to return (default 5)." },
419
- file: { type: "string", description: "Only reports whose path ends with this." },
495
+ limit: {
496
+ type: "number",
497
+ description: "Max reports to return (default 5).",
498
+ },
499
+ file: {
500
+ type: "string",
501
+ description: "Only reports whose path ends with this.",
502
+ },
420
503
  },
421
504
  },
422
505
  },
@@ -439,6 +522,13 @@ const ALL_TOOLS = [
439
522
  properties: {
440
523
  cwd: { type: "string" },
441
524
  maxFiles: { type: "number", description: "Cap files scanned." },
525
+ includeGenerated: {
526
+ type: "boolean",
527
+ description: "Scan WITHOUT the generated/artifact NAME-heuristic filter " +
528
+ "(lockfiles, gen.ts-style names, generated/ dirs, …). Default " +
529
+ "false. Use when a scan's 'excluded by generated-name " +
530
+ "heuristics' notice suggests a real file was skipped.",
531
+ },
442
532
  },
443
533
  },
444
534
  },
@@ -460,7 +550,7 @@ const ALL_TOOLS = [
460
550
  properties: {
461
551
  query: {
462
552
  type: "string",
463
- description: "Identifier-ish query, e.g. 'authenticate user'.",
553
+ description: "Identifier-ish query, e.g. 'authenticate user'. Mix in composable prefix filters: lang:<kind> (e.g. lang:jsts, lang:python — kinds from file-kinds.ts), file:<substr> (path substring), ext:<ext> (e.g. ext:ts or ext:.ts), each optionally negated with a leading '-' (-file:test). Filters apply before ranking; e.g. 'lang:jsts file:clients/ -file:test rank'. Unknown prefixes/kinds error with the supported list.",
464
554
  },
465
555
  cwd: { type: "string" },
466
556
  limit: {
@@ -495,7 +585,9 @@ const ALL_TOOLS = [
495
585
  "(this path never calls LSP). `semantic.source` reports whether graph " +
496
586
  "data was used. Pass `blastRadius: true` for the cross-file blast radius " +
497
587
  "(transitive dependents as ranked file reads, read-only over the cached " +
498
- 'graph). `view: "compact"` returns a line-oriented text rendering ' +
588
+ "graph). Pass `callGraph: true` for bounded derived callers/callees; a " +
589
+ "cold or stale call-graph cache is reported explicitly, never as zero calls. " +
590
+ '`view: "compact"` returns a line-oriented text rendering ' +
499
591
  "(cheapest option) instead of JSON. An outline shows shape, not bodies.",
500
592
  inputSchema: {
501
593
  type: "object",
@@ -526,6 +618,14 @@ const ALL_TOOLS = [
526
618
  type: "number",
527
619
  description: "Max hops for the blast-radius walk (default 3). Only used with blastRadius.",
528
620
  },
621
+ callGraph: {
622
+ type: "boolean",
623
+ description: "Include bounded derived callers/callees from the cached FunctionCallGraph; cold or stale cache state is explicit.",
624
+ },
625
+ maxCallGraphEntries: {
626
+ type: "number",
627
+ description: "Per-direction cap for call-graph relations (default 20).",
628
+ },
529
629
  },
530
630
  required: ["file"],
531
631
  },
@@ -685,8 +785,11 @@ const ALL_TOOLS = [
685
785
  {
686
786
  name: "pilens_ast_grep_search",
687
787
  description: "Structural (AST) code search via ast-grep — match by code structure, not " +
688
- "text. Use meta-variables ($X) and AST context (e.g. 'console.log($MSG)', " +
689
- "'function $NAME() { $$$ }'). Far more precise than grep for code shapes.",
788
+ "text. Use complete patterns with arguments (e.g. 'console.log($MSG)', " +
789
+ "'function $NAME($$$ARGS) { $$$BODY }'), or use nodeKind to find every " +
790
+ "node of a known grammar kind. Metavariables do not match text inside " +
791
+ "quoted string literals; use an exact string or grep for wildcard text. " +
792
+ "This is the same schema and synthesis path as the pi tool.",
690
793
  inputSchema: schemaWithCwd(astGrepSearchTool.parameters),
691
794
  },
692
795
  {
@@ -739,7 +842,10 @@ async function callTool(name, args) {
739
842
  if (name === "pilens_analyze") {
740
843
  const file = args.file;
741
844
  if (typeof file !== "string" || file.length === 0) {
742
- return { ...toolText("pilens_analyze requires a 'file' string."), isError: true };
845
+ return {
846
+ ...toolText("pilens_analyze requires a 'file' string."),
847
+ isError: true,
848
+ };
743
849
  }
744
850
  const cwd = typeof args.cwd === "string" ? args.cwd : DEFAULT_CWD;
745
851
  const requestedMode = args.mode === "fresh" ? "fresh" : "warm";
@@ -814,7 +920,8 @@ async function callTool(name, args) {
814
920
  const maxFiles = typeof args.maxFiles === "number" && Number.isFinite(args.maxFiles)
815
921
  ? Math.max(1, Math.floor(args.maxFiles))
816
922
  : undefined;
817
- const snapshot = await projectScan(cwd, maxFiles);
923
+ const includeGenerated = args.includeGenerated === true;
924
+ const snapshot = await projectScan(cwd, maxFiles, includeGenerated);
818
925
  // #747: a cwd at/above $HOME refuses to walk — say so instead of letting
819
926
  // "Scanned 0 file(s) → 0 diagnostics" read as a clean project.
820
927
  if (snapshot.unsafeRoot) {
@@ -840,6 +947,11 @@ async function callTool(name, args) {
840
947
  const truncationNotice = scanTruncationNotice(snapshot);
841
948
  if (truncationNotice)
842
949
  summaryLines.push(truncationNotice);
950
+ // #1107 phase 2: same "reached the seam but nothing rendered it" gap as
951
+ // #784's scanTruncationNotice, for the generated-name skip counters.
952
+ const skipNotice = generatedSkipNotice(snapshot);
953
+ if (skipNotice)
954
+ summaryLines.push(skipNotice);
843
955
  return toolText(summaryLines.join("\n"), {
844
956
  filesScanned: snapshot.filesScanned,
845
957
  runners: snapshot.runners,
@@ -852,6 +964,15 @@ async function callTool(name, args) {
852
964
  ...(snapshot.treeSitterStatus
853
965
  ? { treeSitterStatus: snapshot.treeSitterStatus }
854
966
  : {}),
967
+ ...(snapshot.generatedFileSkips
968
+ ? { generatedFileSkips: snapshot.generatedFileSkips }
969
+ : {}),
970
+ ...(snapshot.generatedNameOnlySkips
971
+ ? { generatedNameOnlySkips: snapshot.generatedNameOnlySkips }
972
+ : {}),
973
+ ...(snapshot.generatedDirSkips
974
+ ? { generatedDirSkips: snapshot.generatedDirSkips }
975
+ : {}),
855
976
  });
856
977
  }
857
978
  if (name === "pilens_symbol_search") {
@@ -868,7 +989,8 @@ async function callTool(name, args) {
868
989
  const lang = typeof args.lang === "string" ? args.lang : undefined;
869
990
  const { available, results, hint, unavailableReason, coverage, snapshotGeneratedAt, } = await symbolSearch(query, cwd, limit, { paths, lang });
870
991
  if (!available) {
871
- return toolText(hint ?? "No word index for this workspace yet — run pilens_session_start first.", { available: false, query, hint, unavailableReason }, true);
992
+ return toolText(hint ??
993
+ "No word index for this workspace yet — run pilens_session_start first.", { available: false, query, hint, unavailableReason }, true);
872
994
  }
873
995
  const stalenessNote = graphStalenessNote(snapshotGeneratedAt, "Project snapshot");
874
996
  if (results.length === 0) {
@@ -934,12 +1056,21 @@ async function callTool(name, args) {
934
1056
  Number.isFinite(args.blastRadiusDepth)
935
1057
  ? Math.max(1, Math.floor(args.blastRadiusDepth))
936
1058
  : undefined;
937
- const view = args.view === "summary" || args.view === "compact" ? args.view : undefined;
1059
+ const callGraph = args.callGraph === true;
1060
+ const maxCallGraphEntries = typeof args.maxCallGraphEntries === "number" &&
1061
+ Number.isFinite(args.maxCallGraphEntries)
1062
+ ? Math.max(1, Math.floor(args.maxCallGraphEntries))
1063
+ : undefined;
1064
+ const view = args.view === "summary" || args.view === "compact"
1065
+ ? args.view
1066
+ : undefined;
938
1067
  const focus = typeof args.focus === "string" ? args.focus : undefined;
939
1068
  const report = await moduleReport(file, cwd, {
940
1069
  maxRefsPerSymbol,
941
1070
  blastRadius,
942
1071
  blastRadiusDepth,
1072
+ callGraph,
1073
+ maxCallGraphEntries,
943
1074
  view,
944
1075
  focus,
945
1076
  });
@@ -960,14 +1091,18 @@ async function callTool(name, args) {
960
1091
  content: [
961
1092
  {
962
1093
  type: "text",
963
- text: graphStaleness ? `${compactText}\n\n${graphStaleness}` : compactText,
1094
+ text: graphStaleness
1095
+ ? `${compactText}\n\n${graphStaleness}`
1096
+ : compactText,
964
1097
  },
965
1098
  ],
966
1099
  };
967
1100
  }
968
1101
  // Compact (unindented) JSON — matches the pi tool's mirror (#512); an
969
1102
  // agent parses this payload, it doesn't read it formatted.
970
- return toolText(summary, graphStaleness ? { ...report, graphStalenessNote: graphStaleness } : report, true);
1103
+ return toolText(summary, graphStaleness
1104
+ ? { ...report, graphStalenessNote: graphStaleness }
1105
+ : report, true);
971
1106
  }
972
1107
  if (name === "pilens_project_report") {
973
1108
  const cwd = typeof args.cwd === "string" ? args.cwd : DEFAULT_CWD;
@@ -992,7 +1127,9 @@ async function callTool(name, args) {
992
1127
  content: [
993
1128
  {
994
1129
  type: "text",
995
- text: graphStaleness ? `${compactText}\n\n${graphStaleness}` : compactText,
1130
+ text: graphStaleness
1131
+ ? `${compactText}\n\n${graphStaleness}`
1132
+ : compactText,
996
1133
  },
997
1134
  ],
998
1135
  };
@@ -1001,7 +1138,9 @@ async function callTool(name, args) {
1001
1138
  `${report.entryPoints?.length ?? 0} entry point(s), ` +
1002
1139
  `${report.riskHotspots?.length ?? 0} risk hotspot(s)` +
1003
1140
  (graphStaleness ? `\n\n${graphStaleness}` : "");
1004
- return toolText(summary, graphStaleness ? { ...report, graphStalenessNote: graphStaleness } : report, true);
1141
+ return toolText(summary, graphStaleness
1142
+ ? { ...report, graphStalenessNote: graphStaleness }
1143
+ : report, true);
1005
1144
  }
1006
1145
  if (name === "pilens_read_symbol") {
1007
1146
  const file = typeof args.file === "string" ? args.file : "";
@@ -1032,13 +1171,20 @@ async function callTool(name, args) {
1032
1171
  ? ` (${result.ambiguous.count} matches — returned the ${result.kind}; pass \`kind\` to disambiguate: ${result.ambiguous.kinds.join(", ")})`
1033
1172
  : "";
1034
1173
  const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
1035
- return { content: [{ type: "text", text: `${header}\n\n${result.source ?? ""}` }] };
1174
+ return {
1175
+ content: [
1176
+ { type: "text", text: `${header}\n\n${result.source ?? ""}` },
1177
+ ],
1178
+ };
1036
1179
  }
1037
1180
  if (name === "pilens_read_enclosing") {
1038
1181
  const file = typeof args.file === "string" ? args.file : "";
1039
1182
  const line = typeof args.line === "number" ? args.line : Number.NaN;
1040
1183
  if (!file.trim() || !Number.isFinite(line)) {
1041
- return { ...toolText("Provide `file` and a numeric `line`."), isError: true };
1184
+ return {
1185
+ ...toolText("Provide `file` and a numeric `line`."),
1186
+ isError: true,
1187
+ };
1042
1188
  }
1043
1189
  const cwd = typeof args.cwd === "string" ? args.cwd : DEFAULT_CWD;
1044
1190
  const kinds = Array.isArray(args.kinds)
@@ -1082,7 +1228,11 @@ async function callTool(name, args) {
1082
1228
  ? `${result.startLine}-${result.endLine} (partial of ${result.enclosingStartLine}-${result.enclosingEndLine})`
1083
1229
  : `${result.startLine}-${result.endLine}`;
1084
1230
  const header = `${result.kind} ${result.name} ${path.relative(cwd, result.path)}:${range}`;
1085
- return { content: [{ type: "text", text: `${header}\n\n${result.source ?? ""}` }] };
1231
+ return {
1232
+ content: [
1233
+ { type: "text", text: `${header}\n\n${result.source ?? ""}` },
1234
+ ],
1235
+ };
1086
1236
  }
1087
1237
  if (name === "pilens_health") {
1088
1238
  const { aliveClients, servers, brokenServers } = lspStatus();
@@ -1090,6 +1240,13 @@ async function callTool(name, args) {
1090
1240
  const stats = diagnosticStats();
1091
1241
  const autoSession = getAutoSessionStatus();
1092
1242
  const treeSitter = treeSitterRuntimeStatus();
1243
+ const degradations = getDegradationSummary();
1244
+ // #1272, following the #544 precedent: the Stop hook is a separate,
1245
+ // short-lived process, so a turn-end it skipped left no trace here at all
1246
+ // — a dead integration looked exactly like a clean turn, forever. The
1247
+ // hook records its outcome per workspace; this is where it becomes
1248
+ // visible without `claude --debug` log spelunking.
1249
+ const turnEnd = readTurnEndStatus(DEFAULT_CWD) ?? null;
1093
1250
  // #620: best-effort — a footprint read failure must never break the rest
1094
1251
  // of pilens_health's (much older, more load-bearing) reporting.
1095
1252
  const footprint = await resourceFootprint().catch(() => null);
@@ -1098,8 +1255,12 @@ async function callTool(name, args) {
1098
1255
  ? `Tree-sitter: DEGRADED — WASM runtime aborted${treeSitter.abortedAt ? ` at ${treeSitter.abortedAt}` : ""}; restart this server to recover`
1099
1256
  : "Tree-sitter: available",
1100
1257
  `LSP: ${aliveClients} alive client(s)`,
1101
- ...servers.map((server) => ` ${server.connected ? "✓" : "✗"} ${server.serverId} (${server.root})`),
1258
+ ...servers.flatMap((server) => [
1259
+ ` ${server.connected ? "✓" : "✗"} ${server.serverId} (${server.root})`,
1260
+ ...server.pullFailureHistory.slice(-1).map((failure) => ` ⚠ diagnostics pull failed: ${failure.method}${failure.code !== undefined ? ` (${failure.code})` : ""} — ${failure.message.length > 200 ? `${failure.message.slice(0, 200)}…` : failure.message}`),
1261
+ ]),
1102
1262
  ...renderLspBrokenStatusLines(brokenServers),
1263
+ ...renderDegradationLines(degradations),
1103
1264
  last
1104
1265
  ? `Last dispatch: ${path.basename(last.filePath)} — ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)`
1105
1266
  : "Last dispatch: none yet",
@@ -1107,6 +1268,11 @@ async function callTool(name, args) {
1107
1268
  autoSession
1108
1269
  ? `Auto session_start: ${autoSession.succeeded ? "succeeded" : autoSession.error ? "FAILED" : autoSession.attempted ? "in progress" : "not yet attempted"}${autoSession.firedAt ? ` (fired ${autoSession.firedAt})` : ""}${autoSession.error ? ` — ${autoSession.error}` : ""}`
1109
1270
  : "Auto session_start: disabled (PI_LENS_MCP_AUTO_SESSION not set)",
1271
+ turnEnd
1272
+ ? `Stop-hook turn-end: ${turnEnd.ran} ran · ${turnEnd.skipped} skipped` +
1273
+ `${turnEnd.lastRunAt ? ` (last ran ${turnEnd.lastRunAt})` : ""}` +
1274
+ `${turnEnd.lastSkipReason ? ` — last skip: ${turnEnd.lastSkipReason}${turnEnd.lastSkipAt ? ` at ${turnEnd.lastSkipAt}` : ""}` : ""}`
1275
+ : "Stop-hook turn-end: no activity recorded (hook not installed, or no Stop yet)",
1110
1276
  footprint
1111
1277
  ? `Resource footprint: ${footprint.instanceCount} pi-lens instance(s) · ` +
1112
1278
  `${(footprint.totalRssBytes / 1024 / 1024).toFixed(0)}MB RSS · ` +
@@ -1132,7 +1298,9 @@ async function callTool(name, args) {
1132
1298
  },
1133
1299
  autoSession,
1134
1300
  treeSitter,
1301
+ turnEnd,
1135
1302
  resourceFootprint: footprint,
1303
+ degradations,
1136
1304
  });
1137
1305
  }
1138
1306
  if (name === "pilens_diagnostics") {
@@ -1186,7 +1354,9 @@ async function callTool(name, args) {
1186
1354
  }
1187
1355
  if (name === "pilens_ast_grep_search" || name === "pilens_ast_grep_replace") {
1188
1356
  const cwd = typeof args.cwd === "string" ? args.cwd : DEFAULT_CWD;
1189
- const tool = name === "pilens_ast_grep_search" ? astGrepSearchTool : astGrepReplaceTool;
1357
+ const tool = name === "pilens_ast_grep_search"
1358
+ ? astGrepSearchTool
1359
+ : astGrepReplaceTool;
1190
1360
  const out = (await tool.execute("mcp", args, new AbortController().signal, undefined, { cwd }));
1191
1361
  return { content: out.content };
1192
1362
  }
@@ -1270,7 +1440,10 @@ function withStaleWarning(result) {
1270
1440
  ...result,
1271
1441
  content: [
1272
1442
  ...result.content.slice(0, -1),
1273
- { ...last, text: `${last.text}\n\nwarmCodeStale: true\n${STALE_WARN_ONLY}` },
1443
+ {
1444
+ ...last,
1445
+ text: `${last.text}\n\nwarmCodeStale: true\n${STALE_WARN_ONLY}`,
1446
+ },
1274
1447
  ],
1275
1448
  };
1276
1449
  }
@@ -18,7 +18,7 @@
18
18
  * instead, so calling this tool is harmless, just unnecessary.
19
19
  */
20
20
  import { Type } from "../clients/deps/typebox.js";
21
- export function createActivateToolsTool(pi, lazyTools) {
21
+ export function createActivateToolsTool(pi, lazyTools, options = {}) {
22
22
  const lazyNames = lazyTools.map((t) => t.name);
23
23
  const lazyNameSet = new Set(lazyNames);
24
24
  const catalog = lazyTools.map((t) => `${t.name} — ${t.summary}`).join("\n");
@@ -35,7 +35,7 @@ export function createActivateToolsTool(pi, lazyTools) {
35
35
  description: "Names of situational tools to activate (see this tool's description for the catalog).",
36
36
  }),
37
37
  }),
38
- async execute(_toolCallId, params, _signal, _onUpdate) {
38
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
39
39
  const requested = Array.isArray(params.tools)
40
40
  ? params.tools.filter((t) => typeof t === "string" && lazyNameSet.has(t))
41
41
  : [];
@@ -53,10 +53,22 @@ export function createActivateToolsTool(pi, lazyTools) {
53
53
  }
54
54
  // Additive only, per the docs' contract: never drop currently active
55
55
  // tools in the same call.
56
+ // Remember every requested tool, not just the newly-added ones: a
57
+ // tool that is already active still has to survive the next
58
+ // fork/reload/resume restore.
59
+ options.onActivated?.(requested);
56
60
  const active = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
57
- const merged = [...new Set([...active, ...requested])];
58
- if (typeof pi.setActiveTools === "function") {
61
+ const activeSet = new Set(active);
62
+ const added = requested.filter((name) => !activeSet.has(name));
63
+ const merged = [...new Set([...active, ...added])];
64
+ if (added.length > 0 && typeof pi.setActiveTools === "function") {
59
65
  pi.setActiveTools(merged);
66
+ options.onMutation?.({
67
+ addedCount: added.length,
68
+ removedCount: 0,
69
+ reason: "lazy_activation",
70
+ deferralApplies: options.deferredToolSupport?.(ctx) ?? false,
71
+ });
60
72
  }
61
73
  return {
62
74
  content: [
@@ -65,7 +77,7 @@ export function createActivateToolsTool(pi, lazyTools) {
65
77
  text: `Activated: ${requested.join(", ")}. Available starting next turn.`,
66
78
  },
67
79
  ],
68
- details: { matches: requested, added: requested },
80
+ details: { matches: requested, added },
69
81
  };
70
82
  },
71
83
  };
@@ -67,7 +67,12 @@ export function createAstGrepReplaceTool(astGrepClient) {
67
67
  }),
68
68
  paths: Type.Optional(Type.Array(Type.String(), { description: "Specific files/folders" })),
69
69
  insideKind: Type.Optional(Type.String({ description: "Restrict matches to nodes inside an ancestor of this AST node kind. Synthesizes a YAML rule." })),
70
- hasKind: Type.Optional(Type.String({ description: "Restrict matches to nodes that contain a descendant of this AST node kind." })),
70
+ hasKind: Type.Optional(Type.String({
71
+ description: 'Restrict matches to nodes whose immediate child has this AST node kind (ast-grep default stopBy: neighbor). Example: `hasKind: "await_expression"`.',
72
+ })),
73
+ hasDescendantKind: Type.Optional(Type.String({
74
+ description: "Restrict matches to nodes containing this AST node kind anywhere in their descendants. Explicit recursive form (`stopBy: end`); use this instead of `hasKind` when nesting is not immediate.",
75
+ })),
71
76
  follows: Type.Optional(Type.String({ description: "Restrict matches to nodes that immediately follow a sibling matching this pattern." })),
72
77
  precedes: Type.Optional(Type.String({ description: "Restrict matches to nodes that immediately precede a sibling matching this pattern." })),
73
78
  apply: Type.Optional(Type.Boolean({ description: "Apply changes (default: false)" })),
@@ -78,7 +83,7 @@ export function createAstGrepReplaceTool(astGrepClient) {
78
83
  }),
79
84
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
80
85
  const startedAt = Date.now();
81
- const { pattern, rewrite, paths, apply, strictness, insideKind, hasKind, follows, precedes } = params;
86
+ const { pattern, rewrite, paths, apply, strictness, insideKind, hasKind, hasDescendantKind, follows, precedes } = params;
82
87
  const lang = (params.lang ?? "").replace(/^"|"$/g, "");
83
88
  const pathsCount = paths?.length ?? 1;
84
89
  const applyFlag = apply ?? false;
@@ -122,10 +127,10 @@ export function createAstGrepReplaceTool(astGrepClient) {
122
127
  }
123
128
  const searchPaths = paths?.length ? paths : [ctx.cwd || "."];
124
129
  // Phase 3: structural-intent params → synthesize YAML with fix: field
125
- if (hasStructuralIntent({ insideKind, hasKind, follows, precedes })) {
130
+ if (hasStructuralIntent({ insideKind, hasKind, hasDescendantKind, follows, precedes })) {
126
131
  let ruleYaml;
127
132
  try {
128
- ruleYaml = synthesizeReplaceRule({ pattern, lang, rewrite, insideKind, hasKind, follows, precedes });
133
+ ruleYaml = synthesizeReplaceRule({ pattern, lang, rewrite, insideKind, hasKind, hasDescendantKind, follows, precedes });
129
134
  }
130
135
  catch (err) {
131
136
  logOutcome("error", { errorRaw: String(err) });