@herbertgao/pi-extensions 2026.8.13 → 2026.8.15

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 (263) hide show
  1. package/README.md +19 -14
  2. package/THIRD_PARTY_NOTICES.md +0 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +19 -0
  4. package/node_modules/@czottmann/pi-automode/README.md +5 -1
  5. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +126 -13
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +7 -3
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +2 -0
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +134 -23
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@herbertgao/{pi-stash → pi-bark}/LICENSE +4 -5
  11. package/node_modules/@herbertgao/pi-bark/README.md +44 -0
  12. package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
  13. package/node_modules/@herbertgao/pi-bark/package.json +53 -0
  14. package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
  15. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  16. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  19. package/node_modules/@herbertgao/pi-subagents/package.json +2 -1
  20. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +8 -1
  21. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  22. package/node_modules/@narumitw/pi-btw/README.md +125 -88
  23. package/node_modules/@narumitw/pi-btw/dist/index.ts +140 -45
  24. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
  25. package/node_modules/@narumitw/pi-btw/package.json +5 -8
  26. package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
  27. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +106 -17
  28. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
  29. package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
  30. package/node_modules/@tifan/pi-handoff/README.md +36 -13
  31. package/node_modules/@tifan/pi-handoff/package.json +2 -2
  32. package/node_modules/@tifan/pi-handoff/src/index.ts +160 -4
  33. package/node_modules/@tifan/pi-preferred-thinking/README.md +1 -1
  34. package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
  35. package/node_modules/@tifan/pi-preferred-thinking/src/index.ts +15 -2
  36. package/node_modules/@tifan/pi-recap/README.md +1 -1
  37. package/node_modules/@tifan/pi-recap/package.json +2 -2
  38. package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
  39. package/node_modules/@tifan/pi-rename/README.md +42 -21
  40. package/node_modules/@tifan/pi-rename/package.json +1 -1
  41. package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
  42. package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
  43. package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
  44. package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
  45. package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
  46. package/node_modules/pi-lens/CHANGELOG.md +320 -0
  47. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
  48. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
  50. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
  51. package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
  52. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
  53. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
  54. package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
  55. package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
  56. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
  57. package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
  58. package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
  59. package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
  60. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
  61. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
  63. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
  64. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
  68. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
  69. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
  85. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
  86. package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
  87. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
  88. package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
  89. package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
  90. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
  91. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
  92. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
  93. package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
  94. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
  95. package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
  96. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
  97. package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
  98. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  99. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
  100. package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
  101. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  102. package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
  103. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
  104. package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
  105. package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
  106. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
  107. package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
  108. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
  109. package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
  110. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
  111. package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
  112. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
  113. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
  114. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
  115. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
  116. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
  117. package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
  118. package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
  119. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
  120. package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
  121. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
  122. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
  123. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
  124. package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
  125. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
  126. package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
  127. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
  128. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
  129. package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
  130. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
  131. package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
  132. package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
  133. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
  134. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
  135. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
  136. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
  137. package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
  138. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
  139. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
  140. package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
  141. package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
  142. package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
  143. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
  144. package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
  145. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
  146. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
  147. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
  148. package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
  149. package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
  150. package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
  151. package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
  152. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  153. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
  154. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
  155. package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
  156. package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
  157. package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
  158. package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
  159. package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
  160. package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
  161. package/node_modules/pi-lens/dist/index.js +11243 -6651
  162. package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
  163. package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
  164. package/node_modules/pi-lens/dist/mcp/server.js +6 -1
  165. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
  166. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
  167. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  168. package/node_modules/pi-lens/docs/agent-guide.md +4 -2
  169. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
  170. package/node_modules/pi-lens/docs/astplayground.md +26 -17
  171. package/node_modules/pi-lens/package.json +5 -1
  172. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
  173. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
  174. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
  175. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
  176. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
  177. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
  178. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
  179. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
  180. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
  181. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
  182. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
  183. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
  184. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
  185. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
  186. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
  187. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
  188. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
  189. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
  190. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
  191. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
  192. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
  193. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
  194. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
  195. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
  196. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
  197. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
  198. package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
  199. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
  200. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
  201. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
  202. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
  203. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
  204. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
  205. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
  206. package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
  207. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
  208. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
  209. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
  210. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
  211. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
  212. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
  213. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
  214. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
  215. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
  216. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
  217. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
  218. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
  219. package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
  220. package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
  221. package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
  222. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
  223. package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
  224. package/node_modules/pi-mcp-adapter/CHANGELOG.md +35 -0
  225. package/node_modules/pi-mcp-adapter/README.md +5 -2
  226. package/node_modules/pi-mcp-adapter/cli.js +4 -4
  227. package/node_modules/pi-mcp-adapter/commands.ts +1 -1
  228. package/node_modules/pi-mcp-adapter/config.ts +1 -0
  229. package/node_modules/pi-mcp-adapter/dist/config.js +1 -0
  230. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  231. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +24 -0
  232. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +336 -0
  233. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -0
  234. package/node_modules/pi-mcp-adapter/dist/types.d.ts +3 -1
  235. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  236. package/node_modules/pi-mcp-adapter/index.ts +90 -6
  237. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +124 -32
  238. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -2
  239. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +89 -0
  240. package/node_modules/pi-mcp-adapter/mcp-references.ts +9 -1
  241. package/node_modules/pi-mcp-adapter/package.json +1 -1
  242. package/node_modules/pi-mcp-adapter/proxy-modes.ts +62 -22
  243. package/node_modules/pi-mcp-adapter/request-headers-command.ts +1 -1
  244. package/node_modules/pi-mcp-adapter/server-manager.ts +6 -0
  245. package/node_modules/pi-mcp-adapter/types.ts +3 -1
  246. package/node_modules/pi-web-access/CHANGELOG.md +42 -0
  247. package/node_modules/pi-web-access/README.md +26 -10
  248. package/node_modules/pi-web-access/curator-page.ts +12 -3
  249. package/node_modules/pi-web-access/curator-server.ts +3 -1
  250. package/node_modules/pi-web-access/extract.ts +88 -13
  251. package/node_modules/pi-web-access/gemini-search.ts +10 -5
  252. package/node_modules/pi-web-access/github-extract.ts +47 -3
  253. package/node_modules/pi-web-access/index.ts +40 -40
  254. package/node_modules/pi-web-access/package.json +1 -1
  255. package/node_modules/pi-web-access/page-query.ts +61 -8
  256. package/node_modules/pi-web-access/xcrawl.ts +264 -0
  257. package/package.json +19 -22
  258. package/node_modules/@herbertgao/pi-stash/README.md +0 -34
  259. package/node_modules/@herbertgao/pi-stash/package.json +0 -51
  260. package/node_modules/@herbertgao/pi-stash/src/index.ts +0 -118
  261. package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
  262. package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
  263. package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
@@ -50,22 +50,23 @@ export function computeBuildStamp(entryPath, stat = realStat) {
50
50
  const info = stat(entryPath);
51
51
  if (!info)
52
52
  return undefined;
53
- return { entryPath, mtimeMs: info.mtimeMs };
53
+ return { entryPath, mtimeMs: info.mtimeMs, size: info.size };
54
54
  }
55
55
  /**
56
56
  * One-shot comparison: re-stats `stamp.entryPath` and compares against the
57
- * startup mtime. A failed re-stat (file briefly missing mid-rebuild-write, or
58
- * an installed-package layout) is treated as NOT stale — we only ever flag a
59
- * build change we can positively observe, never assume one from an I/O
60
- * hiccup (false positives would make every tool call route to fresh, which
61
- * defeats the warm server's entire purpose).
57
+ * startup mtime AND size (#2300 — a same-mtime-bucket rebuild that changes
58
+ * the bundle's length is otherwise invisible). A failed re-stat (file briefly
59
+ * missing mid-rebuild-write, or an installed-package layout) is treated as
60
+ * NOT stale — we only ever flag a build change we can positively observe,
61
+ * never assume one from an I/O hiccup (false positives would make every tool
62
+ * call route to fresh, which defeats the warm server's entire purpose).
62
63
  */
63
64
  export function checkStaleness(stamp, stat = realStat) {
64
65
  const info = stat(stamp.entryPath);
65
66
  if (!info)
66
67
  return { stale: false, stamp, currentMtimeMs: undefined };
67
68
  return {
68
- stale: info.mtimeMs !== stamp.mtimeMs,
69
+ stale: info.mtimeMs !== stamp.mtimeMs || info.size !== stamp.size,
69
70
  stamp,
70
71
  currentMtimeMs: info.mtimeMs,
71
72
  };
@@ -47,7 +47,9 @@ async function buildGraph() {
47
47
  if (!stat.isDirectory())
48
48
  fail(`cwd is not a directory: ${cwd}`);
49
49
  const startedAt = Date.now();
50
- const graph = await buildOrUpdateGraph(cwd, [], new FactStore());
50
+ // Subject labels this store's capacity-eviction telemetry distinctly from
51
+ // the other five production FactStore instances (#2243 review round 3, F1).
52
+ const graph = await buildOrUpdateGraph(cwd, [], new FactStore("mcp-cli"));
51
53
  const attempt = getLastReviewGraphBuildAttempt(cwd);
52
54
  // "succeeded" can still carry a `reason` string — persistGraph() stamps one
53
55
  // on to explain a partial (over-cap) persist, which is NOT a failure (#960
@@ -32,6 +32,7 @@ import { createLensDiagnosticsTool } from "../tools/lens-diagnostics.js";
32
32
  import { peekMcpSessionRuntime } from "../clients/mcp/session.js";
33
33
  import { createLspDiagnosticsTool } from "../tools/lsp-diagnostics.js";
34
34
  import { createLspNavigationTool } from "../tools/lsp-navigation.js";
35
+ import { shouldInitializeSessionRoot } from "../clients/lsp/session-roots.js";
35
36
  import { computeBuildStamp, STALE_SERVED_BY_FRESH, STALE_WARN_ONLY, StalenessGate, stalenessCheckEnabled, } from "./build-staleness.js";
36
37
  // Any stray stdout write corrupts the JSON-RPC stream; force it onto stderr.
37
38
  console.log = (...args) => {
@@ -131,8 +132,12 @@ const REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT
131
132
  : findRepoRoot(SERVER_DIR);
132
133
  async function ensureReady(cwd) {
133
134
  const normalized = path.resolve(cwd);
134
- if (lspReadyCwds.has(normalized))
135
+ // #2052 R1: the session-root registry evicts old roots at a fixed cap. The
136
+ // readiness memo is only a fast path; consult the registry so an evicted
137
+ // root can initialize again when it is used later.
138
+ if (!shouldInitializeSessionRoot(normalized, lspReadyCwds)) {
135
139
  return;
140
+ }
136
141
  try {
137
142
  await ensureLspConfig(normalized);
138
143
  // pi-lens-ignore: missing-error-propagation
@@ -929,6 +929,9 @@ const UNCONFIRMED_REASON_SENTENCE = {
929
929
  // completely different failure from a timeout. Must never collapse into
930
930
  // "within budget", the exact string this PR exists to stop misrendering.
931
931
  binding_mismatch: "the file changed on disk since this result was computed",
932
+ // #2052: no server was asked. Say WHY and say it is permanent for this
933
+ // path, so the reader does not read an empty result as "clean" or retry it.
934
+ outside_project_root: "the file is outside every initialized session project root, so no language server was asked",
932
935
  };
933
936
  // Short per-reason label used only when MORE than one reason is present, so
934
937
  // the note can still say "N timed out, M errored" instead of collapsing to
@@ -940,6 +943,7 @@ const UNCONFIRMED_REASON_COUNT_LABEL = {
940
943
  service_destroyed: "service reset mid-sweep",
941
944
  error: "errored",
942
945
  binding_mismatch: "stale binding",
946
+ outside_project_root: "outside project root",
943
947
  };
944
948
  /**
945
949
  * #1618: a result predating the `unconfirmedReason` field (a legacy/test
@@ -559,6 +559,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
559
559
  return {
560
560
  diagnostics: filtered,
561
561
  timedOut,
562
+ skipReason: touched?.skipReason,
562
563
  confirmedByTouch,
563
564
  // #1470: only a touch actually contributes a coverage gap; the
564
565
  // openFile+getDiagnostics fallback never reports one, which is honest —
@@ -813,7 +814,7 @@ onConfirmedNoBlockers) {
813
814
  };
814
815
  }
815
816
  }
816
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
817
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
817
818
  const health = lspService.getDiagnosticsHealth?.(file);
818
819
  // #570: a timed-out priming check is never a confirmed "clean" — treat it
819
820
  // as unconfirmed without consulting the (unrelated) silent-tier
@@ -824,7 +825,10 @@ onConfirmedNoBlockers) {
824
825
  // be merged in rather than discarded.
825
826
  let effectiveRawDiags = rawDiags;
826
827
  let confirmation;
827
- if (timedOut) {
828
+ if (skipReason !== undefined) {
829
+ confirmation = "unconfirmed";
830
+ }
831
+ else if (timedOut) {
828
832
  if (applySeverityFilter(rawDiags, severity).length === 0) {
829
833
  confirmation = "unconfirmed";
830
834
  }
@@ -887,7 +891,7 @@ onConfirmedNoBlockers) {
887
891
  unconfirmedServerIds.length === 0) {
888
892
  cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
889
893
  ? hashDiagnosticContent(collectedContent)
890
- : undefined);
894
+ : undefined, stat.size);
891
895
  }
892
896
  return {
893
897
  file,
@@ -895,6 +899,7 @@ onConfirmedNoBlockers) {
895
899
  unavailable: lspUnavailableMessage(file, health),
896
900
  confirmation,
897
901
  timedOut: confirmation === "unconfirmed" ? timedOut : undefined,
902
+ ...(skipReason !== undefined && { skipReason }),
898
903
  primaryServerId: primaryServerId(file),
899
904
  };
900
905
  }
@@ -905,7 +910,7 @@ onConfirmedNoBlockers) {
905
910
  // Reserve the token before awaiting this file's LSP result. The direct-file
906
911
  // path performs its own confirmation/reconciliation below (#1198).
907
912
  const writeIndex = nextWriteIndex?.();
908
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
913
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
909
914
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
910
915
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
911
916
  // #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
@@ -919,7 +924,10 @@ onConfirmedNoBlockers) {
919
924
  // diagnostics it surfaces are merged in, not discarded.
920
925
  let effectiveRawDiags = rawDiags;
921
926
  let confirmation;
922
- if (timedOut) {
927
+ if (skipReason !== undefined) {
928
+ confirmation = "unconfirmed";
929
+ }
930
+ else if (timedOut) {
923
931
  if (applySeverityFilter(rawDiags, severity).length === 0) {
924
932
  confirmation = "unconfirmed";
925
933
  }
@@ -1060,6 +1068,7 @@ onConfirmedNoBlockers) {
1060
1068
  truncated,
1061
1069
  unconfirmed,
1062
1070
  timedOut: unconfirmed ? timedOut : undefined,
1071
+ ...(skipReason !== undefined && { skipReason }),
1063
1072
  // #1470: which servers this result does NOT speak for. Absent when it
1064
1073
  // speaks for all of them.
1065
1074
  ...(unconfirmedServerIds.length > 0 && {
@@ -467,6 +467,8 @@ function formatCapabilities(snapshots, filePath) {
467
467
  ["hover", (s) => !!s.operationSupport.hover],
468
468
  ["rename", (s) => !!s.operationSupport.rename],
469
469
  ["codeAction", (s) => !!s.operationSupport.codeAction],
470
+ ["codeAction/resolve", (s) => !!s.operationSupport.codeActionResolve],
471
+ ["workspace/willRenameFiles", (s) => !!s.operationSupport.willRenameFiles],
470
472
  ["workspaceSymbol", (s) => !!s.operationSupport.workspaceSymbol],
471
473
  ["implementation", (s) => !!s.operationSupport.implementation],
472
474
  ["signatureHelp", (s) => !!s.operationSupport.signatureHelp],
@@ -480,7 +482,7 @@ function formatCapabilities(snapshots, filePath) {
480
482
  [
481
483
  "rename_file",
482
484
  () => true,
483
- "willRenameFiles/didRenameFiles helper available",
485
+ "filesystem rename; preflight support shown above",
484
486
  ],
485
487
  ];
486
488
  const lines = [];
@@ -143,8 +143,10 @@ Edits fail or warn when:
143
143
 
144
144
  A blocked edit returns a retryable message, e.g.:
145
145
 
146
- > `🔄 RETRYABLE — Edit without read … Read the file first, then retry the edit:
147
- > read path="<file>"`
146
+ ```text
147
+ 🔄 RETRYABLE — Edit without read: you have not read `<file>` in this
148
+ conversation. Read it first, then retry: `read path="<file>"`.
149
+ ```
148
150
 
149
151
  **Correct behavior:** read the file (or the relevant range/symbol) *before* editing.
150
152
  Helpful mechanics you can rely on:
@@ -2,16 +2,17 @@
2
2
 
3
3
  <!-- GENERATED by scripts/gen-rule-catalogs.mjs — do not edit by hand. Run `npm run docs:rule-catalogs` after changing rules. -->
4
4
 
5
- pi-lens bundles **455 enabled** ast-grep rules (271 pi-lens-authored + 184 vendored from CodeRabbit) across **16 languages**, plus 16 disabled.
5
+ pi-lens bundles **481 enabled** ast-grep rules (297 pi-lens-authored + 184 vendored from CodeRabbit) across **17 languages**, plus 16 disabled.
6
6
 
7
7
  See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `rules/ast-grep-rules/rules/` (pi-lens), `rules/ast-grep-rules/coderabbit/rules/` (vendored).
8
8
 
9
9
  ## Enabled rules
10
10
 
11
- ### C (9)
11
+ ### C (10)
12
12
 
13
13
  | Rule | Severity | Fixable | Source | Description |
14
14
  |---|---|---|---|---|
15
+ | `c-no-malloc-free` | warning | no | pi-lens | Manual malloc/calloc/realloc/free; verify ownership and error handling |
15
16
  | `dont-call-system-c` | warning | no | CodeRabbit | Don't call `system`. It's a high-level wrapper that allows for stacking multiple commands. Always prefer a more restrictive API such as cal… |
16
17
  | `file-access-before-action-c` | warning | no | CodeRabbit | A check is done with `access` and then the file is later used. There is no guarantee that the status of the file has not changed since the… |
17
18
  | `file-stat-before-action-c` | warning | no | CodeRabbit | A check is done with `stat` and then the file is used. There is no guarantee that the status of the file has not changed since the call to… |
@@ -22,10 +23,12 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
22
23
  | `small-key-size-c` | warning | no | CodeRabbit | $KEY_FUNCTION` is using a key size of only $KEY_BITS bits. This is less than the recommended key size of 2048 bits. |
23
24
  | `world-writable-file-c` | warning | no | CodeRabbit | This call makes a world-writable file which allows any user on a machine to write to the file. This may allow attackers to influence the be… |
24
25
 
25
- ### C++ (15)
26
+ ### C++ (17)
26
27
 
27
28
  | Rule | Severity | Fixable | Source | Description |
28
29
  |---|---|---|---|---|
30
+ | `cpp-no-malloc-free` | warning | no | pi-lens | Manual malloc/calloc/realloc/free in C++; consider RAII (new/unique_ptr/vector) |
31
+ | `cpp-no-printf` | warning | no | pi-lens | Avoid printf in C++; prefer std::cout/std::format for type-safe formatting |
29
32
  | `dont-call-system-cpp` | warning | no | CodeRabbit | Don't call `system`. It's a high-level wrapper that allows for stacking multiple commands. Always prefer a more restrictive API such as cal… |
30
33
  | `file-access-before-action-cpp` | warning | no | CodeRabbit | A check is done with `access` and then the file is later used. There is no guarantee that the status of the file has not changed since the… |
31
34
  | `file-stat-before-action-cpp` | warning | no | CodeRabbit | A check is done with `stat` and then the file is used. There is no guarantee that the status of the file has not changed since the call to… |
@@ -58,13 +61,24 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
58
61
  | `stacktrace-disclosure-csharp` | warning | no | CodeRabbit | Stacktrace information is displayed in a non-Development environment. Accidentally disclosing sensitive stack trace information in a produc… |
59
62
  | `use-ecb-mode-csharp` | warning | no | CodeRabbit | "Usage of the insecure ECB mode detected. You should use an authenticated encryption mode instead, which is implemented by the classes AesG… |
60
63
 
61
- ### Go (23)
64
+ ### CSS (1)
65
+
66
+ | Rule | Severity | Fixable | Source | Description |
67
+ |---|---|---|---|---|
68
+ | `no-important` | warning | no | pi-lens | Avoid `!important` — increase selector specificity instead |
69
+
70
+ ### Go (28)
62
71
 
63
72
  | Rule | Severity | Fixable | Source | Description |
64
73
  |---|---|---|---|---|
65
74
  | `avoid-bind-to-all-interfaces-go` | warning | no | CodeRabbit | "Detected a network listener listening on 0.0.0.0 or an empty string. This could unexpectedly expose the server publicly as it binds to all… |
66
75
  | `defer-in-loop` | warning | no | pi-lens | defer is used inside a loop. This defers execution until after the entire function, not each iteration. |
67
76
  | `go-defer-func-call-antipattern` | warning | yes | pi-lens | defer with function-call args — arguments are evaluated immediately, not at defer time |
77
+ | `go-no-fmt-println` | warning | no | pi-lens | Avoid fmt.Println/Printf-family calls in production code; use a structured logger |
78
+ | `go-no-panic-in-lib` | warning | no | pi-lens | Avoid panic() in library code; return errors instead |
79
+ | `go-no-underscore-func-name` | hint | no | pi-lens | Use camelCase for Go function names, not snake_case |
80
+ | `go-prefer-errors-is` | hint | no | pi-lens | Use errors.Is(err, target) instead of direct comparison for error checking |
81
+ | `go-prefer-string-builder` | hint | no | pi-lens | Use strings.Builder instead of repeated string concatenation |
68
82
  | `go-test-functions` | warning | yes | pi-lens | Go test function detected — verify it follows the project's test naming convention |
69
83
  | `gorilla-cookie-store-hardcoded-session-key-go` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
70
84
  | `gorilla-csrf-hardcoded-auth-key-go` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
@@ -92,7 +106,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
92
106
  |---|---|---|---|---|
93
107
  | `plaintext-http-link-html` | warning | no | CodeRabbit | "This link points to a plaintext HTTP URL. Prefer an encrypted HTTPS URL if possible." |
94
108
 
95
- ### Java (36)
109
+ ### Java (40)
96
110
 
97
111
  | Rule | Severity | Fixable | Source | Description |
98
112
  |---|---|---|---|---|
@@ -118,7 +132,11 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
118
132
  | `missing-httponly-java` | warning | no | CodeRabbit | Detected a cookie where the `HttpOnly` flag is either missing or disabled. The `HttpOnly` cookie flag instructs the browser to forbid clien… |
119
133
  | `missing-secure-java` | warning | no | CodeRabbit | Detected a cookie where the `Secure` flag is either missing or disabled. The `Secure` cookie flag instructs the browser to forbid sending t… |
120
134
  | `no-null-cipher-java` | warning | no | CodeRabbit | NullCipher was detected. This will not encrypt anything; the cipher text will be the same as the plain text. Use a valid, secure cipher: Ci… |
135
+ | `no-raw-types` | warning | no | pi-lens | Use parameterized types instead of raw types. |
136
+ | `no-string-concat-in-loop` | warning | no | pi-lens | Use StringBuilder instead of string concatenation in loops. |
137
+ | `no-system-out-println` | warning | no | pi-lens | Avoid System.out.println; use a logging framework instead. |
121
138
  | `passwordauthentication-hardcoded-password-java` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
139
+ | `prefer-string-is-empty` | warning | no | pi-lens | Use .isEmpty() instead of .length() == 0 or .equals(""). |
122
140
  | `rsa-no-padding-java` | warning | no | CodeRabbit | Using RSA without OAEP mode weakens the encryption. |
123
141
  | `simple-command-injection-direct-input-java` | warning | no | CodeRabbit | "Untrusted input might be injected into a command executed by the application, which can lead to a command injection vulnerability. An atta… |
124
142
  | `system-setproperty-hardcoded-secret-java` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
@@ -217,13 +235,21 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
217
235
  | `unchecked-throwing-call-js` | error | no | pi-lens | $CALL without try/catch — throws on invalid input |
218
236
  | `weak-rsa-key-js` | error | no | pi-lens | Weak RSA key size — use at least 2048 bits |
219
237
 
220
- ### Kotlin (5)
238
+ ### Kotlin (13)
221
239
 
222
240
  | Rule | Severity | Fixable | Source | Description |
223
241
  |---|---|---|---|---|
224
242
  | `des-is-deprecated-kotlin` | warning | no | CodeRabbit | DES is considered deprecated. AES is the recommended cipher. Upgrade to use AES. See https://www.nist.gov/news-events/news/2005/06/nist-wit… |
225
243
  | `desede-is-deprecated-kotlin` | warning | no | CodeRabbit | Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES. |
226
244
  | `jwt-hardcode-kotlin` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
245
+ | `kotlin-no-lateinit` | warning | no | pi-lens | Avoid lateinit var; prefer a nullable type or constructor injection |
246
+ | `kotlin-no-nullable-boolean` | warning | no | pi-lens | Avoid a nullable Boolean; a two-value enum or a non-null default is usually clearer |
247
+ | `kotlin-no-println` | warning | no | pi-lens | Avoid println/print; use a structured logger |
248
+ | `kotlin-no-unnecessary-let` | warning | no | pi-lens | let on a non-null-checked receiver adds indirection; use the value directly |
249
+ | `kotlin-no-unsafe-cast` | warning | no | pi-lens | Unsafe cast (as) throws ClassCastException on a mismatch; consider as? |
250
+ | `kotlin-no-var` | warning | no | pi-lens | Prefer val over var; mutability should be explicit and scoped |
251
+ | `kotlin-prefer-data-class` | warning | no | pi-lens | This class looks like a plain value holder; consider a data class |
252
+ | `kotlin-prefer-is-empty` | warning | no | pi-lens | Use isEmpty() instead of comparing size/length to zero |
227
253
  | `rsa-no-padding-kotlin` | warning | no | CodeRabbit | Using RSA without OAEP mode weakens the encryption. |
228
254
  | `system-setproperty-hardcoded-secret-kotlin` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
229
255
 
@@ -234,7 +260,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
234
260
  | `openssl-cbc-static-iv-php` | warning | no | CodeRabbit | Static IV used with AES in CBC mode. Static IVs enable chosen-plaintext attacks against encrypted data. |
235
261
  | `search-active-debug-php` | warning | no | CodeRabbit | Debug logging is explicitly enabled. This can potentially disclose sensitive information and should never be active on production systems. |
236
262
 
237
- ### Python (96)
263
+ ### Python (98)
238
264
 
239
265
  | Rule | Severity | Fixable | Source | Description |
240
266
  |---|---|---|---|---|
@@ -308,6 +334,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
308
334
  | `python-mysqlclient-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
309
335
  | `python-neo4j-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
310
336
  | `python-neo4j-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
337
+ | `python-optional-type` | hint | no | pi-lens | Optional[T] requires Python 3.10+ to rewrite as T \| None |
311
338
  | `python-peewee-mysql-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
312
339
  | `python-peewee-mysql-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
313
340
  | `python-peewee-pg-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
@@ -333,9 +360,10 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
333
360
  | `python-urllib3-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
334
361
  | `python-webrepl-empty-password-python` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
335
362
  | `python-webrepl-hardcoded-secret-python` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
363
+ | `reducible-list-comprehension` | warning | yes | pi-lens | A list comprehension is unnecessary here; use a generator expression |
336
364
  | `unchecked-throwing-call-python` | error | no | pi-lens | Call without try/except — throws on invalid input or missing file |
337
365
 
338
- ### Ruby (19)
366
+ ### Ruby (20)
339
367
 
340
368
  | Rule | Severity | Fixable | Source | Description |
341
369
  |---|---|---|---|---|
@@ -358,8 +386,9 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
358
386
  | `ruby-pg-hardcoded-secret-ruby` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
359
387
  | `ruby-redis-empty-password-ruby` | warning | no | CodeRabbit | The application creates a database connection with an empty password. This can lead to unauthorized access by either an internal or externa… |
360
388
  | `ruby-redis-hardcoded-secret-ruby` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
389
+ | `ruby-symbol-to-proc-candidate` | warning | yes | pi-lens | Use a symbol-to-proc shorthand for this simple iterator block |
361
390
 
362
- ### Rust (11)
391
+ ### Rust (12)
363
392
 
364
393
  | Rule | Severity | Fixable | Source | Description |
365
394
  |---|---|---|---|---|
@@ -370,6 +399,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
370
399
  | `redundant-unsafe-function` | error | no | pi-lens | Unsafe function without unsafe block inside |
371
400
  | `reqwest-accept-invalid-rust` | warning | no | CodeRabbit | Dangerously accepting invalid TLS |
372
401
  | `rust-2024-let-chain-candidate` | info | no | pi-lens | nested if conditions can be collapsed into a Rust 2024 let-chain |
402
+ | `rust-no-chars-enumerate` | warning | no | pi-lens | chars().enumerate() may confuse character indices with byte offsets |
373
403
  | `secrets-reqwest-hardcoded-auth-rust` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
374
404
  | `ssl-verify-none-rust` | warning | no | CodeRabbit | SSL verification disabled, this allows for MitM attacks |
375
405
  | `tokio-postgres-empty-password-rust` | warning | no | CodeRabbit | The application uses an empty credential. This can lead to unauthorized access by either an internal or external malicious actor. It is rec… |
@@ -392,10 +422,11 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
392
422
  | `insecure-biometrics-swift` | info | no | CodeRabbit | The application was observed to leverage biometrics via Local Authentication, which returns a simple boolean result for authentication. Thi… |
393
423
  | `rabbit-hardcoded-secret-swift` | warning | no | CodeRabbit | A secret is hard-coded in the application. Secrets stored in source code, such as credentials, identifiers, and other types of sensitive da… |
394
424
 
395
- ### TSX (7)
425
+ ### TSX (8)
396
426
 
397
427
  | Rule | Severity | Fixable | Source | Description |
398
428
  |---|---|---|---|---|
429
+ | `hyphenated-svg-attribute` | warning | no | pi-lens | Use camelCase for this known SVG attribute in JSX |
399
430
  | `inline-styles` | hint | no | pi-lens | Inline styles — use CSS modules or styled components |
400
431
  | `jsx-boolean-short-circuit` | error | yes | pi-lens | React JSX && pitfall — potentially renders 0 or NaN |
401
432
  | `no-nested-links` | error | no | pi-lens | Nested <a> tags are invalid HTML and cause unexpected behavior |
@@ -27,38 +27,49 @@ released `ast-grep`), so we get a fresh-engine cross-check.
27
27
 
28
28
  ## What the playground actually does
29
29
 
30
- The playground's source code is **fixed** — the user cannot inject custom
31
- source via the URL hash. The URL hash only carries:
30
+ The verifier encodes `--code` into the playground's URL-hash state so the
31
+ upstream engine matches the rule against it. The state (`ast-grep/ast-
32
+ grep.github.io`, `website/src/components/astGrep/state.ts` +
33
+ `index.ts`) carries:
32
34
 
33
35
  - `mode: "Config"` + `config: <rule YAML>` (the rule)
34
- - `query` (an ast-grep pattern string, only used in `mode: "Pattern"`)
36
+ - `source: <code>` — the code the rule is matched against, in **both**
37
+ modes
38
+ - `query` — an ast-grep pattern string, used only in `mode: "Pattern"`
39
+ (irrelevant here; the verifier always requests Config mode)
35
40
 
36
- So this verifier is a **pattern-level smoke test**: "does the rule's
37
- pattern fire on the playground's hardcoded sample source?" It catches:
41
+ So this verifier checks: "does the rule match `--code` on the upstream
42
+ engine, and does the count agree with the local `ast-grep` CLI?" It catches:
38
43
 
39
44
  - **YAML/pattern rejection** — the playground shows an error if the rule's
40
45
  YAML uses features the upstream engine doesn't support
41
46
  - **Pattern-engine divergence** — local ast-grep matches but upstream
42
47
  doesn't (or vice versa)
43
48
  - **Match-count drift** — `Found N match(es)` count differs between local
44
- and upstream
49
+ and upstream, for the *same* source
45
50
 
46
- It does **not** catch rule-vs-source bugs where the source is what the user
47
- wrote — the local CLI test is the source of truth for that.
51
+ #2208: earlier versions of this verifier wrote `--code` into `query`
52
+ instead of `source`. Since `query` is ignored in Config mode, the hash
53
+ carried no source at all, and the playground's `{...defaultState,
54
+ ...parsed}` state merge silently fell back to its own hardcoded sample —
55
+ every run graded that fixed sample, not `--code`. Every "ok:true
56
+ matches:N" this verifier produced before the fix reflected the playground's
57
+ sample source, never the caller's code.
48
58
 
49
59
  ## Usage
50
60
 
51
61
  ```bash
52
- # Smoke-test a single rule (auto-launches headless Chrome on 9224, then kills it)
53
- node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml --code "ignored"
62
+ # Smoke-test a single rule against a snippet it matches
63
+ # (auto-launches headless Chrome on 9224, then kills it)
64
+ node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml --code "console.log('x');"
54
65
 
55
66
  # Assert a specific match count
56
67
  node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/jsx-boolean-short-circuit.yml \
57
- --code "ignored" --expected 0
68
+ --code "const a = 1;" --expected 0
58
69
 
59
70
  # Keep Chrome alive for follow-up runs (avoids the 5–10s cold-start cost)
60
71
  node scripts/playground-verify-rule.mjs rules/ast-grep-rules/rules/no-console-except-error.yml \
61
- --code "ignored" --keep-chrome
72
+ --code "console.log('x');" --keep-chrome
62
73
 
63
74
  ```
64
75
 
@@ -142,7 +153,9 @@ Three scripts, one job each:
142
153
  resolution, accessibility tree, and network tracing stripped out.
143
154
  - `playground-verify-rule.mjs` — the CLI the user invokes. Reads the rule,
144
155
  builds the playground URL, runs the page through CDP, scrapes the
145
- result, prints JSON, kills Chrome.
156
+ result, prints JSON, and kills Chrome. Its source-drift sentinel reads only
157
+ the first `.monaco-editor` in the source pane, not the config editor, so a
158
+ rule note cannot satisfy the check by repeating the fixture text.
146
159
 
147
160
  ## Skipping the test
148
161
 
@@ -165,10 +178,6 @@ auto-skipped when Google Chrome is not on `PATH` and
165
178
 
166
179
  ## Known limitations
167
180
 
168
- - **Source is fixed.** The playground uses its own hardcoded sample
169
- source. Custom source code passed via `--code` is ignored by the
170
- upstream playground in `mode: "Config"`. This is by design — the
171
- playground is a manual tool, not a test harness.
172
181
  - **Slow.** ~11s per rule. Don't enable in the default `npm test` run.
173
182
  - **Line numbers are best-effort.** The playground's gutter layout
174
183
  changes between builds. If the line-extraction JS misses, `lines` is
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {
@@ -19,6 +19,7 @@
19
19
  "bundle:dist": "node scripts/bundle-dist.mjs",
20
20
  "prepare": "npm run build:dist && node scripts/download-grammars.js --core --dest grammars && node scripts/setup-git-hooks.mjs && node scripts/warm-loader-cache.mjs",
21
21
  "lint": "tsc --project tsconfig.json",
22
+ "depcruise:check": "depcruise --validate --config .dependency-cruiser.cjs --ignore-known .dependency-cruiser-known-violations.json --output-type err index.ts \"clients/**/*.ts\"",
22
23
  "fmt": "oxfmt",
23
24
  "fmt:check": "oxfmt --check",
24
25
  "watch": "tsc --watch",
@@ -38,6 +39,7 @@
38
39
  "astgrep:self-scan:update-baseline": "node scripts/run-astgrep-pi-lens.mjs --update-baseline",
39
40
  "audit:playground": "node scripts/playground-verify-rule.mjs",
40
41
  "bench:cascade-budget": "node scripts/bench-cascade-budget.mjs",
42
+ "bench:word-index-replacement": "node scripts/bench-word-index-replacement.mjs",
41
43
  "logs:smells": "node scripts/analyze-pi-lens-logs.mjs",
42
44
  "changelog:check": "node scripts/rollup-changelog.mjs --check",
43
45
  "changelog:release": "node scripts/changelog-release.mjs",
@@ -126,7 +128,9 @@
126
128
  "@types/node": "^26.0.0",
127
129
  "@types/pidusage": "^2.0.5",
128
130
  "@vitest/coverage-v8": "^4.1.5",
131
+ "dependency-cruiser": "^18.2.0",
129
132
  "husky": "^9.1.7",
133
+ "json5": "^2.2.3",
130
134
  "oxfmt": "0.64.0",
131
135
  "oxlint": "1.79.0",
132
136
  "typebox": "^1.0.0",
@@ -0,0 +1,20 @@
1
+ id: c-no-malloc-free
2
+ valid:
3
+ - |
4
+ int sum(const int *values, int count) {
5
+ int total = 0;
6
+ for (int i = 0; i < count; i++) {
7
+ total += values[i];
8
+ }
9
+ return total;
10
+ }
11
+ invalid:
12
+ - |
13
+ int *make_buffer(int count) {
14
+ int *buf = malloc(sizeof(int) * count);
15
+ return buf;
16
+ }
17
+ - |
18
+ void release(int *buf) {
19
+ free(buf);
20
+ }
@@ -0,0 +1,25 @@
1
+ id: cpp-no-malloc-free
2
+ valid:
3
+ - |
4
+ std::unique_ptr<int[]> make_buffer(int count) {
5
+ return std::make_unique<int[]>(count);
6
+ }
7
+ invalid:
8
+ - |
9
+ int* make_buffer(int count) {
10
+ int* buf = static_cast<int*>(malloc(sizeof(int) * count));
11
+ return buf;
12
+ }
13
+ - |
14
+ void release(int* buf) {
15
+ free(buf);
16
+ }
17
+ - |
18
+ int* make_buffer(int count) {
19
+ int* buf = static_cast<int*>(std::malloc(sizeof(int) * count));
20
+ return buf;
21
+ }
22
+ - |
23
+ void release(int* buf) {
24
+ ::free(buf);
25
+ }
@@ -0,0 +1,19 @@
1
+ id: cpp-no-printf
2
+ valid:
3
+ - |
4
+ void report(int count) {
5
+ std::cout << "count: " << count << "\n";
6
+ }
7
+ - |
8
+ void log(const char* msg) {
9
+ fprintf(stderr, "%s\n", msg);
10
+ }
11
+ invalid:
12
+ - |
13
+ void report(int count) {
14
+ printf("count: %d\n", count);
15
+ }
16
+ - |
17
+ void report(int count) {
18
+ std::printf("count: %d\n", count);
19
+ }
@@ -0,0 +1,19 @@
1
+ id: go-no-fmt-println
2
+ valid:
3
+ - |
4
+ func main() {
5
+ slog.Info("hello", "key", "value")
6
+ }
7
+ - |
8
+ func main() {
9
+ log.Printf("started on port %d", port)
10
+ }
11
+ invalid:
12
+ - |
13
+ func main() {
14
+ fmt.Println("hello")
15
+ }
16
+ - |
17
+ func main() {
18
+ fmt.Printf("value: %d\n", 42)
19
+ }
@@ -0,0 +1,17 @@
1
+ id: go-no-panic-in-lib
2
+ valid:
3
+ - |
4
+ func divide(a, b int) (int, error) {
5
+ if b == 0 {
6
+ return 0, errors.New("division by zero")
7
+ }
8
+ return a / b, nil
9
+ }
10
+ invalid:
11
+ - |
12
+ func mustDivide(a, b int) int {
13
+ if b == 0 {
14
+ panic("division by zero")
15
+ }
16
+ return a / b
17
+ }
@@ -0,0 +1,18 @@
1
+ id: go-no-underscore-func-name
2
+ valid:
3
+ - |
4
+ func handleRequest(w http.ResponseWriter, r *http.Request) {
5
+ }
6
+ - |
7
+ func getUserName(id int) string {
8
+ return ""
9
+ }
10
+ invalid:
11
+ - |
12
+ func _helper() string {
13
+ return ""
14
+ }
15
+ - |
16
+ func get_user_name(id int) string {
17
+ return ""
18
+ }
@@ -0,0 +1,15 @@
1
+ id: go-prefer-errors-is
2
+ valid:
3
+ - |
4
+ if errors.Is(err, os.ErrNotExist) {
5
+ return nil
6
+ }
7
+ invalid:
8
+ - |
9
+ if err == os.ErrNotExist {
10
+ return nil
11
+ }
12
+ - |
13
+ if err != os.ErrNotExist {
14
+ return nil
15
+ }
@@ -0,0 +1,21 @@
1
+ id: go-prefer-string-builder
2
+ valid:
3
+ - |
4
+ func build(parts []string) string {
5
+ var b strings.Builder
6
+ for _, p := range parts {
7
+ b.WriteString(p)
8
+ }
9
+ return b.String()
10
+ }
11
+ invalid:
12
+ - |
13
+ func concat(a string, b string) string {
14
+ a = a + b
15
+ return a
16
+ }
17
+ - |
18
+ func appendSuffix(s string) string {
19
+ s += "suffix"
20
+ return s
21
+ }