@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
@@ -18,17 +18,34 @@ pi-lens hooks into the pi agent lifecycle:
18
18
 
19
19
  ## On-write pipeline
20
20
 
21
- For each write/edit, pi-lens runs a language-aware pipeline:
22
-
23
- 1. Format queue / immediate formatting when configured.
24
- 2. Safe autofix from tools with deterministic fix support.
21
+ For each write/edit, pi-lens runs a language-aware pipeline. Format and safe
22
+ autofix (steps 1–2) run on different schedules depending on the tool:
23
+
24
+ - **`write`** (new file, full overwrite, bash-authored write): autofix runs
25
+ immediately, in the same tool result, carrying the fixed file's full content
26
+ back to the agent when it fits under the per-file cap.
27
+ - **`edit`**: autofix defers to `agent_end`, joining the same per-file queue as
28
+ deferred formatting; autofix drains before format so the final state is
29
+ formatter-stable. A `write` followed by an `edit` on the same file in the
30
+ same turn demotes the write's autofix to deferred too.
31
+
32
+ 1. Format queue / immediate formatting when configured (deferred to
33
+ `agent_end` for an `edit` even under `--immediate-format`, so it lands
34
+ after autofix reformats the file).
35
+ 2. Safe autofix from tools with deterministic fix support — immediate for
36
+ `write`, deferred to `agent_end` for `edit` (see above).
25
37
  3. LSP file sync and diagnostic wait.
26
38
  4. Parallel dispatch runners: LSP, ast-grep, tree-sitter, fact rules, and
27
- language-specific linters/security scanners.
39
+ language-specific linters/security scanners. For a deferred `edit`, these
40
+ run against the not-yet-autofixed disk state, so a lint finding autofix
41
+ would have cleared may appear here and resolve itself at `agent_end`.
28
42
  5. Cascade diagnostics for likely affected neighbors.
29
43
  6. Deduplication and routing to blockers, actionable warnings, or code-quality
30
44
  history.
31
45
 
46
+ See [`docs/agent-guide.md`](agent-guide.md#6-auto-format--auto-fix-timing--dont-be-surprised)
47
+ for the consumer-facing version of this routing.
48
+
32
49
  ## Agent tools
33
50
 
34
51
  pi-lens exposes these high-value tools to agents:
@@ -91,6 +108,8 @@ pi --lens-guard # Block git commit/push when unresolved blockers exist
91
108
  pi --no-opengrep # Disable the Opengrep security scanner (default-on auxiliary LSP)
92
109
  pi --no-read-guard # Disable the read-before-edit behavior monitor
93
110
  pi --lens-turn-summary # Persist a per-turn summary of diagnostics, autofixes, and autoformats
111
+ pi --lens-compact-tool-line # Render tool results as one compact, theme-aware line (closes #1327)
112
+ pi --no-lazy-tools # Keep every pi-lens tool active instead of activating the situational ones on demand
94
113
 
95
114
  # Actionable warnings (all default off)
96
115
  pi --lens-actionable-warnings # Report fixable warnings at turn end
@@ -141,6 +160,23 @@ second host adapter that calls the same `clients/lens-engine.ts` seam as the pi
141
160
  extension. Use `npm run build:dist` after MCP/engine changes so the user-scoped
142
161
  server loads fresh compiled code.
143
162
 
163
+ Two Claude Code hooks give the MCP mirror the same automatic cadence the pi
164
+ extension has. PostToolUse runs the per-edit pass, `Stop` runs the per-turn one:
165
+
166
+ ```json
167
+ { "hooks": {
168
+ "PostToolUse": [
169
+ { "matcher": "Edit|Write",
170
+ "hooks": [ { "type": "command", "command": "pi-lens-analyze --hook" } ] } ],
171
+ "Stop": [
172
+ { "hooks": [ { "type": "command", "command": "pi-lens-analyze --turn-end", "timeout": 60 } ] } ]
173
+ } }
174
+ ```
175
+
176
+ The `Stop` hook only works against a running MCP server because only that
177
+ process owns the session state and pending turn work. With no warm server the
178
+ turn-end is skipped, not faked: one line on stderr and nothing on stdout.
179
+
144
180
  ## Troubleshooting
145
181
 
146
182
  - Run `npm run build` before tests after editing TypeScript; tests import
@@ -42,6 +42,41 @@ HTTPServer → httpserver, http, server
42
42
  each through `splitIdentifier`; queries are tokenized the same way so
43
43
  `"authenticate user"` matches `authenticateUser`, `auth_user`, etc.
44
44
 
45
+ ## Query prefix filters (#1450)
46
+
47
+ The query string can mix plain terms with composable `key:value` filters,
48
+ parsed by `parseWordIndexQuery` (`clients/word-index.ts`) before tokenization:
49
+
50
+ - `lang:<kind>` — restrict to a file kind's extensions, drawn from
51
+ `KIND_EXTENSIONS` (`clients/file-kinds.ts`) — the single source of truth
52
+ (#894 invariant), so there is no second, hand-maintained language list here.
53
+ - `file:<substr>` — path substring match against the index's own stored path,
54
+ normalized through `wordIndexKey` (case-folded on win32 only, matching how
55
+ the index's own path-keyed maps already fold win32 paths, #1025).
56
+ - `ext:<ext>` — literal extension match (`ext:ts` and `ext:.ts` are
57
+ equivalent).
58
+ - Any filter may be negated with a leading `-` (`-file:test`).
59
+
60
+ Example: `lang:jsts file:clients/ -file:test rank`. Multiple positive filters
61
+ of the SAME key OR together; different keys AND; negations always subtract.
62
+ Filters are applied as a `fileFilter` predicate BEFORE BM25/priors/centrality
63
+ scoring (same seam as the pre-existing `paths`/`lang` structured options,
64
+ #771), so a surviving file's score is unaffected by filtering. An unrecognized
65
+ `key:` token passes through as an ordinary search term (colon-bearing terms
66
+ like `std::vector`, URLs, and Windows paths search normally). Only a
67
+ recognized key with a bad value — an unrecognized `lang:` kind — throws
68
+ `WordIndexQueryError`
69
+ naming the supported list — never silently falls through as a literal search
70
+ term. Both the pi `symbol_search` tool and the MCP `pilens_symbol_search`
71
+ mirror inherit the syntax for free since both pass their `query` argument
72
+ straight through to `searchWordIndex` (the entry point where parsing happens).
73
+
74
+ DF-normalization: BM25's per-token `idf` is computed from the FULL,
75
+ unfiltered posting for that token before `fileFilter` is applied per
76
+ candidate file — a filtered query reuses the global, corpus-wide document
77
+ frequency rather than recomputing it over the filtered subset. Documented as
78
+ an acceptable approximation (see the doc comment on `searchWordIndex`).
79
+
45
80
  ## Lifecycle
46
81
 
47
82
  The index is built/refreshed in **every** startup mode: load the persisted
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "3.8.74",
3
+ "version": "4.0.1",
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
+ }
@@ -0,0 +1,8 @@
1
+ id: no-chained-type-assertions
2
+ valid:
3
+ - 'const v = x as A'
4
+ - 'const w = x as A as const'
5
+ - 'const u = x as unknown as Foo'
6
+ invalid:
7
+ - 'const v = x as A as B'
8
+ - 'const v = foo.bar() as A as B;'
@@ -0,0 +1,9 @@
1
+ id: no-conditional-empty-object-spread-js
2
+ valid:
3
+ - 'const o = {...(cond ? {a: 1} : {b: 2})}'
4
+ - 'const o = {...base}'
5
+ - 'const o = {...(cond ? extra : other)}'
6
+ invalid:
7
+ - 'const o = {...(cond ? {} : {x})}'
8
+ - 'const o = {...(cond ? {y} : {})}'
9
+ - 'const o = {...cond ? {} : {x}}'
@@ -0,0 +1,9 @@
1
+ id: no-conditional-empty-object-spread
2
+ valid:
3
+ - 'const o = {...(cond ? {a: 1} : {b: 2})}'
4
+ - 'const o = {...base}'
5
+ - 'const o = {...(cond ? extra : other)}'
6
+ invalid:
7
+ - 'const o = {...(cond ? {} : {x})}'
8
+ - 'const o = {...(cond ? {y} : {})}'
9
+ - 'const o = {...cond ? {} : {x}}'
@@ -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,7 @@
1
+ id: no-reflect-apply-js
2
+ valid:
3
+ - 'fn.apply(obj, args)'
4
+ - 'fn(...args)'
5
+ invalid:
6
+ - 'Reflect.apply(fn, obj, args)'
7
+ - 'const r = Reflect.apply(target[method], target, argumentsList);'
@@ -0,0 +1,7 @@
1
+ id: no-reflect-apply
2
+ valid:
3
+ - 'fn.apply(obj, args)'
4
+ - 'fn(...args)'
5
+ invalid:
6
+ - 'Reflect.apply(fn, obj, args)'
7
+ - 'const r = Reflect.apply(target[method], target, argumentsList);'
@@ -0,0 +1,8 @@
1
+ id: no-reflect-get-js
2
+ valid:
3
+ - 'obj.prop'
4
+ - 'obj[key]'
5
+ - 'Reflect.get(target, prop, receiver)'
6
+ invalid:
7
+ - 'Reflect.get(obj, "key")'
8
+ - 'const v = Reflect.get(target, prop);'
@@ -0,0 +1,8 @@
1
+ id: no-reflect-get
2
+ valid:
3
+ - 'obj.prop'
4
+ - 'obj[key]'
5
+ - 'Reflect.get(target, prop, receiver)'
6
+ invalid:
7
+ - 'Reflect.get(obj, "key")'
8
+ - 'const v = Reflect.get(target, prop);'
@@ -0,0 +1,11 @@
1
+ id: no-unknown-laundering
2
+ valid:
3
+ - 'const x: unknown = foo()'
4
+ - 'function f(x: unknown) {}'
5
+ - 'function isValidPayload(data: unknown): data is Foo { return true; }'
6
+ - 'function f(cause: unknown) {}'
7
+ - 'type X = { a: unknown }'
8
+ - 'type Y = Record<string, unknown>'
9
+ invalid:
10
+ - 'type X = unknown'
11
+ - 'type LooseHandler = unknown'
@@ -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:"
@@ -0,0 +1,25 @@
1
+ id: no-bare-host-path-in-win32-branch
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use win32 path operations inside a branch committed to Windows path semantics"
5
+ note: |
6
+ A branch guarded by isWindowsPath/isFullyQualifiedWin32 has committed to
7
+ Windows path parsing. Host-default dirname/basename/join/relative/sep
8
+ follows the process OS and is wrong for a Windows-shaped path on POSIX.
9
+
10
+ SCOPE (review-verified, #1339): same-function consequence only. The
11
+ original #1150 defect — a bare dirname inside an UNGUARDED helper that
12
+ the guarded branch calls (cross-function) — is NOT reachable by this
13
+ engine and stays review-enforced via AGENTS.md defect-shape 2. The
14
+ rule's fixture file encodes that miss as a documented negative; flip it
15
+ to a positive only if cross-function reach is ever added. What this
16
+ rule does catch is the in-branch shape (#1152 class).
17
+ rule:
18
+ all:
19
+ - kind: if_statement
20
+ - has:
21
+ field: condition
22
+ regex: "\\b(isWindowsPath|isFullyQualifiedWin32)\\s*\\("
23
+ - has:
24
+ field: consequence
25
+ regex: "(?s)\\bpath\\.(dirname|basename|join|relative)\\s*\\(|\\bpath\\.sep\\b|(^|[^.])\\b(dirname|basename|join|relative)\\s*\\("
@@ -0,0 +1,21 @@
1
+ id: no-chained-type-assertions
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Chained type assertion (x as A as B) — stacking assertions with no runtime check between them"
5
+ note: |
6
+ `x as A as B` stacks two assertions with no runtime check between
7
+ them, so the compiler can't verify either hop. Narrow with a type
8
+ guard, or assert once to the type you actually need. `as const`
9
+ chains are allowed — they don't launder the value's type, they just
10
+ freeze a literal. `as unknown as B` chains are also allowed: routing
11
+ a cast through `unknown` (rather than through an unrelated concrete
12
+ type) is the standard, TypeScript-endorsed way to perform a cast that
13
+ the compiler can't otherwise verify as safe — see no-as-any.yml's own
14
+ note, which recommends this exact form as the safe replacement for
15
+ `as any`. An FP-scan of the pi-lens codebase found 22 chained-assertion
16
+ hits and all 22 were `as unknown as`; none were a genuine two
17
+ concrete-type stack.
18
+ rule:
19
+ pattern: $X as $A as $B
20
+ not:
21
+ pattern: "$X as unknown as $B"
@@ -0,0 +1,21 @@
1
+ id: no-conditional-empty-object-spread-js
2
+ language: JavaScript
3
+ severity: hint
4
+ message: "Conditional empty-object spread — use an explicit if/else to omit fields"
5
+ note: |
6
+ `{...(cond ? {} : {x})}` hides field omission behind a ternary — an
7
+ explicit `if` reads better. Matched structurally (spread of a ternary
8
+ whose consequence or alternative is an empty object literal), not by
9
+ exact source text, so it also catches the un-parenthesized form.
10
+
11
+ Kept at `hint` to match no-conditional-empty-object-spread.yml — see
12
+ that rule's note for the FP-scan finding (147 hits, all a deliberate
13
+ existing idiom).
14
+ rule:
15
+ kind: spread_element
16
+ has:
17
+ stopBy: end
18
+ kind: ternary_expression
19
+ any:
20
+ - has: { field: consequence, stopBy: neighbor, kind: object, regex: '^\{\s*\}$' }
21
+ - has: { field: alternative, stopBy: neighbor, kind: object, regex: '^\{\s*\}$' }
@@ -0,0 +1,29 @@
1
+ id: no-conditional-empty-object-spread
2
+ language: TypeScript
3
+ severity: hint
4
+ message: "Conditional empty-object spread — use an explicit if/else to omit fields"
5
+ note: |
6
+ `{...(cond ? {} : {x})}` hides field omission behind a ternary — an
7
+ explicit `if` reads better and lets the object's shape stay typed.
8
+ Matched structurally (spread of a ternary whose consequence or
9
+ alternative is an empty object literal), not by exact source text, so
10
+ it also catches the un-parenthesized and multi-property-spread forms.
11
+
12
+ FP-scan finding (pi-lens codebase, clients/ tools/ mcp/ index.ts): 147
13
+ hits, and every sampled hit is this exact shape used deliberately to
14
+ build an optional-field object (`...(hit.annotations ? {
15
+ annotations: hit.annotations } : {})`). That is a real, established
16
+ idiom here, not a shape mismatch — so the rule stays at `hint` rather
17
+ than `warning`: still discoverable in a scan, but it does not read as
18
+ a call to fix 147 existing sites.
19
+ rule:
20
+ kind: spread_element
21
+ has:
22
+ # the ternary sits under a parenthesized_expression when the source
23
+ # parenthesizes it, so this needs stopBy: end — spread_element's
24
+ # DIRECT child is not the ternary in that (common) form.
25
+ stopBy: end
26
+ kind: ternary_expression
27
+ any:
28
+ - has: { field: consequence, stopBy: neighbor, kind: object, regex: '^\{\s*\}$' }
29
+ - has: { field: alternative, stopBy: neighbor, kind: object, regex: '^\{\s*\}$' }
@@ -8,6 +8,12 @@ note: |
8
8
  rule:
9
9
  all:
10
10
  - kind: jsx_element
11
+ - has:
12
+ field: open_tag
13
+ kind: jsx_opening_element
14
+ has:
15
+ field: name
16
+ regex: "^a$"
11
17
  - has:
12
18
  kind: jsx_element
13
19
  stopBy: end
@@ -20,6 +26,7 @@ rule:
20
26
  not:
21
27
  inside:
22
28
  kind: jsx_element
29
+ stopBy: end
23
30
  has:
24
31
  field: open_tag
25
32
  kind: jsx_opening_element
@@ -0,0 +1,30 @@
1
+ id: no-raw-json-store-write
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use the atomic write seam for JSON stores instead of writeFileSync"
5
+ note: |
6
+ JSON-backed durable state must be committed through clients/durable-store.ts
7
+ when read-modify-write isolation is required; that seam publishes through
8
+ clients/atomic-write.ts. This rule covers the direct
9
+ writeFileSync(path, JSON.stringify(data)) shape. Build scripts and the
10
+ installer's wx lock writes are intentionally outside this rule's scope.
11
+ ignores:
12
+ - "**/clients/atomic-write.ts"
13
+ - "**/clients/installer/index.ts"
14
+ - "**/scripts/**"
15
+ rule:
16
+ any:
17
+ - pattern: writeFileSync($PATH, JSON.stringify($$$ARGS))
18
+ - pattern: $FS.writeFileSync($PATH, JSON.stringify($$$ARGS))
19
+ - pattern: writeFileSync($PATH, JSON.stringify($$$ARGS), $OPTS)
20
+ - pattern: $FS.writeFileSync($PATH, JSON.stringify($$$ARGS), $OPTS)
21
+ - kind: expression_statement
22
+ has:
23
+ pattern: writeFileSync($PATH, $PAYLOAD)
24
+ follows:
25
+ pattern: const $PAYLOAD = JSON.stringify($DATA)
26
+ - kind: expression_statement
27
+ has:
28
+ pattern: $FS.writeFileSync($PATH, $PAYLOAD)
29
+ follows:
30
+ pattern: const $PAYLOAD = JSON.stringify($DATA)
@@ -0,0 +1,9 @@
1
+ id: no-reflect-apply-js
2
+ language: JavaScript
3
+ severity: warning
4
+ message: "Reflect.apply() — use a typed call (fn.apply(...) or fn(...args)) instead"
5
+ note: |
6
+ Reflect.apply reaches for reflection where a typed call already works.
7
+ Prefer Function#apply or a direct call with spread arguments.
8
+ rule:
9
+ pattern: Reflect.apply($$$ARGS)
@@ -0,0 +1,9 @@
1
+ id: no-reflect-apply
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Reflect.apply() — use a typed call (fn.apply(...) or fn(...args)) instead"
5
+ note: |
6
+ Reflect.apply reaches for reflection where a typed call already works.
7
+ Prefer Function#apply or a direct call with spread arguments.
8
+ rule:
9
+ pattern: Reflect.apply($$$ARGS)