@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
@@ -21,7 +21,11 @@
21
21
  import { safeSpawnAsync } from "../../safe-spawn.js";
22
22
  import { PRIORITY } from "../priorities.js";
23
23
  import { createAvailabilityChecker } from "./utils/runner-helpers.js";
24
+ import { parseToolRun } from "./utils/tool-failure.js";
24
25
  const typos = createAvailabilityChecker("typos", ".exe");
26
+ // typos-cli exit codes: 0 = no typos, 2 = typos found, 1 = an error that
27
+ // stopped the scan. Anything nonzero outside {2} is a rejected invocation.
28
+ const TYPOS_EXIT_CODES = { ran: [2] };
25
29
  /**
26
30
  * Parse typos-cli JSON output (JSON Lines format)
27
31
  *
@@ -90,14 +94,19 @@ const spellcheckRunner = {
90
94
  const result = await safeSpawnAsync(typos.getCommand(ctx.cwd || process.cwd()), args, {
91
95
  timeout: 15000,
92
96
  });
93
- // typos-cli exits with code 2 if typos found, 0 if clean
94
- const hasTypos = result.status === 2 || result.stdout?.trim();
95
- if (!hasTypos) {
96
- return { status: "succeeded", diagnostics: [], semantic: "none" };
97
- }
98
- // Parse diagnostics
99
- const raw = result.stdout + result.stderr;
100
- const diagnostics = parseTyposOutput(raw, ctx.filePath);
97
+ // #1816: typos-cli exits 0 clean, 2 with typos found, and 1 on an ERROR
98
+ // (unreadable config, bad argument). The `status === 2 || stdout` test
99
+ // below is false for exit 1 with an empty stdout, so a failed run was
100
+ // reported as a clean file. Only 0 and 2 are runs.
101
+ //
102
+ // #1948: parse through the shared seam so an exit-2 run whose JSON lines
103
+ // yield nothing leaves a record. `parseOutput` keeps the historical
104
+ // split: the gate judges "did it run" on stdout, the parser reads both
105
+ // streams.
106
+ const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
107
+ if (run.skipped)
108
+ return run.skipped;
109
+ const diagnostics = run.diagnostics;
101
110
  if (diagnostics.length === 0) {
102
111
  return { status: "succeeded", diagnostics: [], semantic: "none" };
103
112
  }
@@ -2,7 +2,17 @@ import { safeSpawnAsync } from "../../safe-spawn.js";
2
2
  import { getLinterPolicyForCwd, hasSqlfluffConfig } from "../../tool-policy.js";
3
3
  import { PRIORITY } from "../priorities.js";
4
4
  import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
5
+ import { parseToolRun } from "./utils/tool-failure.js";
5
6
  const sqlfluff = createAvailabilityChecker("sqlfluff", ".exe");
7
+ // sqlfluff exit codes (its CLI docs): 0 = clean, 1 = violations found, 2 = a
8
+ // user error that stopped the lint (unknown dialect, unreadable config).
9
+ // Only 2 is a rejected invocation.
10
+ //
11
+ // The exit TABLE is what protects this runner, not the nothing-to-parse rule:
12
+ // sqlfluff writes its user errors to STDOUT, not stderr (live: an unknown
13
+ // dialect prints the error there), so stdout is non-empty and the fallback
14
+ // rule would read the failed run as a run that found nothing.
15
+ const SQLFLUFF_EXIT_CODES = { ran: [1] };
6
16
  export { hasSqlfluffConfig };
7
17
  // sqlfluff's JSON lint output does not carry a per-violation fixable flag —
8
18
  // `sqlfluff fix` is a separate command and the lint surface doesn't tell us
@@ -117,7 +127,7 @@ const sqlfluffRunner = {
117
127
  ctx.log("sqlfluff: no config detected, using ANSI dialect defaults");
118
128
  }
119
129
  let cmd = null;
120
- if (await (sqlfluff.isAvailableAsync(cwd))) {
130
+ if (await sqlfluff.isAvailableAsync(cwd)) {
121
131
  cmd = sqlfluff.getCommand(cwd);
122
132
  }
123
133
  else {
@@ -125,14 +135,32 @@ const sqlfluffRunner = {
125
135
  }
126
136
  if (!cmd)
127
137
  return { status: "skipped", diagnostics: [], semantic: "none" };
128
- const args = ["lint", "--format", "json", ctx.filePath];
138
+ // #1937: this used to be `args.splice(2, 0, "--dialect", "ansi")` on
139
+ // `["lint", "--format", "json", file]`, which inserts BETWEEN the flag
140
+ // and its value and yields `lint --format --dialect ansi json file`.
141
+ // sqlfluff rejects `--dialect` as the value of `--format` and exits 2,
142
+ // so every SQL project WITHOUT a .sqlfluff — exactly the case this
143
+ // dialect default exists to serve — was never linted. Built in order
144
+ // rather than patched by index, and pinned by the argv-tie assertion
145
+ // in runners/captured-real-output.test.ts.
146
+ const args = ["lint", "--format", "json"];
129
147
  if (!hasConfig) {
130
- args.splice(2, 0, "--dialect", "ansi");
148
+ args.push("--dialect", "ansi");
131
149
  }
150
+ args.push(ctx.filePath);
132
151
  const result = await safeSpawnAsync(cmd, args, {
133
152
  timeout: 20000,
153
+ cwd,
134
154
  });
135
- const diagnostics = parseSqlfluffOutput(result.stdout ?? "", ctx.filePath);
155
+ // #1816: this runner read `result.status` zero times, so a user error
156
+ // fell through `parseSqlfluffOutput`'s JSON catch to zero violations and
157
+ // reported the SQL file as clean.
158
+ // #1948: sqlfluff exit 1 with a JSON report we read nothing out of is a
159
+ // parser break, not a clean file.
160
+ const run = parseToolRun("sqlfluff", { result, exitCodes: SQLFLUFF_EXIT_CODES }, (out) => parseSqlfluffOutput(out, ctx.filePath), { parseOutput: result.stdout ?? "" });
161
+ if (run.skipped)
162
+ return run.skipped;
163
+ const diagnostics = run.diagnostics;
136
164
  if (diagnostics.length === 0) {
137
165
  return { status: "succeeded", diagnostics: [], semantic: "none" };
138
166
  }
@@ -3,7 +3,43 @@ import { safeSpawnAsync } from "../../safe-spawn.js";
3
3
  import { getLinterPolicyForCwd, hasStylelintConfig, } from "../../tool-policy.js";
4
4
  import { PRIORITY } from "../priorities.js";
5
5
  import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
+ import { parseToolRun } from "./utils/tool-failure.js";
6
7
  const stylelint = createAvailabilityChecker("stylelint", ".cmd");
8
+ // stylelint exit codes (its "Exit codes" docs page): 0 = no problems, 1 = a
9
+ // fatal error, 2 = lint problems found, 78 = a configuration error.
10
+ //
11
+ // 1 is listed as a run here rather than a rejection. The evidence for that is
12
+ // NOT a live stylelint: it is this repo's own hand-written fixture
13
+ // (tests/clients/dispatch/runners/stylelint-fixable.test.ts), which reports
14
+ // findings under exit 1. Rather than rewrite a fixture whose provenance is
15
+ // unknown, the table keeps 1 permissive. 78 is the rejection this table
16
+ // exists to name.
17
+ //
18
+ // #1937 corrected the safety argument that used to sit here: it claimed a
19
+ // fatal stylelint "writes nothing to stdout", which is true but useless,
20
+ // because a stylelint that DID find problems writes nothing to stdout either.
21
+ // `stylelintReport` below is what now separates the two.
22
+ const STYLELINT_EXIT_CODES = { ran: [1, 2] };
23
+ /**
24
+ * The JSON report out of a stylelint run, whichever stream carried it (#1937).
25
+ *
26
+ * stylelint 16+ writes the formatted report to STDERR whenever the run
27
+ * "errored" — which is every run that found an error-severity warning, i.e.
28
+ * every run with something to say. Reading stdout alone parsed nothing and
29
+ * called the stylesheet clean, the #1933 vale shape. Verified against
30
+ * stylelint 17.8.0 in tests/fixtures/runner-output/stylelint/.
31
+ *
32
+ * Only a stream that actually starts a JSON array counts. A crash's stack
33
+ * trace on stderr must stay "nothing to parse" so #1816's empty-result skip
34
+ * still fires instead of reporting a clean file.
35
+ */
36
+ export function stylelintReport(stdout, stderr) {
37
+ for (const stream of [stdout, stderr]) {
38
+ if (stream?.trimStart().startsWith("["))
39
+ return stream;
40
+ }
41
+ return "";
42
+ }
7
43
  // Stylelint's standard JSON output reports only aggregate fixableErrorCount /
8
44
  // fixableWarningCount per file — there is no per-warning fix flag in the CLI
9
45
  // surface. To route the actionable warnings here without a tool rewrite, we
@@ -91,7 +127,7 @@ const stylelintRunner = {
91
127
  ctx.log("stylelint: no config detected, running with default rules");
92
128
  }
93
129
  let cmd = null;
94
- if (await (stylelint.isAvailableAsync(cwd))) {
130
+ if (await stylelint.isAvailableAsync(cwd)) {
95
131
  cmd = stylelint.getCommand(cwd);
96
132
  }
97
133
  else {
@@ -100,8 +136,16 @@ const stylelintRunner = {
100
136
  if (!cmd)
101
137
  return { status: "skipped", diagnostics: [], semantic: "none" };
102
138
  const result = await safeSpawnAsync(cmd, ["--formatter", "json", ctx.filePath], { timeout: 20000, cwd });
103
- const raw = result.stdout ?? "";
104
- const diagnostics = parseStylelintJson(raw, ctx.filePath);
139
+ // #1816: this runner read `result.status` zero times, so an exit-78
140
+ // config error (or an exit-1 crash) fell through parseStylelintJson's
141
+ // catch to zero diagnostics and reported a clean stylesheet.
142
+ const raw = stylelintReport(result.stdout, result.stderr);
143
+ // #1948: the stdout-vs-stderr report bug landed here as exit 2 plus a
144
+ // full JSON report and zero parsed warnings. That now leaves a record.
145
+ const run = parseToolRun("stylelint", { result, output: raw, exitCodes: STYLELINT_EXIT_CODES }, (out) => parseStylelintJson(out, ctx.filePath));
146
+ if (run.skipped)
147
+ return run.skipped;
148
+ const diagnostics = run.diagnostics;
105
149
  if (diagnostics.length === 0) {
106
150
  return { status: "succeeded", diagnostics: [], semantic: "none" };
107
151
  }
@@ -2,6 +2,7 @@ import * as path from "node:path";
2
2
  import { safeSpawnAsync } from "../../safe-spawn.js";
3
3
  import { PRIORITY } from "../priorities.js";
4
4
  import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
5
+ import { parseToolRun } from "./utils/tool-failure.js";
5
6
  const swiftlint = createAvailabilityChecker("swiftlint", ".exe");
6
7
  // SwiftLint rules whose rule class declares a `corrector` — i.e. rules that
7
8
  // `swiftlint --fix` rewrites deterministically. The JSON reporter does not
@@ -117,7 +118,7 @@ const swiftlintRunner = {
117
118
  async run(ctx) {
118
119
  const cwd = ctx.cwd || process.cwd();
119
120
  let cmd = null;
120
- if (await (swiftlint.isAvailableAsync(cwd))) {
121
+ if (await swiftlint.isAvailableAsync(cwd)) {
121
122
  cmd = swiftlint.getCommand(cwd);
122
123
  }
123
124
  else {
@@ -126,9 +127,18 @@ const swiftlintRunner = {
126
127
  if (!cmd)
127
128
  return { status: "skipped", diagnostics: [], semantic: "none" };
128
129
  const result = await safeSpawnAsync(cmd, ["--reporter", "json", path.resolve(cwd, ctx.filePath)], { cwd, timeout: 15000 });
129
- // SwiftLint exits non-zero on violations stdout still has the JSON
130
- const raw = result.stdout || "";
131
- const diagnostics = parseSwiftLintOutput(raw, ctx.filePath);
130
+ // #1816: this runner read `result.status` zero times, so a SwiftLint
131
+ // that refused the invocation reported a clean Swift file. No exit-code
132
+ // table is declared: SwiftLint's nonzero codes are not documented
133
+ // stably enough to call any of them a rejection, so classification
134
+ // rests on the conservative rule — a nonzero exit with nothing to parse
135
+ // never ran.
136
+ // SwiftLint exits non-zero on violations — stdout still has the JSON, so
137
+ // a nonzero exit whose JSON yields nothing is a parser break (#1948).
138
+ const run = parseToolRun("swiftlint", { result }, (out) => parseSwiftLintOutput(out, ctx.filePath));
139
+ if (run.skipped)
140
+ return run.skipped;
141
+ const diagnostics = run.diagnostics;
132
142
  if (diagnostics.length === 0) {
133
143
  return { status: "succeeded", diagnostics: [], semantic: "none" };
134
144
  }
@@ -1,30 +1,104 @@
1
1
  import * as path from "node:path";
2
+ import { findLocalBinUpwards } from "../../package-manager.js";
2
3
  import { safeSpawnAsync } from "../../safe-spawn.js";
3
4
  import { getLinterPolicyForCwd } from "../../tool-policy.js";
4
5
  import { PRIORITY } from "../priorities.js";
5
6
  import { createAvailabilityChecker, lspPrimaryCoversFile, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
- import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
7
+ import { parseToolRun } from "./utils/tool-failure.js";
7
8
  const taplo = createAvailabilityChecker("taplo", ".exe");
8
- function parseTaploOutput(raw, filePath) {
9
- try {
10
- const parsed = JSON.parse(raw);
11
- const errors = parsed.errors ?? [];
12
- return errors.map((err, idx) => ({
13
- id: `taplo-${err.kind}-${err.range?.start.line ?? idx}`,
14
- message: `[${err.kind}] ${err.message}`,
9
+ // Verified against taplo 0.10.0: 0 = clean, 1 = the file is invalid (lint
10
+ // findings, or a schema it could not load), 2 = clap rejected the invocation.
11
+ //
12
+ // #1937 round 2: the parser fix alone turned the old silent-clean into a false
13
+ // BLOCKING diagnostic. clap prints `error: unexpected argument '--output'
14
+ // found`, which starts with the same lowercase `error:` a real taplo
15
+ // diagnostic does, so a mistyped flag would have reddened every valid TOML
16
+ // file. Both readings are wrong; the exit code is what separates them.
17
+ const TAPLO_EXIT_CODES = { ran: [1] };
18
+ /** `error: <summary>` or `warning: <summary>` at the head of a codespan block. */
19
+ const HEADER_PATTERN = /^\s*(error|warning):(.*)$/;
20
+ /** The box-drawing lead taplo puts on a codespan location line. */
21
+ const LOCATION_LEAD = "┌─";
22
+ /** The `:line:column` tail of a codespan location line. */
23
+ const LOCATION_PATTERN = /:(\d+):(\d+)$/;
24
+ /**
25
+ * Parse `taplo lint` output (#1937).
26
+ *
27
+ * This parser used to `JSON.parse` a `{ errors: [{ range, message, kind }] }`
28
+ * envelope that taplo has never emitted, fed by a `--output=json` flag taplo
29
+ * has never accepted. Real taplo rejected the flag with exit 2 and a usage
30
+ * error, the JSON parse threw, the catch returned [], and malformed TOML was
31
+ * reported clean. Same shape as the vale parser in #1933.
32
+ *
33
+ * Real taplo 0.10.0 writes codespan diagnostics to STDERR, interleaved with
34
+ * uppercase tracing lines:
35
+ *
36
+ * error: invalid TOML
37
+ * ┌─ /path/to/bad.toml:2:9
38
+ * │
39
+ * 2 │ [package
40
+ *
41
+ * Verified against tests/fixtures/runner-output/taplo/real.captured.json.
42
+ *
43
+ * A header alone is NOT enough to emit a diagnostic. The codespan location
44
+ * line must follow it. clap's own failures start with the same lowercase
45
+ * `error:` and carry no location, so without that requirement a rejected
46
+ * invocation becomes a blocking finding on line 1 of a perfectly valid file
47
+ * (#1937 round 2). The exit-code table in the runner is the primary guard;
48
+ * this is the structural one, and each covers a case the other does not.
49
+ */
50
+ export function parseTaploOutput(raw, filePath) {
51
+ const diagnostics = [];
52
+ const lines = (raw ?? "").split(/\r?\n/);
53
+ for (let i = 0; i < lines.length; i++) {
54
+ // Lowercase `error:`/`warning:` starts a diagnostic. taplo's tracing
55
+ // lines are uppercase (`ERROR taplo:lint_files: ...`), so a
56
+ // case-sensitive match keeps them out.
57
+ //
58
+ // Both patterns below trim in code rather than with a trailing `\s*`,
59
+ // and the location one tests for its box-drawing lead separately
60
+ // instead of skipping to it with `.*`. Either shape backtracks
61
+ // super-linearly on a long line, and this parser reads bytes a third
62
+ // party wrote.
63
+ const header = HEADER_PATTERN.exec(lines[i].trimEnd());
64
+ if (!header)
65
+ continue;
66
+ const severityWord = header[1];
67
+ const summary = header[2].trim();
68
+ if (!summary)
69
+ continue;
70
+ // The location arrives on a following `┌─ file:line:col` line. Scan a
71
+ // short window rather than assuming adjacency: taplo puts a gutter line
72
+ // between them in some layouts.
73
+ let line = null;
74
+ let column = 1;
75
+ for (let j = i + 1; j < Math.min(i + 4, lines.length); j++) {
76
+ if (!lines[j].includes(LOCATION_LEAD))
77
+ continue;
78
+ const location = LOCATION_PATTERN.exec(lines[j].trimEnd());
79
+ if (!location)
80
+ continue;
81
+ line = Number.parseInt(location[1], 10) || 1;
82
+ column = Number.parseInt(location[2], 10) || 1;
83
+ break;
84
+ }
85
+ if (line === null)
86
+ continue;
87
+ const severity = severityWord === "warning" ? "warning" : "error";
88
+ diagnostics.push({
89
+ id: `taplo-${severity}-${line}-${column}`,
90
+ message: summary,
15
91
  filePath,
16
- line: (err.range?.start.line ?? 0) + 1,
17
- column: (err.range?.start.col ?? 0) + 1,
18
- severity: "error",
19
- semantic: "blocking",
92
+ line,
93
+ column,
94
+ severity,
95
+ semantic: severity === "error" ? "blocking" : "warning",
20
96
  tool: "taplo",
21
- rule: err.kind,
97
+ rule: `taplo/${severity}`,
22
98
  fixable: false,
23
- }));
24
- }
25
- catch {
26
- return [];
99
+ });
27
100
  }
101
+ return diagnostics;
28
102
  }
29
103
  const taploRunner = {
30
104
  id: "taplo",
@@ -45,21 +119,46 @@ const taploRunner = {
45
119
  if (lspPrimaryCoversFile(ctx, "toml") && (await ctx.hasTool("taplo"))) {
46
120
  return { status: "skipped", diagnostics: [], semantic: "none" };
47
121
  }
48
- let cmd = null;
49
- if (await (taplo.isAvailableAsync(cwd))) {
50
- cmd = taplo.getCommand(cwd);
51
- }
52
- else {
53
- cmd = await resolveToolCommandWithInstallFallback(cwd, "taplo");
122
+ // Project binary first (#1731, discipline B): `taplo.isAvailableAsync`
123
+ // resolves through `findManagedNodeToolBinary`, pi-lens's own managed
124
+ // shim — checked BEFORE any project-local candidate, so a project's own
125
+ // `node_modules/.bin/taplo` (npm `@taplo/cli`) never won once the managed
126
+ // copy answered. `findLocalBinUpwards` defaults to `.cmd` on Windows,
127
+ // matching that npm shim; the availability checker's `.exe` extension is
128
+ // correct for pi-lens's OWN managed install (a GitHub-release binary,
129
+ // `clients/installer/index.ts` taplo entry), a different artifact with a
130
+ // different extension, so it stays as the checker's fallback only.
131
+ let cmd = findLocalBinUpwards("taplo", cwd) ?? null;
132
+ if (!cmd) {
133
+ if (await taplo.isAvailableAsync(cwd)) {
134
+ cmd = taplo.getCommand(cwd);
135
+ }
136
+ else {
137
+ cmd = await resolveToolCommandWithInstallFallback(cwd, "taplo");
138
+ }
54
139
  }
55
140
  if (!cmd)
56
141
  return { status: "skipped", diagnostics: [], semantic: "none" };
57
142
  const absPath = path.resolve(cwd, ctx.filePath);
58
- const result = await safeSpawnAsync(cmd, ["check", "--output=json", absPath], { cwd, timeout: 15000 });
59
- if (spawnFailedWithNoOutput(result)) {
60
- return { status: "skipped", diagnostics: [], semantic: "none" };
61
- }
62
- const diagnostics = parseTaploOutput(result.stdout || "", ctx.filePath);
143
+ // #1937: `--output=json` is not a taplo flag. taplo rejected it with a
144
+ // usage error, so this runner never linted anything. `--colors never`
145
+ // keeps ANSI escapes out of the bytes the parser reads.
146
+ const result = await safeSpawnAsync(cmd, ["check", "--colors", "never", absPath], { cwd, timeout: 15000 });
147
+ // taplo reports on stderr and leaves stdout empty, so the
148
+ // "nothing to parse" test must read the same string the parser gets —
149
+ // otherwise every real run looks like a failed spawn.
150
+ const raw = result.stderr?.trim() ? result.stderr : (result.stdout ?? "");
151
+ //
152
+ // taplo exits 1 only when a file is INVALID, and it does not always draw
153
+ // a codespan block when it does: a schema it could not load is reported
154
+ // through its tracing output alone. Claiming that file clean would be an
155
+ // outright lie, so taplo opts into `skipWhenParsedNothing` (#1948). This
156
+ // replaces a hand-rolled per-runner copy of the same rule, which wrote
157
+ // its own `runner-empty-result` row with its own wording.
158
+ const run = parseToolRun("taplo", { result, output: raw, exitCodes: TAPLO_EXIT_CODES }, (out) => parseTaploOutput(out, ctx.filePath), { skipWhenParsedNothing: true });
159
+ if (run.skipped)
160
+ return run.skipped;
161
+ const diagnostics = run.diagnostics;
63
162
  if (diagnostics.length === 0) {
64
163
  return { status: "succeeded", diagnostics: [], semantic: "none" };
65
164
  }
@@ -2,9 +2,9 @@ import * as path from "node:path";
2
2
  import { safeSpawnAsync } from "../../safe-spawn.js";
3
3
  import { getLinterPolicyForCwd } from "../../tool-policy.js";
4
4
  import { findNearestDirWithAnyBasename } from "../../workspace-topology.js";
5
- import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
6
- import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
7
5
  import { PRIORITY } from "../priorities.js";
6
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
7
+ import { parseToolRun } from "./utils/tool-failure.js";
8
8
  const tflint = createAvailabilityChecker("tflint", ".exe");
9
9
  const TFLINT_CONFIG = ".tflint.hcl";
10
10
  /**
@@ -58,7 +58,7 @@ const tflintRunner = {
58
58
  return { status: "skipped", diagnostics: [], semantic: "none" };
59
59
  }
60
60
  let cmd = null;
61
- if (await (tflint.isAvailableAsync(cwd))) {
61
+ if (await tflint.isAvailableAsync(cwd)) {
62
62
  cmd = tflint.getCommand(cwd);
63
63
  }
64
64
  else {
@@ -82,10 +82,15 @@ const tflintRunner = {
82
82
  cwd: fileDir,
83
83
  timeout: 30000,
84
84
  });
85
- if (spawnFailedWithNoOutput(result)) {
86
- return { status: "skipped", diagnostics: [], semantic: "none" };
87
- }
88
- const diagnostics = parseTflintOutput(result.stdout || "", ctx.filePath);
85
+ // #1948: tflint exits nonzero and writes its JSON report to stdout, so a
86
+ // nonzero exit whose report yields zero diagnostics is a parser break,
87
+ // not a clean file. No exit-code table: tflint's nonzero codes are not
88
+ // verified against a real binary here, so the conservative
89
+ // nothing-to-parse rule stays the only discriminator.
90
+ const run = parseToolRun("tflint", { result }, (out) => parseTflintOutput(out, ctx.filePath));
91
+ if (run.skipped)
92
+ return run.skipped;
93
+ const diagnostics = run.diagnostics;
89
94
  if (diagnostics.length === 0) {
90
95
  return { status: "succeeded", diagnostics: [], semantic: "none" };
91
96
  }
@@ -10,6 +10,11 @@
10
10
  * v1 scope — the highest-value, lowest-overlap surface:
11
11
  * - **Kubernetes manifests** (yaml with an `apiVersion:` + `kind:` signature):
12
12
  * zero existing coverage in pi-lens, so no dedup needed.
13
+ * - **CloudFormation templates** (yaml OR json with an
14
+ * `AWSTemplateFormatVersion` key, or a `Resources` map whose entries carry
15
+ * an `AWS::`/`Custom::`-namespaced `Type`; refs #1757): same zero-overlap
16
+ * rationale as Kubernetes — trivy's `cloudformation` misconfig scanner is
17
+ * the only IaC-misconfig coverage pi-lens has for CFN.
13
18
  * - **Dockerfiles**: overlaps hadolint on a few rules (`:latest`, root, …);
14
19
  * the dispatcher suppresses trivy-config findings that hadolint already
15
20
  * reports at the same line (`suppressTrivyConfigDockerOverlap`), so trivy
@@ -17,12 +22,11 @@
17
22
  *
18
23
  * - **Terraform**: the `.tf` language files themselves — trivy evaluates
19
24
  * the Terraform language directly, so no content gate is needed (unlike
20
- * the yaml/k8s heuristic above). Terragrunt (`.hcl`) is deliberately
21
- * excluded: trivy has no terragrunt support, and terragrunt config is
22
- * covered by the terragrunt runner instead.
25
+ * the yaml/k8s/CloudFormation heuristics above). Terragrunt (`.hcl`) is
26
+ * deliberately excluded: trivy has no terragrunt support, and terragrunt
27
+ * config is covered by the terragrunt runner instead.
23
28
  *
24
- * Deferred (tracked on #131): Helm chart rendering, Docker Compose,
25
- * CloudFormation.
29
+ * Deferred (tracked on #131): Helm chart rendering, Docker Compose.
26
30
  *
27
31
  * Gating: the same explicit `trivy.enabled` opt-in as the session-scan modes —
28
32
  * trivy is opt-in, period. (Misconfig needs only the small policy bundle, not
@@ -31,13 +35,14 @@
31
35
  *
32
36
  * Refs: #131 (Mode 2)
33
37
  */
38
+ import { formatToolFailure } from "./utils/tool-failure.js";
34
39
  import * as fs from "node:fs";
35
40
  import * as path from "node:path";
41
+ import { incrementDegradationCount } from "../../degradation-ledger.js";
36
42
  import { safeSpawnAsync } from "../../safe-spawn.js";
37
43
  import { isTrivyEnabled, resolveSeverityFloor, } from "../../trivy-client.js";
38
44
  import { PRIORITY } from "../priorities.js";
39
45
  import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
40
- import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
41
46
  const trivy = createAvailabilityChecker("trivy", ".exe");
42
47
  /**
43
48
  * Heuristic: does this YAML look like a Kubernetes manifest (vs a CI workflow,
@@ -53,6 +58,23 @@ export function looksLikeKubernetesManifest(content) {
53
58
  }
54
59
  return false;
55
60
  }
61
+ /**
62
+ * Heuristic: does this file (yaml OR json) look like a CloudFormation
63
+ * template? The unambiguous signal is the `AWSTemplateFormatVersion` key,
64
+ * present on the vast majority of real templates; SAM templates instead
65
+ * declare `Transform: AWS::Serverless-2016-10-31` (or an array containing
66
+ * it). Templates that skip both still always declare a `Resources` map whose
67
+ * entries carry a `Type` in the `AWS::`/`Custom::`/`Alexa::` namespaces — the
68
+ * one field CFN mandates on every resource. Any one signal qualifies (refs
69
+ * #1757).
70
+ */
71
+ export function looksLikeCloudFormationTemplate(content) {
72
+ if (/AWSTemplateFormatVersion/.test(content))
73
+ return true;
74
+ if (/Transform:\s*(\[.*)?["']?AWS::Serverless/.test(content))
75
+ return true;
76
+ return /Type["']?\s*:\s*["']?(AWS|Custom|Alexa)::/.test(content);
77
+ }
56
78
  function normalizeSeverity(raw) {
57
79
  const s = typeof raw === "string" ? raw.toUpperCase() : "";
58
80
  if (s === "CRITICAL" || s === "HIGH" || s === "MEDIUM" || s === "LOW") {
@@ -122,7 +144,7 @@ export function parseTrivyConfigOutput(raw, filePath) {
122
144
  }
123
145
  const trivyConfigRunner = {
124
146
  id: "trivy-config",
125
- appliesTo: ["docker", "yaml", "terraform"],
147
+ appliesTo: ["docker", "yaml", "terraform", "json"],
126
148
  priority: PRIORITY.GENERAL_ANALYSIS,
127
149
  enabledByDefault: true,
128
150
  skipTestFiles: false,
@@ -133,8 +155,10 @@ const trivyConfigRunner = {
133
155
  return { status: "skipped", diagnostics: [], semantic: "none" };
134
156
  }
135
157
  const absPath = path.resolve(cwd, ctx.filePath);
136
- // YAML is far broader than k8s; only scan files that look like manifests.
137
- if (ctx.kind === "yaml") {
158
+ // YAML is far broader than k8s/CloudFormation; JSON is far broader than
159
+ // CloudFormation only scan files that look like one of the IaC
160
+ // manifest shapes trivy's misconfig scanners understand (refs #1757).
161
+ if (ctx.kind === "yaml" || ctx.kind === "json") {
138
162
  let content = "";
139
163
  try {
140
164
  content = fs.readFileSync(absPath, "utf-8");
@@ -142,7 +166,11 @@ const trivyConfigRunner = {
142
166
  catch {
143
167
  return { status: "skipped", diagnostics: [], semantic: "none" };
144
168
  }
145
- if (!looksLikeKubernetesManifest(content)) {
169
+ const isManifest = ctx.kind === "yaml"
170
+ ? looksLikeKubernetesManifest(content) ||
171
+ looksLikeCloudFormationTemplate(content)
172
+ : looksLikeCloudFormationTemplate(content);
173
+ if (!isManifest) {
146
174
  return { status: "skipped", diagnostics: [], semantic: "none" };
147
175
  }
148
176
  }
@@ -160,15 +188,48 @@ const trivyConfigRunner = {
160
188
  const severities = resolveSeverityFloor(cwd).join(",");
161
189
  const result = await safeSpawnAsync(cmd, [
162
190
  "config",
191
+ // `--quiet` alone suppresses both the progress bar and log output.
192
+ // `--no-progress` is NOT a `config` subcommand flag (unlike `fs`,
193
+ // which does accept it) — trivy 0.73.0 exits 1 on the rejected
194
+ // flag, but prints its full usage/help text (thousands of bytes)
195
+ // to STDOUT before the FATAL line on stderr. That stdout is
196
+ // non-empty, so an empty-output-only guard did not catch it: the
197
+ // help text failed to JSON-parse, `parseTrivyConfigOutput`
198
+ // returned `[]`, and this reported `{ status: "succeeded",
199
+ // diagnostics: [] }` — a clean scan — on every single real
200
+ // invocation (refs #1757; verified against the real installed
201
+ // binary, not assumed).
163
202
  "--quiet",
164
- "--no-progress",
165
203
  "--format",
166
204
  "json",
167
205
  "--severity",
168
206
  severities,
169
207
  absPath,
170
208
  ], { cwd, timeout: 60_000 });
171
- if (spawnFailedWithNoOutput(result)) {
209
+ // `trivy config` is not given `--exit-code`, so it exits 0 whenever it
210
+ // completed (findings included — see parseTrivyConfigOutput's severity
211
+ // mapping for how CRITICAL becomes blocking). ANY nonzero status is
212
+ // therefore a real error, WITH or WITHOUT something on stdout — mirrors
213
+ // the model already in clients/dispatch/runners/helm-render.ts's
214
+ // runIacPass, which has the same "trivy prints usage text on a bad
215
+ // flag" exposure and already got this right. A bounded ledger record
216
+ // means a broken trivy invocation can no longer go unnoticed for an
217
+ // entire lane's lifetime the way this one did (refs #1757).
218
+ if (result.error || result.status !== 0) {
219
+ // #1816: one shared wording, one truncation, signal named. `subject`
220
+ // stays the file path — the discriminating identity a reader needs
221
+ // is WHICH file trivy stopped covering.
222
+ incrementDegradationCount({
223
+ kind: "runner-empty-result",
224
+ subject: absPath,
225
+ reason: formatToolFailure({
226
+ tool: "trivy config",
227
+ status: result.status,
228
+ signal: result.signal,
229
+ stderr: result.stderr || result.error?.message,
230
+ stdout: result.stdout,
231
+ }),
232
+ });
172
233
  return { status: "skipped", diagnostics: [], semantic: "none" };
173
234
  }
174
235
  const diagnostics = parseTrivyConfigOutput(result.stdout || "", ctx.filePath);