@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

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 (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -9,6 +9,7 @@
9
9
  * Docs: https://knip.dev/
10
10
  */
11
11
  import { createSubsystemLogger } from "./extension-log.js";
12
+ import { incrementDegradationCount } from "./degradation-ledger.js";
12
13
  import * as fs from "node:fs";
13
14
  import * as path from "node:path";
14
15
  import { getProjectDataDir } from "./file-utils.js";
@@ -16,6 +17,10 @@ import { findNearestMarkerRoot } from "./path-utils.js";
16
17
  import { safeSpawnAsync } from "./safe-spawn.js";
17
18
  import { createAvailabilityChecker, findManagedNodeToolBinary, getManagedToolEnvironment, resolveAvailableOrInstall, } from "./dispatch/runners/utils/runner-helpers.js";
18
19
  import { createAvailabilityLatch, describeUnavailability, } from "./dispatch/runners/utils/availability-policy.js";
20
+ import { spawnFailedWithNoOutput } from "./dispatch/runners/utils/spawn-outcome.js";
21
+ import { formatToolFailure } from "./dispatch/runners/utils/tool-failure.js";
22
+ import { findLocalBinUpwards } from "./package-manager.js";
23
+ import { logSessionStart } from "./sessionstart-logger.js";
19
24
  const EMPTY_RESULT = {
20
25
  success: false,
21
26
  issues: [],
@@ -57,6 +62,105 @@ export function readOverridePinnedPackageNames(targetDir) {
57
62
  collectKeys(pkg.pnpm?.overrides);
58
63
  return names;
59
64
  }
65
+ /**
66
+ * knip's own config-file names, in knip's discovery order (knip 6
67
+ * `configFilesLookup`). Used for REPORTING which config a run resolved — the
68
+ * runner never passes `--config`, so knip still does its own discovery from the
69
+ * spawn cwd. Reading the same list here keeps the recorded answer honest
70
+ * instead of guessing "knip.json or nothing".
71
+ */
72
+ const KNIP_CONFIG_FILENAMES = [
73
+ "knip.json",
74
+ "knip.jsonc",
75
+ "knip.ts",
76
+ "knip.js",
77
+ "knip.config.ts",
78
+ "knip.config.js",
79
+ ".knip.json",
80
+ ".knip.jsonc",
81
+ ".knip.ts",
82
+ ".knip.js",
83
+ ];
84
+ /**
85
+ * The knip config this project ships, relative to `targetDir`, or null when the
86
+ * project ships none (knip then runs on its built-in defaults).
87
+ *
88
+ * `package.json` is reported last and only when it carries a `knip` field —
89
+ * knip reads that field as a config source, and a project that configures knip
90
+ * there is just as configured as one with a `knip.json`.
91
+ */
92
+ export function resolveProjectKnipConfig(targetDir) {
93
+ for (const name of KNIP_CONFIG_FILENAMES) {
94
+ if (fs.existsSync(path.join(targetDir, name)))
95
+ return name;
96
+ }
97
+ try {
98
+ const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, "package.json"), "utf-8"));
99
+ if (pkg && typeof pkg === "object" && pkg.knip !== undefined) {
100
+ return "package.json#knip";
101
+ }
102
+ }
103
+ catch {
104
+ // No/malformed package.json — "no project config" is the honest answer.
105
+ }
106
+ return null;
107
+ }
108
+ /**
109
+ * The knip the PROJECT itself would run: its own `node_modules/.bin/knip`,
110
+ * walking up so a workspace package finds the monorepo's hoisted copy. Null
111
+ * when the project installs no knip and pi-lens's managed shim is all there is.
112
+ *
113
+ * A project that installs knip pins the version its config is written against,
114
+ * and that version is what the project's `npx knip` / `npm run knip` runs.
115
+ * Preferring pi-lens's managed shim instead makes the lens report a DIFFERENT
116
+ * tool's verdict under the project's config (#1721): a managed knip 6.4.1
117
+ * flagged 62 unused type exports on a tree that knip 6.32.2 leaves clean, and
118
+ * every one of those flags is destructive advice.
119
+ *
120
+ * The walk is the shared `findLocalBinUpwards`, the same one behind jscpd's
121
+ * (`clients/jscpd-client.ts:269`) and madge's
122
+ * (`clients/dependency-checker.ts:420`) resolution, rather than a fourth copy.
123
+ * It stops at the filesystem half deliberately: the global-bin step those two
124
+ * add spawns a probe per package manager, and this runs on every analyze().
125
+ */
126
+ export function resolveProjectKnipBinary(targetDir) {
127
+ return findLocalBinUpwards("knip", targetDir) ?? null;
128
+ }
129
+ /**
130
+ * Classify a resolved shim for the toolchain record: inside the project tree,
131
+ * elsewhere on the user's machine, or pi-lens's own managed/PATH fallback.
132
+ */
133
+ export function classifyKnipBinary(binary, targetDir) {
134
+ if (binary === null)
135
+ return "managed-or-path";
136
+ const relative = path.relative(path.resolve(targetDir), path.resolve(binary));
137
+ return relative === "" || relative.startsWith("..") || path.isAbsolute(relative)
138
+ ? "global"
139
+ : "project";
140
+ }
141
+ /**
142
+ * The version of the knip package behind a resolved shim, read from disk (no
143
+ * spawn — a `--version` probe per project root would add a process to a hot
144
+ * path for a telemetry field). Returns null for a bare PATH command, whose
145
+ * package location is unknown.
146
+ */
147
+ function readKnipShimVersion(binary) {
148
+ if (!path.isAbsolute(binary))
149
+ return null;
150
+ const binDir = path.dirname(binary);
151
+ if (path.basename(binDir) !== ".bin")
152
+ return null;
153
+ try {
154
+ const pkg = JSON.parse(fs.readFileSync(path.join(path.dirname(binDir), "knip", "package.json"), "utf-8"));
155
+ const version = pkg.version;
156
+ return typeof version === "string" ? version : null;
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ }
162
+ /** Distinct toolchain records kept per client instance (bounded telemetry). */
163
+ const MAX_RECORDED_TOOLCHAINS = 32;
60
164
  // --- Client ---
61
165
  export class KnipClient {
62
166
  knipAvailability = createAvailabilityChecker("knip", ".cmd", ["--version"], {
@@ -87,11 +191,22 @@ export class KnipClient {
87
191
  * in-flight promise; completing clears the slot.
88
192
  */
89
193
  inFlight = new Map();
194
+ /** Last successful result per project and runtime content generation. */
195
+ completedByProject = new Map();
196
+ /**
197
+ * (project root, binary, config) triples already recorded, so the toolchain
198
+ * row is written once per distinct resolution instead of once per run.
199
+ */
200
+ recordedToolchains = new Set();
90
201
  constructor(verbose = false) {
91
202
  this.log = verbose
92
203
  ? createSubsystemLogger("knip")
93
204
  : () => { };
94
205
  }
206
+ /** Re-arm content-keyed reuse at the session boundary. */
207
+ resetSessionState() {
208
+ this.completedByProject.clear();
209
+ }
95
210
  /**
96
211
  * Find the nearest directory with a project/knip config marker.
97
212
  *
@@ -177,8 +292,13 @@ export class KnipClient {
177
292
  *
178
293
  * Re-entrancy safe: concurrent calls resolving to the same project
179
294
  * root share a single knip process via `inFlight`.
295
+ *
296
+ * Successful memo hits validate package.json and the resolved Knip config
297
+ * with bounded metadata checks. The two statSync calls replace a 10-23
298
+ * second project scan. Source-only external edits remain undetected until
299
+ * pi observes a write or the session resets.
180
300
  */
181
- async analyze(cwd, _ignore) {
301
+ async analyze(cwd, _ignore, options = {}) {
182
302
  const targetDir = this.resolveProjectRoot(cwd || process.cwd());
183
303
  if (!targetDir) {
184
304
  // No package.json / knip config anywhere up the tree. Running knip
@@ -191,30 +311,97 @@ export class KnipClient {
191
311
  summary: "No project root found; knip skipped",
192
312
  };
193
313
  }
194
- if (!(await this.ensureAvailable())) {
314
+ const key = path.resolve(targetDir);
315
+ const completed = this.completedByProject.get(key);
316
+ if (options.projectSeq !== undefined &&
317
+ completed?.projectSeq === options.projectSeq &&
318
+ this.matchesMemoSignal(targetDir, completed.signal)) {
319
+ this.log(`Analysis cache hit for ${key} at projectSeq ${options.projectSeq}`);
320
+ return { ...completed.result, execution: "cache" };
321
+ }
322
+ // A project that ships its own knip needs no managed install and no
323
+ // managed probe — the shim is already on disk, and gating on
324
+ // `ensureAvailable()` would tell such a project to "npm install -D knip"
325
+ // whenever the MANAGED copy is missing (#1721).
326
+ if (resolveProjectKnipBinary(targetDir) === null &&
327
+ !(await this.ensureAvailable())) {
195
328
  return this.unavailableResult();
196
329
  }
197
- const key = path.resolve(targetDir);
198
330
  const existing = this.inFlight.get(key);
199
331
  if (existing) {
200
332
  this.log(`Analysis already in flight for ${key}; sharing result`);
201
333
  return existing;
202
334
  }
203
- const promise = this.runAnalyze(key).finally(() => {
335
+ const promise = this.runAnalyze(key)
336
+ .then((result) => {
337
+ const executed = { ...result, execution: "executed" };
338
+ if (result.success && options.projectSeq !== undefined) {
339
+ this.completedByProject.set(key, {
340
+ projectSeq: options.projectSeq,
341
+ result: executed,
342
+ signal: this.readMemoSignal(key),
343
+ });
344
+ }
345
+ return executed;
346
+ })
347
+ .finally(() => {
204
348
  this.inFlight.delete(key);
205
349
  });
206
350
  this.inFlight.set(key, promise);
207
351
  return promise;
208
352
  }
353
+ readMemoFileSignal(filePath) {
354
+ try {
355
+ const stat = fs.statSync(filePath);
356
+ return { path: filePath, mtimeMs: stat.mtimeMs, size: stat.size };
357
+ }
358
+ catch {
359
+ return null;
360
+ }
361
+ }
362
+ readMemoSignal(targetDir) {
363
+ const config = resolveProjectKnipConfig(targetDir);
364
+ return {
365
+ packageJson: this.readMemoFileSignal(path.join(targetDir, "package.json")),
366
+ config: config && config !== "package.json#knip"
367
+ ? this.readMemoFileSignal(path.join(targetDir, config))
368
+ : null,
369
+ };
370
+ }
371
+ matchesMemoSignal(targetDir, signal) {
372
+ const current = {
373
+ packageJson: this.readMemoFileSignal(path.join(targetDir, "package.json")),
374
+ config: signal.config ? this.readMemoFileSignal(signal.config.path) : null,
375
+ };
376
+ return (JSON.stringify(current.packageJson) === JSON.stringify(signal.packageJson) &&
377
+ JSON.stringify(current.config) === JSON.stringify(signal.config));
378
+ }
209
379
  async runAnalyze(targetDir) {
210
380
  // Cache dir is routed through pi-lens's project-data-dir convention (NOT
211
381
  // knip's own default `./node_modules/.cache/knip`) so it lives alongside
212
382
  // every other project cache (see cache-manager.ts, call-graph.ts) and is
213
383
  // covered by the existing `.pi-lens/` gitignore entry.
214
384
  //
215
- // Caveat (per knip's docs): a cached run does NOT pick up newly-added
216
- // `.gitignore` files automatically — the cache must be deleted to detect
217
- // them. Not auto-handled here; this is a documented tradeoff, not a bug.
385
+ // Documented cache-invalidation gaps (both live in knip's own cache, not
386
+ // in pi-lens):
387
+ //
388
+ // 1. `.gitignore` (per knip's docs): a cached run does NOT pick up
389
+ // newly-added `.gitignore` files automatically — the cache must be
390
+ // deleted to detect them. Not auto-handled here; documented tradeoff.
391
+ // 2. Consumer-side imports (#1630): knip's glob cache records the mtime of
392
+ // every directory that CONTRIBUTED a matched path, and revalidates
393
+ // against those. A directory that matched nothing at cache time is not
394
+ // recorded, so a file later added to it is invisible to the cached
395
+ // glob. A new test that imports an export therefore never reaches the
396
+ // graph, and knip keeps reporting that export as unused — a stale
397
+ // verdict pi-lens then renders as fresh. Fixed upstream in knip 6.28.0
398
+ // (glob cache now tracks every directory it reads), but the managed
399
+ // install floats and older versions are common in the field, so
400
+ // `pruneStaleGlobCache()` below drops the glob cache on every run
401
+ // regardless of version. Measured on this repo with knip 6.4.1:
402
+ // uncached 3.3s, fully cached 1.3s, glob-cache-dropped 1.4s. The glob
403
+ // cache is worth ~0.1s of the ~2.0s the cache saves, so dropping it
404
+ // buys correctness for almost nothing.
218
405
  const cacheLocation = path.join(getProjectDataDir(targetDir), "cache", "knip");
219
406
  // knip (verified against 6.26.0) silently fails to persist the cache when
220
407
  // `--cache-location` points at a directory that doesn't exist yet: its
@@ -230,6 +417,7 @@ export class KnipClient {
230
417
  catch (err) {
231
418
  this.log(`Failed to pre-create knip cache dir ${cacheLocation}: ${err}`);
232
419
  }
420
+ this.pruneStaleGlobCache(cacheLocation);
233
421
  const args = [
234
422
  "--reporter=json",
235
423
  "--include",
@@ -242,7 +430,14 @@ export class KnipClient {
242
430
  "--cache-location",
243
431
  cacheLocation,
244
432
  ];
245
- const result = await safeSpawnAsync(this.knipCommand, args, {
433
+ // Project toolchain first. #1199 already put `<targetDir>/node_modules/.bin`
434
+ // at the front of the child PATH for exactly this preference, but the spawn
435
+ // command is an ABSOLUTE managed path, so PATH order never got a say
436
+ // (#1721). Resolving the command here is what makes that intent real.
437
+ const projectBinary = resolveProjectKnipBinary(targetDir);
438
+ const command = projectBinary ?? this.knipCommand;
439
+ this.recordToolchain(targetDir, command, classifyKnipBinary(projectBinary, targetDir));
440
+ const result = await safeSpawnAsync(command, args, {
246
441
  timeout: ANALYSIS_TIMEOUT_MS,
247
442
  cwd: targetDir,
248
443
  env: await getManagedToolEnvironment("knip", targetDir),
@@ -254,13 +449,55 @@ export class KnipClient {
254
449
  summary: `Error: ${result.error.message}`,
255
450
  };
256
451
  }
257
- // Knip exits 0 on success (even with issues), 1 on errors
452
+ // Empirical exit-code table (knip 6.4.1, verified live for #1736): 0 =
453
+ // clean run, JSON stdout `{"issues":[]}`; 1 = clean run WITH findings,
454
+ // JSON stdout `{"issues":[...]}`; 2 = config/load error, empty or
455
+ // non-JSON stdout with the error on stderr. A genuinely clean run's
456
+ // stdout is therefore NEVER empty — knip always prints at least
457
+ // `{"issues":[]}` on exit 0. Empty stdout only ever shows up paired
458
+ // with a NONZERO exit (a broken shim, a crash, or knip's own load
459
+ // error), so `status !== 0 && !output.trim()` is unambiguous evidence
460
+ // of a failed run, never a clean one.
258
461
  const output = result.stdout || "";
259
- this.log(`Knip output length: ${output.length}`);
462
+ this.log(`Knip output length: ${output.length}, exit status: ${result.status}`);
260
463
  if (output.length < 500) {
261
464
  this.log(`Knip output sample: ${output}`);
262
465
  }
263
466
  if (!output.trim()) {
467
+ // Reuses the SAME discriminator every dispatch/runners linter uses
468
+ // (`spawnFailedWithNoOutput`, `clients/dispatch/runners/utils/
469
+ // spawn-outcome.ts`) rather than a parallel hand-rolled check — a
470
+ // nonzero exit with nothing to parse is never a clean run, here or
471
+ // there.
472
+ if (spawnFailedWithNoOutput(result, output)) {
473
+ // #1816: one shared wording, one truncation, signal named. The
474
+ // binary-source discriminator (#1721's whole point — WHICH knip
475
+ // ran) survives as a named field rather than as prose.
476
+ const reason = formatToolFailure({
477
+ tool: "knip",
478
+ status: result.status,
479
+ signal: result.signal,
480
+ stderr: result.stderr,
481
+ fields: {
482
+ command,
483
+ source: classifyKnipBinary(projectBinary, targetDir),
484
+ },
485
+ });
486
+ this.log(reason);
487
+ incrementDegradationCount({
488
+ kind: "runner-empty-result",
489
+ subject: "knip",
490
+ reason,
491
+ });
492
+ return {
493
+ ...EMPTY_RESULT,
494
+ summary: `Knip exited ${result.status} with no output; run skipped`,
495
+ };
496
+ }
497
+ // status === 0 with empty stdout has never been observed against a
498
+ // real knip binary (a genuine clean run still prints
499
+ // `{"issues":[]}`), but a defensive fallback is cheap insurance
500
+ // against a future knip release that changes this.
264
501
  return {
265
502
  ...EMPTY_RESULT,
266
503
  success: true,
@@ -269,6 +506,69 @@ export class KnipClient {
269
506
  }
270
507
  return this.dropOverridePinnedDeps(this.parseOutput(output), targetDir);
271
508
  }
509
+ /**
510
+ * Record WHICH knip a run used and WHICH config it ran under (#1721).
511
+ *
512
+ * The dogfood failure this fixes was invisible from the outside: the lens
513
+ * reported 62 unused type exports and the project's own `npx knip` reported
514
+ * none, with nothing in any log to say the two ran different binaries. This
515
+ * row is that missing evidence — it names the shim, its version, the config
516
+ * knip will discover, and whether the shim came from the project or from
517
+ * pi-lens.
518
+ *
519
+ * Bounded twice over: one row per distinct (project root, binary, config)
520
+ * triple, and at most `MAX_RECORDED_TOOLCHAINS` distinct triples per client.
521
+ * Steady-state cost is therefore one line per project per session, not one
522
+ * per turn_end.
523
+ */
524
+ recordToolchain(targetDir, command, source) {
525
+ const config = resolveProjectKnipConfig(targetDir);
526
+ const key = `${targetDir} ${command} ${config ?? ""}`;
527
+ if (this.recordedToolchains.has(key))
528
+ return;
529
+ if (this.recordedToolchains.size >= MAX_RECORDED_TOOLCHAINS)
530
+ return;
531
+ this.recordedToolchains.add(key);
532
+ const version = readKnipShimVersion(command);
533
+ logSessionStart(`knip toolchain ${targetDir}: binary=${command}` +
534
+ `${version ? ` version=${version}` : ""}` +
535
+ ` source=${source}` +
536
+ ` config=${config ?? "none (knip defaults)"}`);
537
+ }
538
+ /**
539
+ * Delete knip's glob cache (`glob-<version>.cache`) before every run (#1630).
540
+ *
541
+ * knip revalidates a cached glob against the mtimes of the directories that
542
+ * contributed a matched path. Directories that matched nothing are never
543
+ * recorded, so files later added to them stay invisible and knip keeps
544
+ * reporting an export as unused after a new consumer imports it. Removing
545
+ * this one file forces a fresh file walk while the module and plugin caches —
546
+ * which carry nearly all of the cache's speed — survive.
547
+ *
548
+ * Failures are logged and ignored: a glob cache we could not delete degrades
549
+ * to the pre-#1630 behavior, which is stale but not fatal.
550
+ */
551
+ pruneStaleGlobCache(cacheLocation) {
552
+ let entries;
553
+ try {
554
+ entries = fs.readdirSync(cacheLocation);
555
+ }
556
+ catch (err) {
557
+ this.log(`Failed to read knip cache dir ${cacheLocation}: ${err}`);
558
+ return;
559
+ }
560
+ for (const entry of entries) {
561
+ if (!entry.startsWith("glob-") || !entry.endsWith(".cache"))
562
+ continue;
563
+ try {
564
+ fs.rmSync(path.join(cacheLocation, entry), { force: true });
565
+ this.log(`Pruned knip glob cache ${entry}`);
566
+ }
567
+ catch (err) {
568
+ this.log(`Failed to prune knip glob cache ${entry}: ${err}`);
569
+ }
570
+ }
571
+ }
272
572
  /**
273
573
  * Drop `dependency`/`devDependency` issues for a package that's also
274
574
  * referenced as an npm `overrides` (or Yarn `resolutions` / pnpm
@@ -61,6 +61,7 @@ export const LANGUAGE_POLICY = {
61
61
  gleam: { lspCapable: true },
62
62
  ocaml: { lspCapable: true },
63
63
  clojure: { lspCapable: true },
64
+ cue: { lspCapable: true },
64
65
  terraform: { lspCapable: true },
65
66
  terragrunt: { lspCapable: false },
66
67
  nix: { lspCapable: true },
@@ -119,7 +120,7 @@ const PRIMARY_DISPATCH_GROUPS = {
119
120
  },
120
121
  yaml: {
121
122
  mode: "all",
122
- runnerIds: ["lsp", "yamllint", "trivy-config", "helm-lint"],
123
+ runnerIds: ["lsp", "yamllint", "trivy-config", "helm-lint", "helm-render"],
123
124
  filterKinds: ["yaml"],
124
125
  },
125
126
  sql: {
@@ -183,7 +184,9 @@ const PRIMARY_DISPATCH_GROUPS = {
183
184
  haskell: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["haskell"] },
184
185
  "helm-template": {
185
186
  mode: "all",
186
- runnerIds: ["helm-lint"],
187
+ // helm-render is opt-in (`helm.renderValidation.enabled`) and skips itself
188
+ // when the switch is absent, so it costs nothing when unconfigured (#1283).
189
+ runnerIds: ["helm-lint", "helm-render"],
187
190
  filterKinds: ["helm-template"],
188
191
  },
189
192
  elixir: {
@@ -198,6 +201,9 @@ const PRIMARY_DISPATCH_GROUPS = {
198
201
  },
199
202
  ocaml: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["ocaml"] },
200
203
  clojure: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["clojure"] },
204
+ // #1522: cuelsp reports syntax/parse errors only; cue-vet covers the
205
+ // evaluation-error class the language server deliberately doesn't publish.
206
+ cue: { mode: "all", runnerIds: ["lsp", "cue-vet"], filterKinds: ["cue"] },
201
207
  terraform: {
202
208
  mode: "all",
203
209
  runnerIds: ["lsp", "tflint", "trivy-config"],
@@ -35,6 +35,7 @@ const PROJECT_MARKERS_BY_KIND = {
35
35
  swift: ["Package.swift"],
36
36
  dart: ["pubspec.yaml"],
37
37
  elixir: ["mix.exs"],
38
+ cue: ["cue.mod"],
38
39
  gleam: ["gleam.toml"],
39
40
  terraform: [".terraform.lock.hcl"],
40
41
  terragrunt: TERRAGRUNT_FILENAMES,
@@ -80,6 +81,7 @@ const ROOT_MARKERS_BY_KIND = {
80
81
  swift: ["Package.swift"],
81
82
  dart: ["pubspec.yaml"],
82
83
  elixir: ["mix.exs"],
84
+ cue: ["cue.mod", ".git"],
83
85
  gleam: ["gleam.toml"],
84
86
  terraform: [".terraform.lock.hcl"],
85
87
  terragrunt: TERRAGRUNT_FILENAMES,
@@ -283,11 +285,16 @@ export async function collectSourceFilesForWarmup(rootDir, maxFiles = MAX_WARMUP
283
285
  stack.push(fullPath);
284
286
  }
285
287
  else if (entry.isFile()) {
286
- if (ignoreMatcher.isIgnored(fullPath, false))
287
- continue;
288
+ // #1974: cheap extension gate before the ignore matcher — isIgnored is
289
+ // O(ancestorDirs x patterns) with a per-call minimatch regex compile
290
+ // (0.68-0.93ms/file, measured), so pay it only for files that already
291
+ // pass the extension check. A large ignored-but-not-dir-prunable pile
292
+ // (e.g. wal/*.log) otherwise pays the full isIgnored cost per file.
288
293
  const ext = path.extname(entry.name).toLowerCase();
289
294
  if (!WARMUP_SOURCE_EXTS.has(ext))
290
295
  continue;
296
+ if (ignoreMatcher.isIgnored(fullPath, false))
297
+ continue;
291
298
  matchedSeen += 1;
292
299
  const bucket = WARMUP_CODE_EXTS.has(ext) ? codeOut : nonCodeOut;
293
300
  // Per-category hard cap — language detection only needs