@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
@@ -81,10 +81,13 @@
81
81
  * Refs: #585, #313 (the SecurityScanClient de-dupe prerequisite), #1004
82
82
  */
83
83
  import * as fs from "node:fs";
84
+ import * as os from "node:os";
84
85
  import * as path from "node:path";
85
86
  import { applyDispositionsMultiFile } from "../diagnostic-dispositions.js";
86
87
  import { getKnipIgnorePatterns } from "../file-utils.js";
87
- import { isAtOrAboveHomeDir } from "../path-utils.js";
88
+ import { isAtOrAboveHomeDir, realpathOrResolve } from "../path-utils.js";
89
+ import { isSameOrWithin } from "../lsp/server.js";
90
+ import { incrementDegradationCount } from "../degradation-ledger.js";
88
91
  import { GitleaksClient } from "../gitleaks-client.js";
89
92
  import { GovulncheckClient } from "../govulncheck-client.js";
90
93
  import { isToolInstallAllowedByTrust, projectTrustDenialReason, } from "../project-trust.js";
@@ -134,7 +137,48 @@ function pushUnique(list, id) {
134
137
  * spawns).
135
138
  */
136
139
  export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, signal, options = {}) {
137
- const analysisRoot = path.resolve(cwd);
140
+ const requestedRoot = options.analysisRoot === undefined
141
+ ? cwd
142
+ : path.resolve(cwd, options.analysisRoot);
143
+ let analysisRoot;
144
+ let analysisRootValidation;
145
+ if (options.analysisRoot !== undefined) {
146
+ try {
147
+ if (!fs.statSync(requestedRoot).isDirectory()) {
148
+ throw new Error("is not a directory");
149
+ }
150
+ analysisRoot = fs.realpathSync(requestedRoot);
151
+ }
152
+ catch (error) {
153
+ const detail = error instanceof Error ? error.message : String(error);
154
+ const reason = `the explicit analysis root ${requestedRoot} is unavailable or ${detail}`;
155
+ analysisRootValidation = {
156
+ state: "undecided",
157
+ root: requestedRoot,
158
+ reason,
159
+ };
160
+ incrementDegradationCount({
161
+ kind: "lens-diagnostics-analysis-root-rejected",
162
+ subject: requestedRoot,
163
+ reason,
164
+ });
165
+ return {
166
+ analysisRootValidation,
167
+ diagnostics: [],
168
+ runners: [],
169
+ analyzed: [],
170
+ authoritativeCoverage: [],
171
+ cold: [...ANALYZER_IDS],
172
+ coldReasons: Object.fromEntries(ANALYZER_IDS.map((id) => [id, reason])),
173
+ failed: [],
174
+ timings: {},
175
+ analysisRootError: reason,
176
+ };
177
+ }
178
+ }
179
+ else {
180
+ analysisRoot = realpathOrResolve(cwd);
181
+ }
138
182
  // #747: refuse to spawn any heavyweight analyzer when the analysis root is
139
183
  // at — or above — the home directory (the #250/#253 escape class). Every
140
184
  // analyzer here treats `analysisRoot` as a whole tree to walk; from $HOME
@@ -144,26 +188,58 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
144
188
  // / review-graph's buildOrUpdateGraph; like the latter, there is no safe
145
189
  // substitute root to fall back to — the caller's `paths` scope only filters
146
190
  // REPORTED results, it never narrows what these analyzers walk.
147
- const unsafeRootReason = "the working directory resolves at or above the home directory; heavyweight analyzers refuse to walk from there (#747)";
148
- if (isAtOrAboveHomeDir(analysisRoot, options.homeDir)) {
191
+ const unsafeRootReason = "the analysis root resolves at or above the home directory; heavyweight analyzers refuse to walk from there (#747)";
192
+ const homeRoot = realpathOrResolve(options.homeDir ?? os.homedir());
193
+ if (options.analysisRoot !== undefined) {
194
+ const unsafeExplicitRoot = !isSameOrWithin(homeRoot, analysisRoot) ||
195
+ isSameOrWithin(analysisRoot, homeRoot);
196
+ analysisRootValidation = unsafeExplicitRoot
197
+ ? {
198
+ state: "unsafe",
199
+ root: analysisRoot,
200
+ reason: "explicit analysis root must be strictly contained by the canonical home directory",
201
+ }
202
+ : { state: "safe", root: analysisRoot };
203
+ }
204
+ else if (isAtOrAboveHomeDir(analysisRoot, options.homeDir)) {
205
+ analysisRootValidation = {
206
+ state: "unsafe",
207
+ root: analysisRoot,
208
+ reason: unsafeRootReason,
209
+ };
210
+ }
211
+ else {
212
+ analysisRootValidation = { state: "safe", root: analysisRoot };
213
+ }
214
+ if (analysisRootValidation.state !== "safe") {
215
+ incrementDegradationCount({
216
+ kind: "lens-diagnostics-analysis-root-rejected",
217
+ subject: analysisRoot,
218
+ reason: analysisRootValidation.reason,
219
+ });
149
220
  return {
221
+ analysisRootValidation,
150
222
  diagnostics: [],
151
223
  runners: [],
152
224
  analyzed: [],
225
+ authoritativeCoverage: [],
153
226
  cold: [...ANALYZER_IDS],
154
227
  coldReasons: Object.fromEntries(ANALYZER_IDS.map((id) => [id, unsafeRootReason])),
155
228
  failed: [],
156
229
  timings: {},
157
- unsafeRoot: true,
230
+ unsafeRoot: analysisRootValidation.state === "unsafe",
158
231
  };
159
232
  }
160
233
  const diagnostics = [];
161
234
  const runners = [];
162
235
  const analyzed = [];
236
+ const authoritativeCoverage = [];
163
237
  const cold = [];
164
238
  // #1623: the specific reason each `cold` id was skipped, captured at the
165
239
  // gate that decided it — see FreshProjectDiagnosticsResult.coldReasons.
166
240
  const coldReasons = {};
241
+ const partial = [];
242
+ const partialReasons = {};
167
243
  const failed = [];
168
244
  const timings = {};
169
245
  // #1623: ms-old each id's data was when this call read it, for lanes that
@@ -190,6 +266,10 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
190
266
  pushUnique(cold, id);
191
267
  coldReasons[id] = reason;
192
268
  }
269
+ function markPartial(id, reason) {
270
+ pushUnique(partial, id);
271
+ partialReasons[id] = reason;
272
+ }
193
273
  /**
194
274
  * `analysedRoot` is the client's own opt-in `AnalysedRootSignal` (#2154),
195
275
  * never a property of reaching this function: `success: true` is also what
@@ -198,9 +278,21 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
198
278
  * id that did not analyse the root simply carries no authority to retire a
199
279
  * retained finding.
200
280
  */
201
- function record(id, adapted, elapsedMs, analysedRoot) {
202
- if (analysedRoot)
281
+ function record(id, adapted, elapsedMs, analysedRoot, analysis) {
282
+ if (analysedRoot) {
203
283
  pushUnique(analyzed, id);
284
+ if (analysis?.analyzedFiles !== undefined &&
285
+ analysis.analyzedFiles.length > 0) {
286
+ const root = realpathOrResolve(analysisRoot);
287
+ authoritativeCoverage.push({
288
+ runnerId: id,
289
+ root,
290
+ files: new Set(analysis.analyzedFiles.map((file) => {
291
+ return realpathOrResolve(file);
292
+ })),
293
+ });
294
+ }
295
+ }
204
296
  timings[id] = (timings[id] ?? 0) + elapsedMs;
205
297
  const kept = applyDispositionsMultiFile(adapted, analysisRoot, (d) => d.filePath);
206
298
  const suppressedHere = adapted.length - kept.length;
@@ -217,6 +309,9 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
217
309
  function recordFailed(id, result) {
218
310
  failed.push({
219
311
  id,
312
+ ...("reason" in result && result.reason !== undefined
313
+ ? { reason: result.reason }
314
+ : {}),
220
315
  summary: "summary" in result && typeof result.summary === "string"
221
316
  ? result.summary
222
317
  : "analyzer reported an unsuccessful run",
@@ -245,7 +340,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
245
340
  cacheManager.writeCache("knip", result, analysisRoot, {
246
341
  scanDurationMs: Date.now() - startMs,
247
342
  });
248
- record("knip", knipIssuesToProjectDiagnostics(analysisRoot, result.issues ?? []), Date.now() - startMs, true);
343
+ record("knip", knipIssuesToProjectDiagnostics(analysisRoot, result.issues ?? []), Date.now() - startMs, true, result);
249
344
  }),
250
345
  // jscpd — duplicate code detection. Cache key varies with TS-project
251
346
  // detection, exactly mirroring session_start's own logic.
@@ -269,7 +364,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
269
364
  cacheManager.writeCache(scannerKey, result, analysisRoot, {
270
365
  scanDurationMs: Date.now() - startMs,
271
366
  });
272
- record("jscpd", jscpdResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, true);
367
+ record("jscpd", jscpdResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, true, result);
273
368
  }),
274
369
  // madge — circular-dependency detection.
275
370
  task("madge", async () => {
@@ -282,7 +377,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
282
377
  cacheManager.writeCache("madge", result, analysisRoot, {
283
378
  scanDurationMs: Date.now() - startMs,
284
379
  });
285
- record("madge", circularDepsToProjectDiagnostics(analysisRoot, result.circular ?? []), Date.now() - startMs, result.analyzed === true);
380
+ record("madge", circularDepsToProjectDiagnostics(analysisRoot, result.circular ?? []), Date.now() - startMs, result.analyzed === true, result);
286
381
  }),
287
382
  // gitleaks — committed-secrets detection. session_start/per-edit stay
288
383
  // config-gated per #130's strict default (GitleaksClient.hasGitleaksSignal),
@@ -313,7 +408,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
313
408
  cacheManager.writeCache("gitleaks", result, analysisRoot, {
314
409
  scanDurationMs: Date.now() - startMs,
315
410
  });
316
- record("gitleaks", gitleaksResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true);
411
+ record("gitleaks", gitleaksResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true, result);
317
412
  }),
318
413
  // govulncheck — Go module CVE detection. Go-module-gated per #132.
319
414
  task("govulncheck", async () => {
@@ -345,7 +440,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
345
440
  cacheManager.writeCache("govulncheck", result, analysisRoot, {
346
441
  scanDurationMs: Date.now() - startMs,
347
442
  });
348
- record("govulncheck", govulncheckResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true);
443
+ record("govulncheck", govulncheckResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true, result);
349
444
  }),
350
445
  // opengrep — full-workspace semgrep-grade security/quality findings via a
351
446
  // single project-wide CLI scan (#584). Structurally always-on: mirrors
@@ -372,10 +467,31 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
372
467
  recordFailed("opengrep", result);
373
468
  return;
374
469
  }
470
+ if (result.analyzed !== true) {
471
+ // A usable partial report with no scanned paths carries findings but
472
+ // no retirement authority. Keep those findings visible and mark the
473
+ // producer partial so the renderer does not call it cold/not-run.
474
+ const reason = result.summary ??
475
+ result.reason ??
476
+ "opengrep did not analyse this root";
477
+ if (result.partial)
478
+ markPartial("opengrep", reason);
479
+ else
480
+ markCold("opengrep", reason);
481
+ record("opengrep", opengrepResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, false, result);
482
+ return;
483
+ }
484
+ if (result.partial) {
485
+ markPartial("opengrep", result.summary ??
486
+ result.reason ??
487
+ "opengrep scan coverage is incomplete");
488
+ record("opengrep", opengrepResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, true, result);
489
+ return;
490
+ }
375
491
  cacheManager.writeCache("opengrep", result, analysisRoot, {
376
492
  scanDurationMs: Date.now() - startMs,
377
493
  });
378
- record("opengrep", opengrepResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true);
494
+ record("opengrep", opengrepResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true, result);
379
495
  }),
380
496
  // trivy — dependency CVE detection. Explicit opt-in per #131.
381
497
  task("trivy", async () => {
@@ -396,7 +512,7 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
396
512
  cacheManager.writeCache("trivy", result, analysisRoot, {
397
513
  scanDurationMs: Date.now() - startMs,
398
514
  });
399
- record("trivy", trivyResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true);
515
+ record("trivy", trivyResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true, result);
400
516
  }),
401
517
  // dead-code — cross-file dead-code for non-JS/TS languages (#127).
402
518
  // Each client self-gates via detect(); only matching-language projects
@@ -415,10 +531,13 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
415
531
  recordFailed("dead-code", result);
416
532
  return;
417
533
  }
418
- cacheManager.writeCache(cacheKey, result, analysisRoot, {
419
- scanDurationMs: Date.now() - startMs,
420
- });
421
- record("dead-code", deadCodeResultToProjectDiagnostics(analysisRoot, result), Date.now() - startMs, result.analyzed === true);
534
+ if (result.analyzed === true) {
535
+ cacheManager.writeCache(cacheKey, result, analysisRoot, {
536
+ scanDurationMs: Date.now() - startMs,
537
+ });
538
+ }
539
+ const adapted = deadCodeResultToProjectDiagnostics(analysisRoot, result);
540
+ record("dead-code", adapted, Date.now() - startMs, result.analyzed === true, result);
422
541
  }));
423
542
  }),
424
543
  // test-runner — CACHE-READ only, unlike every task above (#1004). Its
@@ -494,11 +613,15 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
494
613
  for (const id of abortedIds)
495
614
  pushUnique(cold, id);
496
615
  return {
616
+ analysisRootValidation,
497
617
  diagnostics,
498
618
  runners,
499
619
  analyzed,
620
+ authoritativeCoverage,
500
621
  cold,
501
622
  coldReasons,
623
+ partial,
624
+ partialReasons,
502
625
  failed,
503
626
  timings,
504
627
  cachedAgeMs,
@@ -509,11 +632,15 @@ export async function fetchFreshProjectDiagnostics(cacheManager, cwd, clients, s
509
632
  };
510
633
  }
511
634
  return {
635
+ analysisRootValidation,
512
636
  diagnostics,
513
637
  runners,
514
638
  analyzed,
639
+ authoritativeCoverage,
515
640
  cold,
516
641
  coldReasons,
642
+ partial,
643
+ partialReasons,
517
644
  failed,
518
645
  timings,
519
646
  cachedAgeMs,
@@ -1,4 +1,7 @@
1
1
  import * as path from "node:path";
2
+ function deadCodeRunnerId(language) {
3
+ return `dead-code-${language}`;
4
+ }
2
5
  function deadCodeMessage(issue) {
3
6
  if (issue.category === "unlisted")
4
7
  return `Unlisted dependency ${issue.name}`;
@@ -25,7 +28,7 @@ export function deadCodeIssueToProjectDiagnostic(cwd, issue, language) {
25
28
  severity: blocking ? "error" : "warning",
26
29
  semantic: blocking ? "blocking" : "warning",
27
30
  tool: "dead-code",
28
- runner: `dead-code-${language}`,
31
+ runner: deadCodeRunnerId(language),
29
32
  rule: `dead-code:${issue.category}`,
30
33
  message: deadCodeMessage(issue),
31
34
  source: "project-scan",
@@ -16,6 +16,7 @@ import { logTreeSitter, logTreeSitterCacheStats, } from "../tree-sitter-logger.j
16
16
  import { queriesForLanguage, queryLoader, } from "../tree-sitter-query-loader.js";
17
17
  import { EXT_TO_LANG, getSharedTreeSitterClient, isTreeSitterWasmAborted, } from "../tree-sitter-shared.js";
18
18
  import { PROJECT_DIAGNOSTICS_CACHE_VERSION, saveProjectDiagnosticsSnapshot, } from "./cache.js";
19
+ import { hashDiagnosticContent } from "../lsp/diagnostic-binding.js";
19
20
  // Side-effect import: registers fact providers and fact rules.
20
21
  import "../dispatch/integration.js";
21
22
  // Skip files this large: matches the per-edit ast-grep runner's guard so a single
@@ -111,6 +112,7 @@ async function scanFileMajorRules(cwd, files, signal) {
111
112
  const treeSitter = [];
112
113
  const factRules = [];
113
114
  const astGrep = [];
115
+ const fingerprints = {};
114
116
  const sgModule = await loadSg();
115
117
  let phaseOneFilesScanned = 0;
116
118
  let astGrepFilesScanned = 0;
@@ -238,13 +240,34 @@ async function scanFileMajorRules(cwd, files, signal) {
238
240
  continue;
239
241
  if (langId || factEligible)
240
242
  phaseOneFilesScanned++;
243
+ // #2154 (#3060 round 2 F1): read the BYTES once, then decode. The
244
+ // fingerprint below must record the file's ON-DISK length, because
245
+ // that is what the reader compares against `statSync().size`. A
246
+ // length taken from the decoded STRING disagrees for every file
247
+ // holding a byte that is not valid UTF-8 — one such byte decodes to
248
+ // a 3-byte U+FFFD — so the size check could never match and every
249
+ // row from that file was retired on every cached read, for a file
250
+ // nobody touched. That is the DROP direction this store's own
251
+ // reconcile doc calls the worse one. `buf.length` equals
252
+ // `stat.size` for every file; both sides then hash the same lossy
253
+ // decode, so an unchanged file still matches exactly.
241
254
  let content;
255
+ let contentBytes;
242
256
  try {
243
- content = fs.readFileSync(filePath, "utf-8");
257
+ const buf = fs.readFileSync(filePath);
258
+ contentBytes = buf.length;
259
+ content = buf.toString("utf-8");
244
260
  }
245
261
  catch {
246
262
  content = null;
263
+ contentBytes = undefined;
247
264
  }
265
+ // #2154: how many rows existed BEFORE this file was scanned, so the
266
+ // fingerprint below is written only for files that actually produced
267
+ // one — the snapshot keeps no row for any other file, and an entry
268
+ // for a file with no row is dead weight in a record that is read on
269
+ // every `refreshRunners=cached` call.
270
+ const rowsBeforeFile = treeSitter.length + factRules.length + astGrep.length;
248
271
  try {
249
272
  await scanTreeSitterFile(filePath, langId, content);
250
273
  if (isTreeSitterWasmAborted()) {
@@ -293,6 +316,18 @@ async function scanFileMajorRules(cwd, files, signal) {
293
316
  ...graphIr,
294
317
  });
295
318
  }
319
+ // #2154: bind this file's rows to the bytes they were computed
320
+ // from. Only reachable with `content` in hand, which is the point
321
+ // — the fingerprint must describe what the rules READ, never what
322
+ // disk holds by the time the scan ends.
323
+ if (content !== null &&
324
+ contentBytes !== undefined &&
325
+ treeSitter.length + factRules.length + astGrep.length > rowsBeforeFile) {
326
+ fingerprints[filePath] = {
327
+ sizeBytes: contentBytes,
328
+ contentHash: hashDiagnosticContent(content),
329
+ };
330
+ }
296
331
  filesScanned++;
297
332
  }
298
333
  finally {
@@ -312,7 +347,14 @@ async function scanFileMajorRules(cwd, files, signal) {
312
347
  };
313
348
  if (!client) {
314
349
  await scan();
315
- return { treeSitter, factRules, astGrep, filesScanned, wasmAborted };
350
+ return {
351
+ treeSitter,
352
+ factRules,
353
+ astGrep,
354
+ filesScanned,
355
+ wasmAborted,
356
+ fingerprints,
357
+ };
316
358
  }
317
359
  const startedAt = Date.now();
318
360
  await client.withParseCacheMeasurement(scan, (stats) => {
@@ -348,7 +390,14 @@ async function scanFileMajorRules(cwd, files, signal) {
348
390
  // `astGrepFilesScanned` still feed the `project_diagnostics_scan` record
349
391
  // above (`astGrep` sub-field), so this cost stays observable without a
350
392
  // second, always-zero record to carry it.
351
- return { treeSitter, factRules, astGrep, filesScanned, wasmAborted };
393
+ return {
394
+ treeSitter,
395
+ factRules,
396
+ astGrep,
397
+ filesScanned,
398
+ wasmAborted,
399
+ fingerprints,
400
+ };
352
401
  }
353
402
  export async function scanProjectDiagnostics(options) {
354
403
  const cwd = path.resolve(options.cwd);
@@ -395,6 +444,7 @@ export async function scanProjectDiagnostics(options) {
395
444
  // scan stops promptly when the agent/user aborts (#341).
396
445
  const runners = [];
397
446
  const diagnostics = [];
447
+ let fileFingerprints = {};
398
448
  let wasmAborted = false;
399
449
  let filesScanned = files.length;
400
450
  if (!signal?.aborted) {
@@ -409,6 +459,11 @@ export async function scanProjectDiagnostics(options) {
409
459
  diagnostics.push(...scanned.astGrep);
410
460
  runners.push("ast-grep-napi");
411
461
  }
462
+ // #2154: keep a fingerprint only for a file that still has a row after
463
+ // the ast-grep discard above, so the record cannot claim to describe a
464
+ // file whose only rows were dropped.
465
+ const rowFiles = new Set(diagnostics.map((d) => d.filePath));
466
+ fileFingerprints = Object.fromEntries(Object.entries(scanned.fingerprints).filter(([file]) => rowFiles.has(file)));
412
467
  }
413
468
  const snapshot = {
414
469
  version: PROJECT_DIAGNOSTICS_CACHE_VERSION,
@@ -418,6 +473,7 @@ export async function scanProjectDiagnostics(options) {
418
473
  diagnostics,
419
474
  filesScanned,
420
475
  runners,
476
+ fileFingerprints,
421
477
  };
422
478
  // #760: only present when true — keeps existing snapshots/serializations
423
479
  // byte-identical for the untruncated (normal) case.
@@ -63,7 +63,7 @@ import * as os from "node:os";
63
63
  import * as path from "node:path";
64
64
  import { toPositiveFinite } from "./env-utils.js";
65
65
  import { FRESHNESS_CADENCE_MS } from "./freshness-cadence.js";
66
- import { assignFlagConfigSection, flagConfigSectionKeys, GLOBAL_NON_FLAG_CONFIG_SECTIONS, LENS_FLAGS, PROJECT_FOREIGN_CONFIG_NAMESPACES, PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS, PROJECT_NON_FLAG_CONFIG_SECTIONS, PROJECT_SCOPED_LENS_FLAGS, readFlagConfigValue, } from "./lens-flag-registry.js";
66
+ import { assignFlagConfigSection, flagConfigSectionKeys, GLOBAL_NON_FLAG_CONFIG_SECTIONS, GLOBAL_ONLY_NON_FLAG_KEYS, hasFlagConfigPath, LENS_FLAGS, PROJECT_FOREIGN_CONFIG_NAMESPACES, PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS, PROJECT_NON_FLAG_CONFIG_SECTIONS, PROJECT_SCOPED_LENS_FLAGS, readFlagConfigValue, } from "./lens-flag-registry.js";
67
67
  import { configSearchDirs, getPiLensGlobalConfigPath, PROJECT_CONFIG_BASENAMES, PROJECT_CONFIG_LOCATIONS, } from "./config-locations.js";
68
68
  import { deprecationRecords, ignoredRecordCollector, projectLocationFor, readConfigDocument, reportConfigReadFailure, reportPiLensConfigRecords, resolveOnePiLensConfigDocument, } from "./config-resolve.js";
69
69
  import { finalizeRecords, } from "./config-core/records.js";
@@ -704,6 +704,15 @@ function parseConfigFile(configPath) {
704
704
  // `lsp.enabled`.
705
705
  const projectScoped = new Set(PROJECT_SCOPED_LENS_FLAGS.map((spec) => spec.configKey));
706
706
  const globalScopeOnlyFlagKeys = LENS_FLAGS.map((spec) => spec.configKey).filter((configKey) => !projectScoped.has(configKey));
707
+ // Non-flag global-only keys sitting inside an otherwise project-recognized
708
+ // section (#3131) — `actionableWarnings.autoFix.maxFixes` is not a
709
+ // `LENS_FLAGS` entry (no CLI flag, not boolean), so it is invisible to the
710
+ // derivation above. `GLOBAL_ONLY_NON_FLAG_KEYS` is the single place that
711
+ // population is enumerated instead of a second hand-typed list here.
712
+ const globalScopeOnlyDottedKeys = [
713
+ ...globalScopeOnlyFlagKeys,
714
+ ...GLOBAL_ONLY_NON_FLAG_KEYS,
715
+ ];
707
716
  // The literal path is derived, not hand-typed (#2426 review round 3, S1):
708
717
  // under `PI_LENS_CONFIG_PATH` the canonical global config is NOT
709
718
  // `~/.pi-lens/config.json`, and a hardcoded string in this notice would name
@@ -735,9 +744,43 @@ function parseConfigFile(configPath) {
735
744
  if (honored.includes(key))
736
745
  continue;
737
746
  const dotted = `${namespace}.${key}`;
738
- warnUnhonoredKey(dotted, globalScopeOnlyFlagKeys.some((configKey) => configKey === dotted || configKey.startsWith(`${dotted}.`)));
747
+ warnUnhonoredKey(dotted, globalScopeOnlyDottedKeys.some((configKey) => configKey === dotted || configKey.startsWith(`${dotted}.`)));
739
748
  }
740
749
  }
750
+ // MIXED-SCOPE SECTIONS (#3112, #3131). A section can be project-scoped as a
751
+ // whole and still hold a global-only setting: `tools` carries the
752
+ // project-owned per-tool `enabled` leaves AND the global `tools.lazy`
753
+ // (`--no-lazy-tools`), the same split `lsp` has (#2426 review round 2, F3);
754
+ // `actionableWarnings` carries the project-owned `autoFix.enabled` AND the
755
+ // global-only `autoFix.maxFixes`, which is not even a flag (#3131). The
756
+ // top-level scan above sees only the SECTION name, so a global-only setting
757
+ // written inside a recognized project section was dropped with no signal at
758
+ // all — the thing `docs/configuration.md` promises never happens.
759
+ //
760
+ // WHICH sub-keys those are is DERIVED from the registry — `LENS_FLAGS` for
761
+ // flag keys, `GLOBAL_ONLY_NON_FLAG_KEYS` for the rest — never listed here,
762
+ // so there is no second scope table to drift from the first. A key whose
763
+ // SECTION is not recognized at project scope is skipped: the top-level
764
+ // scan already reported that whole section, and a second notice naming the
765
+ // same setting twice under two spellings is the duplicate-notice noise #2426
766
+ // review round 6 removed. Everything else under a recognized section is left
767
+ // to that section's own parser — `readToolConfig` reports an unknown tool
768
+ // name under its own code (`PILENS_CFG_0009`).
769
+ //
770
+ // An enumerated-honored-keys namespace (`lsp`) needs no skip here: the loop
771
+ // above reports the very same dotted key with the very same reason, and
772
+ // `warnIgnoredConfigOnce`'s latch collapses the two into one notice. A skip
773
+ // for it was written and then deleted — mutating it out changed no output.
774
+ for (const configKey of globalScopeOnlyDottedKeys) {
775
+ const dotIndex = configKey.indexOf(".");
776
+ if (dotIndex < 0)
777
+ continue;
778
+ if (!knownProjectKeys.has(configKey.slice(0, dotIndex)))
779
+ continue;
780
+ if (!hasFlagConfigPath(obj, configKey))
781
+ continue;
782
+ warnUnhonoredKey(configKey, true);
783
+ }
741
784
  return {
742
785
  config: {
743
786
  ...(toolConfig === undefined ? {} : { tools: toolConfig }),