@herbertgao/pi-extensions 2026.9.6 → 2026.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +3 -8
  2. package/THIRD_PARTY_NOTICES.md +0 -4
  3. package/node_modules/@narumitw/pi-btw/README.md +2 -0
  4. package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
  5. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  6. package/node_modules/@narumitw/pi-btw/package.json +52 -53
  7. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
  8. package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
  9. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
  10. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
  11. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
  12. package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
  13. package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
  14. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
  15. package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
  16. package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
  17. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
  18. package/node_modules/grok-mermaid/CHANGELOG.md +46 -0
  19. package/node_modules/grok-mermaid/LICENSE +205 -0
  20. package/node_modules/grok-mermaid/README.md +191 -0
  21. package/node_modules/grok-mermaid/dist/ansi.d.ts +16 -0
  22. package/node_modules/grok-mermaid/dist/ansi.d.ts.map +1 -0
  23. package/node_modules/grok-mermaid/dist/ansi.js +23 -0
  24. package/node_modules/grok-mermaid/dist/ansi.js.map +1 -0
  25. package/node_modules/grok-mermaid/dist/canvas.d.ts +87 -0
  26. package/node_modules/grok-mermaid/dist/canvas.d.ts.map +1 -0
  27. package/node_modules/grok-mermaid/dist/canvas.js +366 -0
  28. package/node_modules/grok-mermaid/dist/canvas.js.map +1 -0
  29. package/node_modules/grok-mermaid/dist/graph.d.ts +74 -0
  30. package/node_modules/grok-mermaid/dist/graph.d.ts.map +1 -0
  31. package/node_modules/grok-mermaid/dist/graph.js +91 -0
  32. package/node_modules/grok-mermaid/dist/graph.js.map +1 -0
  33. package/node_modules/grok-mermaid/dist/index.d.ts +32 -0
  34. package/node_modules/grok-mermaid/dist/index.d.ts.map +1 -0
  35. package/node_modules/grok-mermaid/dist/index.js +100 -0
  36. package/node_modules/grok-mermaid/dist/index.js.map +1 -0
  37. package/node_modules/grok-mermaid/dist/labels.d.ts +62 -0
  38. package/node_modules/grok-mermaid/dist/labels.d.ts.map +1 -0
  39. package/node_modules/grok-mermaid/dist/labels.js +324 -0
  40. package/node_modules/grok-mermaid/dist/labels.js.map +1 -0
  41. package/node_modules/grok-mermaid/dist/layout-seq.d.ts +12 -0
  42. package/node_modules/grok-mermaid/dist/layout-seq.d.ts.map +1 -0
  43. package/node_modules/grok-mermaid/dist/layout-seq.js +194 -0
  44. package/node_modules/grok-mermaid/dist/layout-seq.js.map +1 -0
  45. package/node_modules/grok-mermaid/dist/layout.d.ts +87 -0
  46. package/node_modules/grok-mermaid/dist/layout.d.ts.map +1 -0
  47. package/node_modules/grok-mermaid/dist/layout.js +881 -0
  48. package/node_modules/grok-mermaid/dist/layout.js.map +1 -0
  49. package/node_modules/grok-mermaid/dist/parse.d.ts +83 -0
  50. package/node_modules/grok-mermaid/dist/parse.d.ts.map +1 -0
  51. package/node_modules/grok-mermaid/dist/parse.js +1151 -0
  52. package/node_modules/grok-mermaid/dist/parse.js.map +1 -0
  53. package/node_modules/grok-mermaid/dist/source-box.d.ts +18 -0
  54. package/node_modules/grok-mermaid/dist/source-box.d.ts.map +1 -0
  55. package/node_modules/grok-mermaid/dist/source-box.js +78 -0
  56. package/node_modules/grok-mermaid/dist/source-box.js.map +1 -0
  57. package/node_modules/grok-mermaid/dist/types.d.ts +42 -0
  58. package/node_modules/grok-mermaid/dist/types.d.ts.map +1 -0
  59. package/node_modules/grok-mermaid/dist/types.js +1 -0
  60. package/node_modules/grok-mermaid/dist/types.js.map +1 -0
  61. package/node_modules/grok-mermaid/dist/width-data.d.ts +2 -0
  62. package/node_modules/grok-mermaid/dist/width-data.d.ts.map +1 -0
  63. package/node_modules/grok-mermaid/dist/width-data.js +994 -0
  64. package/node_modules/grok-mermaid/dist/width-data.js.map +1 -0
  65. package/node_modules/grok-mermaid/dist/width.d.ts +18 -0
  66. package/node_modules/grok-mermaid/dist/width.d.ts.map +1 -0
  67. package/node_modules/grok-mermaid/dist/width.js +76 -0
  68. package/node_modules/grok-mermaid/dist/width.js.map +1 -0
  69. package/node_modules/grok-mermaid/package.json +49 -0
  70. package/node_modules/grok-mermaid/src/ansi.ts +34 -0
  71. package/node_modules/grok-mermaid/src/canvas.ts +373 -0
  72. package/node_modules/grok-mermaid/src/graph.ts +142 -0
  73. package/node_modules/grok-mermaid/src/index.ts +104 -0
  74. package/node_modules/grok-mermaid/src/labels.ts +326 -0
  75. package/node_modules/grok-mermaid/src/layout-seq.ts +203 -0
  76. package/node_modules/grok-mermaid/src/layout.ts +1015 -0
  77. package/node_modules/grok-mermaid/src/parse.ts +1189 -0
  78. package/node_modules/grok-mermaid/src/source-box.ts +89 -0
  79. package/node_modules/grok-mermaid/src/types.ts +43 -0
  80. package/node_modules/grok-mermaid/src/width-data.ts +993 -0
  81. package/node_modules/grok-mermaid/src/width.ts +74 -0
  82. package/node_modules/pi-lens/CHANGELOG.md +239 -0
  83. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
  84. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
  85. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
  86. package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
  87. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
  88. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
  89. package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
  90. package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
  91. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
  92. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
  93. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
  95. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
  97. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
  99. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
  100. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
  108. package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
  109. package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
  110. package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
  111. package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
  112. package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
  113. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
  114. package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
  115. package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
  116. package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
  117. package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
  118. package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
  119. package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
  120. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
  121. package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
  122. package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
  123. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
  125. package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
  126. package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
  128. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
  129. package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
  130. package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
  131. package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
  132. package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
  133. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
  134. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
  135. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
  136. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
  137. package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
  138. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
  139. package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
  140. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
  141. package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
  142. package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
  143. package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
  144. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
  145. package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
  146. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
  147. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
  148. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
  149. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
  150. package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
  151. package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
  152. package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
  153. package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
  154. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
  155. package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
  156. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
  157. package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
  158. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
  159. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
  160. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
  161. package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
  162. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
  163. package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
  164. package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
  165. package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
  166. package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
  167. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
  168. package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
  169. package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
  170. package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
  171. package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
  172. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
  173. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
  174. package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
  175. package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
  176. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
  177. package/node_modules/pi-lens/dist/index.js +27416 -24153
  178. package/node_modules/pi-lens/dist/mcp/server.js +5 -2
  179. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
  180. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
  181. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
  182. package/node_modules/pi-lens/docs/audit1.md +5 -5
  183. package/node_modules/pi-lens/docs/dependencies.md +11 -0
  184. package/node_modules/pi-lens/docs/dispositions.md +52 -0
  185. package/node_modules/pi-lens/docs/environment-variables.md +2 -2
  186. package/node_modules/pi-lens/docs/features.md +4 -4
  187. package/node_modules/pi-lens/docs/globalconfig.md +3 -1
  188. package/node_modules/pi-lens/docs/language-coverage.md +1 -0
  189. package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
  190. package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
  191. package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
  192. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
  193. package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
  194. package/node_modules/pi-lens/docs/settings.md +7 -4
  195. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  196. package/node_modules/pi-lens/docs/usage.md +2 -0
  197. package/node_modules/pi-lens/package.json +1 -1
  198. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
  199. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
  200. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
  201. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
  202. package/package.json +6 -11
  203. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
  204. package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
  205. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
  206. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
  207. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
  208. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
  209. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
  210. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  211. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  212. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
  213. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
  214. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
  215. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
  216. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
  217. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
  218. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
  219. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
  220. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
  221. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
  222. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
  223. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
  224. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
  225. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
  226. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
  227. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
  228. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
  229. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
  230. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
  231. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
  232. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
  233. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
  234. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
  235. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
  236. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
  237. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
  238. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
  239. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
  240. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
  241. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
  242. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
  243. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
  244. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
  245. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
  246. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
  247. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
  248. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
  249. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
  250. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
  251. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
  252. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
  253. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
  254. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
  255. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
  256. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
  257. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
  258. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
  259. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
  260. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
  261. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
  262. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
  263. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
  264. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
  265. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
  266. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
  267. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
  268. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
  269. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
  270. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
  271. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
  272. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
  273. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
  274. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
  275. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
  276. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
  277. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
  278. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
  279. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
  280. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
  281. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
  282. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
  283. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
  284. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
  285. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
  286. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
  287. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
  288. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
  289. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
  290. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
  291. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
  292. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
  293. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
  294. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
  295. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
  296. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
  297. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
  298. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
  299. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
  300. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
  301. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
  302. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
  303. package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
  304. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
@@ -10,8 +10,13 @@ import { getProjectIgnoreMatcher, isExcludedDirName, } from "../clients/file-uti
10
10
  import { getLSPService, groupFilesByPrimaryServer, runPerServerGroups, } from "../clients/lsp/index.js";
11
11
  import { buildScopeKey, createWorkspaceDiagnosticsCacheContext, } from "../clients/lsp/workspace-diagnostics-cache.js";
12
12
  import { getServersForFileWithConfig, primaryServerId, } from "../clients/lsp/config.js";
13
+ import { mapWithConcurrency } from "../clients/map-with-concurrency.js";
13
14
  import { combineAbortSignals, withDeadline, } from "../clients/deadline-utils.js";
14
15
  import { applyAuxiliarySuppressions, retagAuxiliaryDiagnostics, } from "../clients/dispatch/auxiliary-lsp.js";
16
+ import { applyLspFindingPolicy, loadProjectRulePolicyMap, } from "../clients/dispatch/finding-policy.js";
17
+ import { hasStrictDispositionMarks } from "../clients/diagnostic-dispositions.js";
18
+ import { recordDegradationOnce } from "../clients/degradation-ledger.js";
19
+ import { logLatency } from "../clients/latency-logger.js";
15
20
  import { detectFileRole } from "../clients/file-role.js";
16
21
  import { hashDiagnosticContent, touchCompletedConfirmationPolicy, touchCoverageGap, } from "../clients/lsp/diagnostic-binding.js";
17
22
  import { classifyCascadeWaitTier } from "../clients/lsp/wait-policy/index.js";
@@ -100,46 +105,6 @@ function boundedPositiveInt(value, fallback, min, max) {
100
105
  * for THIS tool's batch/directory sweep the same way #667 fixed it for the
101
106
  * workspace-diagnostics sweep.
102
107
  */
103
- async function mapWithConcurrency(items, concurrency, mapper, lspService, signal, onProgress) {
104
- const results = [];
105
- let completed = 0;
106
- // Multiple original indices can map to the same file path (duplicate
107
- // entries in an explicit `paths` batch) — track them as a per-file queue
108
- // so each occurrence still lands in its own original slot.
109
- const pendingIndices = new Map();
110
- items.forEach((item, index) => {
111
- const queue = pendingIndices.get(item);
112
- if (queue)
113
- queue.push(index);
114
- else
115
- pendingIndices.set(item, [index]);
116
- });
117
- const groups = groupFilesByPrimaryServer(items);
118
- await runPerServerGroups(groups, concurrency, async (group) => {
119
- if (signal?.aborted)
120
- return;
121
- const first = group.files[0];
122
- if (first &&
123
- lspService &&
124
- !isWarmAttached() &&
125
- typeof lspService.ensureWarmForSweep === "function") {
126
- await lspService.ensureWarmForSweep(first, { signal });
127
- if (signal?.aborted)
128
- return;
129
- }
130
- for (const item of group.files) {
131
- // Honor cancellation (Escape / turn abort): stop pulling new items
132
- // rather than grind the whole batch. Completed entries are returned.
133
- if (signal?.aborted)
134
- return;
135
- const index = pendingIndices.get(item).shift();
136
- results[index] = await mapper(item, index);
137
- completed += 1;
138
- onProgress?.(completed, items.length);
139
- }
140
- }, signal);
141
- return results;
142
- }
143
108
  /**
144
109
  * Project-ignore predicate rooted at `root`, fail-open. Lets a directory scan
145
110
  * honor the user's `.pi-lens.json` / `.gitignore` patterns — not just the
@@ -331,7 +296,17 @@ onConfirmedNoBlockers, getService = getLSPService) {
331
296
  },
332
297
  };
333
298
  }
334
- async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope = "all") {
299
+ async function collectDiagnosticsForFile(absPath, lspService,
300
+ /**
301
+ * #3041: project root the per-rule `ignores` globs (#965) are resolved
302
+ * against. ast-grep's LSP publishes per-document diagnostics without
303
+ * applying them, so this standalone `source=lsp` query has to — the same
304
+ * carve-out the NAPI runner applies on the same file. Positioned before the
305
+ * optional parameters so it is structurally REQUIRED: an optional
306
+ * `string | undefined` would silently disable the carve-out for whichever
307
+ * caller forgot to pass it.
308
+ */
309
+ scanRoot, waitMs, serverScope = "all") {
335
310
  let timedOut = false;
336
311
  let content;
337
312
  // `touchFile` is the authoritative collection boundary for every scope: it
@@ -360,7 +335,11 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
360
335
  const scopedDiagnostics = serverScope === "primary"
361
336
  ? attached.response.diagnostics.filter((item) => item.serverId === primaryServerId(absPath))
362
337
  : attached.response.diagnostics;
363
- const filtered = applyAuxiliarySuppressions(scopedDiagnostics, content, { fileRole: detectFileRole(absPath, content) });
338
+ const filtered = applyAuxiliarySuppressions(scopedDiagnostics, content, {
339
+ fileRole: detectFileRole(absPath, content),
340
+ filePath: absPath,
341
+ scanRoot,
342
+ });
364
343
  return {
365
344
  diagnostics: filtered,
366
345
  timedOut: false,
@@ -420,6 +399,8 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
420
399
  const filtered = content !== undefined
421
400
  ? applyAuxiliarySuppressions(diagnostics, content, {
422
401
  fileRole: detectFileRole(absPath, content),
402
+ filePath: absPath,
403
+ scanRoot,
423
404
  })
424
405
  : diagnostics;
425
406
  // #1095: surface the touch's content binding (only a touch that resolved
@@ -588,6 +569,97 @@ function demoteInferredForFile(file, diagnostics, cwd, lspService) {
588
569
  service: lspService,
589
570
  });
590
571
  }
572
+ /**
573
+ * #3088: the probe lane's stored-disposition / rule-policy / inline-suppression
574
+ * gate — the same `clients/dispatch/finding-policy.ts` stack `mode=full` and
575
+ * the per-edit dispatcher apply, called once per file at the position
576
+ * `demoteInferredForFile` already established: AFTER the confirmation verdict
577
+ * (which asks "did the server answer for this content" and must not be
578
+ * influenced by a policy drop) and BEFORE the rendered output, the severity
579
+ * filter and `reconcileWidgetFromLspResult`. One filtered set for all three, so
580
+ * the text, the counts and the widget footer cannot disagree — and so the
581
+ * footer reconcile can no longer re-arm a finding the mark-time
582
+ * `reconcileWidgetDisposition` demoted.
583
+ *
584
+ * Fails OPEN: a filter that throws returns the unfiltered set plus one bounded
585
+ * degradation record. A finding that stays visible is recoverable; one silently
586
+ * hidden by a broken filter is the harm this lane exists to prevent
587
+ * (AGENTS.md shape 48).
588
+ */
589
+ function applyProbeFindingPolicy(file, diagnostics, cwd, content,
590
+ /**
591
+ * Round 2 F1: the caller's severity threshold. The count reported to the
592
+ * agent must be scoped to the SAME threshold the rendered result is, or a
593
+ * `severity: "error"` probe of a repo carrying warning-level marks prints
594
+ * "suppressed by disposition: N … Not a clean verdict" on a result that IS
595
+ * clean at that threshold — the skill's own folder-check recipe would emit
596
+ * the banner on every check. The threshold is applied HERE, in the tool that
597
+ * owns both the render and the record, rather than in the three call sites
598
+ * (three copies of one subtraction) or in
599
+ * `clients/dispatch/finding-policy.ts`, which must stay threshold-free: its
600
+ * other caller (`mode=full`) has no severity axis at all.
601
+ */
602
+ severity) {
603
+ const started = Date.now();
604
+ try {
605
+ const result = applyLspFindingPolicy(diagnostics, {
606
+ cwd,
607
+ filePath: file,
608
+ content,
609
+ policyMap: loadProjectRulePolicyMap(cwd),
610
+ fileRole: detectFileRole(file, content),
611
+ });
612
+ const inScopeBefore = applySeverityFilter(diagnostics, severity).length;
613
+ const suppressed = inScopeBefore - applySeverityFilter(result.kept, severity).length;
614
+ if (suppressed > 0) {
615
+ // One record per FILE that actually dropped something the caller would
616
+ // otherwise have SEEN, never one per finding (AGENTS.md "bounded
617
+ // observability"). `total` is the in-scope population for the same
618
+ // reason the count is, so the record and the rendered line agree.
619
+ logLatency({
620
+ type: "phase",
621
+ toolName: "lsp_diagnostics",
622
+ filePath: file,
623
+ phase: "lsp_probe_disposition_filter",
624
+ durationMs: Date.now() - started,
625
+ metadata: {
626
+ suppressed,
627
+ total: inScopeBefore,
628
+ },
629
+ });
630
+ }
631
+ return { kept: result.kept, inlineKept: result.inlineKept, suppressed };
632
+ }
633
+ catch (err) {
634
+ recordDegradationOnce({
635
+ kind: "lsp-probe-finding-policy",
636
+ subject: cwd,
637
+ reason: err instanceof Error ? err.message : String(err),
638
+ });
639
+ return { kept: diagnostics, inlineKept: diagnostics, suppressed: 0 };
640
+ }
641
+ }
642
+ /**
643
+ * #3088 AC5/AC7: the content a cache REPLAY needs to evaluate STRICT
644
+ * (`false-positive`) disposition anchors, which hash the finding's own line.
645
+ * A cache hit means the file is unchanged since the entry was recorded, so this
646
+ * is the same content the recording touch read — but it is a disk read the
647
+ * replay path exists to avoid, so it is paid ONLY when the store actually holds
648
+ * a strict mark. With no marks (the overwhelmingly common case) the probe path
649
+ * adds zero I/O and the weak-anchored marks apply without content at all.
650
+ */
651
+ function replayContentForStrictMarks(file, cwd) {
652
+ if (!hasStrictDispositionMarks(cwd))
653
+ return undefined;
654
+ try {
655
+ return fs.readFileSync(file, "utf-8");
656
+ }
657
+ catch {
658
+ // Fail open — an unreadable file degrades to the weak-anchored filter
659
+ // rather than hiding or dropping anything.
660
+ return undefined;
661
+ }
662
+ }
591
663
  function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex, cwd, content,
592
664
  // #1095: true when the result's content binding demonstrably mismatches disk
593
665
  // (boundToCurrentDisk === false). Such a result — even a NON-EMPTY one, which
@@ -670,7 +742,13 @@ onConfirmedNoBlockers) {
670
742
  // fresh touch does, so it gets the same demotion. Without this, replaying
671
743
  // yesterday's cached blocker would re-cement an orphan file as blocking
672
744
  // after mode=full had demoted it.
673
- const cachedDiags = await demoteInferredForFile(file, cached.diagnostics, cwd, lspService);
745
+ const demotedCached = await demoteInferredForFile(file, cached.diagnostics, cwd, lspService);
746
+ // #3088 AC5: a replay is served to the agent exactly like a fresh
747
+ // observation, so it passes the same filter. The cached set is already
748
+ // inline-suppression-clean (the record below stores `inlineKept`), so
749
+ // only the revocable store/config filters run here.
750
+ const cachedPolicy = applyProbeFindingPolicy(file, demotedCached, cwd, replayContentForStrictMarks(file, cwd), severity);
751
+ const cachedDiags = cachedPolicy.kept;
674
752
  const filteredDiags = applySeverityFilter(cachedDiags, severity);
675
753
  // #692: cached.diagnostics were already suppression-/skipTestFiles-
676
754
  // filtered at write time (this same code path); no file content was
@@ -691,10 +769,13 @@ onConfirmedNoBlockers) {
691
769
  diagnostics: diagnosticsToFileDiags(file, filteredDiags),
692
770
  confirmation,
693
771
  primaryServerId: primaryServerId(file),
772
+ ...(cachedPolicy.suppressed > 0 && {
773
+ dispositionSuppressed: cachedPolicy.suppressed,
774
+ }),
694
775
  };
695
776
  }
696
777
  }
697
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
778
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, cwd, waitMs, serverScope);
698
779
  const health = lspService.getDiagnosticsHealth?.(file);
699
780
  // #570: a timed-out priming check is never a confirmed "clean" — treat it
700
781
  // as unconfirmed without consulting the (unrelated) silent-tier
@@ -753,6 +834,9 @@ onConfirmedNoBlockers) {
753
834
  }
754
835
  // #1645 review F3: one demotion rule for every writer of the widget store.
755
836
  effectiveRawDiags = await demoteInferredForFile(file, effectiveRawDiags, cwd, lspService);
837
+ // #3088: one filter for the output, the counts and the footer reconcile.
838
+ const policy = applyProbeFindingPolicy(file, effectiveRawDiags, cwd, collectedContent, severity);
839
+ effectiveRawDiags = policy.kept;
756
840
  const filteredDiags = applySeverityFilter(effectiveRawDiags, severity);
757
841
  const verdict = reconcileWidgetFromLspResult(file, effectiveRawDiags, confirmation, writeIndex, cwd, collectedContent, boundMismatch);
758
842
  if (verdict.confirmed && !verdict.blocking) {
@@ -775,7 +859,14 @@ onConfirmedNoBlockers) {
775
859
  confirmation !== "unconfirmed" &&
776
860
  diagnosticsUnsupportedServerIds.length === 0 &&
777
861
  unconfirmedServerIds.length === 0) {
778
- cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
862
+ // #3088: the entry records the INLINE-suppressed set, never the
863
+ // disposition/rule-policy-filtered one. An inline `pi-lens-ignore` comment
864
+ // cannot change without invalidating this content-keyed entry, so baking it
865
+ // in is safe and makes every later replay consistent with a fresh probe; a
866
+ // disposition mark or a `.pi-lens.json` rule can be revoked at any moment,
867
+ // so caching THEIR effect would keep a finding hidden after its mark was
868
+ // removed. The replay branch above re-applies those two on every serve.
869
+ cacheCtx.record(file, scopeKey, policy.inlineKept, stat.mtimeMs, collectedContent !== undefined
779
870
  ? hashDiagnosticContent(collectedContent)
780
871
  : undefined, stat.size);
781
872
  }
@@ -788,6 +879,9 @@ onConfirmedNoBlockers) {
788
879
  ...(skipReason !== undefined && { skipReason }),
789
880
  primaryServerId: primaryServerId(file),
790
881
  diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
882
+ ...(policy.suppressed > 0 && {
883
+ dispositionSuppressed: policy.suppressed,
884
+ }),
791
885
  };
792
886
  }
793
887
  async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(),
@@ -797,7 +891,7 @@ onConfirmedNoBlockers) {
797
891
  // Reserve the token before awaiting this file's LSP result. The direct-file
798
892
  // path performs its own confirmation/reconciliation below (#1198).
799
893
  const writeIndex = nextWriteIndex?.();
800
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
894
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, cwd, waitMs, serverScope);
801
895
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
802
896
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
803
897
  // #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
@@ -857,6 +951,9 @@ onConfirmedNoBlockers) {
857
951
  confirmation = "unconfirmed";
858
952
  // #1645 review F3: one demotion rule for every writer of the widget store.
859
953
  effectiveRawDiags = await demoteInferredForFile(absPath, effectiveRawDiags, cwd, lspService);
954
+ // #3088: one filter for the output, the counts and the footer reconcile.
955
+ const policy = applyProbeFindingPolicy(absPath, effectiveRawDiags, cwd, collectedContent, severity);
956
+ effectiveRawDiags = policy.kept;
860
957
  const filtered = applySeverityFilter(effectiveRawDiags, severity);
861
958
  const total = filtered.length;
862
959
  const truncated = total > MAX_DIAGNOSTICS;
@@ -912,6 +1009,10 @@ onConfirmedNoBlockers) {
912
1009
  const coverageLine = unconfirmedServerIds.length > 0
913
1010
  ? `Auxiliary coverage INCOMPLETE — ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit. waitMs can extend an ordinary wait, but it cannot override a session-demoted scanner.`
914
1011
  : undefined;
1012
+ // #3088: the #1616 suppressed-bucket rule on this lane — a policy drop is
1013
+ // stated as a count, never left to read as "nothing was wrong here"
1014
+ // (AGENTS.md shape 10). Bounded: one line per call, whatever the count.
1015
+ const suppressedLine = dispositionSuppressedLine(policy.suppressed);
915
1016
  let text;
916
1017
  if (total === 0) {
917
1018
  text = [
@@ -919,6 +1020,7 @@ onConfirmedNoBlockers) {
919
1020
  "",
920
1021
  unavailable ?? "No auxiliary findings.",
921
1022
  ...(coverageLine ? [coverageLine] : []),
1023
+ ...(suppressedLine ? [suppressedLine] : []),
922
1024
  ].join("\n");
923
1025
  }
924
1026
  else {
@@ -932,6 +1034,8 @@ onConfirmedNoBlockers) {
932
1034
  }
933
1035
  if (coverageLine)
934
1036
  lines.push("", coverageLine);
1037
+ if (suppressedLine)
1038
+ lines.push("", suppressedLine);
935
1039
  if (unavailable)
936
1040
  lines.unshift(unavailable, "");
937
1041
  if (truncated) {
@@ -958,6 +1062,9 @@ onConfirmedNoBlockers) {
958
1062
  code: d.code,
959
1063
  })),
960
1064
  totalDiagnostics: total,
1065
+ ...(policy.suppressed > 0 && {
1066
+ dispositionSuppressed: policy.suppressed,
1067
+ }),
961
1068
  truncated,
962
1069
  unconfirmed,
963
1070
  timedOut: unconfirmed ? timedOut : undefined,
@@ -1083,32 +1190,67 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
1083
1190
  const resolvedCwd = options.cwd ?? process.cwd();
1084
1191
  const cacheCtx = createWorkspaceDiagnosticsCacheContext(resolvedCwd);
1085
1192
  const scopeKey = buildScopeKey(options.serverScope ?? "all");
1086
- const results = await mapWithConcurrency(files, options.concurrency, async (file) => {
1087
- const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers);
1088
- const bounded = withDeadline(work, {
1089
- ms: batchFileDeadlineMs(),
1090
- onTimeout: "undefined",
1091
- onReject: "undefined",
1092
- });
1093
- const result = options.signal
1094
- ? await Promise.race([
1095
- bounded,
1096
- new Promise((resolve) => {
1097
- if (options.signal?.aborted) {
1098
- resolve(undefined);
1099
- return;
1100
- }
1101
- options.signal?.addEventListener("abort", () => resolve(undefined), {
1102
- once: true,
1103
- });
1104
- }),
1105
- ])
1106
- : await bounded;
1107
- return (result ??
1108
- inconclusiveBatchResult(file, options.signal?.aborted
1109
- ? "Batch aborted before this file completed."
1110
- : `File check exceeded ${batchFileDeadlineMs()}ms.`));
1111
- }, lspService, options.signal, options.onProgress);
1193
+ const resultsByIndex = new Map();
1194
+ let completed = 0;
1195
+ const pendingIndices = new Map();
1196
+ files.forEach((file, index) => {
1197
+ const queue = pendingIndices.get(file);
1198
+ if (queue)
1199
+ queue.push(index);
1200
+ else
1201
+ pendingIndices.set(file, [index]);
1202
+ });
1203
+ const groups = groupFilesByPrimaryServer(files);
1204
+ await runPerServerGroups(groups, options.concurrency, async (group) => {
1205
+ if (options.signal?.aborted)
1206
+ return;
1207
+ const first = group.files[0];
1208
+ if (first &&
1209
+ lspService &&
1210
+ !isWarmAttached() &&
1211
+ typeof lspService.ensureWarmForSweep === "function") {
1212
+ await lspService.ensureWarmForSweep(first, {
1213
+ signal: options.signal,
1214
+ });
1215
+ if (options.signal?.aborted)
1216
+ return;
1217
+ }
1218
+ await mapWithConcurrency(group.files, 1, async (file) => {
1219
+ // Keep duplicate explicit paths in their original result slots.
1220
+ const index = pendingIndices.get(file).shift();
1221
+ const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers);
1222
+ const bounded = withDeadline(work, {
1223
+ ms: batchFileDeadlineMs(),
1224
+ onTimeout: "undefined",
1225
+ onReject: "undefined",
1226
+ });
1227
+ const result = options.signal
1228
+ ? await Promise.race([
1229
+ bounded,
1230
+ new Promise((resolve) => {
1231
+ if (options.signal?.aborted) {
1232
+ resolve(undefined);
1233
+ return;
1234
+ }
1235
+ options.signal?.addEventListener("abort", () => resolve(undefined), {
1236
+ once: true,
1237
+ });
1238
+ }),
1239
+ ])
1240
+ : await bounded;
1241
+ resultsByIndex.set(index, result ??
1242
+ inconclusiveBatchResult(file, options.signal?.aborted
1243
+ ? "Batch aborted before this file completed."
1244
+ : `File check exceeded ${batchFileDeadlineMs()}ms.`));
1245
+ completed += 1;
1246
+ options.onProgress?.(completed, files.length);
1247
+ }, options.signal);
1248
+ }, options.signal);
1249
+ // The retired local pool returned only slots whose mapper had run. Keep that
1250
+ // dense abort contract while restoring the original file order for callers.
1251
+ const results = [...resultsByIndex.entries()]
1252
+ .sort(([left], [right]) => left - right)
1253
+ .map(([, result]) => result);
1112
1254
  // Persist whatever was recorded, including a partial/aborted sweep's
1113
1255
  // already-completed files — same "don't throw away confirmed work"
1114
1256
  // posture as `runWorkspaceDiagnostics`.
@@ -1167,6 +1309,9 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1167
1309
  }
1168
1310
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, outcomeCounts, incompleteFiles, } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
1169
1311
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1312
+ // #3088: the batch's own suppressed bucket — one summed line, never one per
1313
+ // file or per finding.
1314
+ const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
1170
1315
  const lines = [
1171
1316
  `Files checked: ${results.length}`,
1172
1317
  `Total diagnostics: ${total}`,
@@ -1221,6 +1366,9 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1221
1366
  lines.push("", `... (${total - MAX_DIAGNOSTICS} more diagnostics not shown)`);
1222
1367
  }
1223
1368
  }
1369
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
1370
+ if (suppressedLine)
1371
+ lines.push("", suppressedLine);
1224
1372
  return {
1225
1373
  content: [{ type: "text", text: lines.join("\n") }],
1226
1374
  details: {
@@ -1234,6 +1382,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1234
1382
  primaryDiagnosticsCount: primaryDisplay.length,
1235
1383
  auxiliaryDiagnosticsCount: auxiliaryDisplay.length,
1236
1384
  totalDiagnostics: total,
1385
+ ...(dispositionSuppressed > 0 && { dispositionSuppressed }),
1237
1386
  truncated,
1238
1387
  cleanFiles: clean,
1239
1388
  unconfirmedFiles: unconfirmed,
@@ -1308,6 +1457,9 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1308
1457
  const filesToProcess = collectedFiles.slice(0, MAX_FILES);
1309
1458
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1310
1459
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1460
+ // #3088: the directory scan's own suppressed bucket — one summed line.
1461
+ const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
1462
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
1311
1463
  let text;
1312
1464
  if (total === 0) {
1313
1465
  // #533/#570: an unconfirmed-containing directory result must never
@@ -1326,6 +1478,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1326
1478
  ...lspHealthWarnings.slice(0, 10),
1327
1479
  ]
1328
1480
  : [cleanLine]),
1481
+ ...(suppressedLine ? ["", suppressedLine] : []),
1329
1482
  ].join("\n");
1330
1483
  }
1331
1484
  else {
@@ -1365,6 +1518,8 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1365
1518
  if (truncated) {
1366
1519
  lines.push("", `... (${total - MAX_DIAGNOSTICS} more diagnostics not shown)`);
1367
1520
  }
1521
+ if (suppressedLine)
1522
+ lines.push("", suppressedLine);
1368
1523
  text = lines.join("\n");
1369
1524
  }
1370
1525
  return {
@@ -1388,6 +1543,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1388
1543
  primaryDiagnosticsCount: primaryDisplay.length,
1389
1544
  auxiliaryDiagnosticsCount: auxiliaryDisplay.length,
1390
1545
  totalDiagnostics: total,
1546
+ ...(dispositionSuppressed > 0 && { dispositionSuppressed }),
1391
1547
  truncated,
1392
1548
  cleanFiles: clean,
1393
1549
  unconfirmedFiles: unconfirmed,
@@ -1401,6 +1557,22 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1401
1557
  };
1402
1558
  }
1403
1559
  // ── helpers ─────────────────────────────────────────────────────────────
1560
+ /**
1561
+ * #3088: the one rendering of the probe lane's policy drop, shared by the
1562
+ * single-file, batch and directory renders so the three cannot word the same
1563
+ * fact differently. Returns `undefined` when nothing was dropped — the #1616
1564
+ * rule asks for a count when there IS one, not a "0 suppressed" line on every
1565
+ * clean probe.
1566
+ */
1567
+ function dispositionSuppressedLine(suppressed) {
1568
+ if (suppressed <= 0)
1569
+ return undefined;
1570
+ return (`suppressed by disposition: ${suppressed} finding(s) dropped from this ` +
1571
+ "result because they're marked false-positive/won't-fix, disabled by " +
1572
+ "`.pi-lens.json` rule policy, or suppressed by an inline `pi-lens-ignore` " +
1573
+ "comment. Not a clean verdict for those locations — they were found, " +
1574
+ "then intentionally hidden.");
1575
+ }
1404
1576
  function applySeverityFilter(diags, severity) {
1405
1577
  if (severity === "all")
1406
1578
  return diags;
@@ -38,16 +38,16 @@ This document records structural inconsistencies and centralization gaps found w
38
38
 
39
39
  ---
40
40
 
41
- ### 3. Tree-sitter query loader uses a hand-rolled YAML parser
41
+ ### 3. Tree-sitter query loader uses a hand-rolled YAML parser (partially resolved)
42
42
 
43
43
  **Files:**
44
- - `clients/tree-sitter-query-loader.ts` — `parseYaml()` and `extractMultilineValue()` are custom
44
+ - `clients/tree-sitter-query-loader.ts` — `parseYaml()` now calls `yaml.load` directly (refs #3054); the hand-rolled scanner and `extractMultilineValue()` are deleted.
45
45
  - `clients/dispatch/runners/yaml-rule-parser.ts` — uses `js-yaml` for ast-grep rules
46
- - `scripts/validate-rule-catalog.mjs` — uses regex to scrape YAML scalars
46
+ - `scripts/validate-rule-catalog.mjs` — still uses regex to scrape YAML scalars (unchanged by #3054; out of that PR's scope)
47
47
 
48
- **Impact:** The tree-sitter loader does not support multiple YAML documents per file, complex nested objects, or arrays the way `js-yaml` does. A contributor writing a tree-sitter rule with richer metadata may hit silent parse failures. The three rule loaders do not share code, so fixes and behavior diverge.
48
+ **Impact:** `scripts/validate-rule-catalog.mjs` remains its own regex-based reader, so the tree-sitter and ast-grep rule catalogs now share one parser (`js-yaml`) while the validation script is a third, still-divergent implementation.
49
49
 
50
- **Recommended fix:** Replace the hand-rolled parser in `tree-sitter-query-loader.ts` with `js-yaml` (already a runtime dependency), or reuse `parseSimpleYaml` from `yaml-rule-parser.ts`.
50
+ **Recommended fix:** Fold `scripts/validate-rule-catalog.mjs`'s scraping onto `js-yaml` too, the same shape #3054 applied to the query loader.
51
51
 
52
52
  ---
53
53
 
@@ -52,3 +52,14 @@ Auto-install behavior depends on gate type:
52
52
  | `psscriptanalyzer` | PowerShell linting | Manual | — |
53
53
 
54
54
  Additional language servers (gopls, ruby-lsp, solargraph, etc.) are auto-detected from PATH or installed via native package managers (`go install`, `gem install`) when their language is detected.
55
+
56
+ ## Pip tool installation order
57
+
58
+ For pip-backed tools, pi-lens uses this order:
59
+
60
+ 1. Use `pipx install` when `pipx` is available.
61
+ 2. Create or reuse `<PI_LENS_HOME>/pip-tools` with `python3 -m venv`, then use that venv's `pip`.
62
+ 3. Use `pip install --user` when the interpreter accepts a normal user install.
63
+ 4. If PEP 668 refuses the user install, use `--user --break-system-packages` only with `PYTHONUSERBASE` set to the pi-lens-private `<PI_LENS_HOME>/pip-user` prefix.
64
+
65
+ Pi-lens never passes `--break-system-packages` to a system prefix. It adds the selected pipx, venv, or private-prefix `bin`/`Scripts` directory to the current process path so the availability probe resolves the installed tool.
@@ -52,6 +52,58 @@ per-disposition binding strength:
52
52
  edits to the line and drift elsewhere in the file. (`suppress` is enforced
53
53
  by the inline comment anyway; the store entry is an audit mirror.)
54
54
 
55
+ Both anchors also hash the producing `tool`, so a finding that changes hands
56
+ between tools gets a fresh anchor. One such change shipped in #3041: `mode:
57
+ "full"` used to render auxiliary-scanner findings (ast-grep, opengrep, zizmor,
58
+ typos) under the generic `tool: "lsp"` while every other surface already showed
59
+ them under their real tool id. It now shows the real id everywhere, so a mark
60
+ recorded against the old `lsp`-labelled copy no longer matches and the finding
61
+ gets one fresh chance to be re-marked — under the same `tool` the per-edit path
62
+ has always used.
63
+
64
+ ## Which surfaces honor a mark
65
+
66
+ Every model-facing surface that renders a diagnostic applies the same filter
67
+ stack, in the same order: inline `pi-lens-ignore` comments, then the stored
68
+ dispositions, then the project's `.pi-lens.json` `rules.<id>.disable`/`select`
69
+ policy (`clients/dispatch/finding-policy.ts`). That covers the per-edit
70
+ feedback, `lens_diagnostics` `mode=delta`/`mode=all`/`mode=full`, and — since
71
+ #3088 — the `lens_diagnostics` `source=lsp` probe lane together with the legacy
72
+ `lsp_diagnostics` tool and the MCP `pilens_lsp_diagnostics` shim that share it.
73
+ Since #3102 it also covers the two PUSH surfaces that were still unfiltered:
74
+ the turn-end **late-auxiliary advisory** (findings an auxiliary LSP published
75
+ after its grace window, drained at the next `turn_end`) and the **cold-neighbour
76
+ cascade run** (`buildResolvedFoundCascadeRun`, built in the quiet-window
77
+ reconcile). Both are pushed rather than asked for, so when a mark suppresses
78
+ everything they had to say they say nothing at all — silence on a push surface
79
+ is not a claim that the file is clean, and the drop count is recorded in the
80
+ lane's own `late_auxiliary_findings` / `cascade_finding_policy` latency row. A
81
+ delivery that still has something to say states what it dropped inline.
82
+
83
+ Before #3088 the probe lane was the one exception: it returned the raw LSP
84
+ result, so a finding marked `false-positive` stayed hidden in `delta`/`full`
85
+ and re-appeared on every probe — the lane
86
+ `skills/pi-lens-lsp-navigation` steers agents to as PRIMARY. It now filters
87
+ like every other surface, its footer reconcile writes the FILTERED set (so a
88
+ probe can no longer re-arm a finding the mark demoted), and a drop is always
89
+ stated as a count:
90
+
91
+ ```text
92
+ suppressed by disposition: 1 finding(s) dropped from this result …
93
+ ```
94
+
95
+ Two properties of the probe lane are worth knowing:
96
+
97
+ - **A mark converges whichever surface you made it from.** The probe renders a
98
+ finding as `[<source>] (<code>)` while the widget footer and `mode=full`
99
+ render the canonical `tool: "lsp"` / `rule: "<source>:<code>"`, and `tool` is
100
+ optional on the mark tool. The probe filter matches every one of those
101
+ spellings, so a mark made from the probe's own output works, and so does one
102
+ made from any other surface.
103
+ - **A blocking finding still needs a strict mark.** `semantic: "blocking"`
104
+ findings are dropped only by a content-bound `false-positive` match, never by
105
+ a weak `suppress`/`defer` — on this lane too (#1625 F1).
106
+
55
107
  ## Suppression comments
56
108
 
57
109
  `suppress` writes a pi-lens-owned ignore comment on the line immediately
@@ -47,9 +47,9 @@ review-graph, install-choices, etc.).
47
47
 
48
48
  **Default resolution order:**
49
49
 
50
- 1. `$PILENS_DATA_DIR/<sanitized-cwd-slug>/` (if `PILENS_DATA_DIR` is set)
50
+ 1. `$PILENS_DATA_DIR/<sanitized-cwd-slug>-<8-hex-hash>/` (if `PILENS_DATA_DIR` is set)
51
51
  2. `<cwd>/.pi-lens/` (legacy — only if it already exists in the project)
52
- 3. `~/.pi-lens/projects/<sanitized-cwd-slug>/` (current default)
52
+ 3. `~/.pi-lens/projects/<sanitized-cwd-slug>-<8-hex-hash>/` (current default)
53
53
 
54
54
  **When to set it:** running pi with a local model server (llama.cpp,
55
55
  Ollama, etc.) that monitors the project directory — cache-file churn
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### LSP Support
4
4
 
5
- pi-lens includes **45 language server definitions** (including four cross-cutting *auxiliary* scanners that attach alongside the file's language server — Opengrep, ast-grep, zizmor, and typos — see below). LSP is **enabled by default**; use `--no-lsp` to disable it for a session. Servers are auto-discovered from PATH, project `node_modules`, and managed installs. When a server is not installed, pi-lens offers an interactive install prompt.
5
+ pi-lens includes **46 language server definitions** (including four cross-cutting *auxiliary* scanners that attach alongside the file's language server — Opengrep, ast-grep, zizmor, and typos — see below). LSP is **enabled by default**; use `--no-lsp` to disable it for a session. Servers are auto-discovered from PATH, project `node_modules`, and managed installs. When a server is not installed, pi-lens offers an interactive install prompt.
6
6
 
7
7
  **LSP Idle Management:** LSP servers shut down after 240 seconds of inactivity (no files modified) to free resources. The timer resets when you resume editing, preventing cold-start penalties during active development.
8
8
 
@@ -19,13 +19,13 @@ pi-lens includes **45 language server definitions** (including four cross-cuttin
19
19
  - **`capabilities`** — shows which operations are supported by the active LSP server(s) for a file, read directly from the cached `initialize` response (no round-trip).
20
20
  - **Symbol column resolution** — passing `symbol: "myFunc"` instead of an exact `character` position resolves the correct column automatically. Use `symbol: "foo#2"` for the second occurrence of `foo` on the line.
21
21
 
22
- LSP servers for: TypeScript, Deno, Python (pyright/basedpyright + jedi), Go, Rust, Ruby (ruby-lsp + solargraph), PHP, PowerShell, C# (omnisharp), F#, Java (JDT LS, with Lombok javaagent support when a Lombok jar is available), Kotlin, Swift, Dart, Lua, C/C++, Zig, Haskell, Elixir, Gleam, Markdown (marksman), OCaml, Clojure, CUE (syntax and parse diagnostics; evaluation errors via the cue-vet auxiliary runner), Terraform, Nix, Bash, Fish, CMake, Docker, YAML, JSON, HTML, TOML, Prisma, Vue, Svelte, CSS.
22
+ LSP servers for: TypeScript, Deno, Python (pyright/basedpyright + jedi), Go, Rust, Ruby (ruby-lsp + solargraph), PHP, PowerShell, C# (omnisharp), F#, Java (JDT LS, with Lombok javaagent support when a Lombok jar is available), Kotlin, Swift, Dart, Lua, C/C++, Zig, Haskell, Elixir, Gleam, Typst (tinymist), Markdown (marksman), OCaml, Clojure, CUE (syntax and parse diagnostics; evaluation errors via the cue-vet auxiliary runner), Terraform, Nix, Bash, Fish, CMake, Docker, YAML, JSON, HTML, TOML, Prisma, Vue, Svelte, CSS.
23
23
 
24
24
  ### Formatters
25
25
 
26
- pi-lens auto-detects and runs **34 formatters** based on project config:
26
+ pi-lens auto-detects and runs **35 formatters** based on project config:
27
27
 
28
- biome, prettier, oxfmt, ruff, black, sqlfluff, gofmt, rustfmt, zig fmt, dart format, shfmt, nixfmt, mix format, ocamlformat, clang-format, ktlint, ktfmt, rubocop, standardrb, gleam format, terraform fmt, terragrunt-hcl, php-cs-fixer, csharpier, fantomas, swiftformat, stylua, ormolu, taplo, google-java-format, cljfmt, cmake-format, cue fmt, psscriptanalyzer-format
28
+ biome, prettier, oxfmt, ruff, black, sqlfluff, gofmt, rustfmt, zig fmt, dart format, shfmt, nixfmt, mix format, ocamlformat, clang-format, ktlint, ktfmt, rubocop, standardrb, gleam format, typstyle, terraform fmt, terragrunt-hcl, php-cs-fixer, csharpier, fantomas, swiftformat, stylua, ormolu, taplo, google-java-format, cljfmt, cmake-format, cue fmt, psscriptanalyzer-format
29
29
 
30
30
  Detection rules:
31
31