@herbertgao/pi-extensions 2026.8.3 → 2026.8.5

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 (408) hide show
  1. package/README.md +45 -4
  2. package/examples/pi-footer.json +275 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  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 +5 -8
  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 +8 -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 +15 -2
  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/schedule-menu.ts +9 -8
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  26. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  27. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  31. package/node_modules/@narumitw/pi-btw/README.md +8 -7
  32. package/node_modules/@narumitw/pi-btw/package.json +2 -2
  33. package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
  34. package/node_modules/pi-footer/CHANGELOG.md +145 -0
  35. package/node_modules/pi-footer/LICENSE +21 -0
  36. package/node_modules/pi-footer/README.md +354 -0
  37. package/node_modules/pi-footer/package.json +68 -0
  38. package/node_modules/pi-footer/src/cache.ts +106 -0
  39. package/node_modules/pi-footer/src/colors.ts +237 -0
  40. package/node_modules/pi-footer/src/config.ts +202 -0
  41. package/node_modules/pi-footer/src/event-widgets.ts +41 -0
  42. package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
  43. package/node_modules/pi-footer/src/git.ts +135 -0
  44. package/node_modules/pi-footer/src/index.ts +268 -0
  45. package/node_modules/pi-footer/src/metrics.ts +141 -0
  46. package/node_modules/pi-footer/src/presets.ts +383 -0
  47. package/node_modules/pi-footer/src/render.ts +114 -0
  48. package/node_modules/pi-footer/src/separators.ts +52 -0
  49. package/node_modules/pi-footer/src/types.ts +122 -0
  50. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
  51. package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
  52. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
  53. package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
  54. package/node_modules/pi-footer/src/ui/events.ts +35 -0
  55. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
  56. package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
  57. package/node_modules/pi-footer/src/ui/fields.ts +252 -0
  58. package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
  59. package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
  60. package/node_modules/pi-footer/src/ui/layout.ts +17 -0
  61. package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
  62. package/node_modules/pi-footer/src/ui/model.ts +64 -0
  63. package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
  64. package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
  65. package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
  66. package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
  67. package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
  68. package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
  69. package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
  70. package/node_modules/pi-footer/src/ui/screen.ts +243 -0
  71. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
  72. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
  73. package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
  74. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
  75. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
  76. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
  77. package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
  78. package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
  79. package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
  80. package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
  81. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
  82. package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
  83. package/node_modules/pi-footer/src/ui/theme.ts +48 -0
  84. package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
  85. package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
  86. package/node_modules/pi-footer/src/ui.ts +66 -0
  87. package/node_modules/pi-footer/src/widgets/context.ts +28 -0
  88. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
  89. package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
  90. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
  91. package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
  92. package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
  93. package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
  94. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
  95. package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
  96. package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
  97. package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
  98. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
  99. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
  100. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
  101. package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
  102. package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
  103. package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
  104. package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
  105. package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
  106. package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
  107. package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
  108. package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
  109. package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
  110. package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
  111. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
  112. package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
  113. package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
  114. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
  115. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
  116. package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
  117. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
  118. package/node_modules/pi-footer/src/widgets/options.ts +122 -0
  119. package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
  120. package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
  121. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
  122. package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
  123. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
  124. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
  125. package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
  126. package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
  127. package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
  128. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
  129. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
  130. package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
  131. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
  132. package/node_modules/pi-footer/src/widgets/store.ts +26 -0
  133. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
  134. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
  135. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
  136. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
  137. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
  138. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
  139. package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
  140. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
  141. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
  142. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
  143. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
  144. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
  145. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
  146. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
  147. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
  148. package/node_modules/pi-footer/src/widgets/types.ts +181 -0
  149. package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
  150. package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
  151. package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
  152. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
  153. package/node_modules/pi-lens/CHANGELOG.md +2440 -954
  154. package/node_modules/pi-lens/README.md +13 -0
  155. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  156. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  157. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  158. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  159. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  160. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  161. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  162. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  163. package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
  164. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  165. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  166. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  167. package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
  168. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  169. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  170. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  171. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  172. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  173. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  174. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  175. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
  177. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  178. package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
  179. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  180. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  181. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  182. package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
  183. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
  184. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  185. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
  186. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  187. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  188. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  189. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  190. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  191. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
  192. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  193. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  194. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  195. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  196. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  197. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  198. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  199. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  201. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  202. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
  204. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  205. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  206. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  219. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  220. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  221. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  222. package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
  223. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  224. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  225. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  226. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  227. package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
  228. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  229. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  230. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  231. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  232. package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
  233. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  234. package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
  235. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
  236. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  237. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  238. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  239. package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
  240. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  241. package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
  242. package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
  243. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  244. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  245. package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
  246. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  247. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  248. package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
  249. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  250. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  251. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
  252. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  253. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
  254. package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
  255. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  256. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
  257. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  258. package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
  259. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  260. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  261. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  262. package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
  263. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  264. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  265. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  266. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
  267. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  268. package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
  269. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  270. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  271. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  272. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  273. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  274. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  275. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  276. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  277. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  278. package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
  279. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  280. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  281. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  282. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  283. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  284. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  285. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  286. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  287. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  288. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  289. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  290. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  291. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  292. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  293. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  294. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  295. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  296. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  297. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  298. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  299. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  300. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  301. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  302. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
  303. package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
  304. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
  305. package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
  306. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  307. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
  308. package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
  309. package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
  310. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  311. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  313. package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
  314. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  315. package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
  316. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  317. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  318. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  319. package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
  320. package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
  321. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  322. package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
  323. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  324. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  325. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  326. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  327. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  328. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  329. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  330. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  331. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  332. package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
  333. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  334. package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
  335. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  336. package/node_modules/pi-lens/dist/index.js +49361 -36888
  337. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  338. package/node_modules/pi-lens/dist/mcp/server.js +211 -44
  339. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  340. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
  341. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
  342. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  343. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  344. package/node_modules/pi-lens/docs/agent-guide.md +2 -2
  345. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  346. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  347. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  348. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
  349. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  350. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  351. package/node_modules/pi-lens/docs/fable.md +27 -0
  352. package/node_modules/pi-lens/docs/features.md +31 -2
  353. package/node_modules/pi-lens/docs/globalconfig.md +45 -1
  354. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  355. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  356. package/node_modules/pi-lens/docs/mcp.md +37 -4
  357. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  358. package/node_modules/pi-lens/docs/settings.md +12 -1
  359. package/node_modules/pi-lens/docs/tools.md +4 -1
  360. package/node_modules/pi-lens/docs/usage.md +18 -0
  361. package/node_modules/pi-lens/package.json +10 -10
  362. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  363. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  364. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  365. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  366. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  367. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  368. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  369. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  370. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  371. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  372. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  373. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  374. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
  375. package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
  376. package/node_modules/pi-mcp-adapter/README.md +34 -3
  377. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  378. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  379. package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
  380. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  381. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  382. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  383. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  384. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  385. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  386. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  387. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  388. package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
  389. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  390. package/node_modules/pi-mcp-adapter/package.json +3 -3
  391. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  392. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  393. package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
  394. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  395. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  396. package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
  397. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  398. package/node_modules/pi-mcp-adapter/types.ts +93 -27
  399. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  400. package/package.json +17 -13
  401. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  402. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  403. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  404. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  405. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  406. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  407. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  408. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -0,0 +1,14 @@
1
+ /** Lazy dispatch integration seam for startup-cost control (#1394). */
2
+ let integrationPromise;
3
+ /** Start loading the runner graph once; callers may fire-and-forget this. */
4
+ export function warmDispatchIntegration() {
5
+ return (integrationPromise ??= import("./integration.js"));
6
+ }
7
+ /** Await the same promise used by session-start warming and first use. */
8
+ export function loadDispatchIntegration() {
9
+ return warmDispatchIntegration();
10
+ }
11
+ /** Test-only reset; production sessions intentionally retain the promise. */
12
+ export function resetDispatchIntegrationForTests() {
13
+ integrationPromise = undefined;
14
+ }
@@ -118,6 +118,11 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
118
118
  { mode: "all", runnerIds: ["actionlint"], filterKinds: ["yaml"] },
119
119
  ],
120
120
  },
121
+ "helm-template": {
122
+ name: "Helm Template Linting",
123
+ capabilities: ["lint"],
124
+ writeGroups: [primary("helm-template")],
125
+ },
121
126
  sql: {
122
127
  name: "SQL Processing",
123
128
  capabilities: ["format", "lint"],
@@ -218,9 +223,14 @@ export const LANGUAGE_CAPABILITY_MATRIX = {
218
223
  },
219
224
  terraform: {
220
225
  name: "Terraform Linting",
221
- capabilities: ["types", "lint"],
226
+ capabilities: ["types", "lint", "security"],
222
227
  writeGroups: [primary("terraform")],
223
228
  },
229
+ terragrunt: {
230
+ name: "Terragrunt Linting",
231
+ capabilities: ["lint", "format"],
232
+ writeGroups: [primary("terragrunt")],
233
+ },
224
234
  nix: {
225
235
  name: "Nix Linting",
226
236
  capabilities: ["types", "lint"],
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Normalize a rule id to the form a user typically writes in a
3
+ * `pi-lens-ignore` comment, an inline suppression, or a project-level
4
+ * `disable`/`select` list. Strips the `ast-grep:` LSP source prefix and the
5
+ * language suffix used by the rule catalogs.
6
+ */
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { getAstGrepRuleSources } from "../sgconfig.js";
10
+ /** Derive language tags from shipped rule filenames, rather than a list. */
11
+ export function deriveRuleIdLanguageSuffixes(ruleRoot) {
12
+ const suffixes = new Set();
13
+ const visit = (dir) => {
14
+ let entries;
15
+ try {
16
+ entries = fs.readdirSync(dir, { withFileTypes: true });
17
+ }
18
+ catch {
19
+ return;
20
+ }
21
+ for (const entry of entries) {
22
+ const entryPath = path.join(dir, entry.name);
23
+ if (entry.isDirectory())
24
+ visit(entryPath);
25
+ else {
26
+ const match = /-([a-z0-9]+)\.ya?ml$/i.exec(entry.name);
27
+ if (match)
28
+ suffixes.add(match[1].toLowerCase());
29
+ }
30
+ }
31
+ };
32
+ visit(ruleRoot);
33
+ return suffixes;
34
+ }
35
+ const bundledCodeRabbitRules = getAstGrepRuleSources().find((source) => source.origin === "bundled" && source.tier === "secondary");
36
+ const RULE_ID_LANGUAGE_SUFFIXES = new Set(["js"]);
37
+ if (bundledCodeRabbitRules) {
38
+ for (const suffix of deriveRuleIdLanguageSuffixes(bundledCodeRabbitRules.dir)) {
39
+ RULE_ID_LANGUAGE_SUFFIXES.add(suffix);
40
+ }
41
+ }
42
+ export function normalizeRuleId(ruleId) {
43
+ const normalized = ruleId.replace(/^ast-grep:/, "");
44
+ for (const suffix of RULE_ID_LANGUAGE_SUFFIXES) {
45
+ if (normalized.endsWith(`-${suffix}`)) {
46
+ return normalized.slice(0, -(suffix.length + 1));
47
+ }
48
+ }
49
+ return normalized;
50
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Project-level rule policy (`.pi-lens.json` `rules.<id>.disable` /
3
+ * `rules.<id>.select`) — output-only filtering of dispatches, so the project's
4
+ * own policy narrows what the user actually sees without widening the trusted
5
+ * surface area (widget state, baseline, dedup, and per-edit record stay
6
+ * authoritative).
7
+ *
8
+ * Matching is PROJECT-WIDE. The outer `rules.<key>` is a grouping label only —
9
+ * it does not scope which diagnostics a `disable`/`select` list under it can
10
+ * match. Every `disable` entry across every key is unioned into one drop list,
11
+ * and every `select` entry across every key is unioned into one allowlist.
12
+ * Disable is checked first, so it wins globally regardless of which key it's
13
+ * configured under.
14
+ *
15
+ * Match normalization is consolidated with the rest of the rule-id surfaces:
16
+ * `inline-suppressions.ts` and `lens-diagnostics.ts`'s `normalizeRuleId`
17
+ * both strip the `ast-grep:` prefix and the `-js` suffix, so a user listing
18
+ * `no-eval` once under `disable` covers both the LSP tag (`ast-grep:no-eval`)
19
+ * and the napi tag (`no-eval` / `no-eval-js`). Sharing the normalization keeps
20
+ * the three surfaces from drifting.
21
+ */
22
+ import { normalizeRuleId } from "./rule-id-normalize.js";
23
+ /**
24
+ * Decide whether a rule id (in the form it's emitted on a diagnostic — e.g.
25
+ * `ast-grep:no-eval`, `no-eval-js`, `no-eval`, `ts:2322`) should be filtered
26
+ * out under a project's rule policy map. Returns `dropped: true` when the
27
+ * diagnostic would be hidden from output.
28
+ *
29
+ * Project-wide: every `disable` entry across every configured key is checked
30
+ * first (disable wins globally over select). Then every `select` entry
31
+ * across every key is unioned into one allowlist — a non-empty union that
32
+ * the rule doesn't match also drops it. An absent/empty union across all
33
+ * keys means "no restriction". Matching uses the same normalization as
34
+ * inline suppression so a configured `no-eval` entry covers
35
+ * `ast-grep:no-eval` and `no-eval-js`.
36
+ */
37
+ export function evaluateRulePolicy(ruleId, policyMap) {
38
+ if (!policyMap)
39
+ return { dropped: false };
40
+ const raw = ruleId || "";
41
+ const norm = normalizeRuleId(raw);
42
+ if (scanList(policyMap, (e) => e.disable, raw, norm).matched) {
43
+ return { dropped: true };
44
+ }
45
+ const select = scanList(policyMap, (e) => e.select, raw, norm);
46
+ return { dropped: select.sawEntry && !select.matched };
47
+ }
48
+ /**
49
+ * Union one list (`disable` or `select`) across every key in the map and test
50
+ * the rule against it. `sawEntry` reports whether the union was non-empty,
51
+ * which is what makes a `select` union restrictive rather than absent.
52
+ */
53
+ function scanList(policyMap, pick, raw, norm) {
54
+ let sawEntry = false;
55
+ for (const entry of Object.values(policyMap)) {
56
+ for (const candidate of (entry && pick(entry)) ?? []) {
57
+ sawEntry = true;
58
+ if (matchesRule(candidate, raw, norm))
59
+ return { matched: true, sawEntry };
60
+ }
61
+ }
62
+ return { matched: false, sawEntry };
63
+ }
64
+ function matchesRule(entry, raw, normalized) {
65
+ if (entry === raw)
66
+ return true;
67
+ return normalizeRuleId(entry) === normalized;
68
+ }
69
+ /**
70
+ * Filter an array of diagnostics (returns a new array) by the project rule
71
+ * policy map. `rule` is preferred; code-only diagnostics use `code` as their
72
+ * policy key so project-diagnostics details and rendered summaries cannot
73
+ * disagree about a finding that has no explicit rule field. `Diagnostic.id`
74
+ * remains a dedup key (`eslint:unknown:12`), not a policy key, so id-only
75
+ * findings (including blocking parse errors) remain exempt.
76
+ *
77
+ * Used by both the per-edit dispatcher and the `lens-diagnostics` tool's
78
+ * `mode=delta`/`mode=all`/`mode=full` paths so a project's own policy
79
+ * applies consistently across every output surface.
80
+ */
81
+ export function applyRulePolicy(diagnostics, policyMap) {
82
+ if (!policyMap)
83
+ return diagnostics;
84
+ // Fast-path: if every entry has neither disable nor select, there is no
85
+ // output filter to apply. Keeps the hot path (most projects have only
86
+ // thresholds) cheap.
87
+ let hasFilter = false;
88
+ for (const rawEntry of Object.values(policyMap)) {
89
+ if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
90
+ continue;
91
+ }
92
+ const entry = rawEntry;
93
+ if ((entry.disable?.length ?? 0) > 0 || (entry.select?.length ?? 0) > 0) {
94
+ hasFilter = true;
95
+ break;
96
+ }
97
+ }
98
+ if (!hasFilter)
99
+ return diagnostics;
100
+ return diagnostics.filter((d) => {
101
+ const ruleId = d.rule ?? d.code;
102
+ if (!ruleId)
103
+ return true;
104
+ const { dropped } = evaluateRulePolicy(ruleId, policyMap);
105
+ return !dropped;
106
+ });
107
+ }
108
+ /**
109
+ * Build the policy map from a `PiLensProjectConfig.rules`-shaped value. Returns
110
+ * undefined when there's nothing to filter (the hot path), or a map suitable
111
+ * for `applyRulePolicy` otherwise. Excludes entries whose only field is
112
+ * `threshold` (a threshold is a separate concern handled elsewhere) so the
113
+ * filter step doesn't repeat the work.
114
+ */
115
+ export function rulePolicyMapFromConfig(rules) {
116
+ if (!rules)
117
+ return undefined;
118
+ let built;
119
+ for (const [key, rawEntry] of Object.entries(rules)) {
120
+ if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) {
121
+ continue;
122
+ }
123
+ const entry = rawEntry;
124
+ const hasDisable = (entry.disable?.length ?? 0) > 0;
125
+ const hasSelect = (entry.select?.length ?? 0) > 0;
126
+ if (!hasDisable && !hasSelect)
127
+ continue;
128
+ built ??= {};
129
+ built[key] = {
130
+ ...(hasDisable ? { disable: entry.disable } : {}),
131
+ ...(hasSelect ? { select: entry.select } : {}),
132
+ };
133
+ }
134
+ return built;
135
+ }
@@ -19,6 +19,12 @@ import { isAuxiliaryLspAlive } from "../../lsp/index.js";
19
19
  import { resolveAstGrepNativeExe } from "../../lsp/wait-policy/index.js";
20
20
  import { PRIORITY } from "../priorities.js";
21
21
  import { calculateRuleComplexity, isOverlyBroadPattern, isStructuredRule, loadYamlRules, loadYamlRulesFresh, MAX_BLOCKING_RULE_COMPLEXITY, } from "./yaml-rule-parser.js";
22
+ const defaultUnsupportedLanguageLog = new Set();
23
+ const UNSUPPORTED_RULE_ID_SAMPLE_SIZE = 5;
24
+ /** Clear per-session unsupported-language telemetry dedupe. */
25
+ export function resetAstGrepUnsupportedLanguageLog() {
26
+ defaultUnsupportedLanguageLog.clear();
27
+ }
22
28
  // Lazy load the napi package
23
29
  let sg;
24
30
  let sgLoadAttempted = false;
@@ -237,7 +243,7 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
237
243
  const maxTotalDiagnostics = options.maxTotalDiagnostics ?? MAX_TOTAL_DIAGNOSTICS;
238
244
  const blockingOnly = options.blockingOnly === true;
239
245
  const log = options.log;
240
- const unsupportedLanguageLog = options.unsupportedLanguageLog ?? new Set();
246
+ const unsupportedLanguageLog = options.unsupportedLanguageLog ?? defaultUnsupportedLanguageLog;
241
247
  const diagnostics = [];
242
248
  const seenRuleIds = new Set();
243
249
  const suppressLinterOverlap = kind === "jsts" && hasEslintConfig(cwd);
@@ -249,15 +255,28 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
249
255
  const flushUnsupportedRuleSkips = () => {
250
256
  if (newlyUnsupported.size === 0)
251
257
  return;
258
+ const firstSeenLanguages = Array.from(newlyUnsupported.entries()).filter(([language]) => !unsupportedLanguageLog.has(language));
259
+ for (const [language] of firstSeenLanguages) {
260
+ unsupportedLanguageLog.add(language);
261
+ }
262
+ if (firstSeenLanguages.length === 0) {
263
+ newlyUnsupported.clear();
264
+ return;
265
+ }
266
+ for (const [language] of firstSeenLanguages)
267
+ unsupportedLanguageLog.add(language);
252
268
  logLatency({
253
269
  type: "phase",
254
270
  phase: "astgrep_napi_unsupported_rules_skipped",
255
271
  filePath,
256
272
  durationMs: 0,
257
273
  metadata: {
258
- skippedByLanguage: Object.fromEntries(Array.from(newlyUnsupported.entries(), ([language, ruleIds]) => [
274
+ skippedByLanguage: Object.fromEntries(firstSeenLanguages.map(([language, ruleIds]) => [
259
275
  language,
260
- { count: ruleIds.length, ruleIds },
276
+ {
277
+ count: ruleIds.length,
278
+ ruleIds: ruleIds.slice(0, UNSUPPORTED_RULE_ID_SAMPLE_SIZE),
279
+ },
261
280
  ])),
262
281
  },
263
282
  });
@@ -315,8 +334,7 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
315
334
  lang !== "typescript" &&
316
335
  lang !== "tsx" &&
317
336
  lang !== "javascript") {
318
- if (!unsupportedLanguageLog.has(rule.id)) {
319
- unsupportedLanguageLog.add(rule.id);
337
+ if (!unsupportedLanguageLog.has(lang)) {
320
338
  const ids = newlyUnsupported.get(lang) ?? [];
321
339
  ids.push(rule.id);
322
340
  newlyUnsupported.set(lang, ids);
@@ -6,9 +6,8 @@
6
6
  * mutating files mid-dispatch after LSP sync has already happened.
7
7
  */
8
8
  import * as path from "node:path";
9
- import { resolvePackagePath } from "../../package-root.js";
10
9
  import { safeSpawnAsync } from "../../safe-spawn.js";
11
- import { getAutofixCapability, getBiomeConfigPath, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
10
+ import { biomeConfigArgs, getAutofixCapability, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
12
11
  import { PRIORITY } from "../priorities.js";
13
12
  import { resolveToolCommandWithInstallFallback } from "./utils/runner-helpers.js";
14
13
  function parseBiomeJson(raw, filePath) {
@@ -60,13 +59,10 @@ const biomeCheckJsonRunner = {
60
59
  if (!cmd) {
61
60
  return { status: "skipped", diagnostics: [], semantic: "none" };
62
61
  }
63
- // Build config path — use user's if exists, else pi-lens config
64
- const userConfigPath = getBiomeConfigPath(cwd);
65
- const configArg = [
66
- "--config-path=" +
67
- (userConfigPath ??
68
- resolvePackagePath(import.meta.url, "config/biome/core.jsonc")),
69
- ];
62
+ // Shared config-args seam (#1247): `biomeClient.fixFileAsync` consumes
63
+ // the same builder, so the user's config / package fallback applies on
64
+ // `lint --write` too.
65
+ const configArg = biomeConfigArgs(cwd);
70
66
  // Run biome lint (diagnostics only - format is handled separately)
71
67
  const checkResult = await safeSpawnAsync(cmd, [
72
68
  "lint",
@@ -4,6 +4,7 @@ import { safeSpawnAsync } from "../../safe-spawn.js";
4
4
  import { getLinterPolicyForCwd } from "../../tool-policy.js";
5
5
  import { PRIORITY } from "../priorities.js";
6
6
  import { createAvailabilityChecker } from "./utils/runner-helpers.js";
7
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
7
8
  const detekt = createAvailabilityChecker("detekt", ".bat");
8
9
  const DETEKT_CONFIG_CANDIDATES = [
9
10
  "detekt.yml",
@@ -145,10 +146,10 @@ const detektRunner = {
145
146
  return { status: "skipped", diagnostics: [], semantic: "none" };
146
147
  const absPath = path.resolve(cwd, ctx.filePath);
147
148
  const result = await safeSpawnAsync(cmd, ["--input", absPath, "--config", configPath], { cwd, timeout: 60000 });
148
- if (result.error && !result.stdout && !result.stderr) {
149
+ const raw = `${result.stdout ?? ""}${result.stderr ?? ""}`;
150
+ if (spawnFailedWithNoOutput(result, raw)) {
149
151
  return { status: "skipped", diagnostics: [], semantic: "none" };
150
152
  }
151
- const raw = `${result.stdout ?? ""}${result.stderr ?? ""}`;
152
153
  const diagnostics = parseDetektOutput(raw, ctx.filePath);
153
154
  if (diagnostics.length === 0) {
154
155
  return { status: "succeeded", diagnostics: [], semantic: "none" };
@@ -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, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
6
7
  const hadolint = createAvailabilityChecker("hadolint", ".exe");
7
8
  function parseHadolintOutput(raw, filePath) {
8
9
  try {
@@ -52,7 +53,7 @@ const hadolintRunner = {
52
53
  return { status: "skipped", diagnostics: [], semantic: "none" };
53
54
  }
54
55
  const result = await safeSpawnAsync(cmd, ["--format", "json", "--no-fail", path.resolve(cwd, ctx.filePath)], { cwd });
55
- if (result.error && !result.stdout) {
56
+ if (spawnFailedWithNoOutput(result)) {
56
57
  return { status: "skipped", diagnostics: [], semantic: "none" };
57
58
  }
58
59
  const output = result.stdout || "";
@@ -0,0 +1,154 @@
1
+ import * as path from "node:path";
2
+ import { PathKeyedMap } from "../../path-keyed-map.js";
3
+ import { normalizeMapKey } from "../../path-utils.js";
4
+ import { safeSpawnAsync } from "../../safe-spawn.js";
5
+ import { findNearestDirWithMarker } from "../../workspace-topology.js";
6
+ import { PRIORITY } from "../priorities.js";
7
+ import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
8
+ const helm = createAvailabilityChecker("helm", ".exe");
9
+ const inFlightByChartRoot = new PathKeyedMap(normalizeMapKey);
10
+ const HELM_LINT_TIMEOUT_MS = 30_000;
11
+ const SKIPPED = {
12
+ status: "skipped",
13
+ diagnostics: [],
14
+ semantic: "none",
15
+ };
16
+ function isWithin(root, candidate) {
17
+ const relative = path.relative(normalizeMapKey(root), normalizeMapKey(candidate));
18
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
19
+ }
20
+ function diagnosticLocation(message, chartRoot) {
21
+ const location = message.match(/(?:^|\s)([^:\r\n]+\.(?:ya?ml|tpl|json))(?::(\d+))?(?::\d+)?(?::|\s)/i);
22
+ if (!location) {
23
+ return { filePath: path.join(chartRoot, "Chart.yaml"), message };
24
+ }
25
+ const candidate = path.resolve(chartRoot, location[1]);
26
+ return {
27
+ filePath: isWithin(chartRoot, candidate)
28
+ ? candidate
29
+ : path.join(chartRoot, "Chart.yaml"),
30
+ line: location[2] ? Number(location[2]) : undefined,
31
+ message,
32
+ };
33
+ }
34
+ export function parseHelmLintOutput(raw, chartRoot) {
35
+ const diagnostics = [];
36
+ for (const line of raw.split(/\r?\n/)) {
37
+ const match = line.trim().match(/^\[(INFO|WARNING|ERROR)\]\s*(.+)$/);
38
+ if (!match) {
39
+ // helm indents multi-line diagnostic detail under its [LEVEL] header
40
+ // (#1342 review): fold continuation text into the open diagnostic
41
+ // instead of silently dropping it.
42
+ const last = diagnostics[diagnostics.length - 1];
43
+ if (last && /^\s+\S/.test(line)) {
44
+ last.message += `
45
+ ${line.trim()}`;
46
+ }
47
+ continue;
48
+ }
49
+ const level = match[1];
50
+ const location = diagnosticLocation(match[2].trim(), chartRoot);
51
+ const severity = level === "ERROR" ? "error" : level === "WARNING" ? "warning" : "info";
52
+ diagnostics.push({
53
+ id: `helm-lint-${diagnostics.length + 1}-${level.toLowerCase()}`,
54
+ message: location.message,
55
+ filePath: location.filePath,
56
+ line: location.line,
57
+ column: 1,
58
+ severity,
59
+ semantic: severity === "error" ? "blocking" : "warning",
60
+ tool: "helm-lint",
61
+ rule: level.toLowerCase(),
62
+ fixable: false,
63
+ });
64
+ }
65
+ return diagnostics;
66
+ }
67
+ function failedResult(kind, message) {
68
+ return {
69
+ status: "failed",
70
+ diagnostics: [],
71
+ semantic: "warning",
72
+ failureKind: kind,
73
+ failureMessage: message.slice(0, 200),
74
+ };
75
+ }
76
+ async function lintChart(chartRoot, cwd) {
77
+ const cmd = await resolveAvailableOrInstall(helm, "helm", cwd);
78
+ if (!cmd) {
79
+ const outcome = helm.getOutcome(cwd);
80
+ return {
81
+ status: "failed",
82
+ diagnostics: [],
83
+ semantic: "warning",
84
+ failureKind: "unavailable",
85
+ failureMessage: `helm unavailable (${outcome ?? "unknown"})`,
86
+ };
87
+ }
88
+ const result = await safeSpawnAsync(cmd, ["lint", chartRoot], {
89
+ cwd,
90
+ timeout: HELM_LINT_TIMEOUT_MS,
91
+ deadlineAt: Date.now() + HELM_LINT_TIMEOUT_MS,
92
+ resourceLabel: "helm-lint",
93
+ });
94
+ const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
95
+ const diagnostics = parseHelmLintOutput(output, chartRoot);
96
+ const typedFailure = result.spawnFailure?.kind;
97
+ if (typedFailure) {
98
+ const failureKind = typedFailure === "tool-not-found"
99
+ ? "unavailable"
100
+ : typedFailure === "timeout"
101
+ ? "timeout"
102
+ : typedFailure === "killed" && result.failure === "aborted"
103
+ ? "aborted"
104
+ : "server_error";
105
+ return failedResult(failureKind, result.error?.message || `helm lint ${typedFailure}`);
106
+ }
107
+ if (result.failure) {
108
+ return failedResult(result.failure === "timeout" ? "timeout" : result.failure, result.error?.message || `helm lint ${result.failure}`);
109
+ }
110
+ if (result.outputTruncated) {
111
+ return failedResult("parser_error", "helm lint output was truncated before parsing completed");
112
+ }
113
+ const hasErrorDiagnostic = diagnostics.some((diagnostic) => diagnostic.severity === "error");
114
+ if (result.status !== 0 && !hasErrorDiagnostic) {
115
+ return failedResult(output.trim() ? "parser_error" : "server_error", output.trim() || "helm lint exited without an error diagnostic");
116
+ }
117
+ return {
118
+ status: "succeeded",
119
+ diagnostics,
120
+ semantic: diagnostics.some((item) => item.severity === "error")
121
+ ? "blocking"
122
+ : diagnostics.length > 0
123
+ ? "warning"
124
+ : "none",
125
+ };
126
+ }
127
+ const helmLintRunner = {
128
+ id: "helm-lint",
129
+ appliesTo: ["yaml", "helm-template"],
130
+ priority: PRIORITY.GENERAL_ANALYSIS,
131
+ enabledByDefault: true,
132
+ skipTestFiles: false,
133
+ timeoutMs: 35_000,
134
+ async run(ctx) {
135
+ const workspaceRoot = path.resolve(ctx.projectRoot ?? ctx.cwd);
136
+ const startDir = path.dirname(path.resolve(ctx.filePath));
137
+ const discovered = findNearestDirWithMarker(startDir, "chartYamlPath");
138
+ if (!discovered)
139
+ return SKIPPED;
140
+ const chartRoot = path.resolve(discovered);
141
+ if (!isWithin(workspaceRoot, chartRoot))
142
+ return SKIPPED;
143
+ const existing = inFlightByChartRoot.get(chartRoot);
144
+ if (existing)
145
+ return existing;
146
+ const promise = lintChart(chartRoot, ctx.cwd).finally(() => {
147
+ if (inFlightByChartRoot.get(chartRoot) === promise)
148
+ inFlightByChartRoot.delete(chartRoot);
149
+ });
150
+ inFlightByChartRoot.set(chartRoot, promise);
151
+ return promise;
152
+ },
153
+ };
154
+ export default helmLintRunner;
@@ -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, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
6
+ import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
6
7
  const htmlhint = createAvailabilityChecker("htmlhint");
7
8
  const HTMLHINT_RULES = {
8
9
  "tag-pair": true,
@@ -71,10 +72,10 @@ const htmlhintRunner = {
71
72
  "unix",
72
73
  path.resolve(cwd, ctx.filePath),
73
74
  ], { cwd });
74
- if (result.error && !result.stdout && !result.stderr) {
75
+ const output = result.stdout || result.stderr || "";
76
+ if (spawnFailedWithNoOutput(result, output)) {
75
77
  return { status: "skipped", diagnostics: [], semantic: "none" };
76
78
  }
77
- const output = result.stdout || result.stderr || "";
78
79
  const diagnostics = parseHtmlhintOutput(output, ctx.filePath);
79
80
  if (diagnostics.length === 0) {
80
81
  return { status: "succeeded", diagnostics: [], semantic: "none" };
@@ -16,6 +16,7 @@ import gleamCheckRunner from "./gleam-check.js";
16
16
  import goVetRunner from "./go-vet.js";
17
17
  import golangciRunner from "./golangci-lint.js";
18
18
  import hadolintRunner from "./hadolint.js";
19
+ import helmLintRunner from "./helm-lint.js";
19
20
  import htmlhintRunner from "./htmlhint.js";
20
21
  import javacRunner from "./javac.js";
21
22
  import ktlintRunner from "./ktlint.js";
@@ -40,6 +41,7 @@ import sqlfluffRunner from "./sqlfluff.js";
40
41
  import stylelintRunner from "./stylelint.js";
41
42
  import swiftlintRunner from "./swiftlint.js";
42
43
  import taploRunner from "./taplo.js";
44
+ import terragruntRunner from "./terragrunt.js";
43
45
  import tflintRunner from "./tflint.js";
44
46
  import trivyConfigRunner from "./trivy-config.js";
45
47
  import valeRunner from "./vale.js";
@@ -81,6 +83,7 @@ export function registerDefaultRunners(registry) {
81
83
  registry.register(factRulesRunner); // FactRule pipeline — all registered rules (priority 21)
82
84
  registry.register(htmlhintRunner); // HTML linting — tag pairs, attribute rules (priority 20)
83
85
  registry.register(hadolintRunner); // Dockerfile linting — syntax, best practices (priority 20)
86
+ registry.register(helmLintRunner); // Helm chart linting (priority 20)
84
87
  registry.register(valeRunner); // Prose/style linting for Markdown — config-gated (.vale.ini) (priority 30)
85
88
  registry.register(phpLintRunner); // PHP syntax validation via php -l (priority 20)
86
89
  registry.register(psScriptAnalyzerRunner); // PowerShell linting via PSScriptAnalyzer module (priority 20)
@@ -88,7 +91,8 @@ export function registerDefaultRunners(registry) {
88
91
  registry.register(ktlintRunner); // Kotlin linting via ktlint (priority 10)
89
92
  registry.register(detektRunner); // Kotlin static analysis via detekt (priority 20, config-gated)
90
93
  registry.register(tflintRunner); // Terraform linting via tflint (priority 20)
91
- registry.register(trivyConfigRunner); // IaC misconfig (Dockerfile/k8s) via trivy config (priority 20, trivy.enabled-gated)
94
+ registry.register(terragruntRunner); // Terragrunt linting via terragrunt hcl validate (priority 20)
95
+ registry.register(trivyConfigRunner); // IaC misconfig (Dockerfile/k8s/Terraform) via trivy config (priority 20, trivy.enabled-gated)
92
96
  registry.register(taploRunner); // TOML linting/validation via taplo (priority 10)
93
97
  registry.register(dartAnalyzeRunner); // Dart analysis via dart analyze (priority 20)
94
98
  registry.register(javacRunner); // Java compile diagnostics via javac (priority 20)
@@ -11,6 +11,7 @@
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";
14
15
  import { getLSPService } from "../../lsp/index.js";
15
16
  import { RUNTIME_CONFIG } from "../../runtime-config.js";
16
17
  import { PRIORITY } from "../priorities.js";
@@ -128,8 +129,13 @@ const lspRunner = {
128
129
  try {
129
130
  const attached = await tryWarmAttachedDiagnostics(ctx.filePath, content, Math.max(LSP_SPAWN_BUDGET_MS, LSP_DIAGNOSTICS_WAIT_MS));
130
131
  usedWarmAttach = attached?.available === true;
132
+ // #1179 (shape-5 structural fix): both branches normalize to the
133
+ // `touchFile` wrapper shape. The warm-attach IPC branch resolves a plain
134
+ // diagnostics array (the socket cannot carry a side-channel; the IPC client
135
+ // already rejected any inconclusive answer, so `available` ⟹ confirmed) —
136
+ // wrap it as `{ diags }`; the incumbent branch already returns the wrapper.
131
137
  const touched = attached?.available
132
- ? attached.response.diagnostics
138
+ ? { diags: attached.response.diagnostics }
133
139
  : await lspService.touchFile(ctx.filePath, content, {
134
140
  diagnostics: "document",
135
141
  collectDiagnostics: true,
@@ -143,10 +149,8 @@ const lspRunner = {
143
149
  lspClientReady = false;
144
150
  }
145
151
  else {
146
- lspDiags = touched;
147
- diagnosticsInconclusive =
148
- touched
149
- .inconclusive === true;
152
+ lspDiags = touched.diags;
153
+ diagnosticsInconclusive = touched.inconclusive === true;
150
154
  }
151
155
  }
152
156
  catch (err) {
@@ -156,7 +160,11 @@ const lspRunner = {
156
160
  failureReason.includes("exited") ||
157
161
  failureReason.includes("connection") ||
158
162
  failureReason.includes("JSON RPC")) {
159
- console.error(`[lsp-runner] LSP server failed for ${diagnosticPath}: ${failureReason}`);
163
+ logExtension({
164
+ subsystem: "lsp-runner",
165
+ message: `LSP server failed for ${diagnosticPath}: ${failureReason}`,
166
+ metadata: { filePath: diagnosticPath },
167
+ });
160
168
  }
161
169
  }
162
170
  if (serverFailed) {
@@ -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,