@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

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 (360) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -11,6 +11,8 @@
11
11
  * Replaces language-specific runners (pyright, etc.) with a single
12
12
  * unified runner that delegates to the LSP service.
13
13
  */
14
+ import { logExtension } from "../../extension-log.js";
15
+ import { touchCoverageGap } from "../../lsp/diagnostic-binding.js";
14
16
  import { getLSPService } from "../../lsp/index.js";
15
17
  import { RUNTIME_CONFIG } from "../../runtime-config.js";
16
18
  import { PRIORITY } from "../priorities.js";
@@ -110,6 +112,11 @@ const lspRunner = {
110
112
  // one spawned server) — an empty `lspDiags` in that case is NOT a
111
113
  // confirmed clean result and must not be reported as one (#570).
112
114
  let diagnosticsInconclusive = false;
115
+ // #1470: server ids the touch carries no evidence for — an auxiliary whose
116
+ // push wait our aux grace timer cut off. The touch is NOT inconclusive (the
117
+ // primary answered, and its findings below are real), so this is tracked
118
+ // separately: the only claim it invalidates is "0 diagnostics means clean".
119
+ let unconfirmedServerIds = [];
113
120
  let usedWarmAttach = false;
114
121
  let failureReason = "";
115
122
  const content = readFileContent(ctx.filePath);
@@ -128,8 +135,25 @@ const lspRunner = {
128
135
  try {
129
136
  const attached = await tryWarmAttachedDiagnostics(ctx.filePath, content, Math.max(LSP_SPAWN_BUDGET_MS, LSP_DIAGNOSTICS_WAIT_MS));
130
137
  usedWarmAttach = attached?.available === true;
138
+ // #1179 (shape-5 structural fix): both branches normalize to the
139
+ // `touchFile` wrapper shape. The warm-attach IPC branch resolves a plain
140
+ // diagnostics array — `available` no longer implies a fully confirmed
141
+ // answer: a `partial` confirmation (an auxiliary cut off by the grace
142
+ // timer) is served as `available: true` too (the IPC gate at
143
+ // `clients/mcp/ipc.ts:248` rejects only `inconclusive`). Carry the
144
+ // incumbent's `unconfirmedServerIds` onto the wrapper so
145
+ // `touchCoverageGap` below sees it — dropping it here is the same
146
+ // false-clean defect already fixed at `clients/lsp/index.ts` (the
147
+ // workspace sweep wrapper) and `tools/lsp-diagnostics.ts` (the tool
148
+ // consumer); wrap it as `{ diags }`; the incumbent branch already
149
+ // returns the wrapper.
131
150
  const touched = attached?.available
132
- ? attached.response.diagnostics
151
+ ? {
152
+ diags: attached.response.diagnostics,
153
+ ...(attached.response.unconfirmedServerIds !== undefined && {
154
+ unconfirmedServerIds: attached.response.unconfirmedServerIds,
155
+ }),
156
+ }
133
157
  : await lspService.touchFile(ctx.filePath, content, {
134
158
  diagnostics: "document",
135
159
  collectDiagnostics: true,
@@ -143,10 +167,9 @@ const lspRunner = {
143
167
  lspClientReady = false;
144
168
  }
145
169
  else {
146
- lspDiags = touched;
147
- diagnosticsInconclusive =
148
- touched
149
- .inconclusive === true;
170
+ lspDiags = touched.diags;
171
+ diagnosticsInconclusive = touched.inconclusive === true;
172
+ unconfirmedServerIds = touchCoverageGap(touched);
150
173
  }
151
174
  }
152
175
  catch (err) {
@@ -156,7 +179,11 @@ const lspRunner = {
156
179
  failureReason.includes("exited") ||
157
180
  failureReason.includes("connection") ||
158
181
  failureReason.includes("JSON RPC")) {
159
- console.error(`[lsp-runner] LSP server failed for ${diagnosticPath}: ${failureReason}`);
182
+ logExtension({
183
+ subsystem: "lsp-runner",
184
+ message: `LSP server failed for ${diagnosticPath}: ${failureReason}`,
185
+ metadata: { filePath: diagnosticPath },
186
+ });
160
187
  }
161
188
  }
162
189
  if (serverFailed) {
@@ -200,6 +227,20 @@ const lspRunner = {
200
227
  return { status: "skipped", diagnostics: [], semantic: "none" };
201
228
  }
202
229
  if (lspDiags.length === 0) {
230
+ if (unconfirmedServerIds.length > 0) {
231
+ // #1470: an auxiliary was cut off by the aux grace timer, so this empty
232
+ // merged result is missing whatever that scanner would have said — a
233
+ // hung opengrep must not read as a clean bill of health on the security
234
+ // lane. `RunnerResult` has no channel for "clean for these servers,
235
+ // unknown for those", so the only honest verdict this seam can express
236
+ // for an EMPTY result is "not checked" — which is what "skipped" means
237
+ // here, and it lets the coverage notice say so once. Nothing is thrown
238
+ // away: the primary answered with zero findings, so there is nothing to
239
+ // report; when it DOES have findings the branches below still report
240
+ // them (see the non-empty path), which is how a trustworthy primary
241
+ // stays trustworthy under a cut-off auxiliary.
242
+ return { status: "skipped", diagnostics: [], semantic: "none" };
243
+ }
203
244
  return {
204
245
  status: "succeeded",
205
246
  diagnostics: [],
@@ -1,6 +1,5 @@
1
- import { resolvePackagePath } from "../../package-root.js";
2
1
  import { safeSpawnAsync } from "../../safe-spawn.js";
3
- import { getLinterPolicyForCwd, hasMarkdownlintConfig, } from "../../tool-policy.js";
2
+ import { getLinterPolicyForCwd, markdownlintConfigArgs, } from "../../tool-policy.js";
4
3
  import { PRIORITY } from "../priorities.js";
5
4
  import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
5
  const markdownlint = createAvailabilityChecker("markdownlint-cli2", ".cmd");
@@ -93,7 +92,6 @@ const markdownlintRunner = {
93
92
  if (policy && !policy.preferredRunners.includes("markdownlint")) {
94
93
  return { status: "skipped", diagnostics: [], semantic: "none" };
95
94
  }
96
- const hasConfig = hasMarkdownlintConfig(cwd);
97
95
  let cmd = null;
98
96
  if (await (markdownlint.isAvailableAsync(cwd))) {
99
97
  cmd = markdownlint.getCommand(cwd);
@@ -103,12 +101,9 @@ const markdownlintRunner = {
103
101
  }
104
102
  if (!cmd)
105
103
  return { status: "skipped", diagnostics: [], semantic: "none" };
106
- const configArgs = hasConfig
107
- ? []
108
- : [
109
- "--config",
110
- resolvePackagePath(import.meta.url, "config/markdownlint/core.json"),
111
- ];
104
+ // Shared config-args seam (#1247): the autofix path consumes the same
105
+ // builder, so the package-owned fallback config can never drift.
106
+ const configArgs = markdownlintConfigArgs(cwd);
112
107
  const result = await safeSpawnAsync(cmd, [...configArgs, ctx.filePath], {
113
108
  timeout: 15000,
114
109
  cwd,
@@ -6,11 +6,11 @@
6
6
  *
7
7
  * Requires: pyright (pip install pyright or npm install -g pyright)
8
8
  */
9
- import { ensureTool } from "../../installer/index.js";
9
+ import { logExtension } from "../../extension-log.js";
10
10
  import { getLSPService } from "../../lsp/index.js";
11
11
  import { safeSpawnAsync } from "../../safe-spawn.js";
12
12
  import { PRIORITY } from "../priorities.js";
13
- import { createAvailabilityChecker } from "./utils/runner-helpers.js";
13
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
14
14
  const pyright = createAvailabilityChecker("pyright", ".exe");
15
15
  const pyrightRunner = {
16
16
  id: "pyright",
@@ -34,9 +34,9 @@ const pyrightRunner = {
34
34
  if (await (pyright.isAvailableAsync(cwd))) {
35
35
  cmd = pyright.getCommand(cwd);
36
36
  }
37
- // Strategy 2: Try to find pyright via ensureTool (installs if needed)
37
+ // Strategy 2: use the shared availability taxonomy and install suppression.
38
38
  if (!cmd) {
39
- const installedPath = await ensureTool("pyright");
39
+ const installedPath = await resolveAvailableOrInstall(pyright, "pyright", cwd);
40
40
  if (installedPath)
41
41
  cmd = installedPath;
42
42
  }
@@ -85,7 +85,11 @@ const pyrightRunner = {
85
85
  // pi-lens-ignore: missing-error-propagation
86
86
  }
87
87
  catch {
88
- console.error(`[runner:pyright] JSON parse failed for ${ctx.filePath} — raw output: ${output.slice(0, 200)}`);
88
+ logExtension({
89
+ subsystem: "runner:pyright",
90
+ message: `JSON parse failed for ${ctx.filePath} — raw output: ${output.slice(0, 200)}`,
91
+ metadata: { filePath: ctx.filePath },
92
+ });
89
93
  return {
90
94
  status: "failed",
91
95
  diagnostics: [],
@@ -6,10 +6,9 @@
6
6
  * mutating files mid-dispatch after LSP sync has already happened.
7
7
  * Supports venv-local installations.
8
8
  */
9
- import { resolvePackagePath } from "../../package-root.js";
10
9
  import { safeSpawnAsync } from "../../safe-spawn.js";
11
10
  import { stripAnsi } from "../../sanitize.js";
12
- import { getAutofixCapability, getLinterPolicyForCwd, hasRuffConfig, } from "../../tool-policy.js";
11
+ import { getAutofixCapability, getLinterPolicyForCwd, ruffConfigArgs, } from "../../tool-policy.js";
13
12
  import { PRIORITY } from "../priorities.js";
14
13
  import { parseRuffOutput } from "./utils/diagnostic-parsers.js";
15
14
  import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
@@ -61,12 +60,9 @@ const ruffRunner = {
61
60
  if (!cmd) {
62
61
  return { status: "skipped", diagnostics: [], semantic: "none" };
63
62
  }
64
- const configArgs = hasRuffConfig(cwd)
65
- ? []
66
- : [
67
- "--config",
68
- resolvePackagePath(import.meta.url, "config/ruff/core.toml"),
69
- ];
63
+ // Shared config-args seam (#1247): the autofix path consumes the same
64
+ // builder, so the package-owned fallback config can never drift.
65
+ const configArgs = ruffConfigArgs(cwd);
70
66
  // Step 1: Capture diagnostics (before fixing) — teaching signal for the agent
71
67
  const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { timeout: 30000 });
72
68
  const raw = stripAnsi(checkResult.stdout + checkResult.stderr);
@@ -21,10 +21,9 @@
21
21
  */
22
22
  import * as fs from "node:fs";
23
23
  import * as path from "node:path";
24
- import { ensureTool } from "../../installer/index.js";
25
24
  import { safeSpawnAsync } from "../../safe-spawn.js";
26
25
  import { PRIORITY } from "../priorities.js";
27
- import { createAvailabilityChecker, lspPrimaryCoversFile, } from "./utils/runner-helpers.js";
26
+ import { createAvailabilityChecker, lspPrimaryCoversFile, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
28
27
  const shellcheck = createAvailabilityChecker("shellcheck", ".exe");
29
28
  function findShellcheckConfig(cwd) {
30
29
  const local = path.join(cwd, ".shellcheckrc");
@@ -127,7 +126,7 @@ const shellcheckRunner = {
127
126
  cmd = shellcheck.getCommand(cwd);
128
127
  }
129
128
  else {
130
- const managed = await ensureTool("shellcheck");
129
+ const managed = await resolveAvailableOrInstall(shellcheck, "shellcheck", cwd);
131
130
  if (managed)
132
131
  cmd = managed;
133
132
  }
@@ -1,9 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { ensureTool } from "../../installer/index.js";
4
3
  import { safeSpawnAsync } from "../../safe-spawn.js";
5
4
  import { PRIORITY } from "../priorities.js";
6
- import { createAvailabilityChecker } from "./utils/runner-helpers.js";
5
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
7
6
  const shfmt = createAvailabilityChecker("shfmt", ".exe");
8
7
  // shfmt's only config source is .editorconfig. We treat its presence as the
9
8
  // opt-in for the (non-error) format-diff warning, so out of the box shfmt only
@@ -39,7 +38,7 @@ const shfmtRunner = {
39
38
  cmd = shfmt.getCommand(cwd);
40
39
  }
41
40
  else {
42
- const installed = await ensureTool("shfmt");
41
+ const installed = await resolveAvailableOrInstall(shfmt, "shfmt", cwd);
43
42
  if (!installed) {
44
43
  return { status: "skipped", diagnostics: [], semantic: "none" };
45
44
  }
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
+ import { BoundedLruCache } from "../../bounded-cache.js";
4
5
  import { safeSpawnAsync } from "../../safe-spawn.js";
5
6
  import { findCompiledClassesDir } from "../../tool-policy.js";
6
7
  import { PRIORITY } from "../priorities.js";
@@ -11,7 +12,7 @@ const java = createAvailabilityChecker("java", ".exe", ["-version"]);
11
12
  const spotbugs = createAvailabilityChecker("spotbugs", ".bat", ["-version"]);
12
13
  const MAX_DIAGNOSTICS = 50;
13
14
  const MAX_XML_BYTES = 4 * 1024 * 1024;
14
- const scanCache = new Map();
15
+ const scanCache = new BoundedLruCache(32);
15
16
  /** Reset the SpotBugs scan cache (tests). */
16
17
  export function _resetSpotbugsCacheForTests() {
17
18
  scanCache.clear();
@@ -3,6 +3,7 @@ import { safeSpawnAsync } from "../../safe-spawn.js";
3
3
  import { getLinterPolicyForCwd } from "../../tool-policy.js";
4
4
  import { PRIORITY } from "../priorities.js";
5
5
  import { createAvailabilityChecker, lspPrimaryCoversFile, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
6
7
  const taplo = createAvailabilityChecker("taplo", ".exe");
7
8
  function parseTaploOutput(raw, filePath) {
8
9
  try {
@@ -55,7 +56,7 @@ const taploRunner = {
55
56
  return { status: "skipped", diagnostics: [], semantic: "none" };
56
57
  const absPath = path.resolve(cwd, ctx.filePath);
57
58
  const result = await safeSpawnAsync(cmd, ["check", "--output=json", absPath], { cwd, timeout: 15000 });
58
- if (result.error && !result.stdout) {
59
+ if (spawnFailedWithNoOutput(result)) {
59
60
  return { status: "skipped", diagnostics: [], semantic: "none" };
60
61
  }
61
62
  const diagnostics = parseTaploOutput(result.stdout || "", ctx.filePath);
@@ -0,0 +1,163 @@
1
+ import * as path from "node:path";
2
+ import { safeSpawnAsync } from "../../safe-spawn.js";
3
+ import { pathsEqual } from "../../path-utils.js";
4
+ import { getLinterPolicyForCwd } from "../../tool-policy.js";
5
+ import { PRIORITY } from "../priorities.js";
6
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
7
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
8
+ const terragrunt = createAvailabilityChecker("terragrunt", ".exe");
9
+ function normalizeSeverity(raw) {
10
+ if (typeof raw === "number")
11
+ return raw === 1 ? "error" : "warning";
12
+ if (typeof raw === "string" && raw.toLowerCase() === "error")
13
+ return "error";
14
+ return "warning";
15
+ }
16
+ function toRawDiagnostics(parsed) {
17
+ if (Array.isArray(parsed))
18
+ return parsed;
19
+ if (!parsed || typeof parsed !== "object")
20
+ return [];
21
+ const invalidFiles = parsed.invalid_files;
22
+ if (!Array.isArray(invalidFiles))
23
+ return [];
24
+ return invalidFiles.flatMap((f) => Array.isArray(f?.diagnostics) ? f.diagnostics : []);
25
+ }
26
+ /**
27
+ * Parse `terragrunt hcl validate --json` output.
28
+ *
29
+ * OBSERVED CONTRACT (captured empirically from terragrunt v1.1.2, win-x64):
30
+ * the payload is a FLAT JSON array of diagnostic objects on stdout, e.g.
31
+ *
32
+ * [{"range":{"filename":"<ABSOLUTE path>","start":{"line":1,"column":8,...},
33
+ * "end":{...}},"snippet":{...},"summary":"Unclosed configuration block",
34
+ * "detail":"...","severity":"error"}]
35
+ *
36
+ * Key facts: `severity` is a STRING ("error"), `range.filename` is an ABSOLUTE
37
+ * path (host separator), and a CLEAN unit prints NOTHING (empty stdout, exit 0)
38
+ * — not `[]`. The nested `{invalid_files:[{diagnostics:[...]}]}` wrapper and the
39
+ * numeric (1=error/2=warning) severity encoding were NOT observed on v1.1.2 and
40
+ * are retained only as a tolerant fallback for other/older shapes.
41
+ * Malformed/unparseable input returns [].
42
+ */
43
+ export function parseTerragruntOutput(raw, filePath, absPath = path.resolve(filePath)) {
44
+ if (!raw.trim())
45
+ return [];
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(raw);
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ // Resolved against the unit dir, not compared by basename: a unit that
54
+ // includes its parent via find_in_parent_folders() gets diagnostics from a
55
+ // parent terragrunt.hcl, whose basename matches but whose lines do not.
56
+ const unitDir = path.dirname(absPath);
57
+ const diagnostics = [];
58
+ for (const d of toRawDiagnostics(parsed)) {
59
+ if (!d || typeof d !== "object")
60
+ continue;
61
+ const diagFile = d.range?.filename;
62
+ if (diagFile && !pathsEqual(path.resolve(unitDir, diagFile), absPath))
63
+ continue;
64
+ const line = d.range?.start?.line ?? 1;
65
+ const column = d.range?.start?.column ?? 1;
66
+ const severity = normalizeSeverity(d.severity);
67
+ const message = d.summary ?? d.detail ?? "terragrunt hcl validate error";
68
+ // No rule code in hcl validate output, so the message carries the identity.
69
+ const idMessage = message
70
+ .toLowerCase()
71
+ .replace(/[^a-z0-9]+/g, "-")
72
+ .slice(0, 80);
73
+ diagnostics.push({
74
+ id: `terragrunt-hclvalidate-${line}-${column}-${idMessage}`,
75
+ message,
76
+ filePath,
77
+ line,
78
+ column,
79
+ severity,
80
+ semantic: severity === "error" ? "blocking" : "warning",
81
+ tool: "terragrunt",
82
+ fixable: false,
83
+ });
84
+ }
85
+ return diagnostics;
86
+ }
87
+ const SKIPPED = {
88
+ status: "skipped",
89
+ diagnostics: [],
90
+ semantic: "none",
91
+ };
92
+ /**
93
+ * Runner for `terragrunt hcl validate --json`, the linter for terragrunt units.
94
+ *
95
+ * REQUIRES the redesigned terragrunt CLI: the `terragrunt hcl validate` /
96
+ * `terragrunt hcl fmt` command group replaced the old `hclvalidate`/`hclfmt`
97
+ * top-level commands in the CLI redesign (~terragrunt v0.75; the legacy names
98
+ * were removed in v1.0). Contract verified empirically on terragrunt v1.1.2.
99
+ *
100
+ * Observed exit-code / output table (terragrunt v1.1.2, win-x64):
101
+ * - clean unit ............ exit 0, EMPTY stdout (no `[]`)
102
+ * - validation findings ... exit 1, flat JSON array on stdout (+ a log line
103
+ * "N HCL validation error(s) found" on stderr)
104
+ * - unknown command ....... an older binary that predates `hcl validate`
105
+ * exits non-zero with the error on STDERR and EMPTY
106
+ * stdout; `spawnFailedWithNoOutput` classifies that
107
+ * as SKIPPED (never a false blocker, and never a
108
+ * false CLEAN — a nonzero exit carries no
109
+ * `SpawnResult.error`, so the status has to be part
110
+ * of the test).
111
+ *
112
+ * `hcl validate` recursively validates the unit(s) at its working dir; it has no
113
+ * per-file flag (`--filter` takes component filter-syntax, NOT a filename — a
114
+ * bare basename silently matches zero components and suppresses ALL findings,
115
+ * verified on v1.1.2). So we validate the edited file's unit directory and
116
+ * attribute findings back to the edited file by absolute path in
117
+ * parseTerragruntOutput.
118
+ *
119
+ * Misdetection edge: `root.hcl` is filename-detected as terragrunt, so a
120
+ * `root.hcl` that is NOT a terragrunt file still gets validated here — `hcl
121
+ * validate` just checks generic HCL, so a non-terragrunt file fails soft
122
+ * (either clean or a plain HCL diagnostic, never a spurious hard blocker).
123
+ */
124
+ const terragruntRunner = {
125
+ id: "terragrunt",
126
+ appliesTo: ["terragrunt"],
127
+ priority: PRIORITY.GENERAL_ANALYSIS,
128
+ enabledByDefault: true,
129
+ skipTestFiles: false,
130
+ async run(ctx) {
131
+ const cwd = ctx.cwd || process.cwd();
132
+ const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
133
+ if (policy && !policy.preferredRunners.includes("terragrunt"))
134
+ return SKIPPED;
135
+ let cmd = null;
136
+ if (await terragrunt.isAvailableAsync(cwd)) {
137
+ cmd = terragrunt.getCommand(cwd);
138
+ }
139
+ else {
140
+ const managed = await resolveAvailableOrInstall(terragrunt, "terragrunt", cwd);
141
+ if (managed)
142
+ cmd = managed;
143
+ }
144
+ if (!cmd)
145
+ return SKIPPED;
146
+ const absPath = path.resolve(cwd, ctx.filePath);
147
+ const fileDir = path.dirname(absPath);
148
+ const result = await safeSpawnAsync(cmd, ["hcl", "validate", "--json", "--non-interactive"], { cwd: fileDir, timeout: 30000 });
149
+ if (spawnFailedWithNoOutput(result))
150
+ return SKIPPED;
151
+ const diagnostics = parseTerragruntOutput(result.stdout || "", ctx.filePath, absPath);
152
+ if (diagnostics.length === 0) {
153
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
154
+ }
155
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
156
+ return {
157
+ status: hasErrors ? "failed" : "succeeded",
158
+ diagnostics,
159
+ semantic: hasErrors ? "blocking" : "warning",
160
+ };
161
+ },
162
+ };
163
+ export default terragruntRunner;
@@ -1,9 +1,26 @@
1
1
  import * as path from "node:path";
2
- import { ensureTool } from "../../installer/index.js";
3
2
  import { safeSpawnAsync } from "../../safe-spawn.js";
4
- import { createAvailabilityChecker } from "./utils/runner-helpers.js";
3
+ import { getLinterPolicyForCwd } from "../../tool-policy.js";
4
+ import { findNearestDirWithAnyBasename } from "../../workspace-topology.js";
5
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
6
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
5
7
  import { PRIORITY } from "../priorities.js";
6
8
  const tflint = createAvailabilityChecker("tflint", ".exe");
9
+ const TFLINT_CONFIG = ".tflint.hcl";
10
+ /**
11
+ * tflint resolves `.tflint.hcl` from its own working directory and never walks
12
+ * parents (its only fallback is `~/.tflint.hcl`). We run it from the edited
13
+ * file's directory, so a repo-root config would govern nothing beneath the
14
+ * root unless we name it explicitly. Returns null when `TFLINT_CONFIG_FILE` is
15
+ * set: `--config` outranks the env var in tflint's own precedence, so passing
16
+ * one would override a deliberate choice.
17
+ */
18
+ function findTflintConfig(fileDir) {
19
+ if (process.env.TFLINT_CONFIG_FILE)
20
+ return null;
21
+ const dir = findNearestDirWithAnyBasename(fileDir, [TFLINT_CONFIG]);
22
+ return dir ? path.join(dir, TFLINT_CONFIG) : null;
23
+ }
7
24
  function parseTflintOutput(raw, filePath) {
8
25
  try {
9
26
  const parsed = JSON.parse(raw);
@@ -36,12 +53,16 @@ const tflintRunner = {
36
53
  skipTestFiles: false,
37
54
  async run(ctx) {
38
55
  const cwd = ctx.cwd || process.cwd();
56
+ const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
57
+ if (policy && !policy.preferredRunners.includes("tflint")) {
58
+ return { status: "skipped", diagnostics: [], semantic: "none" };
59
+ }
39
60
  let cmd = null;
40
61
  if (await (tflint.isAvailableAsync(cwd))) {
41
62
  cmd = tflint.getCommand(cwd);
42
63
  }
43
64
  else {
44
- const managed = await ensureTool("tflint");
65
+ const managed = await resolveAvailableOrInstall(tflint, "tflint", cwd);
45
66
  if (managed)
46
67
  cmd = managed;
47
68
  }
@@ -49,8 +70,19 @@ const tflintRunner = {
49
70
  return { status: "skipped", diagnostics: [], semantic: "none" };
50
71
  const absPath = path.resolve(cwd, ctx.filePath);
51
72
  const fileDir = path.dirname(absPath);
52
- const result = await safeSpawnAsync(cmd, ["--format=json", "--no-color", `--filter=${path.basename(absPath)}`], { cwd: fileDir, timeout: 30000 });
53
- if (result.error && !result.stdout) {
73
+ const args = [
74
+ "--format=json",
75
+ "--no-color",
76
+ `--filter=${path.basename(absPath)}`,
77
+ ];
78
+ const configPath = findTflintConfig(fileDir);
79
+ if (configPath)
80
+ args.push(`--config=${configPath}`);
81
+ const result = await safeSpawnAsync(cmd, args, {
82
+ cwd: fileDir,
83
+ timeout: 30000,
84
+ });
85
+ if (spawnFailedWithNoOutput(result)) {
54
86
  return { status: "skipped", diagnostics: [], semantic: "none" };
55
87
  }
56
88
  const diagnostics = parseTflintOutput(result.stdout || "", ctx.filePath);
@@ -525,7 +525,9 @@ const treeSitterRunner = {
525
525
  });
526
526
  }
527
527
  else {
528
- console.error("[tree-sitter] Batched query run failed:", err);
528
+ // #1333: the logTreeSitter call below already carries this failure to
529
+ // tree-sitter.log — the console.error was a duplicate RAW write into
530
+ // pi's frame, not a second destination. Sink kept, write dropped.
529
531
  logTreeSitter({
530
532
  phase: "query_error",
531
533
  filePath,
@@ -15,8 +15,14 @@
15
15
  * reports at the same line (`suppressTrivyConfigDockerOverlap`), so trivy
16
16
  * only adds the security checks hadolint lacks.
17
17
  *
18
- * Deferred (tracked on #131): Terraform (tflint overlap), Helm chart rendering,
19
- * Docker Compose, CloudFormation.
18
+ * - **Terraform**: the `.tf` language files themselves trivy evaluates
19
+ * 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.
23
+ *
24
+ * Deferred (tracked on #131): Helm chart rendering, Docker Compose,
25
+ * CloudFormation.
20
26
  *
21
27
  * Gating: the same explicit `trivy.enabled` opt-in as the session-scan modes —
22
28
  * trivy is opt-in, period. (Misconfig needs only the small policy bundle, not
@@ -30,7 +36,8 @@ import * as path from "node:path";
30
36
  import { safeSpawnAsync } from "../../safe-spawn.js";
31
37
  import { isTrivyEnabled, resolveSeverityFloor, } from "../../trivy-client.js";
32
38
  import { PRIORITY } from "../priorities.js";
33
- import { createAvailabilityChecker } from "./utils/runner-helpers.js";
39
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
40
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
34
41
  const trivy = createAvailabilityChecker("trivy", ".exe");
35
42
  /**
36
43
  * Heuristic: does this YAML look like a Kubernetes manifest (vs a CI workflow,
@@ -115,7 +122,7 @@ export function parseTrivyConfigOutput(raw, filePath) {
115
122
  }
116
123
  const trivyConfigRunner = {
117
124
  id: "trivy-config",
118
- appliesTo: ["docker", "yaml"],
125
+ appliesTo: ["docker", "yaml", "terraform"],
119
126
  priority: PRIORITY.GENERAL_ANALYSIS,
120
127
  enabledByDefault: true,
121
128
  skipTestFiles: false,
@@ -144,8 +151,7 @@ const trivyConfigRunner = {
144
151
  cmd = trivy.getCommand(cwd);
145
152
  }
146
153
  else {
147
- const { ensureTool } = await import("../../installer/index.js");
148
- const managed = await ensureTool("trivy");
154
+ const managed = await resolveAvailableOrInstall(trivy, "trivy", cwd);
149
155
  if (managed)
150
156
  cmd = managed;
151
157
  }
@@ -162,7 +168,7 @@ const trivyConfigRunner = {
162
168
  severities,
163
169
  absPath,
164
170
  ], { cwd, timeout: 60_000 });
165
- if (result.error && !result.stdout) {
171
+ if (spawnFailedWithNoOutput(result)) {
166
172
  return { status: "skipped", diagnostics: [], semantic: "none" };
167
173
  }
168
174
  const diagnostics = parseTrivyConfigOutput(result.stdout || "", ctx.filePath);