@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
@@ -0,0 +1,13 @@
1
+ id: no-reflect-get-js
2
+ language: JavaScript
3
+ severity: warning
4
+ message: "Reflect.get(obj, key) — use a typed property access (obj.prop or obj[key]) instead"
5
+ note: |
6
+ Reflect.get reaches for reflection where a typed property access
7
+ already works. Prefer dot or bracket access.
8
+
9
+ Scoped to the 2-argument form only — see no-reflect-get.yml's note:
10
+ the 3-argument receiver-forwarding form is the standard Proxy `get`
11
+ trap idiom.
12
+ rule:
13
+ pattern: Reflect.get($OBJ, $KEY)
@@ -0,0 +1,16 @@
1
+ id: no-reflect-get
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Reflect.get(obj, key) — use a typed property access (obj.prop or obj[key]) instead"
5
+ note: |
6
+ Reflect.get reaches for reflection where a typed property access
7
+ already works. Prefer dot or bracket access.
8
+
9
+ Scoped to the 2-argument form only. The 3-argument form
10
+ (`Reflect.get(target, key, receiver)`) is the standard, correct way to
11
+ forward a `receiver` inside a Proxy `get` trap — an FP-scan of the
12
+ pi-lens codebase found 2 hits, both inside a Proxy trap using the
13
+ 3-argument receiver-forwarding form, so that arity is excluded rather
14
+ than allowlisted by call site.
15
+ rule:
16
+ pattern: Reflect.get($OBJ, $KEY)
@@ -0,0 +1,27 @@
1
+ id: no-unknown-laundering
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "type alias to bare 'unknown' — give the alias real shape or inline 'unknown' at the one call site that needs it"
5
+ note: |
6
+ `type X = unknown` adds a name but zero type information — every
7
+ consumer of X still has to narrow from scratch, so the alias only
8
+ launders the fact that nothing was typed. Give it real shape, or drop
9
+ the alias and let the (rare) genuine unknown-input site say so
10
+ directly.
11
+
12
+ FP-scan finding (pi-lens codebase, clients/ tools/ mcp/ index.ts):
13
+ the originally proposed wider fan-out — bare `unknown` return types,
14
+ `unknown` parameters, and `Record<string, unknown>` / index-signature
15
+ dictionaries — was dropped from this rule. Each of those arms fired
16
+ overwhelmingly on legitimate, idiomatic TypeScript already in this
17
+ codebase: `unknown` parameters on type-guard/validator functions
18
+ (`errorMessage(err: unknown)`, `asRecord(value: unknown)`), `unknown`
19
+ return types on parsers and a `ProxyHandler.get` trap (whose signature
20
+ TypeScript itself requires), and `Record<string, unknown>` on
21
+ telemetry/metadata bags (`metadata?: Record<string, unknown>`). A
22
+ declared `unknown` at those positions is the CORRECT contract — it
23
+ forces the caller to narrow — so it is not syntactically distinguishable
24
+ from a laundered one; only `type X = unknown` stayed a reliable
25
+ precision signal (zero hits in the same scan).
26
+ rule:
27
+ pattern: "type $T = unknown"
@@ -0,0 +1,23 @@
1
+ id: no-win32-isabsolute-for-qualification
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use isFullyQualified for path qualification instead of win32.isAbsolute"
5
+ note: |
6
+ Qualification must follow the input path's shape. Use clients/path-utils.ts
7
+ isFullyQualified(), which distinguishes drive-absolute, UNC, rooted-relative,
8
+ POSIX-absolute, and relative paths across host operating systems. The
9
+ implementation of isFullyQualified itself is excluded because it is the
10
+ compatibility seam that owns the win32 parser call.
11
+
12
+ SCOPE (review-verified, #1349): direct call forms only --
13
+ path.win32.isAbsolute(...) and a destructured win32.isAbsolute(...). An
14
+ ALIASED parser (const w = path.win32; w.isAbsolute(p) -- the shape the
15
+ actual pre-#1330 defect used) is beyond the engine's reach and stays
16
+ review-enforced; the fixture file encodes that miss as a documented
17
+ negative, matching the #1339 precedent.
18
+ ignores:
19
+ - "**/clients/path-utils.ts"
20
+ rule:
21
+ any:
22
+ - pattern: path.win32.isAbsolute($P)
23
+ - pattern: win32.isAbsolute($P)
@@ -18,15 +18,13 @@ query: |
18
18
  (jsx_element
19
19
  open_tag: (jsx_opening_element
20
20
  (identifier) @OUTER
21
- (#eq? @OUTER "a"))
22
- (jsx_element
23
- open_tag: (jsx_opening_element
24
- (identifier) @INNER
25
- (#eq? @INNER "a"))))
21
+ (#eq? @OUTER "a"))) @OUTER_ELEMENT
26
22
 
27
23
  metavars:
28
24
  - OUTER
29
- - INNER
25
+ - OUTER_ELEMENT
26
+
27
+ post_filter: no_nested_anchor_chain
30
28
 
31
29
  tags:
32
30
  - correctness
@@ -22,8 +22,8 @@ description: |
22
22
 
23
23
  query: |
24
24
  (formal_parameters
25
- (required_parameter (identifier) @PARAM1)
26
- (required_parameter (identifier) @NAME))
25
+ (_ pattern: (_) @PARAM1)
26
+ (_ pattern: (_) @NAME))
27
27
 
28
28
  metavars:
29
29
  - PARAM1
@@ -8,9 +8,11 @@
8
8
  * ~/.pi-lens/cascade*.log JSONL impact-cascade logs
9
9
  * ~/.pi-lens/read-guard*.log JSONL read-guard friction logs
10
10
  * ~/.pi-lens/tree-sitter*.log JSONL structural runner logs
11
+ * ~/.pi-lens/extension*.log JSONL extension diagnostics and console-guard telemetry
11
12
  * ~/.pi-lens/actionable-warnings*.log JSONL advisory pipeline (inject/suppress)
12
13
  * ~/.pi-lens/ast-grep-tools*.log JSONL MCP ast-grep search/replace telemetry
13
14
  * ~/.pi-lens/logs/*.jsonl JSONL diagnostic findings
15
+ * ~/.pi-lens/projects/<slug>/worklog.jsonl JSONL fix worklog (rule/tool/model/provider, #1448)
14
16
  */
15
17
 
16
18
  import fs from "node:fs";
@@ -18,10 +20,12 @@ import os from "node:os";
18
20
  import path from "node:path";
19
21
  import readline from "node:readline";
20
22
  import { fileURLToPath } from "node:url";
23
+ import { minimatch } from "minimatch";
21
24
 
22
25
  const DEFAULT_ROOT = path.join(os.homedir(), ".pi-lens");
23
26
  const DEFAULT_SINCE = "2d";
24
27
  const DEFAULT_LIMIT = 12;
28
+ const DEFAULT_EXCLUDE_GLOBS = ["**/AppData/Local/Temp/claude/**", "**/heap-corpus*/**", "**/.plegma/work/**"];
25
29
 
26
30
  const args = parseArgs(process.argv.slice(2));
27
31
  const root = path.resolve(expandHome(args.root ?? DEFAULT_ROOT));
@@ -30,6 +34,10 @@ const limit =
30
34
  Number.parseInt(args.limit ?? `${DEFAULT_LIMIT}`, 10) || DEFAULT_LIMIT;
31
35
  const outputJson = Boolean(args.json);
32
36
  const includeArchived = Boolean(args.archived);
37
+ const excludeGlobs = [
38
+ ...DEFAULT_EXCLUDE_GLOBS,
39
+ ...(Array.isArray(args.exclude) ? args.exclude : args.exclude ? [args.exclude] : []),
40
+ ];
33
41
 
34
42
  const thresholds = {
35
43
  startupSlowMs: Number.parseInt(args.startupSlowMs ?? "500", 10),
@@ -62,6 +70,7 @@ async function main() {
62
70
  analyzeSessionStart(files.sessionStart, state),
63
71
  analyzeActionableWarnings(files.actionableWarnings, state),
64
72
  analyzeAstGrepTools(files.astGrepTools, state),
73
+ analyzeWorklog(files.worklog, state),
65
74
  ]);
66
75
 
67
76
  const report = buildReport(state);
@@ -83,7 +92,14 @@ function parseArgs(argv) {
83
92
  const key = arg.slice(2);
84
93
  const next = argv[i + 1];
85
94
  if (!next || next.startsWith("--")) out[key] = "true";
86
- else out[key] = argv[++i];
95
+ else if (key === "exclude") {
96
+ const value = argv[++i];
97
+ out.exclude = out.exclude
98
+ ? Array.isArray(out.exclude)
99
+ ? [...out.exclude, value]
100
+ : [out.exclude, value]
101
+ : value;
102
+ } else out[key] = argv[++i];
87
103
  }
88
104
  }
89
105
  return out;
@@ -125,6 +141,13 @@ function discoverLogFiles(logRoot, archived) {
125
141
  .filter((name) => name.endsWith(".jsonl"))
126
142
  .map((name) => path.join(logsDir, name));
127
143
 
144
+ // worklog.jsonl lives per-project under projects/<slug>/ (getProjectDataDir),
145
+ // not at the log root — walk one level to find every project's file (#1448).
146
+ const projectsDir = path.join(logRoot, "projects");
147
+ const worklogs = safeReaddir(projectsDir)
148
+ .map((slug) => path.join(projectsDir, slug, "worklog.jsonl"))
149
+ .filter((file) => fs.existsSync(file));
150
+
128
151
  const byPrefix = (prefix) =>
129
152
  allRootFiles.filter((file) => {
130
153
  const base = path.basename(file);
@@ -142,6 +165,7 @@ function discoverLogFiles(logRoot, archived) {
142
165
  actionableWarnings: byPrefix("actionable-warnings"),
143
166
  astGrepTools: byPrefix("ast-grep-tools"),
144
167
  diagnostics: dailyLogs,
168
+ worklog: worklogs,
145
169
  };
146
170
  }
147
171
 
@@ -158,6 +182,7 @@ function createState(files) {
158
182
  window: { since: since?.toISOString() ?? "all", root },
159
183
  files,
160
184
  parseErrors: counter(),
185
+ excludedRows: 0,
161
186
  seen: counter(),
162
187
  projects: counter(),
163
188
  smellTotals: counter(),
@@ -248,6 +273,14 @@ function createState(files) {
248
273
  errors: [],
249
274
  slow: [],
250
275
  },
276
+ worklog: {
277
+ // key: "<rule>||<model>" (model "" when the entry predates #1448 or the
278
+ // runtime didn't know it) -> { total, autoFixed }
279
+ byRuleModel: new Map(),
280
+ byModel: counter(),
281
+ byModelAutoFixed: counter(),
282
+ byProvider: counter(),
283
+ },
251
284
  };
252
285
  }
253
286
 
@@ -557,6 +590,10 @@ async function analyzeSessionStart(files, state) {
557
590
  const ts = dateOf(match[1]);
558
591
  if (!inWindow(ts)) return;
559
592
  const message = match[2];
593
+ if (isExcludedText(message)) {
594
+ state.excludedRows++;
595
+ return;
596
+ }
560
597
  state.seen.inc("sessionstart");
561
598
 
562
599
  const cwd = /session_start cwd:\s*(.*)$/.exec(message)?.[1];
@@ -718,17 +755,82 @@ async function analyzeAstGrepTools(files, state) {
718
755
  }
719
756
  }
720
757
 
758
+ /**
759
+ * Per-model rollup (#1448): rule × model counts and auto-fixed vs
760
+ * agent-required rates, from worklog.jsonl's optional `model`/`provider`
761
+ * fields. Entries predating #1448 (or written outside a live agent turn)
762
+ * have neither field — bucketed under the empty-string model/provider key so
763
+ * "unattributed" volume stays visible rather than silently dropped.
764
+ */
765
+ async function analyzeWorklog(files, state) {
766
+ for (const file of files) {
767
+ await forEachJsonLine(file, "worklog", state, (entry) => {
768
+ const ts = dateOf(entry.timestamp);
769
+ if (!inWindow(ts)) return;
770
+ state.seen.inc("worklog");
771
+ const rule = entry.rule ?? "unknown";
772
+ const model = entry.model ?? "";
773
+ const provider = entry.provider ?? "";
774
+ const key = `${rule}||${model}`;
775
+ const row = state.worklog.byRuleModel.get(key) ?? {
776
+ rule,
777
+ model,
778
+ total: 0,
779
+ autoFixed: 0,
780
+ };
781
+ row.total += 1;
782
+ if (entry.autoFixed) row.autoFixed += 1;
783
+ state.worklog.byRuleModel.set(key, row);
784
+ state.worklog.byModel.inc(model || "(unknown)");
785
+ if (entry.autoFixed) state.worklog.byModelAutoFixed.inc(model || "(unknown)");
786
+ state.worklog.byProvider.inc(provider || "(unknown)");
787
+ });
788
+ }
789
+ }
790
+
721
791
  async function forEachJsonLine(file, bucket, state, visitor) {
722
792
  await forEachLine(file, async (line) => {
723
793
  if (!line.trim()) return;
724
794
  try {
725
- visitor(JSON.parse(line));
795
+ const entry = JSON.parse(line);
796
+ if (isExcludedEntry(entry)) {
797
+ state.excludedRows++;
798
+ return;
799
+ }
800
+ visitor(entry);
726
801
  } catch {
727
802
  state.parseErrors.inc(`${bucket}:${path.basename(file)}`);
728
803
  }
729
804
  });
730
805
  }
731
806
 
807
+ function isExcludedEntry(entry) {
808
+ return pathValues(entry).some((value) =>
809
+ excludeGlobs.some((glob) =>
810
+ minimatch(value, glob, { nocase: true, dot: true }),
811
+ ),
812
+ );
813
+ }
814
+
815
+ function pathValues(value, key = "") {
816
+ if (typeof value === "string") {
817
+ return /(file|path|cwd|root|project)/i.test(key) ? [normalizePath(value)] : [];
818
+ }
819
+ if (!value || typeof value !== "object") return [];
820
+ return Object.entries(value).flatMap(([childKey, child]) =>
821
+ pathValues(child, childKey),
822
+ );
823
+ }
824
+
825
+ function isExcludedText(text) {
826
+ const cwd = /session_start cwd:\s*(.*)$/.exec(text)?.[1];
827
+ return cwd
828
+ ? excludeGlobs.some((glob) =>
829
+ minimatch(normalizePath(cwd), glob, { nocase: true, dot: true }),
830
+ )
831
+ : false;
832
+ }
833
+
732
834
  async function forEachLine(file, visitor) {
733
835
  if (!fs.existsSync(file)) return;
734
836
  const rl = readline.createInterface({
@@ -1129,6 +1231,7 @@ function buildReport(state) {
1129
1231
  Object.entries(state.files).map(([key, list]) => [key, list.length]),
1130
1232
  ),
1131
1233
  rowsSeen: state.seen.toJSON(),
1234
+ rowsExcluded: state.excludedRows,
1132
1235
  parseErrors: state.parseErrors.toJSON(),
1133
1236
  projects: state.projects.top(limit),
1134
1237
  smells,
@@ -1207,6 +1310,17 @@ function buildReport(state) {
1207
1310
  errors: state.astGrep.errors.slice(0, limit),
1208
1311
  slow: state.astGrep.slow.slice(0, limit),
1209
1312
  },
1313
+ worklog: {
1314
+ byModel: state.worklog.byModel.top(limit * 2),
1315
+ byProvider: state.worklog.byProvider.top(limit * 2),
1316
+ byRuleModel: [...state.worklog.byRuleModel.values()]
1317
+ .map((row) => ({
1318
+ ...row,
1319
+ autoFixedRate: row.total ? row.autoFixed / row.total : 0,
1320
+ }))
1321
+ .sort((a, b) => b.total - a.total)
1322
+ .slice(0, limit * 3),
1323
+ },
1210
1324
  };
1211
1325
  }
1212
1326
 
@@ -1232,6 +1346,7 @@ function printReport(report) {
1232
1346
  .map(([k, v]) => `${k}=${v}`)
1233
1347
  .join(", ")}`,
1234
1348
  );
1349
+ console.log(`${report.rowsExcluded} rows excluded (synthetic corpora)`);
1235
1350
  if (Object.keys(report.parseErrors).length)
1236
1351
  console.log(`parse errors: ${JSON.stringify(report.parseErrors)}`);
1237
1352
 
@@ -1344,6 +1459,23 @@ function printReport(report) {
1344
1459
  ` started=${ws.started} completed=${ws.completed} incomplete=${ws.incomplete} aborted=${ws.aborted} fileTimeouts=${ws.timedOutFilesTotal} (in ${ws.timedOutSweeps} sweeps)`,
1345
1460
  );
1346
1461
  }
1462
+ const wl = report.worklog;
1463
+ if (wl && (wl.byModel.length || wl.byRuleModel.length)) {
1464
+ console.log("\nWorklog per-model rollup (#1448)");
1465
+ console.log(
1466
+ ` by model: ${wl.byModel.map((x) => `${x.key}=${x.count}`).join(", ")}`,
1467
+ );
1468
+ if (wl.byProvider.length)
1469
+ console.log(
1470
+ ` by provider: ${wl.byProvider.map((x) => `${x.key}=${x.count}`).join(", ")}`,
1471
+ );
1472
+ console.log(" rule × model (auto-fixed vs agent-required):");
1473
+ for (const row of wl.byRuleModel.slice(0, limit))
1474
+ console.log(
1475
+ ` ${String(row.total).padStart(5)} ${row.rule} [${row.model || "(unknown)"}] autoFixed=${row.autoFixed} (${(row.autoFixedRate * 100).toFixed(0)}%)`,
1476
+ );
1477
+ }
1478
+
1347
1479
  const timeouts = Object.entries(report.latency.phaseTimeouts ?? {});
1348
1480
  if (timeouts.length) {
1349
1481
  console.log("\nPhase timeouts");
@@ -52,12 +52,15 @@ Use `$$$` when you don't need the captured value; `$$$NAME` when you do.
52
52
 
53
53
  Use these instead of writing raw YAML:
54
54
 
55
- | Parameter | What it does |
56
- |---|---|
57
- | `insideKind` | Only match inside an ancestor of this node kind |
58
- | `hasKind` | Only match nodes that contain a descendant of this kind |
59
- | `follows` | Only match nodes preceded by a sibling matching this pattern |
60
- | `precedes` | Only match nodes followed by a sibling matching this pattern |
55
+ | Parameter | Tool | What it does |
56
+ |---|---|---|
57
+ | `insideKind` | both | Only match inside an ancestor of this node kind (searches ALL ancestors, `stopBy: end`) |
58
+ | `hasKind` | both | Only match nodes whose **immediate child** has this kind (`stopBy: neighbor` — NOT recursive) |
59
+ | `hasDescendantKind` | both | Only match nodes containing this kind **anywhere in their descendants** (`stopBy: end`) — use this instead of `hasKind` when the target isn't a direct child |
60
+ | `follows` | both | Only match nodes preceded by a sibling matching this pattern |
61
+ | `precedes` | both | Only match nodes followed by a sibling matching this pattern |
62
+
63
+ `hasKind` and `hasDescendantKind` are mutually exclusive on both tools — combining them errors.
61
64
 
62
65
  ```
63
66
  # console.log only inside functions
@@ -152,13 +155,9 @@ Use these as starting points, then scope `paths` tightly.
152
155
 
153
156
  | Task | Pattern / params |
154
157
  |---|---|
155
- | Find pi lifecycle handlers | `pattern: pi.on($EVENT, $HANDLER)` |
156
- | Find timers | `pattern: setTimeout($CALLBACK, $$$REST)` or `setInterval($CALLBACK, $$$REST)` |
157
- | Find immediate/deferred work | `pattern: setImmediate($CALLBACK)` |
158
- | Find promise callbacks | `pattern: $PROMISE.then($CALLBACK)` / `.catch($CALLBACK)` / `.finally($CALLBACK)` |
159
158
  | Find object-literal function dependency by name | `pattern: { resetLSPService: $FN, $$$REST }` |
160
- | Find fire-and-forget async calls | `pattern: void $CALL` |
161
159
  | Find empty catches | `pattern: try { $$$BODY } catch ($ERR) { }` |
160
+ | Find fire-and-forget async calls | `pattern: void $CALL` |
162
161
 
163
162
  For lifecycle bugs, search first, then use the returned `details.matchLocations[].readSlice` handle for bounded context.
164
163
 
@@ -13,39 +13,39 @@ Use `lsp_diagnostics` before builds/tests or after touching several files:
13
13
 
14
14
  | Need | Tool call |
15
15
  |---|---|
16
- | Check one file | `lsp_diagnostics({ filePath: "src/file.ts" })` |
17
- | Check a folder | `lsp_diagnostics({ filePath: "src/", severity: "error" })` |
18
- | Check exact touched files | `lsp_diagnostics({ filePaths: ["src/a.ts", "src/b.ts"], concurrency: 8 })` |
19
- | Slow server (Rust, Java) | `lsp_diagnostics({ filePaths: files, waitMs: 2000 })` |
20
- | Include warnings | `lsp_diagnostics({ filePaths: files, severity: "all" })` |
16
+ | Check one file | `lsp_diagnostics({ path: "src/file.ts" })` |
17
+ | Check a folder | `lsp_diagnostics({ path: "src/", severity: "error" })` |
18
+ | Check exact touched files | `lsp_diagnostics({ paths: ["src/a.ts", "src/b.ts"], concurrency: 8 })` |
19
+ | Slow server (Rust, Java) | `lsp_diagnostics({ paths: files, waitMs: 2000 })` |
20
+ | Include warnings | `lsp_diagnostics({ paths: files, severity: "all" })` |
21
21
 
22
- Prefer explicit `filePaths` batches after multi-file edits — bounded concurrency, no unrelated directory noise.
22
+ Prefer explicit `paths` batches after multi-file edits — bounded concurrency, no unrelated directory noise.
23
23
 
24
24
  ## Navigation (Code Intelligence)
25
25
 
26
26
  | Question | Operation | Parameters |
27
27
  |---|---|---|
28
- | Where is this defined? | `definition` | filePath, line, character |
29
- | Where is this symbol's *type* defined? | `typeDefinition` | filePath, line, character |
30
- | Where is this declared (vs defined)? | `declaration` | filePath, line, character |
31
- | Find all usages | `references` | filePath, line, character |
32
- | What type is this? | `hover` | filePath, line, character |
33
- | Call signature | `signatureHelp` | filePath, line, character (at arg position) |
34
- | Symbols in this file | `documentSymbol` | filePath |
35
- | Find symbol across project | `workspaceSymbol` | query + filePath (strongly recommended) |
36
- | Quick fixes available | `codeAction` | filePath, line, character, endLine, endCharacter |
37
- | Rename symbol safely | `rename` | filePath, line, character, newName |
38
- | Who implements this? | `implementation` | filePath, line, character |
39
- | Who calls this function? | `prepareCallHierarchy` → `incomingCalls` | filePath, line, character |
40
- | What does this call? | `prepareCallHierarchy` → `outgoingCalls` | filePath, line, character |
41
- | What commands does the server offer? | `capabilities` | (optional filePath) — lists advertised commands |
28
+ | Where is this defined? | `definition` | path, line, character |
29
+ | Where is this symbol's *type* defined? | `typeDefinition` | path, line, character |
30
+ | Where is this declared (vs defined)? | `declaration` | path, line, character |
31
+ | Find all usages | `references` | path, line, character |
32
+ | What type is this? | `hover` | path, line, character |
33
+ | Call signature | `signatureHelp` | path, line, character (at arg position) |
34
+ | Symbols in this file | `documentSymbol` | path |
35
+ | Find symbol across project | `workspaceSymbol` | query + path (strongly recommended) |
36
+ | Quick fixes available | `codeAction` | path, line, character, endLine, endCharacter |
37
+ | Rename symbol safely | `rename` | path, line, character, newName |
38
+ | Who implements this? | `implementation` | path, line, character |
39
+ | Who calls this function? | `prepareCallHierarchy` → `incomingCalls` | path, line, character |
40
+ | What does this call? | `prepareCallHierarchy` → `outgoingCalls` | path, line, character |
41
+ | What commands does the server offer? | `capabilities` | (optional path) — lists advertised commands |
42
42
  | Run a server command (e.g. organize imports) | `executeCommand` | command (+ commandArguments); dry-run unless `apply:true` |
43
43
 
44
44
  ## Call Hierarchy Pattern
45
45
 
46
46
  ```
47
47
  // Step 1
48
- lsp_navigation(operation="prepareCallHierarchy", filePath="src/api.ts", line=42, character=10)
48
+ lsp_navigation(operation="prepareCallHierarchy", path="src/api.ts", line=42, character=10)
49
49
  // → returns callHierarchyItem
50
50
 
51
51
  // Step 2
@@ -56,7 +56,7 @@ lsp_navigation(operation="outgoingCalls", callHierarchyItem=<item from step 1>)
56
56
  ## Operational Notes
57
57
 
58
58
  - **`definition` returns nothing?** The file may not be open/indexed yet. Read it first, then retry.
59
- - **`workspaceSymbol` empty?** Always pass `filePath`. Unscoped queries are best-effort and frequently return nothing. If TypeScript returns "No Project", open the scoped file first.
59
+ - **`workspaceSymbol` empty?** Always pass `path`. Unscoped queries are best-effort and frequently return nothing. If TypeScript returns "No Project", open the scoped file first.
60
60
  - **`references`** — query from the *definition site* for full cross-file coverage; usage-site queries can be partial.
61
61
  - **`signatureHelp`** — only valid at call-site argument positions; declaration positions return empty.
62
62
  - **`workspaceDiagnostics`** — tracked push snapshot only, not an active check. Use `lsp_diagnostics` when you need fresh results.
@@ -67,7 +67,7 @@ constraints: # metavariable regex constraints work
67
67
 
68
68
  ⚠ **`has`/`inside` default to the immediate child/parent (`stopBy: neighbor`).** For a
69
69
  recursive descendant/ancestor search add `stopBy: end`. This is the #1 migration
70
- gotcha — see the `has` note below.
70
+ gotcha — see the `has` note in `reference.md`.
71
71
 
72
72
  ## YAML quoting — REQUIRED (js-yaml will reject the rule otherwise)
73
73
 
@@ -100,120 +100,14 @@ The parser is a real YAML parser, so unquoted special chars throw and the rule i
100
100
  ✅ Docs: docs/custom-rules.md
101
101
  ```
102
102
 
103
- ## Hard-won gotchas (NAPI runner specifics — verified)
103
+ ## Reference doc — read before writing a NAPI-runner-specific or hard-to-express rule
104
104
 
105
- ```
106
- ⚠ `has`/`inside` default to DIRECT child/parent — add `stopBy: end` for a recursive search.
107
- This cuts BOTH ways, so think about where the target node actually lives:
108
- - Target is a grandchild+ → you MUST add `stopBy: end` or the `has` never matches.
109
- `switch-without-default` = `switch_statement` not has `switch_default`: the default
110
- lives under `switch_body`, so without `stopBy: end` it matches nothing and every
111
- switch (even ones WITH a default) is flagged. Same for `nested-ternary` catching a
112
- parenthesized `a ? (b ? c : d) : e`.
113
- - Target is the direct child → leave it at `neighbor` (default). Adding `stopBy: end`
114
- OVER-reports: `throw_statement` has `string` + `stopBy: end` flags `throw new
115
- Error("x")` (the string is nested), and `expression_statement` has `new_expression`
116
- + `stopBy: end` flags `fn(new Error())` as a discarded error. Keep these direct.
117
- napi's `has` never matches the node itself, so a self-referential `kind: X` has
118
- `kind: X` (with `stopBy: end`) correctly flags only genuinely-nested X.
119
-
120
- ✅ Prefer `regex` on the matched node's OWN text over `has` when you only need to
121
- inspect the node — avoids recursive-descendant false positives:
122
- kind: export_statement
123
- regex: '^export\s+(let|var)\b' # precise; no has-recursion FP
124
- (NAPI evaluates `regex` with JS RegExp on node.text() — keep it LINEAR so the
125
- detector can't itself ReDoS.)
126
-
127
- ⚠ String-literal regexes match SOURCE text, not the runtime string value.
128
- Inspect the exact node text before writing constraints:
129
- ast-grep run --kind string --lang ts sample.ts --json=compact
130
- Example: source `"\\|"` is node text `"\\\\|"` in JSON; to match a
131
- source-level escaped backslash (`\\`) followed by a non-backslash, the rule
132
- regex needs FOUR regex backslashes, preferably in a YAML block scalar:
133
- regex: >-
134
- ^["'`]\\\\[^\\A-Za-z0-9$]
135
- This is how `incomplete-string-escaping` catches both `"\\|"` and
136
- `'\\"'`. Avoid shell here-doc probes for this class — shell/JSON escaping
137
- can silently eat a backslash and make the rule look broken.
138
-
139
- ⚠ `-js` twins: remember there are TWO execution surfaces.
140
- - ast-grep CLI/LSP language-gates by `language:`. A `language: TypeScript`
141
- rule is not enough for standalone `.js` coverage, so shipped user-facing
142
- TS/JS rules that should fire under the ast-grep LSP usually need a `-js`
143
- twin with `language: JavaScript` plus its own fixture.
144
- - the in-process NAPI fallback (`ast-grep-napi.ts`) parses the target file's
145
- own grammar and currently runs both TS and JS rules on every jsts file. A
146
- grammar-agnostic twin can therefore duplicate in fallback mode.
147
- - **Decide explicitly:** if the rule must cover `.js` through the ast-grep
148
- CLI/LSP baseline, ship the twin and test both. If a rule is NAPI-only or
149
- fallback duplication is unacceptable, fix runner dedup/normalization before
150
- relying on a single TypeScript rule for JS coverage.
151
- - **Grammar-divergent bodies** still need separate variants regardless:
152
- e.g. `no-flag-argument` uses `required_parameter` in TS and
153
- `assignment_pattern` in JS.
154
-
155
- ✅ Node-kind facts (tree-sitter-typescript grammar — NOT the TS compiler / Roslyn):
156
- - let / const → `lexical_declaration` (var is NOT here)
157
- - var → `variable_declaration`
158
- - a regex literal's pattern text → `regex_pattern`
159
- - x[i] index access → `subscript_expression` (NOT element_access_expression)
160
- - obj.prop access → `member_expression` (NOT property_access_expression)
161
- - !x / -x / typeof x → `unary_expression`
162
- - a ? b : c → `ternary_expression`
163
-
164
- ❌ Wrong-grammar kind names = silent dead rule. `element_access_expression`,
165
- `property_access_expression`, `binary_operator`, etc. are TS-compiler/Roslyn names, not
166
- tree-sitter's. napi REJECTS the whole rule ("invalid kind matcher") so it never runs.
167
- Verify a kind exists before shipping:
168
- node -e 'import("@ast-grep/napi").then(s=>{const r=s.ts.parse("x[i]").root();
169
- const f=(n,k)=>{let c=n.kind()===k?1:0;for(const x of n.children())c+=f(x,k);return c};
170
- console.log(f(r,"subscript_expression"))})' # >0 means the kind is real
171
-
172
- ✅ Test through the REAL runner from the repo root — it loads the actual shipped
173
- rules from rules/ast-grep-rules/rules. Assert on diagnostic `rule` ids:
174
- const res = await runner.run(ctx); // ctx.filePath = temp .ts, cwd = repo
175
- For pattern/kind/regex-only rules (CLI-identical semantics) `ast-grep scan` is fine.
176
-
177
- ✅ Before shipping any text/regex detector, FP-scan the codebase:
178
- ast-grep scan -r <rule>.yml clients tools
179
- Real safe variants bite (e.g. ReDoS: (ba+)+ is safe — a mandatory prefix makes
180
- the partition unique; flag only a single quantified atom inside the group).
181
- ```
182
-
183
- ## Matching things a pattern can't express (#305)
184
-
185
- ```
186
- ❌ A parameter default is NOT a `$X = false` pattern. `pattern: $FLAG = false` parses as
187
- an `assignment_expression` (statement context) and never matches a function parameter.
188
- Match the PARAM NODE + its child literal instead, capturing the name for reuse:
189
- # TS grammar
190
- - kind: required_parameter
191
- all:
192
- - has: { field: pattern, pattern: $FLAG }
193
- - has: { any: [ { kind: "true" }, { kind: "false" } ] }
194
- # JS grammar (assignment_pattern, with fields left/right)
195
- - kind: assignment_pattern
196
- all:
197
- - has: { field: left, pattern: $FLAG }
198
- - has: { field: right, any: [ { kind: "true" }, { kind: "false" } ] }
199
-
200
- ✅ Metavar consistency works ACROSS sibling clauses of an `all` — a metavar bound in one
201
- `has` must match the SAME text everywhere it reappears. Use it to CORRELATE nodes, which
202
- is what makes a structural rule precise:
203
- all:
204
- - has: { stopBy: end, kind: required_parameter, has: { field: pattern, pattern: $FLAG } }
205
- - has: { stopBy: end, any: [ { pattern: "if ($FLAG) $$$" }, { pattern: "if (!$FLAG) $$$" } ] }
206
- This fires ONLY when the function branches on the SAME param it declared boolean — a
207
- boolean default that's never branched on, or a branch on a different var, won't match.
208
-
209
- ❌ Two `has:` keys in one mapping silently OVERWRITE (YAML: last key wins). For multiple
210
- descendant constraints use `all:` with a LIST of `has` entries, never repeated `has:`.
211
-
212
- ✅ Prefer a high-precision structural guard over an unbounded denylist. Message-chain
213
- (Demeter) floods on fluent/promise/builder chains; rather than denylist every fluent
214
- method name, REQUIRE the chain's first calls to be accessors (`get*`/`is*`/`has*`) via
215
- `constraints` regex — promise/fluent/builder methods aren't accessor-named, so they're
216
- excluded by construction. Precision over recall.
105
+ `reference.md` (same directory) covers: ReDoS-safe regex authoring, node-text
106
+ string-escape quirks, `has`/`inside` `stopBy` defaults (and when to override
107
+ them), the `-js` twin dedup behavior (#657), boolean-parameter matching
108
+ across the TS/JS grammars, and precision-over-recall heuristics for
109
+ denylist-shaped rules. Read it when a rule isn't matching (or over-matching)
110
+ the way you expect, or before shipping a `regex`/`has`-heavy rule.
217
111
 
218
112
  ## Validating a candidate rule against the REAL engine (not the warm MCP cache)
219
113