@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
@@ -63,4 +63,35 @@ export class RustClient {
63
63
  isRustFile(filePath) {
64
64
  return path.extname(filePath).toLowerCase() === ".rs";
65
65
  }
66
+ /** Forget the memoized cargo path and latched availability verdict — #2455. */
67
+ resetAvailability() {
68
+ this.availability.reset();
69
+ }
70
+ }
71
+ /**
72
+ * The one `RustClient` this process owns (#2455 fix round 4, F2).
73
+ *
74
+ * Same shape as `go-client.ts`'s `goClient`, and the same round-2 defect: the
75
+ * runner (`dispatch/runners/rust-clippy.ts`) and `bootstrap.ts` each built
76
+ * their own instance, so `resetRustAvailability` re-armed one latch while
77
+ * `handleSessionStart`'s "Active tools" line read the other's never-expiring
78
+ * probe-class "missing" verdict. One instance, in the module that owns the
79
+ * class; `tests/clients/toolchain-client-singleton.test.ts` holds the line.
80
+ */
81
+ export const rustClient = new RustClient();
82
+ /**
83
+ * Forget `rustClient`'s memoized cargo path and latched availability verdict —
84
+ * #2455. Same #1496/#1535 shape as `resetZizmorTokenAvailability`; wired into
85
+ * `handleSessionStart` beside it (`clients/runtime-session.ts`) so a Rust
86
+ * toolchain installed mid-process is observed by the next session instead of
87
+ * staying "missing" for the rest of the process's life.
88
+ *
89
+ * This module — not `rust-clippy.ts` — because the reset must sit beside the
90
+ * single instance it clears (#2455 fix round 4, F2). `rust-clippy.ts`'s
91
+ * `clippyAvailabilityByCargo` is a separate, already-covered latch: it rides
92
+ * `createCwdCachedProbe`'s shared `availabilityGeneration` counter (see
93
+ * `runner-helpers.ts`'s `resetDispatchAvailabilityState`), not this reset.
94
+ */
95
+ export function resetRustAvailability() {
96
+ rustClient.resetAvailability();
66
97
  }
@@ -16,6 +16,7 @@
16
16
  import { spawn, spawnSync, } from "node:child_process";
17
17
  import * as fs from "node:fs";
18
18
  import * as path from "node:path";
19
+ import { BoundedFifoMap } from "./bounded-cache.js";
19
20
  import { logLatency } from "./latency-logger.js";
20
21
  import { recordDegradation } from "./degradation-ledger.js";
21
22
  import { logExtension } from "./extension-log.js";
@@ -570,23 +571,21 @@ function resolveWindowsPathEntry(entry, effectiveCwd, env) {
570
571
  * cache is also count-bounded with oldest-entry eviction because a long-lived
571
572
  * process can encounter unbounded cwd/environment combinations.
572
573
  */
573
- const WINDOWS_COMMAND_CACHE_MAX_ENTRIES = 256;
574
+ export const WINDOWS_COMMAND_CACHE_MAX_ENTRIES = 256;
574
575
  const WINDOWS_COMMAND_NEGATIVE_CACHE_TTL_MS = 1000;
575
- const windowsCommandCache = new Map();
576
+ const windowsCommandCache = new BoundedFifoMap(WINDOWS_COMMAND_CACHE_MAX_ENTRIES);
576
577
  /** Reset after session replacement or a successful managed install. */
577
578
  export function resetSafeSpawnWindowsCommandCache() {
578
579
  windowsCommandCache.clear();
579
580
  loggedSpawnClassifications.clear();
580
581
  }
581
582
  function cacheWindowsCommandResult(key, resolved) {
582
- if (windowsCommandCache.size >= WINDOWS_COMMAND_CACHE_MAX_ENTRIES) {
583
- // Resolution entries are session-scoped and cheap to recompute. Evict the
584
- // oldest insertion first so a long-lived process cannot retain every cwd /
585
- // environment it has ever touched.
586
- const oldest = windowsCommandCache.keys().next().value;
587
- if (oldest !== undefined)
588
- windowsCommandCache.delete(oldest);
589
- }
583
+ // Resolution entries are session-scoped and cheap to recompute. The
584
+ // caller always deletes any stale entry for `key` before reaching here
585
+ // (see resolveWindowsCommandForEnvironment), so this can only grow the
586
+ // map; BoundedFifoMap evicts the oldest insertion first once the map
587
+ // exceeds capacity, so a long-lived process cannot retain every cwd /
588
+ // environment it has ever touched.
590
589
  windowsCommandCache.set(key, { resolved, checkedAt: Date.now() });
591
590
  }
592
591
  function getPathExts(env) {
@@ -848,6 +847,30 @@ const EXIT_PIPE_IDLE_MAX_WAIT_MS = 2000;
848
847
  * - Kills the process on timeout (preventing zombies)
849
848
  * - Supports cancellation via AbortSignal
850
849
  *
850
+ * ## Why this bound is NOT `bounded()` — the one documented exception (#2523)
851
+ *
852
+ * #2523 slice 2 folded every other "deadline AND abort signal" implementation
853
+ * in the tree onto `clients/deadline-utils.ts#bounded`. This one stays, and it
854
+ * is not a copy of the same idea:
855
+ *
856
+ * `bounded()` ABANDONS a promise — it stops waiting and lets the work run on
857
+ * unobserved, which is safe precisely because everything it wraps is in-process
858
+ * (a dynamic import, an LSP round trip, a `stat`). This function's work is a
859
+ * CHILD PROCESS, and abandoning one leaks it. So its timer does not settle a
860
+ * race at all: `timeoutId` sets `timedOut`, then runs `killTree()` — SIGTERM,
861
+ * an escalation timer, SIGKILL, and on Windows a `taskkill /T` of the whole
862
+ * tree — and the promise settles only when the child's own `close` arrives (or
863
+ * the escalation gives up). The result it produces carries the teardown
864
+ * EVIDENCE (`timeoutTeardown`, #2010): which signal killed it, whether the
865
+ * tree actually died, how long it took. Replacing that with `bounded()` would
866
+ * return `undefined` at the deadline and leave a live `vitest`/`gradle` behind
867
+ * — the orphan class #2504 measured as 59 concurrent spawns starving a turn.
868
+ *
869
+ * The rule the exception preserves: a bound that can CANCEL its work must
870
+ * cancel it. `bounded()` is for work that cannot be cancelled; this is not
871
+ * that. Anything wrapping THIS function still gets `bounded()` around its own
872
+ * await if it needs one.
873
+ *
851
874
  * @example
852
875
  * const result = await safeSpawnAsync("npm", ["test"], { timeout: 30000 });
853
876
  * if (result.error) console.error("Failed:", result.error);
@@ -6,8 +6,14 @@
6
6
  */
7
7
  // --- Constants ---
8
8
  // ANSI escape codes for colors and formatting
9
+ // oxlint-disable-next-line no-control-regex -- ESC (\x1b) is the literal ANSI escape-sequence lead byte this pattern strips, not accidental input.
9
10
  const ANSI_ESCAPE = /\x1b\[[0-9;]*m/g;
10
- const ANSI_ESCAPE_EXTENDED = /\x1b\[[0-9;]*[A-Za-z]/g;
11
+ // Full ECMA-48 CSI grammar (parameter bytes 0-9;? , intermediate bytes
12
+ // space-/, final byte @-~) — matches every CSI sequence ANSI_ESCAPE does
13
+ // plus non-SGR ones (cursor moves, private-mode `?` sequences like
14
+ // `\x1b[?25l`, etc.), not just the `m`-terminated color codes.
15
+ // oxlint-disable-next-line no-control-regex -- ESC (\x1b) is the literal ANSI escape-sequence lead byte this pattern strips, not accidental input.
16
+ const ANSI_ESCAPE_EXTENDED = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
11
17
  // Common error patterns from different tools
12
18
  const ERROR_INDICATORS = [
13
19
  /\berror\b/i,
@@ -54,6 +54,7 @@
54
54
  * fully in scope.
55
55
  */
56
56
  import { EXCLUDED_DIRS, getExcludedDirGlobs } from "./file-utils.js";
57
+ import { escapeRegExp } from "./string-utils.js";
57
58
  /** Directory-name entries only — drops glob entries (e.g. `*.dSYM`) that a
58
59
  * bare directory-name/regex exclude can't express. */
59
60
  function literalExcludedDirNames() {
@@ -68,9 +69,6 @@ function literalExcludedDirNames() {
68
69
  export function getScratchTreeGlobPatterns() {
69
70
  return getExcludedDirGlobs();
70
71
  }
71
- function escapeRegExp(literal) {
72
- return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
73
- }
74
72
  /** Bare directory names, for tools (opengrep/semgrep `--exclude`) that treat a
75
73
  * slash-free pattern as "this directory name, anywhere in the tree". */
76
74
  export function getScratchTreeDirNames() {
@@ -71,6 +71,7 @@ export class SecurityScanClient {
71
71
  logDurableAbsence(evidence, elapsedMs = 0) {
72
72
  logAvailabilityDecision({
73
73
  tool: this.toolName,
74
+ producer: "security-scan",
74
75
  verdict: "unavailable",
75
76
  outcome: "missing",
76
77
  cause: "not-found",
@@ -154,6 +155,7 @@ export class SecurityScanClient {
154
155
  this.log(`${this.toolName} found: ${probe.stdout.trim().split("\n")[0]}`);
155
156
  logAvailabilityDecision({
156
157
  tool: this.toolName,
158
+ producer: "security-scan",
157
159
  verdict: "available",
158
160
  outcome: "success",
159
161
  cause: "ok",
@@ -185,6 +187,7 @@ export class SecurityScanClient {
185
187
  : 0;
186
188
  logAvailabilityDecision({
187
189
  tool: this.toolName,
190
+ producer: "security-scan",
188
191
  verdict: "unavailable",
189
192
  outcome,
190
193
  cause,
@@ -91,7 +91,7 @@ export function getActivePrimaryRoot() {
91
91
  * `concurrent-secondary`: skip the destructive resets and the expensive
92
92
  * battery, leave the registered primary's ctx/session id/root untouched. The
93
93
  * root still gets served — `initLSPConfig` registers session roots lazily,
94
- * per file (`clients/lsp/session-roots.ts:48`), not from this handler.
94
+ * per file (`clients/lsp/session-roots.ts`), not from this handler.
95
95
  *
96
96
  * Ordering note: the root check sits BELOW the `priorCtxActive === true`
97
97
  * branch so a live sibling still reports the more specific
@@ -1,10 +1,42 @@
1
1
  import { logLatency } from "./latency-logger.js";
2
+ import { getProcessSingleton } from "./process-singletons.js";
2
3
  /**
3
4
  * The complete observability path for a concurrent secondary. It deliberately
4
5
  * emits one bind record only: secondary sessions skip every primary reset and
5
6
  * hydration phase.
6
7
  */
8
+ // #2249: bounded per-session rollup of declined binds, counted at this shared
9
+ // seam so every decline the process's PRIMARY has seen this session —
10
+ // `concurrent-secondary` and `secondary-root` alike — is summarized in one row
11
+ // at session end instead of requiring a reader to hand-count
12
+ // `concurrent_session_bind` lines. Process-singleton backed (AGENTS.md catalog
13
+ // shape 25), NOT a module-scope `let` like the other `*_rollup` counters
14
+ // (bus-events-logger.ts, path-attribution-telemetry.ts): a `let` here exists
15
+ // once per module EVALUATION, not once per process, and #2146 measured one pid
16
+ // evaluating this exact graph nine times — a bare `let` would only ever see
17
+ // the binds routed through its own copy, undercounting every rollup that
18
+ // isn't reached through the first evaluation. `decideSessionStart` never
19
+ // returns a declined classification outside `concurrent-secondary`/
20
+ // `secondary-root` (session-lifecycle.ts), so those two are the whole
21
+ // contract; `unclassified` is a bounded third bucket for the legacy callers
22
+ // this function's `classification?` param still accepts.
23
+ const BIND_ROLLUP_FAMILY = "concurrent-session-bind-rollup";
24
+ const BIND_ROLLUP_VERSION = 1;
25
+ function bindRollupCounts() {
26
+ return getProcessSingleton(BIND_ROLLUP_FAMILY, BIND_ROLLUP_VERSION, () => ({
27
+ "concurrent-secondary": 0,
28
+ "secondary-root": 0,
29
+ unclassified: 0,
30
+ }));
31
+ }
32
+ function bindRollupKey(classification) {
33
+ return classification === "concurrent-secondary" ||
34
+ classification === "secondary-root"
35
+ ? classification
36
+ : "unclassified";
37
+ }
7
38
  export function logConcurrentSessionBind(args) {
39
+ bindRollupCounts()[bindRollupKey(args.classification)] += 1;
8
40
  logLatency({
9
41
  type: "phase",
10
42
  filePath: "<pi-lens>",
@@ -13,3 +45,50 @@ export function logConcurrentSessionBind(args) {
13
45
  metadata: args,
14
46
  });
15
47
  }
48
+ /** Snapshot of the current rollup counters, keyed by classification.
49
+ * Non-mutating — for tests/observability, mirroring
50
+ * `getBusEventRollupCounts`/`getVerifiedPathAttributionGuessCount`. */
51
+ export function getConcurrentSessionBindRollupCounts() {
52
+ return { ...bindRollupCounts() };
53
+ }
54
+ /**
55
+ * Log one `concurrent_session_bind_rollup` row summarizing this session's
56
+ * declined binds by classification, then clear the counters. Call from
57
+ * index.ts's `session_shutdown` handler at the same primary-only placement as
58
+ * `emitBusEventRollupAtSessionEnd`/`emitVerifiedPathAttributionRollup` — a
59
+ * concurrent secondary's own shutdown returns before reaching that point, and
60
+ * the counters are process-wide state a still-live secondary would still need.
61
+ * A no-op when nothing was ever declined this session, matching those same
62
+ * rollups' "no noise on an ordinary session" shape.
63
+ */
64
+ export function emitConcurrentSessionBindRollupAtSessionEnd(cwd) {
65
+ const counts = bindRollupCounts();
66
+ const total = counts["concurrent-secondary"] +
67
+ counts["secondary-root"] +
68
+ counts.unclassified;
69
+ if (total === 0)
70
+ return;
71
+ logLatency({
72
+ type: "phase",
73
+ filePath: cwd,
74
+ phase: "concurrent_session_bind_rollup",
75
+ durationMs: 0,
76
+ metadata: { ...counts },
77
+ });
78
+ resetConcurrentSessionBindRollupCounts();
79
+ }
80
+ /**
81
+ * Session-boundary reset (AGENTS.md catalog shape 17). Call from index.ts's
82
+ * `session_start` handler on the PRIMARY continuation path only — never from
83
+ * a declined bind's own `session_start`, which is exactly what increments
84
+ * these counters, so resetting there would erase every prior sibling's tally
85
+ * from the same primary session. Covers the case a primary session never
86
+ * reaches `session_shutdown` (a crash, a forced kill) and the process's next
87
+ * primary must still start from zero. Also exported for tests.
88
+ */
89
+ export function resetConcurrentSessionBindRollupCounts() {
90
+ const counts = bindRollupCounts();
91
+ counts["concurrent-secondary"] = 0;
92
+ counts["secondary-root"] = 0;
93
+ counts.unclassified = 0;
94
+ }
@@ -1,10 +1,19 @@
1
1
  import * as path from "node:path";
2
2
  import { isTestMode } from "./env-utils.js";
3
- import { getGlobalPiLensDir } from "./file-utils.js";
3
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
4
+ import { getMaxLogSizeMB } from "./log-cleanup.js";
4
5
  import { createNdjsonLogger } from "./ndjson-logger.js";
5
- export const SESSIONSTART_LOG_FILE = path.join(getGlobalPiLensDir(), "sessionstart.log");
6
+ export const SESSIONSTART_LOG_FILE = path.join(getGlobalPiLensLogDir(), "sessionstart.log");
6
7
  // All ordinary writers share this queue, preserving their in-process order.
7
- const writer = createNdjsonLogger({ filePath: SESSIONSTART_LOG_FILE });
8
+ // Bounded like every other global sink (#2505): an unbounded sink has no
9
+ // size check on the write path at all, so it grows until the
10
+ // once-per-process session-start sweep happens to look at it — which, in a
11
+ // long-lived process (the warm MCP server), may be never.
12
+ const writer = createNdjsonLogger({
13
+ filePath: SESSIONSTART_LOG_FILE,
14
+ maxBytes: getMaxLogSizeMB() * 1024 * 1024,
15
+ backupPath: `${SESSIONSTART_LOG_FILE}.1`,
16
+ });
8
17
  export function logSessionStart(message) {
9
18
  if (isTestMode())
10
19
  return;
@@ -9,7 +9,12 @@ import { findLocalToolConfig } from "./path-utils.js";
9
9
  // it only operates in a project that has an `sgconfig.y[a]ml` at (or above) the
10
10
  // file. These are the names ast-grep itself looks for as root markers.
11
11
  export const SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
12
- /** Nearest `sgconfig.y[a]ml` walking up from `startDir`, or undefined if none. */
12
+ /**
13
+ * Nearest `sgconfig.y[a]ml` walking up from `startDir`, or undefined if none.
14
+ * Deliberately UNCEILINGED at $HOME (refs #2472 review round 3, F1): ast-grep
15
+ * itself resolves `~/sgconfig.yml` as a legitimate global root config, not an
16
+ * escaped-workspace accident.
17
+ */
13
18
  export function findLocalSgconfig(startDir) {
14
19
  return findLocalToolConfig(startDir, SGCONFIG_NAMES);
15
20
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Resolves the skills directory the `resources_discover` handler (#205)
3
+ * registers, and reports the one condition that pi previously loaded with NO
4
+ * visible signal: `<packageRoot>/skills` is absent, unreadable, or holds no
5
+ * skill pi's own loader would find (#2626).
6
+ *
7
+ * `resolvePackagePath(import.meta.url, "skills")`-equivalent resolution
8
+ * (`clients/package-root.ts`'s `getPackageRoot`) is correct for both the
9
+ * source and compiled `dist/` layouts — it walks up from the loaded entry
10
+ * file to the nearest `package.json`. It goes wrong silently, not loudly,
11
+ * when that walk lands somewhere with no `skills/` beside it: `skills/`
12
+ * missing from an installed package, or the entry file copied out of the
13
+ * package tree by a managed extension cache so the nearest `package.json`
14
+ * is the cache's own. Either way `resources_discover` used to hand pi a
15
+ * path that does not exist, `pi` registered zero skills, and nothing — no
16
+ * extension error, no stderr line — said so (investigated on #2587).
17
+ *
18
+ * #2626 review round 2, F1: the FIRST version of this fix inverted the
19
+ * acceptance criterion — it returned `[]` (no skills registered) whenever
20
+ * its own predicate disagreed with pi's real loader, which is worse than
21
+ * the bug it fixed on every layout the predicate got wrong (dropping skills
22
+ * pi WOULD have loaded, a regression vs. pre-fix `master`). The acceptance
23
+ * criteria ask for a RECORD, never a changed return value: this module
24
+ * returns `[skillsDir]` UNCONDITIONALLY, exactly like the pre-fix handler —
25
+ * pi's own `loadSkills`/`collectSkillEntries` already treat a nonexistent
26
+ * path as zero skills from that entry, gracefully, so handing it a path
27
+ * that turns out to be empty is exactly as safe as `master`'s behavior
28
+ * always was. The health check below is PURELY observational.
29
+ *
30
+ * #2636 review F4: the ENOENT/EACCES classification shell below used to be
31
+ * hand-rolled here, character-identical to the ast-grep/tree-sitter sites
32
+ * #2636 fixed for the same silent-zero shape — now shared via
33
+ * `bundled-resource-health.ts`, with `scanEntriesForSkills` supplied as this
34
+ * module's own "what counts as found" predicate.
35
+ */
36
+ import * as path from "node:path";
37
+ import { fileURLToPath } from "node:url";
38
+ import * as fs from "node:fs";
39
+ import { scanEntriesForSkills } from "../scripts/lib/skills-predicate.mjs";
40
+ import { classifyBundledResourceDir, describeBundledResourceHealth, reportBundledResourceDirHealth, } from "./bundled-resource-health.js";
41
+ import { logLatency } from "./latency-logger.js";
42
+ import { getPackageRoot } from "./package-root.js";
43
+ /** The ledger kind this module records under (`clients/degradation-ledger.ts`). */
44
+ const SKILLS_DIR_MISSING_KIND = "skills-dir-missing";
45
+ /**
46
+ * Read `skillsDir` and classify it, sharing `classifyBundledResourceDir`'s
47
+ * ENOENT/EACCES/empty shell but counting pi-LOADABLE skill entry points
48
+ * (`scanEntriesForSkills`), not a bare directory listing — a `skills/`
49
+ * holding only a stray `.gitkeep` must read "empty", not "healthy".
50
+ */
51
+ function checkSkillsHealth(skillsDir) {
52
+ return classifyBundledResourceDir(skillsDir, (dir) => {
53
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
54
+ return scanEntriesForSkills(dir, entries, true).length;
55
+ });
56
+ }
57
+ /**
58
+ * Resolve the skill path for `resources_discover`. ALWAYS returns
59
+ * `[skillsDir]` (see the module doc for why — F1). When `skillsDir` is
60
+ * absent, unreadable, or holds no skill pi's loader would find, records ONE
61
+ * bounded `skills-dir-missing` degradation and a single `notifyUserDegradation`
62
+ * warning, both naming the resolved path and the entry file's directory.
63
+ *
64
+ * Either way, one `skills_resolved` phase/latency record is written so an
65
+ * empty ledger (no `skills-dir-missing` row) is distinguishable from
66
+ * "`resources_discover` never ran at all" (#2626 review F5) — the SAME
67
+ * silent-zero shape one level up the call stack.
68
+ */
69
+ export function resolveSkillPaths(importMetaUrl) {
70
+ const packageRoot = getPackageRoot(importMetaUrl);
71
+ const skillsDir = path.join(packageRoot, "skills");
72
+ const health = checkSkillsHealth(skillsDir);
73
+ logLatency({
74
+ type: "phase",
75
+ phase: "skills_resolved",
76
+ filePath: skillsDir,
77
+ durationMs: 0,
78
+ metadata: {
79
+ status: health.status,
80
+ entryCount: health.status === "healthy" ? health.entryCount : 0,
81
+ },
82
+ });
83
+ if (health.status !== "healthy") {
84
+ const entryDir = path.dirname(fileURLToPath(importMetaUrl));
85
+ const reason = `${describeBundledResourceHealth(health, skillsDir, `no SKILL.md (or loadable .md) found under ${skillsDir}`)} (entry loaded from ${entryDir})`;
86
+ // #2626 review F3: the notify-once gate previously re-derived "have we
87
+ // already recorded this" by scanning `getDegradationSummary()` and
88
+ // comparing the RAW `skillsDir` against subjects the ledger stores
89
+ // `truncateForLedger`-ed — a mismatch on any subject over
90
+ // `LEDGER_FIELD_MAX` (200 chars, an ordinary length for a managed-cache
91
+ // path) renotified on every call. `reportBundledResourceDirHealth`
92
+ // reads `incrementDegradationCount`'s own return value for the rising
93
+ // edge, computed after the SAME truncation the ledger stores by, so
94
+ // there is no separate key to keep in sync.
95
+ // #2636 review round 2, F1: the shipped notify sentence ("registers
96
+ // zero skills") is user-visible product text #2626 chose deliberately
97
+ // — it names the exact SYMPTOM this whole issue exists to surface, so
98
+ // the shared helper's generic "<label> unavailable" template must
99
+ // never silently replace it. Passed explicitly rather than inferred
100
+ // from `label` so this stays a per-caller decision, not a convention
101
+ // the next caller has to remember.
102
+ reportBundledResourceDirHealth(SKILLS_DIR_MISSING_KIND, skillsDir, health, "skills", reason, `pi-lens: registers zero skills — ${reason}.`);
103
+ }
104
+ return [skillsDir];
105
+ }
@@ -55,7 +55,7 @@
55
55
  */
56
56
  import * as fs from "node:fs";
57
57
  import * as path from "node:path";
58
- import { getGlobalPiLensDir } from "./file-utils.js";
58
+ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
59
59
  /** Bounded tail-read budget PER source log file — never a full-file scan. */
60
60
  export const SMELLS_TAIL_BYTES_PER_FILE = 64 * 1024;
61
61
  export const SMELLS_ROLLING_WINDOW_MS = 24 * 60 * 60_000;
@@ -159,7 +159,7 @@ function isOpengrepRespawn(entry) {
159
159
  * `2 * SMELLS_TAIL_BYTES_PER_FILE` regardless of how large the (size-rotated,
160
160
  * up to ~10MB) source files are. `root` is injectable for tests.
161
161
  */
162
- export function countRecentSmells(root = getGlobalPiLensDir(), sessionStartMs) {
162
+ export function countRecentSmells(root = getGlobalPiLensLogDir(), sessionStartMs) {
163
163
  const sinceMs = sessionStartMs ?? Date.now() - SMELLS_ROLLING_WINDOW_MS;
164
164
  const busTail = tailReadText(path.join(root, "bus-events.log"), SMELLS_TAIL_BYTES_PER_FILE);
165
165
  const latencyTail = tailReadText(path.join(root, "latency.log"), SMELLS_TAIL_BYTES_PER_FILE);
@@ -18,3 +18,16 @@
18
18
  export function compareOrdinal(a, b) {
19
19
  return a < b ? -1 : a > b ? 1 : 0;
20
20
  }
21
+ /**
22
+ * Escape a string for safe interpolation into a `RegExp` source, so every
23
+ * regex metacharacter it contains is matched literally instead of taking on
24
+ * its regex meaning. This is the ONE runtime copy (#2558) — string-utils.ts
25
+ * is a leaf (no imports of its own), so importing it never creates a cycle.
26
+ * Before #2558 this same body was hand-copied under this name (and as
27
+ * `escapeRegExpChar`/`escapeRegExpLiteral`) into ~8 production modules and 3
28
+ * test helpers; fold new call sites onto this export instead of re-copying
29
+ * it.
30
+ */
31
+ export function escapeRegExp(value) {
32
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
33
+ }
@@ -4,8 +4,16 @@
4
4
  * Two known subagent-spawning ecosystems set different env vocabularies in
5
5
  * every spawned child's environment:
6
6
  *
7
- * - nicobailon/pi-subagents sets `PI_SUBAGENT_CHILD=1` unconditionally (plus
8
- * `PI_SUBAGENT_RUN_ID` / `PI_SUBAGENT_CHILD_AGENT` for best-effort identity).
7
+ * - nicobailon/pi-subagents sets `PI_SUBAGENT_CHILD=1` unconditionally on
8
+ * every process that hosts a child session — the only var light mode keys
9
+ * on for this vocabulary. It ALSO used to set `PI_SUBAGENT_RUN_ID` /
10
+ * `PI_SUBAGENT_CHILD_AGENT` alongside it for best-effort identity, but
11
+ * removed both entirely in `pi-subagents@0.65.0`'s native-AgentSession
12
+ * rewrite (grep-verified absent from the whole 0.66.0 source tree, #2581/
13
+ * #2680) — child identity now travels through an in-process object, not
14
+ * env vars. `getSubagentIdentity()` below already treats identity as
15
+ * optional best-effort metadata, so this permanent absence for the
16
+ * nicobailon vocabulary needed no behavior change, only the doc update.
9
17
  * - avtc-pi-subagent (the spawn engine under avtc-pi-feature-flow) is the same
10
18
  * execution model — real child-process `pi --mode rpc` / `--mode json -p`
11
19
  * spawns, full env inheritance — but never sets `PI_SUBAGENT_CHILD`. It sets
@@ -63,8 +71,13 @@ export function isSubagentSession() {
63
71
  /**
64
72
  * Best-effort identity of the current subagent, read from the env vars the
65
73
  * detected extension sets alongside its subagent marker. Returns `undefined`
66
- * when neither identity var is present (e.g. not a subagent session, or a
67
- * future extension that doesn't set them).
74
+ * when neither identity var is present — not a subagent session, an
75
+ * extension that never set them, OR nicobailon/pi-subagents@0.65.0+, which
76
+ * removed both identity vars upstream while still setting `PI_SUBAGENT_CHILD`
77
+ * (#2581/#2680): light mode still engages via `isSubagentSession()` alone,
78
+ * `marker` is still populated, only `runId`/`agentName` degrade to
79
+ * `undefined`. Identity is metadata for observability, never a light-mode
80
+ * gate — callers must not treat its absence as "not a subagent".
68
81
  */
69
82
  export function getSubagentIdentity() {
70
83
  const runId = process.env.PI_SUBAGENT_RUN_ID || undefined;