@herbertgao/pi-extensions 2026.8.4 → 2026.8.5

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 (288) hide show
  1. package/README.md +4 -4
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
  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/index.ts +7 -6
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  13. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  14. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  15. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  16. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
  20. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  21. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  22. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  25. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  30. package/node_modules/@narumitw/pi-btw/README.md +8 -7
  31. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  32. package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
  33. package/node_modules/pi-lens/CHANGELOG.md +2440 -954
  34. package/node_modules/pi-lens/README.md +13 -0
  35. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  36. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  37. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  38. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  39. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  40. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  41. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  42. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  43. package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
  44. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  45. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  46. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  47. package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
  48. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  49. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  50. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  51. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  52. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  53. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  54. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  55. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  56. package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
  57. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  58. package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
  59. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  60. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
  63. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
  64. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
  66. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  67. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  68. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  69. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  70. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  71. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
  72. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  73. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  75. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  99. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  100. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  101. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  102. package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
  103. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  104. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  105. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  106. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  107. package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
  108. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  109. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  110. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  111. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  112. package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
  113. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  114. package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
  115. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
  116. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  117. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  118. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  119. package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
  120. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  121. package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
  122. package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
  123. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  124. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  125. package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
  126. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  127. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  128. package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
  129. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  130. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  131. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
  132. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  133. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
  134. package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
  135. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  136. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
  137. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  138. package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
  139. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  140. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  141. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  142. package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
  143. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  144. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  145. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  146. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
  147. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  148. package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
  149. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  150. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  151. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  152. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  153. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  154. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  155. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  157. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  158. package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
  159. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  160. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  161. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  162. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  163. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  164. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  165. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  166. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  167. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  168. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  169. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  170. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  171. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  172. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  173. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  174. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  175. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  177. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  178. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  179. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  180. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  181. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  182. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
  183. package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
  184. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
  185. package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
  186. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  187. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
  188. package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
  189. package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
  190. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  191. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
  192. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  193. package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
  194. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  195. package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
  196. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  197. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  198. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  199. package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
  200. package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
  201. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  202. package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
  203. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  204. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  205. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  210. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  211. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  212. package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
  213. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  214. package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
  215. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  216. package/node_modules/pi-lens/dist/index.js +49361 -36888
  217. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  218. package/node_modules/pi-lens/dist/mcp/server.js +211 -44
  219. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  220. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
  221. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
  222. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  223. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  224. package/node_modules/pi-lens/docs/agent-guide.md +2 -2
  225. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  226. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  227. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  228. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
  229. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  230. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  231. package/node_modules/pi-lens/docs/fable.md +27 -0
  232. package/node_modules/pi-lens/docs/features.md +31 -2
  233. package/node_modules/pi-lens/docs/globalconfig.md +45 -1
  234. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  235. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  236. package/node_modules/pi-lens/docs/mcp.md +37 -4
  237. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  238. package/node_modules/pi-lens/docs/settings.md +12 -1
  239. package/node_modules/pi-lens/docs/tools.md +4 -1
  240. package/node_modules/pi-lens/docs/usage.md +18 -0
  241. package/node_modules/pi-lens/package.json +10 -10
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  252. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  253. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  254. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
  255. package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
  256. package/node_modules/pi-mcp-adapter/README.md +34 -3
  257. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  258. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  259. package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
  260. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  261. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  262. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  263. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  264. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  265. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  266. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  267. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  268. package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
  269. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  270. package/node_modules/pi-mcp-adapter/package.json +3 -3
  271. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  272. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  273. package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
  274. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  275. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  276. package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
  277. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  278. package/node_modules/pi-mcp-adapter/types.ts +93 -27
  279. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  280. package/package.json +11 -12
  281. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  282. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  283. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  284. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  285. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  286. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  287. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  288. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -40,6 +40,21 @@ Repositories can disable all immediate and deferred auto-format mutations with
40
40
  keeping formatter detection, lint dispatch, LSP synchronization, and
41
41
  diagnostics available.
42
42
 
43
+ Deferred formatting (the `agent_end` default) runs with **bounded
44
+ concurrency**: at most three formatter subprocesses in flight at once, with
45
+ results applied in admission order and cooperative yields between files, so a
46
+ large batch of queued files can't stall the event loop or the turn-end pass.
47
+
48
+ ### Commit/Push Guard (Experimental)
49
+
50
+ `--lens-guard` (also `guard.enabled: true` in `~/.pi-lens/config.json`) opts
51
+ into blocking `git commit`/`git push` while unresolved pi-lens blockers exist.
52
+ Detection covers normalized wrapper launchers, shell-escaped and
53
+ keyword/combined-flag verb forms, and shell substitutions, while literal text
54
+ in non-executing contexts remains allowed. Off by default; see
55
+ [docs/agent-guide.md](agent-guide.md) and [docs/settings.md](settings.md) for
56
+ the full behavior and honest limits.
57
+
43
58
  ### Review Graph - Cascade Diagnostics
44
59
 
45
60
  pi-lens builds a review graph (`file → symbol → dependency`) during session and uses it at turn end to render an impact cascade: which files were affected by a change and how diagnostics propagated through the dependency graph. Nodes track kind, language, and export status; edges track contains/imports/calls/references.
@@ -317,9 +332,9 @@ pi-lens ships an MCP (Model Context Protocol) server so Claude Code — or any M
317
332
  | Layer | MCP tools | What they expose |
318
333
  |---|---|---|
319
334
  | **Per-edit** | `pilens_analyze`, `pilens_lsp_diagnostics`, `pilens_lsp_navigation`, `pilens_ast_grep_search`, `pilens_ast_grep_replace`, `pilens_module_report`, `pilens_read_symbol` | The fast pipeline (format → autofix → LSP diagnostics → parallel runners) plus the structured read-substitute pair. `analyze` accepts `mode: warm \| fresh` — `warm` reuses the server's in-process LSP, `fresh` forks a worker that loads freshly-built code from disk so the result reflects the latest commit. |
320
- | **Per-turn** | `pilens_turn_end` | Drives the **real** `handleTurnEnd` (knip incremental, jscpd delta, dep-circular, cascade, tests, actionable+code-quality warnings) — not a re-implementation. Caller-supplied edited files are auto-registered into turn-state via `addModifiedRange`. |
335
+ | **Per-turn** | `pilens_turn_end` | Drives the **real** `handleTurnEnd` (knip incremental, dep-circular, cascade, tests, actionable+code-quality warnings) — not a re-implementation. Caller-supplied edited files are auto-registered into turn-state via `addModifiedRange`. |
321
336
  | **Per-session** | `pilens_session_start` | Drives the **real** `handleSessionStart` — full jscpd/knip/madge/govulncheck/gitleaks/trivy scans + complexity baselines + LSP warm. The error-debt baseline is not currently populated by the production session-start path. |
322
- | **Project / observability** | `pilens_project_scan`, `pilens_diagnostics`, `pilens_health`, `pilens_latency`, `pilens_symbol_search` | Cheap project-wide scans, cached diagnostic state, latency telemetry, ranked identifier search (BM25 over the persisted word index — see [docs/word-index.md](word-index.md)). Cross-file blast radius now lives in `pilens_module_report`'s `blastRadius` option. |
337
+ | **Project / observability** | `pilens_project_scan`, `pilens_diagnostics`, `pilens_health`, `pilens_latency`, `pilens_symbol_search` | Cheap project-wide scans, cached diagnostic state, latency telemetry, ranked identifier search (BM25 over the persisted word index — see [docs/word-index.md](word-index.md)). Cross-file blast radius now lives in `pilens_module_report`'s `blastRadius` option. `pilens_health` (and its pi-side `/lens-health` counterpart) also reports a bounded, process-local **degradation ledger** — trust refusals, mode suppressions, LSP breaker trips, formatter skips/failures, TypeScript/word-index/review-graph/project-snapshot idle evictions, WASM aborts, and diagnostics-timeout tallies — so silently degraded behavior stays visible instead of vanishing into a log. |
323
338
  | **Lifecycle / loop** | `pilens_rebuild` | Runs `npm run build:dist` so `pilens_analyze mode=fresh` reflects the latest commit. Makes the review loop self-contained: commit → `pilens_rebuild` → `pilens_analyze mode=fresh` → `pilens_latency`. |
324
339
 
325
340
  **Honest limits** (live-tested, documented in `mcp.md`):
@@ -342,4 +357,18 @@ claude mcp add --scope user pi-lens \
342
357
  -- node <repo>/dist/mcp/server.js
343
358
  ```
344
359
 
360
+ **Hooks** (`settings.json`) close the loop: PostToolUse = per-edit, Stop = per-turn.
361
+
362
+ ```json
363
+ { "hooks": {
364
+ "PostToolUse": [
365
+ { "matcher": "Edit|Write",
366
+ "hooks": [ { "type": "command", "command": "pi-lens-analyze --hook" } ] } ],
367
+ "Stop": [
368
+ { "hooks": [ { "type": "command", "command": "pi-lens-analyze --turn-end", "timeout": 60 } ] } ]
369
+ } }
370
+ ```
371
+
372
+ The per-edit hook falls back to a cold local analysis when no server is up; the `Stop` hook is **warm-server-only** because only the server process owns the session state and pending turn work. It skips with a single stderr line when unavailable. Workspace IPC requests are ordered, so a timed-out PostToolUse client cannot let `Stop` overtake analysis still running in the server. `SubagentStop` is deliberately not registered because subagent edits already reach turn-state through PostToolUse.
373
+
345
374
  The full design + tier-by-tier progress (and known limits) lives in [`docs/mcp.md`](docs/mcp.md). Status: **experimental** — the foundation is solid (transport, warm LSP, lifecycle handlers wired), but the surface is still maturing. Use the pi extension for production agent work; reach for the MCP server for debugging, dogfooding, and direct Claude Code access.
@@ -24,6 +24,7 @@ Each runtime toggle is settable from the CLI *and* from `config.json`. The two a
24
24
  | `--lens-actionable-warning-actions` | `actionableWarnings.includeLspCodeActions` | `false` |
25
25
  | `--lens-actionable-warning-autofix` | `actionableWarnings.autoFix.enabled` | `false` |
26
26
  | `--lens-actionable-warning-all` | `actionableWarnings.deltaOnly` (`false`) | `true` |
27
+ | `--lens-compact-tool-line` | `ui.compactToolLine` | `false` |
27
28
 
28
29
  Keys are positive (`"enabled": true` means the feature runs), so a `--no-*` flag corresponds to setting its key `false`. A `no-*` flag on the command line is a one-way switch: it can disable, never re-enable, so `--no-lsp` wins over `lsp.enabled: true` but nothing on the CLI overrides `lsp.enabled: false`. Set the key back to `true` to re-enable.
29
30
 
@@ -167,10 +168,52 @@ Note: `maxProjectFiles` (below) is discovered differently — via an upward walk
167
168
 
168
169
  ### `rules`
169
170
 
170
- Per-rule threshold overrides. Currently honored:
171
+ Per-rule threshold overrides and policy filters. Currently honored:
171
172
 
172
173
  - `high-complexity.threshold` — cyclomatic complexity (default `15`)
173
174
  - `high-fan-out.threshold` — distinct function calls (default `20`)
175
+ - `<id>.disable` — disable diagnostics for the listed rule ids (output-only
176
+ filter). Same rule-id normalization as `pi-lens-ignore`: a user lists
177
+ `no-eval` once and the filter covers `no-eval`, `ast-grep:no-eval`, and
178
+ `no-eval-js`. Side effect: a rule whose real name genuinely ends in `-js`
179
+ (e.g. `prefer-js`) is conflated with its stem (`prefer`) — disabling or
180
+ selecting one also disables or selects the other.
181
+ - `<id>.select` — allowlist of rule ids; only the listed rule ids survive
182
+ filtering. Disable wins over select project-wide, even when the two lists
183
+ are configured under different keys.
184
+
185
+ The `<id>` key is a grouping label only — it does not scope which rules the
186
+ `disable`/`select` list underneath it can match. Matching is **project-wide**:
187
+ every `disable` list across every key is unioned into one drop list, and
188
+ every `select` list across every key is unioned into one allowlist. So
189
+ `"security": { "disable": ["no-eval"] }` disables `no-eval` regardless of
190
+ `security` being the rule's own id, and two keys can each contribute to the
191
+ same select union.
192
+
193
+ ```json
194
+ {
195
+ "rules": {
196
+ "high-complexity": { "threshold": 25 },
197
+ "high-fan-out": { "threshold": 30 },
198
+ "no-eval": { "disable": ["no-eval", "no-eval-js", "ast-grep:no-eval"] },
199
+ "my-rule-set": { "select": ["rule-a", "rule-b"] }
200
+ }
201
+ }
202
+ ```
203
+
204
+ **Warning:** because select is project-wide, a non-empty `select` list
205
+ *anywhere* in `rules` silences every rule not in that list, across the whole
206
+ project — including tsc/eslint findings, not just the rule types you're
207
+ thinking about when you write it. It's a big hammer; reach for `disable`
208
+ instead unless you actually want an allowlist over everything. Findings that
209
+ carry no rule id at all (an eslint parse error, for example) are exempt from
210
+ select, so an allowlist can never hide them.
211
+
212
+ Disable and select are output-only filters applied AFTER inline suppression,
213
+ disposition, and dedup — the project-level policy never widens the trusted
214
+ surface area (widget state, baseline, dedup cache stay authoritative). The
215
+ baseline still records the full unfiltered set so a project-config edit does
216
+ not corrupt delta baselines.
174
217
 
175
218
  ### `maxProjectFiles`
176
219
 
@@ -197,5 +240,6 @@ Explicit override for the review graph's own file budget (#775), for monorepos t
197
240
  - A malformed JSON file is logged once and treated as "no config" — your diagnostics never get blocked by a syntax error in your own config.
198
241
  - Rule thresholds must be positive finite numbers; invalid, zero, or negative values are logged once and ignored.
199
242
  - Mutation-control `enabled` values must be booleans; invalid values are logged once and ignored.
243
+ - `rules.<id>.disable` and `rules.<id>.select` must each be a non-empty array of non-empty strings; a non-array value, an empty array, or an all-whitespace array is logged once and the entry is dropped. A policy-only entry (no `threshold`) is honored. A policy entry with an invalid value still keeps any valid `threshold` alongside it.
200
244
  - The depth sub-threshold of `high-complexity` (default `6`) is intentionally not exposed; only the cyclomatic-complexity knob ships today to keep the schema tight.
201
245
  - The file is mtime-cached, so editing it takes effect on the next scan without restarting the agent.
@@ -20,8 +20,8 @@ Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen i
20
20
  | HTML | ✓ | lsp, htmlhint | prettier |
21
21
  | YAML | ✓ | lsp, yamllint, actionlint (GitHub workflows) | prettier |
22
22
  | JSON | ✓ | lsp | biome, prettier |
23
- | Svelte | ✓ | lsp | — |
24
- | Vue | ✓ | lsp | — |
23
+ | Svelte | ✓ | lsp | oxfmt (needs `svelte` pkg installed + config `svelte: true`) |
24
+ | Vue | ✓ | lsp | prettier, oxfmt |
25
25
  | SQL | — | sqlfluff | sqlfluff |
26
26
  | Markdown | — | spellcheck, markdownlint, vale | prettier |
27
27
  | Docker | ✓ | lsp, hadolint | — |
@@ -42,7 +42,8 @@ Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen i
42
42
  | Gleam | ✓ | lsp, gleam-check | gleam format |
43
43
  | OCaml | ✓ | lsp | ocamlformat |
44
44
  | Clojure | ✓ | lsp | cljfmt |
45
- | Terraform | ✓ | lsp, tflint | terraform fmt |
45
+ | Terraform | ✓ | lsp, tflint, trivy-config (opt-in) | terraform fmt |
46
+ | Terragrunt | — | terragrunt | terragrunt hcl fmt |
46
47
  | Nix | ✓ | lsp | nixfmt |
47
48
  | TOML | ✓ | lsp, taplo | taplo |
48
49
  | CMake | ✓ (cmake-language-server) | lsp | cmake-format |
@@ -18,7 +18,7 @@ regenerated docs — so this file self-populates from CI without manual copy-pas
18
18
 
19
19
  `probe-clean-signal.mjs` also runs a **drift check** (#529): it compares each
20
20
  probed server's observed `clean-behavior` against the hand-set `silentOnClean`
21
- marker in `clients/lsp/server-strategies.ts` and writes any mismatch to the
21
+ marker in `clients/lsp/wait-policy/strategies.ts` and writes any mismatch to the
22
22
  `## silentOnClean drift` section below. This is telemetry only — never a CI
23
23
  gate — because the probe is a timing-based negative observation; a mismatch
24
24
  just tells a human the marker may need updating. `unknown` observations are
@@ -150,7 +150,7 @@ the rest fill in as the nightly reaches them.
150
150
  ## silentOnClean drift (nightly-generated)
151
151
 
152
152
  Telemetry only — never a CI gate. Compares each probed server's observed
153
- `clean-behavior` against `clients/lsp/server-strategies.ts`'s `silentOnClean`
153
+ `clean-behavior` against `clients/lsp/wait-policy/strategies.ts`'s `silentOnClean`
154
154
  marker; a mismatch means the marker may need a human update (#529). `unknown`
155
155
  observations are never compared (a slow/absent server is not evidence either way).
156
156
 
@@ -378,13 +378,18 @@ be exercised + debugged directly through Claude Code without running pi.
378
378
  Wire it in Claude Code `settings.json`:
379
379
 
380
380
  ```json
381
- { "hooks": { "PostToolUse": [
382
- { "matcher": "Edit|Write",
383
- "hooks": [ { "type": "command", "command": "pi-lens-analyze --hook" } ] } ] } }
381
+ { "hooks": {
382
+ "PostToolUse": [
383
+ { "matcher": "Edit|Write",
384
+ "hooks": [ { "type": "command", "command": "pi-lens-analyze --hook" } ] } ],
385
+ "Stop": [
386
+ { "hooks": [ { "type": "command", "command": "pi-lens-analyze --turn-end", "timeout": 60 } ] } ]
387
+ } }
384
388
  ```
385
389
 
386
390
  (the cold-LSP fix means the type-check is honestly reported as skipped, not a
387
- false clean; the agent pulls `pilens_analyze` warm when it wants types.)
391
+ false clean; the agent pulls `pilens_analyze` warm when it wants types. The
392
+ `Stop` half is the per-turn pass — see the bullet below.)
388
393
 
389
394
  - [ ] **booboo** full-codebase review tool (`/lens-booboo` → handleBooboo:
390
395
  complexity, AI-slop, TODOs, dead-code, dupes, type-coverage).
@@ -410,6 +415,34 @@ be exercised + debugged directly through Claude Code without running pi.
410
415
  project. 5 IPC unit tests (path stability, named-pipe round-trip, no-server +
411
416
  error → undefined fallback); live-proven (server "warm analyze" log fired,
412
417
  bin returned the diagnostic). Removes the one real weakness of the push half.
418
+ - [x] **Stop hook → per-turn pass** (#538) — the `pi-lens-analyze` bin gains a
419
+ turn-end mode (`--turn-end`, or a Claude Code `Stop` payload on stdin), the
420
+ analogue of pi's `agent_end`: incremental knip/madge, dep-circular,
421
+ cascade-to-dependents, tests, actionable-warnings aggregation. PostToolUse has
422
+ already accumulated the edited files into turn-state by the time `Stop` fires,
423
+ so the hook passes no files and clears any inherited pi `sessionId` — turn-
424
+ end's stale-session eviction leaves the registered worklist alone. The request is a
425
+ tagged route (`{route:"turn-end"}`, own schema version) on the **workspace**
426
+ socket, not the PID-scoped one: a Stop hook knows its cwd, never the server's
427
+ pid. It rides in ahead of the parse, so it inherits the #535 build-staleness
428
+ gate for free, and an untagged analyze request on the same socket is byte-for-
429
+ byte unchanged. All workspace IPC requests share one server-side queue, so a
430
+ timed-out PostToolUse client cannot let `Stop` overtake analysis still running
431
+ in the server, and concurrent turn-ends cannot race the turn-state clear.
432
+ **Warm-only, no cold fallback**: only the server process owns the session state
433
+ and pending turn work, so a local pass would report a false clean — no warm
434
+ server means one stderr line, silent stdout, exit 0.
435
+ The client waits 55s so it gives up inside Claude Code's 60s hook timeout.
436
+ `SubagentStop` is deliberately NOT registered: subagent edits already fire
437
+ PostToolUse into the shared turn-state, the consume bridges are one-shot (a
438
+ subagent pass would eat the main agent's findings into a transcript nobody
439
+ reads), and the fan-out would multiply the heavy pass. Visibility, honestly:
440
+ Stop-hook stdout is user-visible in transcript mode (Ctrl-R), NOT model
441
+ context — blockers still gate commits through the retained lens-guard record,
442
+ and a `decision:"block"` render is a follow-up. Tests run fire-and-forget
443
+ inside turn-end, so the `Tests:` section you see is the *previous* turn's
444
+ failures. Unit, bin, and live-route smoke tests cover transport, ordering,
445
+ rendering, and spawned-server behavior.
413
446
 
414
447
  ## Transport decision — RESOLVED: hand-roll (zero new deps)
415
448
 
@@ -6,7 +6,7 @@ server's `initialize` handshake. Generated by `node scripts/server-capabilities.
6
6
  absent on the generating host are listed under *Unavailable* — run in a
7
7
  provisioned environment (the nightly) to capture those rows.
8
8
 
9
- _Last generated: 2026-08-01 on linux; 29 servers captured, 14 unavailable._
9
+ _Last generated: 2026-08-14 on linux; 29 servers captured, 14 unavailable._
10
10
 
11
11
  ## Diagnostic mode + navigation/edit operations
12
12
 
@@ -19,7 +19,7 @@ Legend: ✓ advertised, · not. **mode** = document diagnostics (`pull` = `textD
19
19
  | clojure | push-only | · | ✓ | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 62 |
20
20
  | csharp | pull | ✓ | ✓ | ✓ | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 0 |
21
21
  | css | pull | · | ✓ | · | · | ✓ | ✓ | · | ✓ | · | ✓ | ✓ | · | · | 0 |
22
- | dart | push-only | · | ✓ | ✓ | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 16 |
22
+ | dart | push-only | · | ✓ | ✓ | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 18 |
23
23
  | deno | pull | · | ✓ | ✓ | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 2 |
24
24
  | docker | push-only | · | ✓ | · | · | · | ✓ | ✓ | ✓ | · | · | ✓ | · | · | 0 |
25
25
  | expert | push-only | · | ✓ | · | · | ✓ | ✓ | · | ✓ | ✓ | ✓ | · | · | · | 2 |
@@ -89,7 +89,7 @@ surface, including providers pi-lens does not yet consume.
89
89
 
90
90
  - **ast-grep** (1): ast-grep.applyAllFixes
91
91
  - **clojure** (62): add-import-to-namespace, add-missing-import, add-missing-libspec, add-require-suggestion, as-to-refer, backward, backward-barf, backward-select, backward-slurp, change-coll, clean-ns, cond->if-refactor, create-function, create-test, cursor-info, cycle-coll, cycle-keyword-auto-resolve, cycle-privacy, demote-fn, destructure-keys, drag-backward, drag-forward, drag-param-backward, drag-param-forward, expand-let, extract-function, extract-to-def, forward, forward-barf, forward-select, forward-slurp, get-in-all, get-in-less, get-in-more, get-in-none, if->cond-refactor, inline-function, inline-symbol, introduce-let, kill-sexp, …
92
- - **dart** (16): dart.edit.codeAction.apply, dart.edit.sortMembers, dart.edit.organizeImports, dart.edit.fixAll, dart.edit.fixAllInWorkspace.preview, dart.edit.fixAllInWorkspace, dart.edit.sendWorkspaceEdit, refactor.perform, refactor.validate, dart.logAction, dart.refactor.add_constructor_name, dart.refactor.convert_all_formal_parameters_to_named, dart.refactor.convert_selected_formal_parameters_to_named, dart.refactor.move_selected_formal_parameters_left, dart.refactor.move_top_level_to_file, dart.refactor.remove_constructor_name
92
+ - **dart** (18): dart.edit.codeAction.apply, dart.edit.sortMembers, dart.edit.organizeImports, dart.edit.fixAll, dart.edit.fixAllInWorkspace.preview, dart.edit.fixAllInWorkspace, dart.edit.sendWorkspaceEdit, refactor.perform, refactor.validate, dart.logAction, dart.refactor.add_constructor_name, dart.refactor_add.import_prefix, dart.refactor.convert_all_formal_parameters_to_named, dart.refactor.convert_selected_formal_parameters_to_named, dart.refactor.move_selected_formal_parameters_left, dart.refactor.move_top_level_to_file, dart.refactor.remove_constructor_name, dart.refactor.remove_import_prefix
93
93
  - **deno** (2): deno.cache, deno.reloadImportRegistries
94
94
  - **expert** (2): Reindex, connectionDetails
95
95
  - **go** (47): gopls.add_dependency, gopls.add_import, gopls.add_telemetry_counters, gopls.add_test, gopls.apply_fix, gopls.assembly, gopls.change_signature, gopls.check_upgrades, gopls.client_open_url, gopls.diagnose_files, gopls.doc, gopls.edit_go_directive, gopls.extract_to_new_file, gopls.fetch_vulncheck_result, gopls.free_symbols, gopls.gc_details, gopls.generate, gopls.go_get_package, gopls.implement_interface, gopls.list_imports, gopls.list_known_packages, gopls.lsp, gopls.maybe_prompt_for_telemetry, gopls.mem_stats, gopls.modify_tags, gopls.modules, gopls.move_type, gopls.package_symbols, gopls.packages, gopls.regenerate_cgo, gopls.remove_dependency, gopls.reset_go_mod_diagnostics, gopls.run_go_work_command, gopls.run_govulncheck, gopls.run_tests, gopls.scan_imports, gopls.split_package, gopls.start_debugging, gopls.start_profile, gopls.stop_profile, …
@@ -60,6 +60,7 @@ column is the effective behavior when nothing is set.
60
60
  | `--no-tests` | `tests.enabled` | global | test runner **on** |
61
61
  | `--no-delta` | `delta.enabled` | global | delta mode **on** (new diagnostics only) |
62
62
  | `--lens-guard` | `guard.enabled` | global | **off** |
63
+
63
64
  | `--no-opengrep` | `opengrep.enabled` | global | Opengrep scanner **on** |
64
65
  | `--no-read-guard` | `readGuard.enabled` | global | read-before-edit monitor **on** |
65
66
  | `--no-lens-context` | `contextInjection.enabled` | global | context injection **on** |
@@ -68,6 +69,13 @@ column is the effective behavior when nothing is set.
68
69
  | `--lens-actionable-warning-actions` | `actionableWarnings.includeLspCodeActions` | global | **off** |
69
70
  | `--lens-actionable-warning-autofix` | `actionableWarnings.autoFix.enabled` | project | **off** |
70
71
  | `--lens-actionable-warning-all` | `actionableWarnings.deltaOnly` (`false`) | global | `deltaOnly` **on** (report this turn only) |
72
+ | `--lens-compact-tool-line` | `ui.compactToolLine` | global | **off** (two-row tool rendering) |
73
+
74
+ `--lens-guard` is **EXPERIMENTAL and strictly opt-in**. When enabled, actual
75
+ `git commit`/`git push` commands are blocked only for current, structured
76
+ blocking findings (including blocking test failures); advisory/no-action-required
77
+ findings do not block. Stale, malformed, or ambiguous persisted state blocks
78
+ conservatively until checks run again.
71
79
 
72
80
  `--immediate-format` and `--lens-actionable-warning-all` are not `--no-*` flags,
73
81
  so they set a value rather than flipping a boolean off.
@@ -87,6 +95,8 @@ field docs.
87
95
  | `actionableWarnings.autoFix.maxFixes` | global | `5` | Cap on quickfixes applied per turn (`0` = report only) |
88
96
  | `rules.high-complexity.threshold` | project | `15` | Cyclomatic-complexity threshold |
89
97
  | `rules.high-fan-out.threshold` | project | `20` | Distinct-function-call threshold |
98
+ | `rules.<id>.disable` | project | absent | Disable diagnostics for a rule (output-only filter, project-wide; same normalization as `pi-lens-ignore`) |
99
+ | `rules.<id>.select` | project | absent | Allowlist of rule ids (output-only filter, project-wide across every key; disable wins over select) |
90
100
  | `maxProjectFiles` | project | `2000` | Base scale knob; derives five subsystem size budgets |
91
101
  | `reviewGraph.maxFiles` | project | derived (clamped `100`–`20000`) | Explicit review-graph file budget |
92
102
  | `trivy.enabled` / `trivy.minSeverity` | project | off | Opt-in Trivy vulnerability scanning |
@@ -140,7 +150,8 @@ tolerated without warning; anything else is logged once as a likely typo.
140
150
  "ignore": ["**/*.test.ts", "vendor/**"],
141
151
  "rules": {
142
152
  "high-complexity": { "threshold": 25 },
143
- "high-fan-out": { "threshold": 30 }
153
+ "high-fan-out": { "threshold": 30 },
154
+ "no-eval": { "disable": ["no-eval", "ast-grep:no-eval", "no-eval-js"] }
144
155
  },
145
156
  "maxProjectFiles": 5000,
146
157
  "format": { "enabled": false }
@@ -5,7 +5,10 @@ pi-lens registers the following slash commands with the pi host:
5
5
  - `/lens-toggle` — toggle pi-lens on/off for the current session without restarting
6
6
  - `/lens-context-toggle` — toggle automatic context injection on/off for the session (tools/LSP/read-guard/formatting stay active)
7
7
  - `/lens-widget-toggle` — show/hide the pi-lens diagnostics widget below the editor
8
- - `/lens-health` — runtime health, latency, and diagnostic telemetry
8
+ - `/lens-health` — runtime health, latency, and diagnostic telemetry, including
9
+ bounded per-session degradation counts (trust refusals, LSP breakers,
10
+ formatter skips/failures, idle evictions, WASM aborts, timeout tallies, and
11
+ more)
9
12
  - `/lens-perf` — independent top-five p50 and p99 rankings for the current process session and machine-wide active latency-log window
10
13
  - `/lens-allow-edit <path>` — override the read-before-edit guard for a single edit
11
14
  - `/lens-tools` — tool installation status: globally installed, auto-installed, or npx fallback
@@ -91,6 +91,7 @@ pi --lens-guard # Block git commit/push when unresolved blockers exist
91
91
  pi --no-opengrep # Disable the Opengrep security scanner (default-on auxiliary LSP)
92
92
  pi --no-read-guard # Disable the read-before-edit behavior monitor
93
93
  pi --lens-turn-summary # Persist a per-turn summary of diagnostics, autofixes, and autoformats
94
+ pi --lens-compact-tool-line # Render tool results as one compact, theme-aware line (closes #1327)
94
95
 
95
96
  # Actionable warnings (all default off)
96
97
  pi --lens-actionable-warnings # Report fixable warnings at turn end
@@ -141,6 +142,23 @@ second host adapter that calls the same `clients/lens-engine.ts` seam as the pi
141
142
  extension. Use `npm run build:dist` after MCP/engine changes so the user-scoped
142
143
  server loads fresh compiled code.
143
144
 
145
+ Two Claude Code hooks give the MCP mirror the same automatic cadence the pi
146
+ extension has. PostToolUse runs the per-edit pass, `Stop` runs the per-turn one:
147
+
148
+ ```json
149
+ { "hooks": {
150
+ "PostToolUse": [
151
+ { "matcher": "Edit|Write",
152
+ "hooks": [ { "type": "command", "command": "pi-lens-analyze --hook" } ] } ],
153
+ "Stop": [
154
+ { "hooks": [ { "type": "command", "command": "pi-lens-analyze --turn-end", "timeout": 60 } ] } ]
155
+ } }
156
+ ```
157
+
158
+ The `Stop` hook only works against a running MCP server because only that
159
+ process owns the session state and pending turn work. With no warm server the
160
+ turn-end is skipped, not faked: one line on stderr and nothing on stdout.
161
+
144
162
  ## Troubleshooting
145
163
 
146
164
  - Run `npm run build` before tests after editing TypeScript; tests import
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "3.8.74",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {
@@ -20,9 +20,9 @@
20
20
  "prepare": "npm run build:dist && node scripts/download-grammars.js --core --dest grammars",
21
21
  "lint": "tsc --project tsconfig.json",
22
22
  "watch": "tsc --watch",
23
- "test": "vitest run",
24
- "test:unit": "vitest run --ignore tests/index-integration.test.ts --ignore tests/clients/lsp/integration.test.ts",
25
- "test:integration": "vitest run tests/index-integration.test.ts tests/clients/lsp/integration.test.ts",
23
+ "test": "node scripts/with-test-lock.mjs -- vitest run",
24
+ "test:unit": "node scripts/with-test-lock.mjs -- vitest run",
25
+ "test:integration": "node scripts/with-test-lock.mjs -- vitest run tests/index-integration.test.ts tests/clients/lsp/integration.test.ts",
26
26
  "test:watch": "vitest",
27
27
  "check": "node scripts/check-extensions.mjs",
28
28
  "selftest:install": "node scripts/install-selftest.mjs",
@@ -34,7 +34,7 @@
34
34
  "audit:astgrep-rule-pairs": "node scripts/audit-astgrep-rule-pairs.mjs",
35
35
  "audit:playground": "node scripts/playground-verify-rule.mjs",
36
36
  "logs:smells": "node scripts/analyze-pi-lens-logs.mjs",
37
- "changelog:check": "node scripts/changelog-release.mjs --check",
37
+ "changelog:check": "node scripts/rollup-changelog.mjs --check",
38
38
  "changelog:release": "node scripts/changelog-release.mjs",
39
39
  "changelog:extract": "node scripts/changelog-extract.mjs",
40
40
  "release:backfill-notes": "node scripts/backfill-github-releases.mjs",
@@ -85,9 +85,9 @@
85
85
  "dependencies": {
86
86
  "@ast-grep/cli": "^0.45.0",
87
87
  "@ast-grep/napi": "^0.45.0",
88
- "@earendil-works/pi-tui": "^0.82.1",
88
+ "@earendil-works/pi-tui": "^0.84.1",
89
89
  "js-yaml": "^5.2.2",
90
- "minimatch": "^10.2.5",
90
+ "minimatch": "^10.2.6",
91
91
  "pidusage": "^4.0.1",
92
92
  "typebox": "^1.0.0",
93
93
  "vscode-jsonrpc": "^9.0.0"
@@ -105,7 +105,7 @@
105
105
  },
106
106
  "devDependencies": {
107
107
  "@biomejs/biome": "^2.4.10",
108
- "@earendil-works/pi-coding-agent": "^0.82.1",
108
+ "@earendil-works/pi-coding-agent": "^0.84.1",
109
109
  "@types/node": "^26.0.0",
110
110
  "@types/pidusage": "^2.0.5",
111
111
  "@vitest/coverage-v8": "^4.1.5",
@@ -134,8 +134,8 @@
134
134
  ]
135
135
  },
136
136
  "allowScripts": {
137
- "@ast-grep/cli@0.44.1": true,
137
+ "@ast-grep/cli@0.45.1": true,
138
138
  "@google/genai@1.52.0": true,
139
- "protobufjs@7.6.4": true
139
+ "protobufjs@7.6.5": true
140
140
  }
141
141
  }
@@ -15,6 +15,55 @@ valid:
15
15
  doThing()
16
16
  except Exception as e:
17
17
  handle(e)
18
+ - |
19
+ # dotted/qualified name (#1031)
20
+ try:
21
+ doThing()
22
+ except asyncio.TimeoutError:
23
+ handle()
24
+ - |
25
+ # parenthesized single name (mirrors #1026's tree-sitter spec set)
26
+ try:
27
+ doThing()
28
+ except (ValueError):
29
+ handle()
30
+ - |
31
+ # subscripted generic (#1244)
32
+ try:
33
+ doThing()
34
+ except dict[str, int]:
35
+ handle()
36
+ - |
37
+ # subscripted PEP 654 exception group (#1244)
38
+ try:
39
+ doThing()
40
+ except BaseExceptionGroup[TypeError, ValueError]:
41
+ handle()
42
+ - |
43
+ try:
44
+ doThing()
45
+ except ValueError | TypeError:
46
+ handle()
47
+ - |
48
+ try:
49
+ doThing()
50
+ except cache[key]:
51
+ handle()
52
+ - |
53
+ try:
54
+ doThing()
55
+ except (ValueError,):
56
+ handle()
57
+ - |
58
+ try:
59
+ doThing()
60
+ except (mod.E1, E2) as error:
61
+ handle(error)
62
+ - |
63
+ try:
64
+ doThing()
65
+ except* ValueError:
66
+ handle()
18
67
  invalid:
19
68
  - |
20
69
  try:
@@ -0,0 +1,87 @@
1
+ id: no-bare-host-path-in-win32-branch
2
+ valid:
3
+ # DOCUMENTED LIMITATION (#1339 review): the exact pre-fix #1150 shape --
4
+ # bare dirname in an unguarded helper CALLED FROM the guarded branch.
5
+ # ast-grep has no cross-function reach, so this is deliberately a
6
+ # negative fixture; it exists so a future strengthening flips it to a
7
+ # positive consciously, not accidentally. Review-enforced meanwhile
8
+ # (AGENTS.md defect-shape 2).
9
+ - |
10
+ import * as path from "node:path";
11
+ import { isWindowsPath } from "./path-utils.js";
12
+ function resolveNonExisting(filePath: string) {
13
+ return path.dirname(filePath);
14
+ }
15
+ function normalize(filePath: string) {
16
+ if (isWindowsPath(filePath)) {
17
+ return resolveNonExisting(filePath);
18
+ }
19
+ }
20
+ - |
21
+ import * as path from "node:path";
22
+ import { isWindowsPath } from "./path-utils.js";
23
+ function f(filePath: string) {
24
+ if (isWindowsPath(filePath)) {
25
+ return path.win32.dirname(filePath);
26
+ }
27
+ return path.dirname(filePath);
28
+ }
29
+ - |
30
+ import * as path from "node:path";
31
+ import { isWindowsPath } from "./path-utils.js";
32
+ function f(filePath: string) {
33
+ return isWindowsPath(filePath)
34
+ ? path.win32.basename(filePath)
35
+ : path.basename(filePath);
36
+ }
37
+ - |
38
+ import { dirname } from "node:path";
39
+ function f(filePath: string) {
40
+ if (someOtherPredicate(filePath)) {
41
+ return dirname(filePath);
42
+ }
43
+ }
44
+ invalid:
45
+ # Fresh probes from the #1339 review, encoded so they stay covered.
46
+ - |
47
+ import * as path from "node:path";
48
+ import { isWindowsPath } from "./path-utils.js";
49
+ function f(filePath: string) {
50
+ if (isWindowsPath(filePath)) {
51
+ for (const part of parts) {
52
+ const nested = path.join(filePath, part);
53
+ }
54
+ }
55
+ }
56
+ - |
57
+ import * as path from "node:path";
58
+ import { isWindowsPath } from "./path-utils.js";
59
+ function f(filePath: string) {
60
+ if (isWindowsPath(filePath)) {
61
+ return filePath.split(path.sep);
62
+ }
63
+ }
64
+ - |
65
+ import * as path from "node:path";
66
+ import { isFullyQualifiedWin32 } from "./path-utils.js";
67
+ function f(filePath: string, other: boolean) {
68
+ if (isFullyQualifiedWin32(filePath) && other) {
69
+ return path.basename(filePath);
70
+ }
71
+ }
72
+ - |
73
+ import * as path from "node:path";
74
+ import { isWindowsPath } from "./path-utils.js";
75
+ function f(filePath: string) {
76
+ if (isWindowsPath(filePath)) {
77
+ return path.dirname(filePath);
78
+ }
79
+ }
80
+ - |
81
+ import { join, sep } from "node:path";
82
+ import { isFullyQualifiedWin32 } from "./path-utils.js";
83
+ function f(filePath: string) {
84
+ if (isFullyQualifiedWin32(filePath)) {
85
+ return join(filePath, sep);
86
+ }
87
+ }
@@ -2,7 +2,17 @@ id: no-nested-links
2
2
  valid:
3
3
  - '<a href="/x">link</a>'
4
4
  - '<a href="/x"><span>nested</span></a>'
5
+ - '<div><a href="/x">link</a></div>'
6
+ - '<div><a href="/x">first</a><a href="/y">second</a></div>'
5
7
  invalid:
6
8
  - '<a href="/x">outer <a href="/y">inner</a></a>'
7
9
  - '<a><a>double</a></a>'
8
- - '<div><a href="/x">link</a></div>'
10
+ - '<a href="/x"><span><a href="/y">inner</a></span></a>'
11
+ - |
12
+ <a href="/outer">
13
+ <span>
14
+ <a href="/middle">
15
+ <em><a href="/inner">deep</a></em>
16
+ </a>
17
+ </span>
18
+ </a>
@@ -0,0 +1,17 @@
1
+ id: no-raw-json-store-write
2
+ valid:
3
+ - "writeFileAtomic(file, JSON.stringify(data))"
4
+ - "writeFileSync(file, text)"
5
+ invalid:
6
+ # #1349 review: encoding/replacer/indent variants are the same raw write.
7
+ - "writeFileSync(file, JSON.stringify(data), 'utf8')"
8
+ - "writeFileSync(file, JSON.stringify(data, null, 2))"
9
+ - "fs.writeFileSync(file, JSON.stringify(data, replacer), { encoding: 'utf8' })"
10
+ - "writeFileSync(file, JSON.stringify(data))"
11
+ - "fs.writeFileSync(file, JSON.stringify(data))"
12
+ - |
13
+ const payload = JSON.stringify(data)
14
+ writeFileSync(file, payload)
15
+ - |
16
+ const payload = JSON.stringify(data)
17
+ fs.writeFileSync(file, payload)
@@ -0,0 +1,16 @@
1
+ id: no-win32-isabsolute-for-qualification
2
+ valid:
3
+ # DOCUMENTED LIMITATION (#1349 review): the aliased parser -- the shape
4
+ # the actual pre-#1330 defect used -- is beyond ast-grep's reach and is
5
+ # deliberately a negative; flipping it to invalid requires an engine
6
+ # capability decision. Review-enforced meanwhile (AGENTS.md shape 2).
7
+ - |
8
+ const w = path.win32;
9
+ if (w.isAbsolute(filePath)) {
10
+ return true;
11
+ }
12
+ - "isFullyQualified(filePath)"
13
+ - "path.posix.isAbsolute(filePath)"
14
+ invalid:
15
+ - "path.win32.isAbsolute(filePath)"
16
+ - "win32.isAbsolute(filePath)"
@@ -12,20 +12,8 @@ note: |
12
12
  identifier, making it fire only for empty bodies or `pass`/`raise`
13
13
  (where there are no function-call identifiers).
14
14
 
15
- The fixed rule checks the except_clause's direct children for ANY of
16
- the three exception-spec shapes (bare identifier `except E`, tuple
17
- `except (E, F)`, or `as` pattern `except E as e`). The original missed
18
- the tuple and `as_pattern` forms — a tuple like `(ValueError, KeyError)`
19
- has no direct `identifier` child (the identifiers are inside the
20
- tuple), and `except Exception as e` has `e` as an identifier child
21
- even though the type is specified.
15
+ The rule now matches the grammar's bare `except:` syntax directly, so
16
+ newly introduced exception-spec node kinds cannot become false positives.
22
17
  rule:
23
18
  kind: except_clause
24
- not:
25
- any:
26
- - has:
27
- kind: identifier
28
- - has:
29
- kind: tuple
30
- - has:
31
- kind: as_pattern
19
+ pattern: "except:"