@herbertgao/pi-extensions 2026.9.0 → 2026.9.1

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 (293) hide show
  1. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  2. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  11. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  12. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  13. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  14. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  15. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  16. package/node_modules/pi-lens/README.md +3 -0
  17. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  18. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  19. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  22. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  23. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  24. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  25. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  26. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  27. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  28. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  29. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  30. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  31. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  32. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  33. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  34. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  35. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  36. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  37. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  38. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  39. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  40. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  41. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  42. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  43. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  44. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  45. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  46. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  47. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  48. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  49. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  50. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  51. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  52. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  53. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  54. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  55. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  56. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  58. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  59. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  60. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  61. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  63. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  64. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  122. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  123. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  124. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  125. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  126. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  128. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  129. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  130. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  131. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  132. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  133. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  134. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  135. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  136. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  137. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  138. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  139. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  140. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  141. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  142. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  143. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  144. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  145. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  146. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  147. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  148. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  149. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  150. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  151. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  152. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  153. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  154. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  155. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  156. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  157. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  158. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  159. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  160. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  162. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  163. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  164. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  165. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  167. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  168. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  171. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  172. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  173. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  174. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  175. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  177. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  178. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  179. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  180. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  181. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  182. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  183. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  184. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  185. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  186. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  187. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  188. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  189. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  190. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  191. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  195. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  196. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  197. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  198. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  199. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  200. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  201. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  203. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  204. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  205. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  206. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  207. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  208. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  209. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  210. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  211. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  212. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  213. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  214. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  215. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  216. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  217. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  218. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  219. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  220. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  221. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  222. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  223. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  224. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  225. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  226. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  227. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  228. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  229. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  230. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  231. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  232. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  233. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  234. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  241. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  242. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  243. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  244. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  245. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  246. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  247. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  249. package/node_modules/pi-lens/dist/index.js +64335 -54300
  250. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  251. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  252. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  253. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  254. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  255. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  256. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  257. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  258. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  259. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  260. package/node_modules/pi-lens/docs/configuration.md +222 -0
  261. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  262. package/node_modules/pi-lens/docs/features.md +44 -5
  263. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  264. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  265. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  266. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  267. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  268. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  269. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  270. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  271. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  272. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  273. package/node_modules/pi-lens/package.json +15 -23
  274. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  275. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  276. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  277. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  278. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  279. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  280. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  281. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  282. package/node_modules/pi-web-access/README.md +62 -15
  283. package/node_modules/pi-web-access/curator-page.ts +3 -1
  284. package/node_modules/pi-web-access/curator-server.ts +5 -1
  285. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  286. package/node_modules/pi-web-access/github-extract.ts +242 -1
  287. package/node_modules/pi-web-access/index.ts +118 -64
  288. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  289. package/node_modules/pi-web-access/package.json +2 -2
  290. package/node_modules/pi-web-access/perplexity.ts +14 -1
  291. package/node_modules/pi-web-access/utils.ts +23 -6
  292. package/node_modules/pi-web-access/xai-search.ts +96 -33
  293. package/package.json +5 -5
@@ -0,0 +1,182 @@
1
+ # Release-QA baseline matrix
2
+
3
+ The feature × modality matrix a release candidate is witnessed against before a
4
+ tag is cut. `scripts/release-qa.mjs` reads THIS FILE — the table below is the
5
+ runner's row list, not a copy of one — exports the committed tree, packs and
6
+ installs the candidate into a scratch `HOME`, drives each row's entry point
7
+ against a real `pi`, and writes `release-qa-report.md` plus one witness file per
8
+ row under `release-qa-evidence/`.
9
+
10
+ Every child process the runner spawns — `pi`, the MCP server, `node`, `npm` —
11
+ runs under a pinned environment: `HOME`, `USERPROFILE`, `PI_LENS_HOME`,
12
+ `PILENS_DATA_DIR`, `PI_LENS_INSTALL_LOG` and `npm_config_cache` all inside the
13
+ scratch root. `PI_LENS_INSTALL_LOG` is on that list by hard experience: it is
14
+ what `scripts/warm-loader-cache.mjs` keys its install log on, `PI_LENS_HOME`
15
+ does NOT redirect it, and the runner's first six runs — which pinned the pi-lens
16
+ home but passed no environment to `npm` at all — put 41 records into the
17
+ maintainer's real `~/.pi-lens/install.log` (#2619 review F1). The pack runs in a
18
+ `git archive HEAD` export, never the live checkout, because `npm pack` fires our
19
+ own `prepack` (rewrites `package.json` + `package-lock.json`) and `prepare`
20
+ (rebuilds `dist/`, downloads grammars, reinstalls git hooks).
21
+
22
+ Why it exists: #2587. The four shipped skills were suspected of never
23
+ registering for four releases because **no check ever asked a real pi what it
24
+ loaded**. The unit suite (~10.8k tests) and the nightly smokes (install, compat,
25
+ tool, lifecycle, parser) are per-seam; nothing composed them into a release
26
+ verdict with counted coverage, so a missing row read as absence rather than
27
+ arithmetic.
28
+
29
+ ## Reading the table
30
+
31
+ - **row id** — the runner's key. Every id here must have a probe in
32
+ `scripts/release-qa.mjs`, and every probe there must appear here; the tie is
33
+ enforced by `tests/scripts/release-qa.test.ts`, so neither list can drift into
34
+ a hand-maintained mirror of the other.
35
+ - **entry point** — the command or RPC a **user path** actually takes. Never a
36
+ raw internal function: a row whose entry point is an internal call proves the
37
+ function works, not the product.
38
+ - **pass criterion** — the concrete thing the witness must SHOW (a status, a
39
+ count, a named record). "It ran without throwing" is not a criterion.
40
+ - **witness** — the artifact captured under `release-qa-evidence/`. A witness
41
+ that merely exists is not a witness; the report quotes the line that shows the
42
+ asserted result.
43
+ - **reuse** — the existing script that already produces this witness, or the
44
+ smoke that would have caught this row's regression. `new` means nothing in the
45
+ repo covers it, and the cell says so rather than leaving the gap implicit.
46
+ - **umbrella** — the open smoke-umbrella issue whose scope this row overlaps
47
+ (#1605 lifecycle/real-host lanes, #1829 tool-contract lanes), with one line on
48
+ where the two differ. `—` means neither umbrella claims this ground. This
49
+ column exists so a reader can tell at a glance which rows are a down-payment
50
+ on an already-filed umbrella and which are genuinely uncovered — and so the
51
+ umbrella issues are not re-litigated row by row when they are eventually
52
+ built.
53
+
54
+ ## Modalities
55
+
56
+ | modality | what it stands for |
57
+ | --- | --- |
58
+ | `npm-pack` | the published artifact itself — what `npm publish` uploads |
59
+ | `npm-install` | a user installing the tarball into a project's `node_modules` |
60
+ | `pi-rpc` | pi loading the installed package, driven headless via `pi --mode rpc` |
61
+ | `mcp-stdio` | an MCP client speaking JSON-RPC to `dist/mcp/server.js` |
62
+ | `git-install` | `pi install git:...` against a pushed ref |
63
+
64
+ ## The matrix
65
+
66
+ | row id | feature | modality | entry point | pass criterion | witness | reuse | umbrella |
67
+ | --- | --- | --- | --- | --- | --- | --- | --- |
68
+ | pack-skills-payload | the four shipped skills and the compiled entry are IN the published artifact | npm-pack | `npm pack --json` on the release candidate | packed file list contains `dist/index.js` and at least 4 `skills/**/SKILL.md` | pack listing JSON | new — `tests/packaging.test.ts` asserts `files[]` NAMES `skills/`, never that the pack carries SKILL.md files | — |
69
+ | install-selftest | the runtime dependency graph and the `pi.skills` manifest resolve AS INSTALLED | npm-install | `node <installed>/scripts/install-selftest.mjs --allow-soft` | process exits 0 and no `[FAIL]` line | selftest stdout | `scripts/install-selftest.mjs` verbatim — install-smoke's `smoke` job | #1605 lane 5 (real-host install), narrower: this row asserts the packaged artifact resolves, not that a host classifier parses |
70
+ | skills-registered | a real pi registers the four pi-lens skills from the installed package | pi-rpc | `pi install <installed pkg>` then `pi --mode rpc` plus `{"type":"get_commands"}` | at least 4 commands with `source` `skill`; every `sourceInfo.path` inside the installed package AND every `sourceInfo.source` equal to `extension:index` | get_commands response JSON | new probe on the #2589 mechanism — the recurrence is #2587 | — |
71
+ | commands-registered | the extension loads and registers its `lens-*` slash commands | pi-rpc | same RPC session as above | at least 1 command with `source` `extension` named `lens-*`, and zero `extension_error` events | same get_commands response plus the event stream | `scripts/rpc-load-check.mjs` assertion — install-smoke's `pi-load` job, which runs it against the PUBLISHED package only | #1605 lane 1 (real-host): the same real-host principle, applied to extension registration rather than stderr classification |
72
+ | mcp-tools-registered | the MCP mirror advertises the `pilens_*` tool surface | mcp-stdio | `node <installed>/dist/mcp/server.js` then `initialize` plus `tools/list` | every advertised tool name starts `pilens_`, and the set contains analyze, diagnostics, turn_end, lsp_navigation, health | tools/list response JSON | new — no smoke drives the MCP mirror from an install | — |
73
+ | mcp-diagnostics-full | `lens_diagnostics` full mode answers on a fixture repo | mcp-stdio | `tools/call` `pilens_diagnostics` with `mode` `full` and `refreshRunners` `cheap`, POLLED | text carries a `Summary (N files diagnosed this session)` line with N at least 1 | tool result text | new — `tests/clients` covers the handler, nothing covers it through a packaged install | — |
74
+ | mcp-turn-end | the turn-end pipeline runs over the turn's files and returns an advisory | mcp-stdio | `tools/call` `pilens_turn_end` with the fixture file | text carries `Turn-end over N file(s).` with N at least 1 | tool result text | new — `scripts/smoke-availability-lifecycle.mjs` covers lifecycle availability, not the packaged turn-end path | #1605 lane 2 (availability-lifecycle): overlaps the turn-end half; #1605 asserts latch recovery, this row asserts the packaged turn-end path answers at all |
75
+ | mcp-lsp-navigation | LSP navigation answers on a fixture | mcp-stdio | `tools/call` `pilens_lsp_navigation` with operation `documentSymbol` and the fixture path | result is not an error and names the fixture's exported `releaseQaFixtureSymbol` | tool result text | `scripts/smoke-tools.mjs --lsp` is the per-server sibling; this row is the packaged-path variant | #1829 lane 2/3 (gated real-binary + the rotating install lane): #1829 pins each tool's own contract, this row pins that the packaged LSP path answers |
76
+ | config-provenance | a project config is LOADED and its provenance is reportable | mcp-stdio | `tools/call` `pilens_effective_config` with the fixture file | result names the fixture's `.pi-lens.json` as a contributing document | tool result text | new — `tests/config/pi-lens-config-schema.test.ts` covers the schema, not the packaged load | — |
77
+ | degradation-visible | a silently-ignored input is RECORDED as a degradation instead of vanishing | mcp-stdio | `tools/call` `pilens_health` with the fixture's project-tier `lsp.enabled` (a global-only setting) loaded | health text carries a `config-ignored` degradation line naming the fixture's `.pi-lens.json` | health tool result text | `clients/degradation-ledger.ts` is the reused machinery; no smoke asserts it end to end | #1605 lane 2 (availability-lifecycle): the degradation-recorded half; #1605 additionally asserts RECOVERY, which this row does not |
78
+ | git-install-loads | a `git:` install of a pushed ref builds and loads in a real pi | git-install | `pi install git:github.com/apmantza/pi-lens@<ref>` then `get_commands` | at least 1 `lens-*` command and at least 4 skills | get_commands response JSON | `scripts/rpc-load-check.mjs` assertion, re-run against the git layout | — |
79
+
80
+ ## Why `skills-registered` pins the registrar
81
+
82
+ pi-lens has **two independent skill registrars**: the `pi.skills` manifest, and
83
+ `index.ts`'s own `resources_discover` handler (#205,
84
+ `resolvePackagePath(import.meta.url, "skills")`), which never reads the
85
+ manifest. #2587 is the proof that one half can be broken for four releases while
86
+ the other silently covers for it — driving published pi-lens 4.1.3, with the
87
+ broken `["../../skills"]` manifest, through a real pi registers all four skills,
88
+ via `extension:index`.
89
+
90
+ So the row asserts `sourceInfo.source === "extension:index"` on every skill, not
91
+ merely that four skills appeared. The cost is stated plainly: a future release
92
+ that deliberately moved registration to the manifest would FAIL this row until
93
+ the criterion is updated. That is the intended trade — the row's job is to name
94
+ which half is load-bearing, and a row that accepts "some path worked" is exactly
95
+ the check that was missing for four releases. The observed registrar values are
96
+ printed in the witness either way, so a pi-side rename reads as a diagnosable
97
+ mismatch rather than a mystery.
98
+
99
+ ## Outcomes
100
+
101
+ Every row ends in exactly one of four states, and the four partition the
102
+ discovered set — the report asserts the identity
103
+ `discovered = pass + fail + untested + skipped` and says ARITHMETIC MISMATCH if
104
+ it does not hold.
105
+
106
+ The printed arithmetic is `discovered / rows / untested`, and the three words
107
+ mean different things:
108
+
109
+ - **`discovered`** — rows in the matrix above. The denominator.
110
+ - **`rows`** — of those, the ones this run actually DROVE a probe for. A row the
111
+ runner has no probe for is not counted; a BLOCKED run drives none, so `rows`
112
+ reads 0. A SKIPPED row IS counted: its probe ran and decided the row
113
+ unreachable.
114
+ - **`untested`** — rows that produced no witness.
115
+
116
+ - **PASS** — the witness shows the pass criterion.
117
+ - **FAIL(cause)** — the witness shows something else. The cause is the observed
118
+ value, not a category.
119
+ - **UNTESTED(reason)** — no witness was produced. An async row that did not
120
+ reach a terminal state before its polling cap expires **UNTESTED, never PASS**;
121
+ a row with no probe implementation is UNTESTED too, so an unimplemented row is
122
+ arithmetic rather than silence.
123
+ - **SKIPPED(reason)** — the row is unreachable in this run by construction (a
124
+ `git:` install with no ref given, for instance). Reachability is decided in
125
+ planning, not discovered mid-run.
126
+
127
+ **BLOCKED is about the HOST, not the candidate.** The runner probes
128
+ `pi --mode rpc` **before anything is installed**; only a pi that cannot start a
129
+ bare session makes the run BLOCKED, because then nothing about the candidate
130
+ was measured. A candidate that will not pack, will not install, or stops a
131
+ working pi from booting is a **result** — DO-NOT-SHIP — not an untestable
132
+ state. A blocked run is not a failed run and must never be reported as one.
133
+
134
+ **A run that witnessed nothing is INCONCLUSIVE, never ship-with-caveats.** If pi
135
+ booted and no row reached PASS, "no row disagreed" is not evidence; the run gets
136
+ no ship verdict either.
137
+
138
+ Ship line, from the outcomes:
139
+
140
+ | condition | verdict | exit | rows |
141
+ | --- | --- | --- | --- |
142
+ | pi did not boot with NO candidate installed | BLOCKED — no verdict | 3 | 0 |
143
+ | pi booted, the candidate would not install or activate | do not ship, cause named on the verdict | 1 | 0 |
144
+ | any row FAILED | do not ship | 1 | N |
145
+ | pi booted but zero rows PASSED | INCONCLUSIVE — no verdict | 3 | N |
146
+ | any UNTESTED or SKIPPED, at least one PASS | ship with caveats, each named | 2 | N |
147
+ | all PASS | ship | 0 | N |
148
+
149
+ A candidate that never activated leaves **every row UNTESTED, none FAILED, and
150
+ `rows` at 0** — no probe ran, so nothing was witnessed and nothing can honestly
151
+ be called a failure of that row. The activation failure is the verdict's own
152
+ cause. (An earlier revision copied it onto all eleven rows as FAILs and wrote an
153
+ empty evidence dir — a verdict with no witness, against the first hard rule.)
154
+
155
+ **A row is PASS only if its witness has something in it.** A probe that reports
156
+ success and produces no artifact — or an EMPTY one — is recorded UNTESTED, not
157
+ PASS, and the report's excerpt column carries the downgrade reason rather than
158
+ the pass line. The empty case is the reachable one: every probe attaches a
159
+ witness object on its pass path, but `install-selftest` passes on "exit 0 with
160
+ no `[FAIL]` line", which a packaged selftest that printed nothing satisfies
161
+ vacuously, and its witness is that same empty stdout. The check is on CONTENT,
162
+ not on the presence of a file.
163
+
164
+ A usage or self-check error (bad option, unparseable matrix, arithmetic
165
+ mismatch) exits **4**. **Exit 2 is the EXPECTED verdict for a working-tree run**:
166
+ `git-install-loads` is SKIPPED without `--git-ref`. A CI lane should treat 2 as
167
+ a warning and 1/3/4 as failures.
168
+
169
+ ## Deliberately out of scope
170
+
171
+ - **Anything needing a model turn.** Every row above is model-free by
172
+ construction; `get_commands` and the MCP tool calls never reach a provider. A
173
+ row that needs a real LLM turn cannot be a release gate on an unfunded key,
174
+ and a stubbed turn would be a double that mirrors our own assumption
175
+ (AGENTS.md, external contracts).
176
+ - **The `concurrent_session_bind` guard.** Observing it needs a second in-process
177
+ `createAgentSession()`, which needs model config. `docs/subagent-compat.md`
178
+ carries the same TODO; duplicating it here would add a row that can only ever
179
+ be UNTESTED.
180
+ - **Per-language tool and LSP coverage.** `scripts/smoke-tools.mjs` sweeps the
181
+ whole registry nightly. This matrix asserts the packaged path answers at all,
182
+ not that every server answers well.
@@ -17,7 +17,7 @@ every run.
17
17
 
18
18
  | # | Contract | Depended on by | Third-party file (as of the versions below) | Verified against |
19
19
  |---|----------|-----------------|-------------------------------------------------|-------------------|
20
- | 1 | `PI_SUBAGENT_CHILD` is set to the literal string `"1"` in every spawned child's env; `PI_SUBAGENT_RUN_ID` / `PI_SUBAGENT_CHILD_AGENT` are set alongside it for best-effort identity. | `clients/subagent-mode.ts` (`isSubagentSession()`, `getSubagentIdentity()`) | `pi-subagents@0.34.0` — `src/runs/shared/pi-args.ts` (`SUBAGENT_CHILD_ENV`/`SUBAGENT_RUN_ID_ENV`/`SUBAGENT_CHILD_AGENT_ENV` consts + the `env[SUBAGENT_CHILD_ENV] = "1"` assignment) | `checkNicobailonChildEnv` |
20
+ | 1 | `PI_SUBAGENT_CHILD` is set to the literal string `"1"` on every process that hosts a child session. | `clients/subagent-mode.ts` (`isSubagentSession()`, `getSubagentIdentity()`) | `pi-subagents@0.34.0` — co-located in `src/runs/shared/pi-args.ts` (`SUBAGENT_CHILD_ENV` const + the `env[SUBAGENT_CHILD_ENV] = "1"` assignment); as of `pi-subagents@0.66.0` the const moved to `src/runs/shared/child-runtime-config.ts` and the assignment to `src/runs/background/subagent-runner.ts` (#2581) — `compat-contract-locator.mjs`'s `locateContractSources` tries both layouts and concatenates whichever resolves. | `checkNicobailonChildEnv` |
21
21
  | 1b | avtc-pi-subagent sets `PI_SUBAGENT_CHILD_AGENT` + `PI_SUBAGENT_PARENT_PID` (never `PI_SUBAGENT_CHILD`) on the per-spawn subagent env; `isSubagentSession()` treats the PAIR (both non-empty) as an additional subagent signal (#507). | `clients/subagent-mode.ts` (`isSubagentSession()`, `getSubagentIdentity()`) | `avtc-pi-subagent@1.0.3` — `src/process-runner.ts` (`subagentEnv.PI_SUBAGENT_CHILD_AGENT = agent.name` + `subagentEnv.PI_SUBAGENT_PARENT_PID = String(process.pid)`) | `checkAvtcChildEnv` |
22
22
  | 2a | The pi SDK's extension loader keeps a **process-global** cache (`extensionCache = new Map()`). This is what makes an in-process `bindExtensions()` reuse pi-lens's own module-scope singletons instead of a fresh isolated instance. | `clients/session-lifecycle.ts` (the whole premise of the concurrent-session guard) | `@earendil-works/pi-coding-agent@0.80.6` — `dist/core/extensions/loader.js` | `checkSdkExtensionCache` |
23
23
  | 2b | `AgentSession.bindExtensions()` **unconditionally** emits a `session_start`-typed event (`this._extensionRunner.emit(this._sessionStartEvent)`). | Same as 2a — this is why an in-process subagent bind re-triggers pi-lens's `session_start` handler at all. | `@earendil-works/pi-coding-agent@0.80.6` — `dist/core/agent-session.js` (`bindExtensions()`, ~line 1717) | `checkSdkBindExtensionsEmitsSessionStart` |
@@ -30,7 +30,24 @@ unit-tested regex matchers against RESILIENT semantic shapes (never a line
30
30
  number — those drift on every third-party release). `scripts/compat-contracts.mjs`
31
31
  is the orchestration script: it `npm install`s the four packages (SDK,
32
32
  `pi-subagents`, `avtc-pi-subagent`, `@tintinweb/pi-subagents`) into a scratch
33
- directory, reads the specific files above, and runs every check.
33
+ directory, resolves each contract's source file(s) independently (see
34
+ "Layer A" below) and runs every check.
35
+
36
+ **Contract 1's identity vars were removed upstream, not relocated (#2581).**
37
+ The doc used to also pin `PI_SUBAGENT_RUN_ID` / `PI_SUBAGENT_CHILD_AGENT` as
38
+ part of contract 1 — nicobailon/pi-subagents set them alongside
39
+ `PI_SUBAGENT_CHILD=1` for best-effort child identity. `pi-subagents@0.65.0`'s
40
+ rewrite to run subagents as native, in-process `AgentSession`s (instead of
41
+ spawning a separate `pi` CLI process per child) deleted both vars from the
42
+ package entirely — grep-verified absent from the whole 0.66.0 source tree;
43
+ child identity now travels through an in-process `ChildRuntimeConfig` object
44
+ that never touches `process.env`. This is real, permanent upstream drift, but
45
+ it needed no pi-lens code change: `getSubagentIdentity()` was already
46
+ documented and tested as best-effort, returning `runId`/`agentName: undefined`
47
+ when the vars are absent (`tests/clients/subagent-mode.test.ts`) — exactly the
48
+ degraded state this vocabulary is now always in. `checkNicobailonChildEnv`
49
+ no longer requires them; only the `PI_SUBAGENT_CHILD='1'` flag pi-lens's
50
+ light-mode detection actually depends on behaviorally still gates the check.
34
51
 
35
52
  ## The three env levers
36
53
 
@@ -45,10 +62,46 @@ directory, reads the specific files above, and runs every check.
45
62
  ### Layer A — pinned-contract verification (`scripts/compat-contracts.mjs`)
46
63
 
47
64
  No `pi` process, no LLM turn. Installs the real packages (table above) and
48
- mechanically re-checks all six contracts against the installed source. Exit
49
- 0 = all pass; exit 1 = at least one contract check failed (real drift); exit
50
- 2 = infrastructure failure (npm install of the third-party packages itself
51
- failed usually a registry/network issue, not a contract regression).
65
+ mechanically re-checks all seven contracts against the installed source.
66
+
67
+ Each contract resolves independently to one of **three outcomes**, printed
68
+ per-contract and rolled up into a run-level `outcome` (also exposed as a
69
+ GITHUB_OUTPUT for the workflow's alert step, distinct from the GH step's own
70
+ success/failure) and exit code:
71
+
72
+ - **verified** (exit 0 when every contract is this) — the contract's source
73
+ file(s) were found and their content matches the pinned shape.
74
+ - **drift** (exit 1) — the source file(s) were found, but the content no
75
+ longer matches. This is an actual upstream behavioral change worth
76
+ investigating (see "What to do when the nightly alerts" below).
77
+ - **infra** (exit 2, when nothing drifted but at least one contract hit
78
+ this) — the contract's source file could not be located at ANY of its
79
+ known candidate paths (each `CONTRACTS` entry's `parts` field in
80
+ `scripts/lib/compat-contracts.mjs`, resolved via
81
+ `compat-contract-locator.mjs` and `compat-contract-resolution.mjs`), or
82
+ the `npm install` of the four packages itself failed. This means Layer A
83
+ has NOT actually re-checked that contract's content at all — it is
84
+ distinct from drift and must never be reported as one. #2581: before this
85
+ three-way split, a single relocated file (`pi-subagents@0.65.0` moving
86
+ `src/runs/shared/pi-args.ts`) threw inside one shared try/catch, aborted
87
+ the whole run, and got reported — both in the run log and the tracking
88
+ issue's body — as generic "failure" alongside the other six contracts that
89
+ were never actually re-checked that run either. A contract's own outcome
90
+ only becomes "infra" when ITS candidate paths are all missing; every other
91
+ contract is still resolved and checked independently in the same run.
92
+
93
+ Candidate paths are walked NEWEST-observed-layout first, not oldest (#2680
94
+ F1) — a stale leftover file at an old path (left behind by a partial
95
+ publish, or an npm install that added files without pruning ones the
96
+ package's current `files` list no longer references) must never outrank the
97
+ package's actual current layout. When a candidate path list needs a NEW
98
+ entry (the package relocated the file again), APPEND it to that contract's
99
+ `parts` in the `CONTRACTS` list (`scripts/lib/compat-contracts.mjs`) —
100
+ oldest-observed-layout first in how the list reads, rather than replacing
101
+ the old entry — the locator resolves the newest EXISTING one regardless of
102
+ authored order, so older installs stay covered too. `parts` and `package`
103
+ live directly on each `CONTRACTS` entry, not a second table keyed by a
104
+ string id (#2680 F2) — there is nowhere else to update.
52
105
 
53
106
  ### Layer B — real-pi behavioral smoke (`scripts/compat-smoke-behavioral.mjs`)
54
107
 
@@ -71,7 +124,7 @@ first session.
71
124
  Assertions:
72
125
 
73
126
  1. **Subagent light mode engages** — with `PI_SUBAGENT_CHILD=1` set,
74
- `subagent_light_mode` is logged to `~/.pi-lens/latency.log` (a `type:
127
+ `subagent_light_mode` is logged to `$PI_LENS_HOME/latency.log` (a `type:
75
128
  "phase"` entry) and none of the seven heavyweight-scan phases
76
129
  (`knip`/`jscpd`/`madge`/`dead-code`/`govulncheck`/`gitleaks`/`trivy`) are
77
130
  logged for that run.
@@ -99,22 +152,32 @@ Assertions:
99
152
  `PI_SUBAGENT_PARENT_PID`), no `subagent_light_mode` phase is logged.
100
153
  `subagent-mode.ts`'s doc comment requires the PAIR — a lone var set by
101
154
  some unrelated tool must not trigger light mode.
102
- 6. **`concurrent_session_bind` (#473) NOT asserted, documented TODO.**
155
+ 6. **`concurrent_session_bind` (#473) and its `concurrent_session_bind_rollup`
156
+ session-end summary (#2249) — NOT asserted, documented TODO.**
103
157
  The guard is fully wired on master (PR #477): `index.ts`'s `session_start`
104
158
  handler calls `decideSessionStart()` and logs a `concurrent_session_bind`
105
159
  latency phase for a concurrent-secondary bind — so the phase exists to
106
- observe. The blocker is DRIVING it keylessly: reproducing tintinweb's
107
- in-process model for real (mirroring `agent-runner.ts`'s
108
- `createAgentSession()` + `DefaultResourceLoader` + `bindExtensions()`
109
- sequence) requires full session construction, which in turn needs
110
- model/provider config not cheaply stubbable without a real model key,
111
- and #476 explicitly asks not to ship something flaky here. The unit +
112
- behavioral coverage in `tests/clients/session-lifecycle.test.ts` guards
113
- the classifier and the no-reset contract in-repo; what Layer B cannot yet
114
- add is the end-to-end SDK-driven variant. **Revisit if the pi SDK grows a
115
- model-free session constructor or stub provider** at that point add a
116
- Layer B assertion analogous to 1-3 checking the `concurrent_session_bind`
117
- phase and the absence of a second LSP fleet teardown.
160
+ observe. `clients/session-start-observability.ts` also keeps a bounded,
161
+ process-singleton-backed tally of declined binds by classification
162
+ (`concurrent-secondary` / `secondary-root`), which `index.ts`'s
163
+ `session_shutdown` handler logs as one `concurrent_session_bind_rollup`
164
+ row (primary sessions only) and clears same primary-only placement as
165
+ `session_end_bus_rollup`/`path_attribution_verified_rollup`. The blocker is
166
+ DRIVING either keylessly: reproducing tintinweb's in-process model for real
167
+ (mirroring `agent-runner.ts`'s `createAgentSession()` +
168
+ `DefaultResourceLoader` + `bindExtensions()` sequence) requires full
169
+ session construction, which in turn needs model/provider config not
170
+ cheaply stubbable without a real model key, and #476 explicitly asks not
171
+ to ship something flaky here. The unit + behavioral coverage in
172
+ `tests/clients/session-lifecycle.test.ts` guards the classifier and the
173
+ no-reset contract, and `tests/clients/session-start-observability.test.ts`
174
+ plus `tests/index-integration.test.ts` guard the rollup's counting,
175
+ process-singleton survival, and primary-only reset/emit wiring, all
176
+ in-repo; what Layer B cannot yet add is the end-to-end SDK-driven variant.
177
+ **Revisit if the pi SDK grows a model-free session constructor or stub
178
+ provider** — at that point add a Layer B assertion analogous to 1-3
179
+ checking the `concurrent_session_bind`/`concurrent_session_bind_rollup`
180
+ phases and the absence of a second LSP fleet teardown.
118
181
 
119
182
  ## What to do when the nightly alerts
120
183
 
@@ -125,18 +188,35 @@ third-party contract drift detected"** — search for it by title before
125
188
  assuming a NEW investigation is needed; the workflow already
126
189
  create-or-updates it, never duplicates.
127
190
 
128
- 1. Read the linked run log — both layers print a `[PASS]`/`[FAIL]` line per
129
- check with a one-line detail on exactly what didn't match.
130
- 2. Find the failed check's row in the pinned-contracts table above and go
131
- read the current third-party source at the referenced file a Layer A
132
- failure means the semantic shape genuinely changed upstream (a renamed
133
- env var, a moved `emit()` call, a reworded error message, ...).
134
- 3. Update the corresponding matcher in `scripts/lib/compat-contracts.mjs`
191
+ 1. Read the linked run log — the alert issue body now says which of Layer
192
+ A's three outcomes fired (**verified** / **drift** / **infra**, #2581 —
193
+ never just a generic "failure"); Layer A/B each print a `[PASS]`/`[FAIL]`
194
+ or `[INFRA]` line per check with a one-line detail on exactly what didn't
195
+ match or couldn't be found.
196
+ 2. **If Layer A says infra**: the run log's `INFRA` line names every
197
+ candidate path tried for that contract. Read the installed package at the
198
+ printed version and find where the file actually lives now, then append
199
+ it to that contract's `parts` in the `CONTRACTS` list
200
+ (`scripts/lib/compat-contracts.mjs`) — don't replace the old candidate,
201
+ the locator resolves whichever existing one is newest regardless of
202
+ authored order (#2680 F1), so older installs stay covered too. This is a
203
+ relocation, not a confirmed content change; only move to step 3 once the
204
+ check actually runs against the relocated file.
205
+ 3. **If Layer A says drift**: find the failed check's row in the
206
+ pinned-contracts table above and read the current third-party source at
207
+ the referenced file — the semantic shape genuinely changed upstream (a
208
+ renamed env var, a moved `emit()` call, a reworded error message, ...).
209
+ Update the corresponding matcher in `scripts/lib/compat-contracts.mjs`
135
210
  (and its test in `tests/scripts/compat-contracts.test.ts`) to match the
136
- new shape, update the pinned-contracts table's version/line reference
137
- above, and fix whichever pi-lens module (`subagent-mode.ts` /
211
+ new shape, update the pinned-contracts table's version/reference above,
212
+ and fix whichever pi-lens module (`subagent-mode.ts` /
138
213
  `session-lifecycle.ts` / `instance-reaper.ts`) actually depended on the
139
- old shape if the drift broke real behavior — not just the check.
214
+ old shape if the drift broke real behavior — not just the check. If
215
+ pi-lens's own dependent code already tolerates the drift gracefully (as
216
+ with contract 1's now-removed identity vars, #2581), relaxing the check
217
+ to stop requiring what upstream removed IS the fix — don't keep a check
218
+ that can only ever report drift forever for something pi-lens never
219
+ needed to work.
140
220
  4. A Layer B failure (an assertion, not an infra error) means real pi-lens
141
221
  behavior regressed under a real `pi` — treat it like any other bug: write
142
222
  a fixture-level test if the gap wasn't otherwise covered, then fix.
@@ -135,10 +135,10 @@ Rule source: `rules/tree-sitter-queries/<language>/`.
135
135
  | `open-invalid-mode` | error | reliability | open() called with invalid mode '{{MODE}}' |
136
136
  | `python-assert-production` | warning | correctness | assert statement stripped by Python -O flag — use explicit checks with exceptions in production code |
137
137
  | `python-command-injection` | error | security | Potential command injection sink — avoid shell execution with dynamic input |
138
- | `python-cross-language-method` | warning | correctness | '{METHOD}' is not a Python method — likely a {LANG} idiom leaking in |
138
+ | `python-cross-language-method` | warning | correctness | '{{METHOD}}' is not a Python method — likely a cross-language idiom leaking in |
139
139
  | `python-debugger` | warning | debugging | Debugger call '{{FUNC}}' — remove before committing |
140
140
  | `python-empty-except` | warning | reliability | Except block only contains 'pass' — handle or re-raise the exception |
141
- | `python-hallucinated-import` | warning | correctness | Hallucinated import — '{NAME}' does not exist in '{MODULE}' |
141
+ | `python-hallucinated-import` | warning | correctness | Hallucinated import — '{{NAME}}' does not exist in '{{MODULE}}' |
142
142
  | `python-hardcoded-secrets` | warning | security | Hardcoded {{VARNAME}} — use environment variables or a secrets manager |
143
143
  | `python-insecure-deserialization` | error | security | Potential insecure deserialization sink — avoid unsafe loaders |
144
144
  | `python-insecure-random` | warning | security | Insecure randomness source detected — use secrets or os.urandom for security-sensitive values |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "4.1.3",
3
+ "version": "4.1.4",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {
@@ -15,10 +15,13 @@
15
15
  },
16
16
  "scripts": {
17
17
  "build": "tsc --project tsconfig.build.json",
18
- "build:dist": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && npx --yes -p typescript@7.0.2 tsc --project tsconfig.dist.json --noCheck && npm run bundle:dist",
18
+ "build:dist": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && node scripts/build-dist-tsc.mjs tsconfig.dist.json && npm run bundle:dist",
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
- "lint": "tsc --project tsconfig.json",
21
+ "prepack": "node scripts/strip-dev-deps-for-pack.mjs --strip",
22
+ "postpack": "node scripts/strip-dev-deps-for-pack.mjs --restore",
23
+ "lint": "tsc --project tsconfig.json && npm run lint:js",
24
+ "lint:js": "oxlint --deny-warnings --ignore-pattern \"tests/**/*.ts\" --ignore-pattern \"tests/**/*.tsx\" --ignore-pattern \"**/*.d.mts\" --ignore-pattern \"tests/fixtures/autofix-smoke/**\" --ignore-pattern \"tests/fixtures/format-smoke/**\" --ignore-pattern \"tests/fixtures/tool-smoke/**\" --ignore-pattern \"tests/fixtures/call-graph/javascript/**\" --ignore-pattern \"tests/fixtures/function-facts/javascript-parameter.js\" .",
22
25
  "depcruise:check": "depcruise --validate --config .dependency-cruiser.cjs --ignore-known .dependency-cruiser-known-violations.json --output-type err index.ts \"clients/**/*.ts\"",
23
26
  "fmt": "oxfmt",
24
27
  "fmt:check": "oxfmt --check",
@@ -26,7 +29,9 @@
26
29
  "test": "node scripts/with-test-lock.mjs -- vitest run",
27
30
  "test:unit": "node scripts/with-test-lock.mjs -- vitest run",
28
31
  "test:integration": "node scripts/with-test-lock.mjs -- vitest run tests/index-integration.test.ts tests/clients/lsp/integration.test.ts",
32
+ "test:targeted": "node scripts/with-test-lock.mjs --shared -- vitest run",
29
33
  "test:watch": "vitest",
34
+ "hygiene": "node scripts/prune-agent-worktrees.mjs",
30
35
  "check": "node scripts/check-extensions.mjs",
31
36
  "selftest:install": "node scripts/install-selftest.mjs",
32
37
  "check:lockfile": "node scripts/check-lockfile-sync.mjs",
@@ -72,6 +77,9 @@
72
77
  ],
73
78
  "author": "Apostolos Mantzaris",
74
79
  "license": "MIT",
80
+ "engines": {
81
+ "node": ">=22.19.0"
82
+ },
75
83
  "files": [
76
84
  "dist/",
77
85
  "docs/",
@@ -85,6 +93,7 @@
85
93
  "scripts/analyze-pi-lens-logs.mjs",
86
94
  "scripts/install-selftest.mjs",
87
95
  "scripts/lib/host-provided-deps.mjs",
96
+ "scripts/lib/skills-predicate.mjs",
88
97
  "scripts/lib/warm-loader-cache.mjs",
89
98
  "scripts/warm-loader-cache.mjs",
90
99
  "scripts/rpc-load-check.mjs",
@@ -103,7 +112,7 @@
103
112
  },
104
113
  "peerDependencies": {
105
114
  "@earendil-works/pi-coding-agent": "*",
106
- "@earendil-works/pi-tui": "^0.84.1",
115
+ "@earendil-works/pi-tui": "^0.84.1 || ^0.85.0",
107
116
  "typebox": "^1.0.0"
108
117
  },
109
118
  "peerDependenciesMeta": {
@@ -121,29 +130,12 @@
121
130
  "jiti": "^2.7.0",
122
131
  "web-tree-sitter": "0.25.10"
123
132
  },
124
- "devDependencies": {
125
- "@biomejs/biome": "^2.4.10",
126
- "@earendil-works/pi-coding-agent": "^0.84.1",
127
- "@earendil-works/pi-tui": "^0.84.1",
128
- "@types/node": "^26.0.0",
129
- "@types/pidusage": "^2.0.5",
130
- "@vitest/coverage-v8": "^4.1.5",
131
- "dependency-cruiser": "^18.2.0",
132
- "husky": "^9.1.7",
133
- "json5": "^2.2.3",
134
- "oxfmt": "0.64.0",
135
- "oxlint": "1.79.0",
136
- "typebox": "^1.0.0",
137
- "typescript": "^7.0.2",
138
- "typescript-language-server": "^6.0.0",
139
- "vitest": "^4.1.1"
140
- },
141
133
  "pi": {
142
134
  "extensions": [
143
135
  "./dist/index.js"
144
136
  ],
145
137
  "skills": [
146
- "../../skills"
138
+ "./skills"
147
139
  ]
148
140
  },
149
141
  "knip": {
@@ -159,7 +151,7 @@
159
151
  ]
160
152
  },
161
153
  "allowScripts": {
162
- "@ast-grep/cli@0.45.1": true,
154
+ "@ast-grep/cli@0.45.2": true,
163
155
  "@google/genai@1.52.0": true,
164
156
  "protobufjs@7.6.5": true
165
157
  }
@@ -9,7 +9,7 @@ defect_class: hallucination
9
9
  inline_tier: warning
10
10
  language: python
11
11
 
12
- message: "'{METHOD}' is not a Python method — likely a {LANG} idiom leaking in"
12
+ message: "'{{METHOD}}' is not a Python method — likely a cross-language idiom leaking in"
13
13
 
14
14
  description: |
15
15
  A method name from another language is being called on a Python object.
@@ -27,7 +27,7 @@ query: |
27
27
  function: (attribute
28
28
  object: (_) @OBJ
29
29
  attribute: (identifier) @METHOD)
30
- (#match? @METHOD "^(push|forEach|indexOf|charAt|substring|hasOwnProperty|unshift|flatMap|padStart|padEnd|trimStart|trimEnd|equals|isEmpty|println|printf|getClass|hashCode|toCharArray|getBytes|compareTo|equalsIgnoreCase|startsWith|endsWith|each|collect|select|reject|detect|inject|chomp|chop|gsub|upcase|downcase|present|blank|Add|Contains|ToLower|ToUpper|Trim|Substring|WriteLine|ReadLine|TryParse|forEach|includes|assign|freeze|splice|unshift|shift|flatMap)$"))
30
+ (#match? @METHOD "^(push|forEach|indexOf|charAt|substring|hasOwnProperty|unshift|flatMap|padStart|padEnd|trimStart|trimEnd|equals|isEmpty|println|printf|getClass|hashCode|toCharArray|getBytes|compareTo|equalsIgnoreCase|startsWith|endsWith|each|collect|reject|detect|inject|chomp|chop|gsub|upcase|downcase|present|blank|Add|Contains|ToLower|ToUpper|Trim|Substring|WriteLine|ReadLine|TryParse|forEach|includes|assign|freeze|splice|unshift|shift|flatMap)$"))
31
31
 
32
32
  metavars:
33
33
  - OBJ
@@ -35,7 +35,7 @@ metavars:
35
35
 
36
36
  post_filter: match_captures
37
37
  post_filter_params:
38
- METHOD: "^(push|forEach|indexOf|charAt|substring|hasOwnProperty|unshift|flatMap|padStart|padEnd|trimStart|trimEnd|equals|isEmpty|println|printf|getClass|hashCode|toCharArray|getBytes|compareTo|equalsIgnoreCase|startsWith|endsWith|each|collect|select|reject|detect|inject|chomp|chop|gsub|upcase|downcase|present|blank|Add|Contains|ToLower|ToUpper|Trim|Substring|WriteLine|ReadLine|TryParse|forEach|includes|assign|freeze|splice|unshift|shift|flatMap)$"
38
+ METHOD: "^(push|forEach|indexOf|charAt|substring|hasOwnProperty|unshift|flatMap|padStart|padEnd|trimStart|trimEnd|equals|isEmpty|println|printf|getClass|hashCode|toCharArray|getBytes|compareTo|equalsIgnoreCase|startsWith|endsWith|each|collect|reject|detect|inject|chomp|chop|gsub|upcase|downcase|present|blank|Add|Contains|ToLower|ToUpper|Trim|Substring|WriteLine|ReadLine|TryParse|forEach|includes|assign|freeze|splice|unshift|shift|flatMap)$"
39
39
 
40
40
  has_fix: false
41
41
 
@@ -9,7 +9,7 @@ defect_class: hallucination
9
9
  inline_tier: blocking
10
10
  language: python
11
11
 
12
- message: "Hallucinated import — '{NAME}' does not exist in '{MODULE}'"
12
+ message: "Hallucinated import — '{{NAME}}' does not exist in '{{MODULE}}'"
13
13
 
14
14
  description: |
15
15
  This import is from the wrong package. A name from one framework is being
@@ -32,7 +32,7 @@ metavars:
32
32
 
33
33
  post_filter: match_captures
34
34
  post_filter_params:
35
- MODULE: "^(requests|flask|django|typing|collections|asyncio|json|unittest|pytest|urllib|sqlalchemy)$"
35
+ MODULE: "^(requests|flask|django|typing|collections|asyncio|json|unittest|pytest|urllib)$"
36
36
  NAME: "^(JSONResponse|HTMLResponse|RedirectResponse|StreamingResponse|Depends|Query|Path|Body|Header|Cookie|Form|File|UploadFile|FastAPI|APIRouter|HTTPException|BackgroundTasks|dataclass|fields|BaseModel|Field|validator|aiohttp|parse|stringify|fixture|TestCase|get|post|put|delete|Model|Session|Column|Integer|String)$"
37
37
 
38
38
  has_fix: false
@@ -18,8 +18,18 @@ description: |
18
18
 
19
19
  Suppressed:
20
20
  - cursor.execute(query, params) — parameterized query (safe)
21
- - session.execute(select(...))SQLAlchemy ORM (safe by construction)
22
- - cursor.execute(f"...") — f-string with literal-only interpolation (rare false positive)
21
+ - session.execute(...) — receiver conventionally named for a SQLAlchemy session
22
+ - x.execute(select(...))statement builder in argument position (safe by
23
+ construction)
24
+ - db.query(...) / db.execute(stmt) where `db: Session` and `Session` is proven
25
+ from a same-file `sqlalchemy.orm` / `sqlalchemy.ext.asyncio` import, and
26
+ `stmt` is bound exactly once to a builder call
27
+ - cursor.execute(sql.SQL("...").format(sql.Identifier(...))) where `sql` is
28
+ an unshadowed same-file psycopg or psycopg2 import and every format argument
29
+ is an Identifier
30
+
31
+ Other raw, dynamic, or ambiguous SQL remains diagnostic so this rule fails
32
+ closed when it cannot prove the safe API provenance.
23
33
 
24
34
  ✅ FIX: use placeholders and parameter binding instead of string composition.
25
35