@herbertgao/pi-extensions 2026.10.1 → 2026.10.3

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 (934) hide show
  1. package/README.md +14 -17
  2. package/THIRD_PARTY_NOTICES.md +2 -2
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  9. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  10. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  11. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  12. package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
  13. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
  14. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  15. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  16. package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
  17. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  18. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
  19. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  20. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
  21. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
  22. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
  23. package/node_modules/{remote-pi → @herbertgao/pi-agency}/LICENSE +1 -1
  24. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  25. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  26. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  34. package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
  35. package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
  38. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
  39. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
  40. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
  41. package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
  43. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
  44. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
  45. package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
  46. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
  47. package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
  48. package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
  49. package/node_modules/@tifan/pi-recap/package.json +1 -1
  50. package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
  51. package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
  52. package/node_modules/pi-antigravity/README.md +23 -2
  53. package/node_modules/pi-antigravity/package.json +5 -3
  54. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
  55. package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
  56. package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +86 -47
  58. package/node_modules/pi-antigravity/src/image/native.ts +69 -0
  59. package/node_modules/pi-antigravity/src/index.ts +52 -137
  60. package/node_modules/pi-antigravity/src/search/search.ts +130 -31
  61. package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
  62. package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
  63. package/node_modules/pi-antigravity/src/types/types.ts +1 -1
  64. package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
  65. package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
  66. package/node_modules/pi-lens/CHANGELOG.md +1159 -0
  67. package/node_modules/pi-lens/README.md +116 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  70. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  71. package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
  72. package/node_modules/pi-lens/dist/chunk-2UMYMRI3.js +118 -0
  73. package/node_modules/pi-lens/dist/chunk-34OCDWOS.js +48 -0
  74. package/node_modules/pi-lens/dist/chunk-3FERWMCH.js +144 -0
  75. package/node_modules/pi-lens/dist/chunk-3JL6IFD7.js +97 -0
  76. package/node_modules/pi-lens/dist/chunk-3JSEWUWI.js +327 -0
  77. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  78. package/node_modules/pi-lens/dist/chunk-3MRXIJ6I.js +79 -0
  79. package/node_modules/pi-lens/dist/chunk-3P4PF2BX.js +2250 -0
  80. package/node_modules/pi-lens/dist/chunk-3PNFWVHT.js +201 -0
  81. package/node_modules/pi-lens/dist/chunk-3TTPKMEF.js +162 -0
  82. package/node_modules/pi-lens/dist/chunk-5JHXUETW.js +326 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-64LU2OVH.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6B5I6ICN.js +44 -0
  86. package/node_modules/pi-lens/dist/chunk-6P54TMWL.js +274 -0
  87. package/node_modules/pi-lens/dist/chunk-6UYA642J.js +145 -0
  88. package/node_modules/pi-lens/dist/chunk-6XX5HCLK.js +398 -0
  89. package/node_modules/pi-lens/dist/chunk-7AXO3ZDF.js +30 -0
  90. package/node_modules/pi-lens/dist/chunk-7BX5DWSA.js +1907 -0
  91. package/node_modules/pi-lens/dist/chunk-7ELY2CSM.js +902 -0
  92. package/node_modules/pi-lens/dist/chunk-7M4T5N3V.js +38 -0
  93. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  94. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  95. package/node_modules/pi-lens/dist/chunk-AFZYMAIV.js +756 -0
  96. package/node_modules/pi-lens/dist/chunk-ALQRYH6O.js +112 -0
  97. package/node_modules/pi-lens/dist/chunk-AXKPQ6ZI.js +1610 -0
  98. package/node_modules/pi-lens/dist/chunk-BHWHXMQ2.js +62 -0
  99. package/node_modules/pi-lens/dist/chunk-BJF25SUG.js +22 -0
  100. package/node_modules/pi-lens/dist/chunk-BNKLYOOO.js +18 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-CBD2B5CP.js +599 -0
  103. package/node_modules/pi-lens/dist/chunk-CFYISHUW.js +42 -0
  104. package/node_modules/pi-lens/dist/chunk-CMU4RMJB.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-CULQYFLU.js +14 -0
  106. package/node_modules/pi-lens/dist/chunk-D65GOCJB.js +289 -0
  107. package/node_modules/pi-lens/dist/chunk-D6YF5HKX.js +59 -0
  108. package/node_modules/pi-lens/dist/chunk-DMS52BEP.js +342 -0
  109. package/node_modules/pi-lens/dist/chunk-ELVWJYRV.js +211 -0
  110. package/node_modules/pi-lens/dist/chunk-EUT3B22Y.js +27 -0
  111. package/node_modules/pi-lens/dist/chunk-F5VLZHUW.js +146 -0
  112. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  113. package/node_modules/pi-lens/dist/chunk-FGALODG3.js +55 -0
  114. package/node_modules/pi-lens/dist/chunk-G3RLJYA2.js +2358 -0
  115. package/node_modules/pi-lens/dist/chunk-GAYGGICG.js +32 -0
  116. package/node_modules/pi-lens/dist/chunk-H2W2AXD3.js +419 -0
  117. package/node_modules/pi-lens/dist/chunk-H5EV3VMF.js +114 -0
  118. package/node_modules/pi-lens/dist/chunk-HREZQCT3.js +106 -0
  119. package/node_modules/pi-lens/dist/chunk-HSIRMK2D.js +517 -0
  120. package/node_modules/pi-lens/dist/chunk-IMSQR4WO.js +184 -0
  121. package/node_modules/pi-lens/dist/chunk-IQZXOZ7S.js +4816 -0
  122. package/node_modules/pi-lens/dist/chunk-IR6XFA5P.js +37 -0
  123. package/node_modules/pi-lens/dist/chunk-IUKPRZER.js +78 -0
  124. package/node_modules/pi-lens/dist/chunk-IUR76K6A.js +262 -0
  125. package/node_modules/pi-lens/dist/chunk-J3FZR3YY.js +98 -0
  126. package/node_modules/pi-lens/dist/chunk-J6DFZDFA.js +562 -0
  127. package/node_modules/pi-lens/dist/chunk-J7WJZ6W7.js +50 -0
  128. package/node_modules/pi-lens/dist/chunk-JONGANYH.js +202 -0
  129. package/node_modules/pi-lens/dist/chunk-KJCWXUUS.js +425 -0
  130. package/node_modules/pi-lens/dist/chunk-KJN4IOEJ.js +17 -0
  131. package/node_modules/pi-lens/dist/chunk-KMGPUKFD.js +25 -0
  132. package/node_modules/pi-lens/dist/chunk-LD7WFTRX.js +4533 -0
  133. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  134. package/node_modules/pi-lens/dist/chunk-LNOMA3MP.js +169 -0
  135. package/node_modules/pi-lens/dist/chunk-MGIWSROG.js +2334 -0
  136. package/node_modules/pi-lens/dist/chunk-MQA2YBAH.js +308 -0
  137. package/node_modules/pi-lens/dist/chunk-MTDRLSI7.js +21720 -0
  138. package/node_modules/pi-lens/dist/chunk-N2IRDBZI.js +2355 -0
  139. package/node_modules/pi-lens/dist/chunk-N77WFUL7.js +50 -0
  140. package/node_modules/pi-lens/dist/chunk-N7YNJE5R.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  142. package/node_modules/pi-lens/dist/chunk-NFXP47RV.js +999 -0
  143. package/node_modules/pi-lens/dist/chunk-NJF2X5XF.js +45 -0
  144. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  145. package/node_modules/pi-lens/dist/chunk-O7HZX7CA.js +309 -0
  146. package/node_modules/pi-lens/dist/chunk-OC7LYAU5.js +708 -0
  147. package/node_modules/pi-lens/dist/chunk-OEX24ECI.js +348 -0
  148. package/node_modules/pi-lens/dist/chunk-OYOB3ZFG.js +78 -0
  149. package/node_modules/pi-lens/dist/chunk-OYTMZXRF.js +41 -0
  150. package/node_modules/pi-lens/dist/chunk-OZJINN4O.js +84 -0
  151. package/node_modules/pi-lens/dist/chunk-PCHDGUKT.js +23 -0
  152. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  153. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  154. package/node_modules/pi-lens/dist/chunk-RJFED73B.js +79 -0
  155. package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
  156. package/node_modules/pi-lens/dist/chunk-S6AMWKPC.js +189 -0
  157. package/node_modules/pi-lens/dist/chunk-T4QZ6FEH.js +308 -0
  158. package/node_modules/pi-lens/dist/chunk-T54VJRAD.js +5456 -0
  159. package/node_modules/pi-lens/dist/chunk-TA5L5KWQ.js +167 -0
  160. package/node_modules/pi-lens/dist/chunk-TQFBZVCG.js +27 -0
  161. package/node_modules/pi-lens/dist/chunk-U52SAGRM.js +62 -0
  162. package/node_modules/pi-lens/dist/chunk-UEAHAMEI.js +5814 -0
  163. package/node_modules/pi-lens/dist/chunk-UGSMCQ4D.js +327 -0
  164. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  165. package/node_modules/pi-lens/dist/chunk-USHG4DLH.js +459 -0
  166. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  167. package/node_modules/pi-lens/dist/chunk-V5MEBST3.js +4139 -0
  168. package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
  169. package/node_modules/pi-lens/dist/chunk-VPWKF3GN.js +87 -0
  170. package/node_modules/pi-lens/dist/chunk-VSMJK3ZJ.js +1250 -0
  171. package/node_modules/pi-lens/dist/chunk-VYKKMYVT.js +560 -0
  172. package/node_modules/pi-lens/dist/chunk-VZHJS6DR.js +442 -0
  173. package/node_modules/pi-lens/dist/chunk-WATC3IQR.js +213 -0
  174. package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
  175. package/node_modules/pi-lens/dist/chunk-WSEWKCXU.js +188 -0
  176. package/node_modules/pi-lens/dist/chunk-WW5BLB74.js +659 -0
  177. package/node_modules/pi-lens/dist/chunk-XGUL4SLQ.js +406 -0
  178. package/node_modules/pi-lens/dist/chunk-XHSCVSOL.js +170 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-YLN4K6WD.js +385 -0
  181. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  182. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  183. package/node_modules/pi-lens/dist/chunk-Z6BB4JLQ.js +478 -0
  184. package/node_modules/pi-lens/dist/chunk-Z7QQA2KT.js +27 -0
  185. package/node_modules/pi-lens/dist/chunk-ZETYB3XG.js +2626 -0
  186. package/node_modules/pi-lens/dist/chunk-ZMOMPX2Z.js +87 -0
  187. package/node_modules/pi-lens/dist/chunk-ZOEYVXSV.js +149 -0
  188. package/node_modules/pi-lens/dist/chunk-ZZUFZUQZ.js +12120 -0
  189. package/node_modules/pi-lens/dist/index.js +39712 -29876
  190. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
  191. package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
  192. package/node_modules/pi-lens/dist/mcp/server.js +22688 -1573
  193. package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
  194. package/node_modules/pi-lens/dist/probes/installer.js +135 -0
  195. package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
  196. package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
  197. package/node_modules/pi-lens/docs/agent-guide.md +13 -4
  198. package/node_modules/pi-lens/docs/agent-tools.md +42 -0
  199. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -4
  200. package/node_modules/pi-lens/docs/configuration.md +28 -2
  201. package/node_modules/pi-lens/docs/custom-rules.md +25 -2
  202. package/node_modules/pi-lens/docs/environment-variables.md +86 -8
  203. package/node_modules/pi-lens/docs/features.md +45 -5
  204. package/node_modules/pi-lens/docs/globalconfig.md +2 -1
  205. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  206. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +37 -4
  207. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  208. package/node_modules/pi-lens/docs/mcp.md +21 -5
  209. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  210. package/node_modules/pi-lens/docs/pi-lens-fixer.md +160 -88
  211. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  212. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +736 -0
  213. package/node_modules/pi-lens/docs/pi-lens-monitor.md +75 -7
  214. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  215. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +189 -48
  216. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  217. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  218. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  219. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  220. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  221. package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
  222. package/node_modules/pi-lens/docs/servercapabilities.md +56 -49
  223. package/node_modules/pi-lens/docs/settings.md +1 -0
  224. package/node_modules/pi-lens/docs/usage.md +5 -1
  225. package/node_modules/pi-lens/docs/word-index.md +6 -0
  226. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  227. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  228. package/node_modules/pi-lens/package.json +18 -6
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  237. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  238. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  239. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  240. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  241. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  242. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  243. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  244. package/node_modules/pi-web-access/README.md +10 -6
  245. package/node_modules/pi-web-access/ceramic.ts +219 -0
  246. package/node_modules/pi-web-access/curator-page.ts +3 -1
  247. package/node_modules/pi-web-access/curator-server.ts +1 -0
  248. package/node_modules/pi-web-access/dist/index.js +846 -588
  249. package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
  250. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  251. package/node_modules/pi-web-access/github-extract.ts +34 -2
  252. package/node_modules/pi-web-access/index.ts +72 -30
  253. package/node_modules/pi-web-access/openai-search.ts +3 -3
  254. package/node_modules/pi-web-access/package.json +2 -2
  255. package/node_modules/pi-web-access/page-query.ts +6 -9
  256. package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
  257. package/node_modules/pi-web-access/query-rewrite.ts +2 -2
  258. package/node_modules/pi-web-access/storage.ts +49 -0
  259. package/node_modules/pi-web-access/summary-review.ts +7 -6
  260. package/node_modules/pi-web-access/utils.ts +21 -6
  261. package/node_modules/pi-web-access/web-tool-core.ts +1 -0
  262. package/package.json +19 -19
  263. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
  264. package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
  265. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
  266. package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
  267. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  268. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  269. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  270. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  271. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  272. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  273. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  274. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  275. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  276. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  277. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  278. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  279. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  280. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  281. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  282. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  283. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  284. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  285. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  286. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  287. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  288. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  289. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  290. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  291. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  292. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  293. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  294. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  295. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  296. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  297. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  298. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  299. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  300. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  301. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  302. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  303. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  304. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  305. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  306. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  307. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  308. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  309. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  310. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  311. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  312. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  313. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  314. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  315. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  316. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  317. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  318. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  319. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  320. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  321. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  322. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  323. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  324. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  325. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  326. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  327. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  328. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  329. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  330. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  331. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  332. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  333. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  334. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  335. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  336. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  337. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  338. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  339. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  340. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  341. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  342. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  343. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  344. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  345. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  346. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  347. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  348. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  349. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  350. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  351. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  352. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  353. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  354. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  355. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  356. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  357. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  358. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  359. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  360. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  361. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  362. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  363. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  364. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  365. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  366. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  367. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  368. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  369. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  370. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  371. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  372. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  373. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  374. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  375. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  376. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  377. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  378. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  379. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  380. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  381. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  382. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  383. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  384. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  385. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  386. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  387. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  388. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  389. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  390. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  391. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  392. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  393. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  394. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  395. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  396. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  397. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  398. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  399. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  400. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  401. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  402. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  403. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  404. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  405. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  406. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  407. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  447. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  448. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  449. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  450. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  451. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  452. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  453. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  454. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  455. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  456. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  457. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  458. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  459. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  460. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  461. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  462. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  463. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  464. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  465. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  466. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  467. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  468. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  469. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  470. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  471. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  472. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  473. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  474. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  475. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  476. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  477. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  478. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  479. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  480. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  481. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  482. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  483. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  484. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  485. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  486. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  487. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  488. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  489. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  490. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  491. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  492. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  493. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  494. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  495. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  496. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  497. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  498. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  499. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  500. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  501. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  502. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  503. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  504. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  505. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  506. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  507. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  508. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  509. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  510. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  511. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  512. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  513. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  514. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  515. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  516. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  517. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  518. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  519. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  520. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  521. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  522. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  523. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  524. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  525. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  526. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  527. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  528. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  529. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  530. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  531. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  532. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  533. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  534. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  535. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  536. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  537. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  538. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  539. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  540. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  541. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  542. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  543. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  544. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  545. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  546. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  547. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  548. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  549. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  550. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  551. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  552. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  553. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  554. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  555. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  556. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  557. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  558. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  559. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  560. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  561. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  562. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  563. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  564. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  565. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  566. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  567. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  568. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  569. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  570. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  571. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  572. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  573. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  574. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  575. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  576. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  577. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  578. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  579. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  580. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  581. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  582. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  583. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  584. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  585. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  586. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  587. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  588. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  589. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  590. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  591. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  592. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  593. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  594. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  595. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  596. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  597. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  598. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  599. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  600. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  601. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  602. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  603. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  604. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  605. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  606. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  607. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  608. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  609. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  610. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  611. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  612. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  613. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  614. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  615. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  616. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  617. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  618. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  619. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  620. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  621. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  623. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  624. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  625. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  626. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  627. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  628. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  629. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  630. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  631. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  632. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  633. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  634. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  635. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  636. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  637. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  638. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  639. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  640. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  641. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  642. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  643. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  644. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  645. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  646. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  647. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  648. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  649. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  650. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  651. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  652. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  653. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  654. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  655. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  656. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  657. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  658. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  659. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  660. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  661. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  662. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  663. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  664. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  665. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  666. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  667. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  668. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  669. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  670. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  671. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  672. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  673. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  674. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  675. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  676. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  677. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  678. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  679. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  680. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  681. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  682. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  683. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  684. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  685. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  686. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  687. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  688. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  689. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  690. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  691. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  692. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  693. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  694. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  695. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  696. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  697. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  698. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  699. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  700. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  701. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  702. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  703. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  704. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  705. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  706. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  707. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  708. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  709. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  710. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  711. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  712. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  713. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  714. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  715. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  716. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  717. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  718. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  719. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  720. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  721. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  722. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  723. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  724. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  725. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  726. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  727. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  728. package/node_modules/pi-lens/dist/i18n.js +0 -69
  729. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  730. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  731. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  732. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  733. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  734. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  735. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  736. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  737. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  738. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  739. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  740. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  741. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  742. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  743. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  744. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  745. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  746. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  747. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  748. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
  749. package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
  750. package/node_modules/remote-pi/README.md +0 -675
  751. package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
  752. package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
  753. package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
  754. package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
  755. package/node_modules/remote-pi/dist/actions/registry.js +0 -58
  756. package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
  757. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
  758. package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
  759. package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
  760. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
  761. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
  762. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
  763. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
  764. package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
  765. package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
  766. package/node_modules/remote-pi/dist/config.d.ts +0 -62
  767. package/node_modules/remote-pi/dist/config.js +0 -115
  768. package/node_modules/remote-pi/dist/config.js.map +0 -1
  769. package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
  770. package/node_modules/remote-pi/dist/daemon/client.js +0 -131
  771. package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
  772. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
  773. package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
  774. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
  775. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
  776. package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
  777. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
  778. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
  779. package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
  780. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
  781. package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
  782. package/node_modules/remote-pi/dist/daemon/id.js +0 -36
  783. package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
  784. package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
  785. package/node_modules/remote-pi/dist/daemon/install.js +0 -547
  786. package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
  787. package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
  788. package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
  789. package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
  790. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
  791. package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
  792. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
  793. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
  794. package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
  795. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
  796. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
  797. package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
  798. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
  799. package/node_modules/remote-pi/dist/index.d.ts +0 -213
  800. package/node_modules/remote-pi/dist/index.js +0 -4996
  801. package/node_modules/remote-pi/dist/index.js.map +0 -1
  802. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
  803. package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
  804. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
  805. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
  806. package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
  807. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
  808. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
  809. package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
  810. package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
  811. package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
  812. package/node_modules/remote-pi/dist/mesh/client.js +0 -125
  813. package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
  814. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
  815. package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
  816. package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
  817. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
  818. package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
  819. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
  820. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
  821. package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
  822. package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
  823. package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
  824. package/node_modules/remote-pi/dist/mesh/types.js +0 -11
  825. package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
  826. package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
  827. package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
  828. package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
  829. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
  830. package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
  831. package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
  832. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
  833. package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
  834. package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
  835. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
  836. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
  837. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
  838. package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
  839. package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
  840. package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
  841. package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
  842. package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
  843. package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
  844. package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
  845. package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
  846. package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
  847. package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
  848. package/node_modules/remote-pi/dist/protocol/types.js +0 -2
  849. package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
  850. package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
  851. package/node_modules/remote-pi/dist/rooms.js +0 -57
  852. package/node_modules/remote-pi/dist/rooms.js.map +0 -1
  853. package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
  854. package/node_modules/remote-pi/dist/session/address.js +0 -58
  855. package/node_modules/remote-pi/dist/session/address.js.map +0 -1
  856. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
  857. package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
  858. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
  859. package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
  860. package/node_modules/remote-pi/dist/session/bridge.js +0 -130
  861. package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
  862. package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
  863. package/node_modules/remote-pi/dist/session/broker.js +0 -507
  864. package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
  865. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
  866. package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
  867. package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
  868. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
  869. package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
  870. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
  871. package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
  872. package/node_modules/remote-pi/dist/session/envelope.js +0 -117
  873. package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
  874. package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
  875. package/node_modules/remote-pi/dist/session/global_config.js +0 -73
  876. package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
  877. package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
  878. package/node_modules/remote-pi/dist/session/ipc.js +0 -22
  879. package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
  880. package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
  881. package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
  882. package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
  883. package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
  884. package/node_modules/remote-pi/dist/session/local_config.js +0 -157
  885. package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
  886. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
  887. package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
  888. package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
  889. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
  890. package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
  891. package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
  892. package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
  893. package/node_modules/remote-pi/dist/session/peer.js +0 -451
  894. package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
  895. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
  896. package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
  897. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
  898. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
  899. package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
  900. package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
  901. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
  902. package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
  903. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
  904. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
  905. package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
  906. package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
  907. package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
  908. package/node_modules/remote-pi/dist/session/tools.js +0 -252
  909. package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
  910. package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
  911. package/node_modules/remote-pi/dist/session/wizard.js +0 -20
  912. package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
  913. package/node_modules/remote-pi/dist/settings.d.ts +0 -15
  914. package/node_modules/remote-pi/dist/settings.js +0 -52
  915. package/node_modules/remote-pi/dist/settings.js.map +0 -1
  916. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
  917. package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
  918. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
  919. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
  920. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
  921. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
  922. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
  923. package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
  924. package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
  925. package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
  926. package/node_modules/remote-pi/dist/ui/footer.js +0 -34
  927. package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
  928. package/node_modules/remote-pi/docs/daemon.md +0 -289
  929. package/node_modules/remote-pi/package.json +0 -92
  930. package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
  931. package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
  932. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
  933. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
  934. package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
@@ -1,1808 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { stat } from "node:fs/promises";
3
- import * as path from "node:path";
4
- import { pathToFileURL } from "node:url";
5
- import { BoundedFifoMap } from "./bounded-cache.js";
6
- import { demotePastEofDiagnostics, } from "./diagnostic-line-freshness.js";
7
- import { visibleWidth } from "./deps/pi-tui.js";
8
- import { normalizeEphemeralMapKey, normalizeMapKey } from "./path-utils.js";
9
- import { fitLine } from "./tui-fit.js";
10
- import { WriteOrderingGuard } from "./write-ordering-guard.js";
11
- import { collectForwardImportMtimes } from "./blocker-freshness.js";
12
- import { normalizeMessage } from "./finding-identity.js";
13
- import { freshnessFromMtime } from "./freshness.js";
14
- import { PAST_EOF_STALE_MARKER } from "./diagnostic-line-freshness.js";
15
- import { STALE_LINE_MARKER } from "./stale-marker.js";
16
- import { anchorsForDiagnostic, applyDispositions, getDisposition, registerWidgetDispositionReconciler, strictAnchorSpan, strictAnchorSpansIn, } from "./diagnostic-dispositions.js";
17
- import { recordDegradationOnce } from "./degradation-ledger.js";
18
- /**
19
- * Canonical key for the `files` map (and `diagnosticsWriteGuard`) — #1020.
20
- *
21
- * The SAME file reaches this module under DIFFERENT path forms in one session:
22
- * forward-slash (`C:/…/x.ts`) from the LSP client + cascade fold via
23
- * `normalizeFilePath`, and backslash (`C:\…\x.ts`) from mode=full's reconcile
24
- * writing `result.filePath` and from `path.resolve`/event inputs on Windows.
25
- * Keyed raw, those coexisted as two entries: `mode=full` re-keyed on read and
26
- * the clean entry hid the stale one, but `mode=all`'s `formatAllMode` reads the
27
- * summaries verbatim and rendered the stale `blocking:1` as a 🔴 (#1020) — a
28
- * resolved state that replayed as still-broken on every `mode=all`.
29
- *
30
- * `normalizeEphemeralMapKey` (slash-fold + win32-lowercase, NO filesystem I/O)
31
- * is chosen over `normalizeMapKey`/`normalizeFilePath`, which call
32
- * `realpathSync.native()` — real disk I/O on EVERY diagnostic/runner/formatter
33
- * write, far too heavy for this hot path. The key only needs to be a stable
34
- * syntactic fold that collapses `\`↔`/` and Windows drive-letter case, which
35
- * this does; on-disk canonical casing is irrelevant for merely deduplicating a
36
- * process-local footer cache. The human-readable path is preserved separately
37
- * on the record's `filePath` (see `toDisplayPath`) for rendering/summaries.
38
- */
39
- function fileMapKey(filePath) {
40
- return normalizeEphemeralMapKey(filePath);
41
- }
42
- /**
43
- * A diagnostic is "blocking" when pi-lens classifies it as a hard stop
44
- * (`semantic === "blocking"`). Falls back to severity for sources that
45
- * don't set `semantic` (raw tsc/eslint diagnostics) so the red dot still
46
- * fires on traditional compile errors.
47
- *
48
- * #1561: exported so the blocker-retire decision in `tools/lsp-diagnostics.ts`
49
- * asks THIS predicate rather than growing a second severity rule that can drift
50
- * from what the footer counts.
51
- */
52
- export function isBlocking(d) {
53
- // #3158 round 2 F1: a finding the agent marked `false-positive`/`suppress` is
54
- // not a hard stop — it is an explicitly dismissed one. Before this line only
55
- // `countDiagnostics` knew that (it skips tagged entries), so the footer header
56
- // counted zero errors while every consumer that asks THIS predicate still
57
- // treated the row as blocking: `renderWidget` drew a red ● row for it,
58
- // `capStoredDiagnostics` hoisted it ahead of live findings (evicting one at the
59
- // display cap), and `getWidgetBlockingFilesForSweep`,
60
- // `markWidgetFileBlockersStale` and `reconcileStaleWidgetDependencyBlockers`
61
- // admitted it to the turn-end blocker sweep. The mismatch was latent while a
62
- // tagged row lived only between a mark and the next scan; retention makes it
63
- // durable, so the predicate has to answer for the tag.
64
- if (d.disposition)
65
- return false;
66
- // #1631: a dependency-drift-demoted finding is no longer a hard stop. The gate
67
- // sets `stale` rather than dropping the entry (#1419 demote-not-drop), so every
68
- // tally and render that asks "is this blocking?" must answer no once demoted.
69
- if (d.stale)
70
- return false;
71
- if (d.semantic === "blocking")
72
- return true;
73
- if (d.semantic == null && d.severity === "error")
74
- return true;
75
- return false;
76
- }
77
- // ── Module state ─────────────────────────────────────────────────────────────
78
- const files = new Map();
79
- let sessionLanguages = [];
80
- let requestRenderFn = null;
81
- /**
82
- * Guards `recordDiagnostics` writes against the same race class fixed for
83
- * `clients/lsp/client.ts` in #555: pi-lens allows concurrent pipeline runs
84
- * for the same file across different same-turn edits, so an older edit's
85
- * (slower) pipeline can finish its `recordDiagnostics` call AFTER a newer
86
- * edit's (faster) pipeline already recorded fresher diagnostics for that
87
- * path. Keyed by `filePath`, tokened by `writeIndex` (see
88
- * `clients/runtime-tool-result.ts:nextWriteIndex`).
89
- */
90
- const diagnosticsWriteGuard = new WriteOrderingGuard();
91
- /**
92
- * Runner completions also mutate the shared file record: they mark the
93
- * diagnostics snapshot pending. Keep that mutation in the same per-file order
94
- * as the final diagnostic replacement, or an older pipeline can set a newer
95
- * confirmed-clean record back to `(pending)` (#1198).
96
- */
97
- const runnerWriteGuard = new WriteOrderingGuard();
98
- const MAX_STORED_DIAGNOSTICS_PER_FILE = 12;
99
- /**
100
- * #3158 / AGENTS.md shape 9: the per-file bound on
101
- * {@link WidgetDiagnostic.suppressedRetained} rows. Retention is the only rule
102
- * that lets a record hold findings the latest scan did not report, so it is the
103
- * only axis of `allDiagnostics` that can grow while the live set shrinks — every
104
- * other row is replaced wholesale by the next write. Its own constant rather
105
- * than `MAX_STORED_DIAGNOSTICS_PER_FILE`: that one is the TUI's display cap
106
- * (`capStoredDiagnostics`) and the two would drift the moment either policy
107
- * moved. Twelve because the chip is a COUNT, not a list — past a dozen
108
- * suppressed findings on one unchanged file the exact total stops informing the
109
- * footer, and the truncation is stated once per file through the ledger rather
110
- * than dropped silently.
111
- */
112
- const MAX_RETAINED_SUPPRESSED_PER_FILE = 12;
113
- const MAX_INACTIVE_FILE_RECORDS = 1024;
114
- const ACTIVE_FILE_IDLE_MS = 30 * 60_000;
115
- export const MAX_LSP_SERVER_RECORDS = 128;
116
- const lspServers = new BoundedFifoMap(MAX_LSP_SERVER_RECORDS);
117
- // Pruning is a cold-size-boundary operation. Do not walk the whole file map
118
- // for every record in a large diagnostics reconciliation; the full-scan path
119
- // can legitimately create thousands of records in one synchronous batch.
120
- let nextInactivePruneSize = MAX_INACTIVE_FILE_RECORDS + 1;
121
- function pruneInactiveFileRecords(now = Date.now()) {
122
- if (files.size <= MAX_INACTIVE_FILE_RECORDS)
123
- return;
124
- const victims = [...files.entries()]
125
- .filter(([, rec]) => now - rec.touchedAt > ACTIVE_FILE_IDLE_MS && !hasLiveDiagnostic(rec))
126
- .sort(([, a], [, b]) => a.touchedAt - b.touchedAt);
127
- for (const [key] of victims) {
128
- if (files.size <= MAX_INACTIVE_FILE_RECORDS)
129
- break;
130
- files.delete(key);
131
- }
132
- }
133
- function maybePruneInactiveFileRecords() {
134
- if (files.size < nextInactivePruneSize)
135
- return;
136
- pruneInactiveFileRecords();
137
- // A live-heavy map may remain above the soft bound. Do not rescan it for
138
- // every subsequent file; the next lifecycle starts with a fresh state map.
139
- nextInactivePruneSize = Number.POSITIVE_INFINITY;
140
- }
141
- // ── Public API ────────────────────────────────────────────────────────────────
142
- export function setRenderCallback(fn) {
143
- requestRenderFn = fn;
144
- }
145
- /** Reconcile a mark through the same apply/count/commit path as dispatch. */
146
- export function reconcileWidgetDisposition(cwd, target, _disposition, content) {
147
- const current = getFileDiagnostics(target.filePath);
148
- if (!current) {
149
- recordDegradationOnce({
150
- kind: "widget-disposition-reconcile-fallback",
151
- subject: target.filePath,
152
- reason: "the marked finding's widget record is absent",
153
- });
154
- return;
155
- }
156
- let source = content ?? target.content;
157
- if (source === undefined) {
158
- try {
159
- source = readFileSync(target.filePath, "utf8");
160
- }
161
- catch {
162
- recordDegradationOnce({
163
- kind: "widget-disposition-reconcile-fallback",
164
- subject: target.filePath,
165
- reason: "the disposition anchor content could not be read",
166
- });
167
- source = "";
168
- }
169
- }
170
- const active = new Set(applyDispositions(current, cwd, target.filePath, source));
171
- const normalized = current.flatMap((diagnostic) => {
172
- const { strict, weak } = anchorsForDiagnostic(cwd, target.filePath, diagnostic, source);
173
- const entry = getDisposition(cwd, strict) ?? getDisposition(cwd, weak);
174
- if (!active.has(diagnostic) &&
175
- (entry?.disposition === "false-positive" ||
176
- entry?.disposition === "suppress")) {
177
- // #3183: stamp the strict anchor's own SPAN so the row's lifetime can
178
- // later ask the mark instead of the file's mtime. `source` is the content
179
- // the anchors above were derived from, so the span is the one the mark
180
- // itself binds to. Only the STRICT arm has a span — a weak `suppress`
181
- // mark hashes no line — and re-tagging always re-derives it, so a stale
182
- // span from an earlier tag can never survive into a new disposition.
183
- const { anchorSpan: _previousSpan, ...base } = diagnostic;
184
- const span = entry.disposition === "false-positive"
185
- ? strictAnchorSpan(source, diagnostic.line)
186
- : undefined;
187
- return [
188
- {
189
- ...base,
190
- disposition: entry.disposition,
191
- flagged: false,
192
- ...(span === undefined ? {} : { anchorSpan: span }),
193
- },
194
- ];
195
- }
196
- // #3158 retirement rule 3: a row the store holds ONLY because it was
197
- // suppressed (`suppressedRetained`) has no live scan behind it — the last
198
- // scan of this file did not report it. Once its mark stops suppressing it
199
- // (a strict `false-positive` anchor whose marked line changed), retire it
200
- // rather than re-arming it below as a finding the agent would read as
201
- // currently present.
202
- if (diagnostic.suppressedRetained)
203
- return [];
204
- const { disposition: _disposition, flagged: _flagged,
205
- // #3183: a row that is no longer disposition-tagged has no mark to
206
- // anchor, so its span goes with the tag — leaving it would exempt a LIVE
207
- // row from the sweep's mtime gate.
208
- anchorSpan: _anchorSpan, ...baseDiagnostic } = diagnostic;
209
- return entry?.disposition === "flagged"
210
- ? [{ ...baseDiagnostic, flagged: true }]
211
- : [baseDiagnostic];
212
- });
213
- const rec = getOrCreate(target.filePath);
214
- const writeIndex = admitWidgetDiagnosticsWrite(target.filePath);
215
- if (!diagnosticsWriteGuard.shouldWrite(fileMapKey(target.filePath), writeIndex))
216
- return;
217
- commitDiagnostics(rec, target.filePath, normalized, Date.now(), fileMapKey(target.filePath), false);
218
- }
219
- /** Reserve a widget write for a producer that has no runtime write index. */
220
- export function admitWidgetDiagnosticsWrite(filePath, writeIndex) {
221
- const key = fileMapKey(filePath);
222
- const token = writeIndex ?? diagnosticsWriteGuard.nextToken(key);
223
- diagnosticsWriteGuard.shouldWrite(key, token);
224
- runnerWriteGuard.shouldWrite(key, token);
225
- return token;
226
- }
227
- registerWidgetDispositionReconciler((cwd, target, disposition) => reconcileWidgetDisposition(cwd, target, disposition, target.content));
228
- /** Consume the host-side disposition event and refresh the real widget store. */
229
- export function wireWidgetDispositionSubscriber(args) {
230
- args.events?.on?.("pilens:diagnostic:disposition", (data) => {
231
- if (!data || typeof data !== "object")
232
- return;
233
- const payload = data;
234
- if (payload.source !== "pi-lens" || typeof payload.cwd !== "string")
235
- return;
236
- if (typeof payload.filePath !== "string" ||
237
- typeof payload.disposition !== "string")
238
- return;
239
- if (typeof payload.message !== "string")
240
- return;
241
- if (!["false-positive", "suppress", "defer", "flagged"].includes(payload.disposition))
242
- return;
243
- reconcileWidgetDisposition(payload.cwd, {
244
- cwd: payload.cwd,
245
- filePath: payload.filePath,
246
- message: typeof payload.message === "string" ? payload.message : "",
247
- ...(typeof payload.tool === "string" ? { tool: payload.tool } : {}),
248
- ...(typeof payload.rule === "string" ? { rule: payload.rule } : {}),
249
- ...(typeof payload.line === "number" ? { line: payload.line } : {}),
250
- }, payload.disposition);
251
- });
252
- }
253
- /**
254
- * #2275 review F1: files whose dependency-drift-demoted rows the footer has
255
- * ACTUALLY DRAWN since the last turn end — the delivery population
256
- * `runtime-turn.ts`'s cap step drains.
257
- *
258
- * The footer is not a broadcast of the whole store: `renderWidget` picks ONE
259
- * record (`withBlocking[0]`) and its first five qualifying entries, and it
260
- * may not be drawn at all (headless, or a turn with no repaint). Counting a
261
- * delivery per turn end therefore charged the cap for rows the agent never
262
- * saw, and retired them a delivery early. A Set (not a counter) is the right
263
- * shape: the widget repaints many times per turn, and all of those repaints
264
- * are the SAME one delivery.
265
- */
266
- const renderedDependencyDriftFiles = new Set();
267
- export function clearWidgetState() {
268
- files.clear();
269
- renderedDependencyDriftFiles.clear();
270
- lspServers.clear();
271
- sessionLanguages = [];
272
- requestRenderFn = null;
273
- diagnosticsWriteGuard.clear();
274
- runnerWriteGuard.clear();
275
- nextInactivePruneSize = MAX_INACTIVE_FILE_RECORDS + 1;
276
- }
277
- // v1 → v2 (#1186): per-entry `WidgetDiagnostic.observedAt`. v2 is a SUPERSET of
278
- // v1 (the field is additive/optional), so `importWidgetState` accepts either and
279
- // migrates a v1 record by inheriting each entry's `observedAt` from the record's
280
- // `touchedAt`. A v1 file must never be rejected (that would silently drop resume
281
- // diagnostics) nor crash.
282
- //
283
- // v2 → v3 (#1631 review F3): a dependency-drift `stale` demotion is a verdict
284
- // about THIS session's disk state — the next session starts with a fresh (and
285
- // possibly different) dependency graph, so a demotion from the last session
286
- // must not outlive it. `exportWidgetState` had been serializing `stale: true`
287
- // verbatim; on restore, a genuine type error stayed non-blocking across a
288
- // resume with the dependency pinned back into the past. Same shape as the
289
- // #1348 failed-formatter precedent one field over: `importWidgetState` strips
290
- // `stale` on every restored entry so a resumed session re-evaluates from
291
- // scratch instead of inheriting a possibly-stale-itself verdict.
292
- export const WIDGET_STATE_VERSION = 3;
293
- /**
294
- * Snapshot the per-file widget diagnostics for persistence (#190). Excludes
295
- * `lspServers` — those are process-bound (servers re-spawn fresh on the next
296
- * launch), so restoring their "ready" status would be misleading.
297
- */
298
- export function exportWidgetState() {
299
- return {
300
- version: WIDGET_STATE_VERSION,
301
- sessionLanguages: [...sessionLanguages],
302
- files: [...files.values()].map((rec) => ({
303
- filePath: rec.filePath,
304
- runners: [...rec.runners.entries()],
305
- formatters: [...rec.formatters.entries()],
306
- diagnostics: rec.diagnostics,
307
- allDiagnostics: rec.allDiagnostics,
308
- diagnosticCounts: rec.diagnosticCounts,
309
- hasFinalDiagnosticsSnapshot: rec.hasFinalDiagnosticsSnapshot,
310
- touchedAt: rec.touchedAt,
311
- })),
312
- };
313
- }
314
- /**
315
- * Restore a {@link PersistedWidgetState} snapshot (#190 resume rehydration).
316
- * Replaces the in-memory `files` map; ignores snapshots from a different
317
- * version. Triggers a re-render if a callback is registered.
318
- */
319
- /**
320
- * #1186 v1→v2 migration: stamp each entry that lacks a per-entry `observedAt`
321
- * with the record's `touchedAt` (the single stamp the whole record shared under
322
- * v1). Non-mutating; entries that already carry a stamp (a v2 record) pass
323
- * through untouched.
324
- *
325
- * #1631 review F3 (v2→v3): also strips `stale` from every entry. A dependency
326
- * drift demotion is scoped to the session that observed the drift; restoring it
327
- * verbatim would let a real blocking finding resume as permanently non-blocking
328
- * (#1348 precedent — see `WIDGET_STATE_VERSION`'s doc comment).
329
- */
330
- function migrateEntryStamps(entries, recordTouchedAt) {
331
- return (entries ?? []).map((d) => {
332
- // #2275: strip staleDeliveryCount and footerRetired alongside stale — a
333
- // delivery count (and the footer-hide it earned) without the demotion
334
- // it counts is meaningless, and a resumed session re-evaluates
335
- // dependency-drift from scratch (#1631 review F3) same as the `stale`
336
- // bit itself. A restored footer has shown the row zero times.
337
- // Fix-round 3 (#2275 review F2): `staleReason` left the round trip
338
- // unstripped, so a resumed row could carry `staleReason:
339
- // "dependency-drift"` with `stale` gone — `isDependencyDriftDemoted`
340
- // reads `stale === true`, so that combination reads as clean today, but
341
- // the stray reason is still a demotion label with no demotion behind
342
- // it, the same "meaningless without the bit it counts" shape as the
343
- // other three fields. Strip it alongside them.
344
- const { stale: _stale, staleDeliveryCount: _staleDeliveryCount, footerRetired: _footerRetired, staleReason: _staleReason, ...rest } = d;
345
- return rest.observedAt == null
346
- ? { ...rest, observedAt: recordTouchedAt }
347
- : rest;
348
- });
349
- }
350
- export function importWidgetState(state) {
351
- // Accept any known-or-older version and migrate (#1186): reject a missing
352
- // snapshot, a missing/non-numeric `version` (NaN/undefined/null — the
353
- // pre-#1186 guard `version !== WIDGET_STATE_VERSION` rejected these, and
354
- // loosening that would silently admit a malformed/foreign snapshot), or a
355
- // FUTURE version this build can't understand. Rejecting a v1
356
- // (pre-per-entry-stamp) file, by contrast, would silently drop all resume
357
- // diagnostics — so v1..current are accepted and migrated.
358
- if (!state ||
359
- typeof state.version !== "number" ||
360
- state.version < 1 ||
361
- state.version > WIDGET_STATE_VERSION) {
362
- return false;
363
- }
364
- files.clear();
365
- // A resumed session's writeIndex counter starts fresh (#190 rehydration is
366
- // process-bound like lspServers, see the export above) — any ordering
367
- // tokens tracked before the restore no longer correspond to anything, so
368
- // drop them rather than risk a legitimate post-resume write being read as
369
- // "superseded" against a stale token.
370
- diagnosticsWriteGuard.clear();
371
- runnerWriteGuard.clear();
372
- for (const f of state.files ?? []) {
373
- // Fold persisted keys through the same normalizer as live writes (#1020),
374
- // or a persisted forward-slash key stays split from a fresh backslash key
375
- // across a resumed session — a primary repro condition. Keep a readable
376
- // display path on the record.
377
- // #1186 migration: a v1 record's entries have no per-entry `observedAt`.
378
- // Inherit the record's `touchedAt` (a safe, over-conservative default —
379
- // the whole record shared that one stamp before), so the per-entry stale
380
- // gate has a concrete observation time and never treats `undefined` as
381
- // epoch-0 (which would drop every migrated entry on the first sweep).
382
- const recordTouchedAt = f.touchedAt ?? Date.now();
383
- // #1631 review V1: `diagnosticCounts` is DERIVED from `allDiagnostics`
384
- // everywhere else in this module (see the `countDiagnostics(rec.allDiagnostics)`
385
- // call sites) — it must be recomputed here too, from the just-migrated
386
- // (stale-stripped) entries, not restored verbatim from the snapshot. The
387
- // persisted counts were computed while a finding was still demoted
388
- // (`blocking: 0`); once F3 strips `stale` so `isBlocking` reports true
389
- // again, a verbatim count would still say `blocking: 0` for a record
390
- // whose entries ARE blocking — inverting the one-predicate invariant
391
- // `isBlocking` exists to hold. Every consumer (`getFileDiagnosticSummaries`,
392
- // the record-tier classifier, the footer) trusts `diagnosticCounts`, not a
393
- // live re-scan of the entries, so a derived count out of sync with its own
394
- // entries is silently wrong everywhere at once.
395
- const migratedAllDiagnostics = migrateEntryStamps(f.allDiagnostics, recordTouchedAt);
396
- files.set(fileMapKey(f.filePath), {
397
- filePath: f.filePath,
398
- runners: new Map(f.runners ?? []),
399
- // Failure entries do NOT survive a session restore (#1348 review):
400
- // a fmt-failed marker is live advice about THIS session's last
401
- // attempt; rehydrating one from a snapshot shows a stale failure the
402
- // current session never observed (and same-mtime fixes would never
403
- // clear it). Successes rehydrate as before.
404
- formatters: new Map((f.formatters ?? []).filter(([, outcome]) => outcome?.success !== false)),
405
- diagnostics: migrateEntryStamps(f.diagnostics, recordTouchedAt),
406
- allDiagnostics: migratedAllDiagnostics,
407
- diagnosticCounts: countDiagnostics(migratedAllDiagnostics),
408
- hasFinalDiagnosticsSnapshot: f.hasFinalDiagnosticsSnapshot ?? false,
409
- touchedAt: recordTouchedAt,
410
- });
411
- }
412
- pruneInactiveFileRecords();
413
- sessionLanguages = state.sessionLanguages ?? [];
414
- requestRenderFn?.();
415
- return true;
416
- }
417
- export function setSessionLanguages(langs) {
418
- sessionLanguages = langs;
419
- requestRender();
420
- }
421
- /** File-kinds detected in use this session (#170 staleness scope). */
422
- export function getSessionLanguages() {
423
- return [...sessionLanguages];
424
- }
425
- /**
426
- * Distinct serverIds with a failed spawn record (#170). Raw — the per-language
427
- * coverage check (a live sibling) and the in-use staleness filter live in
428
- * `selectLspStatus`, which joins this against the alive set and session kinds.
429
- */
430
- export function getFailedLspServerIds() {
431
- const ids = [];
432
- const seen = new Set();
433
- for (const rec of lspServers.values()) {
434
- if (rec.status !== "failed" || seen.has(rec.serverId))
435
- continue;
436
- seen.add(rec.serverId);
437
- ids.push(rec.serverId);
438
- }
439
- return ids;
440
- }
441
- export function recordFormatter(filePath, formatter, changed, success, outcome) {
442
- const rec = getOrCreate(filePath);
443
- rec.formatters.set(formatter, { changed, success, outcome });
444
- rec.touchedAt = Date.now();
445
- files.set(fileMapKey(filePath), rec);
446
- requestRender();
447
- }
448
- export function recordRunner(filePath, runnerId, status, diagnosticCount, durationMs, writeIndex) {
449
- const key = fileMapKey(filePath);
450
- if (!runnerWriteGuard.shouldWrite(key, writeIndex))
451
- return;
452
- // Advance the final-diagnostics guard too. A runner completion is part of
453
- // this pipeline's ordered write, even though its immediate effect is only to
454
- // mark the record pending. This prevents an older final replacement from
455
- // landing after a newer runner state (and vice versa).
456
- diagnosticsWriteGuard.shouldWrite(key, writeIndex);
457
- const rec = getOrCreate(filePath);
458
- rec.runners.set(runnerId, { status, count: diagnosticCount, durationMs });
459
- rec.hasFinalDiagnosticsSnapshot = false;
460
- rec.touchedAt = Date.now();
461
- files.set(fileMapKey(filePath), rec);
462
- requestRender();
463
- }
464
- /**
465
- * Collapse a (possibly multi-line) diagnostic message to a single line.
466
- * TS2769 / "no overload matches" and many compiler errors are multi-line;
467
- * embedded newlines/tabs would otherwise render across several widget rows
468
- * (and break the `L<line>: <message>` inline-blocker format), so flatten all
469
- * whitespace runs to a single space before storing.
470
- */
471
- function toSingleLineMessage(message) {
472
- return (message ?? "").replace(/\s+/g, " ").trim();
473
- }
474
- /** Build the OSC-8 target used by every stored diagnostic row. */
475
- export function widgetDiagnosticUri(filePath, line, column) {
476
- const base = pathToFileURL(filePath).href;
477
- return line != null
478
- ? `${base}#L${line}${column != null ? `:${column}` : ""}`
479
- : base;
480
- }
481
- export function recordDiagnostics(filePath, diagnostics, writeIndex,
482
- // #1093: when the truth was OBSERVED, not when it's being written. Defaults
483
- // to `Date.now()` for the per-edit/live path (observed now). A reconcile
484
- // replaying a CACHED view (e.g. the workspace-diagnostics cache-hit branch in
485
- // `tools/lsp-diagnostics.ts`) must pass the cache entry's own scan timestamp
486
- // here — otherwise a repeat "fresh check" that merely re-serves a stale
487
- // cached view keeps bumping `touchedAt` to now(), permanently disarming
488
- // `reconcileStaleWidgetFiles`'s `mtimeMs > touchedAt` gate so a resolved
489
- // finding renders forever (the #1092 touchedAt-re-arming defect).
490
- observedAt) {
491
- // Drop a write that's superseded by a later same-turn edit to this file
492
- // whose pipeline finished first (same race class as #555). No cache write,
493
- // no count/timestamp update, no render trigger — the recorded state must
494
- // stay exactly as the fresher write left it. `writeIndex` omitted (e.g.
495
- // the `clients/mcp/analyze.ts` on-demand call site, which has no per-edit
496
- // ordering token) always proceeds, same as version-less LSP servers in the
497
- // #555 guard.
498
- const key = fileMapKey(filePath);
499
- if (!diagnosticsWriteGuard.shouldWrite(key, writeIndex))
500
- return false;
501
- // Keep runner state ordered with the final diagnostic replacement. The
502
- // guards are deliberately advanced in both directions because either verb
503
- // may be the first completion from a pipeline.
504
- runnerWriteGuard.shouldWrite(key, writeIndex);
505
- // Resolve the observation time ONCE (#1186): every incoming entry is stamped
506
- // with it, and it also seeds the record's `touchedAt`. A fresh write (no
507
- // `observedAt`) is observed now.
508
- const observedTs = observedAt ?? Date.now();
509
- const rec = getOrCreate(filePath, key);
510
- commitDiagnostics(rec, filePath, normalizeDiagnostics(filePath, diagnostics, observedTs), observedTs, key);
511
- return true;
512
- }
513
- /** Map the raw diagnostic shape callers pass into stored {@link WidgetDiagnostic}s.
514
- * Every produced entry is stamped with `observedTs` (#1186) — the time THIS batch
515
- * of diagnostics was observed — so the per-entry stale gate can later drop just
516
- * the entries older than the file's mtime rather than the whole record. */
517
- function normalizeDiagnostics(filePath, diagnostics, observedTs) {
518
- return diagnostics.map((d) => {
519
- const rule = d.rule ?? d.id;
520
- return {
521
- severity: d.severity ?? "info",
522
- semantic: d.semantic,
523
- message: toSingleLineMessage(d.message),
524
- line: d.line,
525
- col: d.column,
526
- rule,
527
- tool: d.tool,
528
- uri: widgetDiagnosticUri(filePath, d.line, d.column),
529
- observedAt: observedTs,
530
- };
531
- });
532
- }
533
- /**
534
- * The identity a retained suppressed row is matched against a later scan's
535
- * entries by (#3158), so a scan that reports the finding AGAIN replaces the
536
- * retained row instead of standing beside it as a duplicate.
537
- *
538
- * These are the WEAK ANCHOR's identity parts — `computeWeakAnchor`
539
- * (`clients/diagnostic-dispositions.ts`) hashes `[tool, rule,
540
- * normalizeMessage(message)]` over the file — deliberately, because the weak
541
- * anchor is how the disposition store itself decides which findings a mark
542
- * covers. Matching at any finer granularity means the store can disagree with
543
- * the mark that created the row.
544
- *
545
- * Round 2 F2: `line` USED to be part of it, and that was the finer granularity
546
- * the paragraph above forbids. A writer that re-reports the marked finding one
547
- * line down — `clients/pipeline.ts`'s per-edit `recordDiagnostics`, or
548
- * `reconcileCascadeNeighborLspErrors`, neither of which runs
549
- * `reconcileStaleWidgetFiles` — left the retained row standing beside the live
550
- * one, so a single finding was counted live AND in `suppressed: N`.
551
- *
552
- * `computeWeakAnchor` itself is not called here: it needs `cwd` to build its
553
- * path component, which `commitDiagnostics` does not have and could only get by
554
- * widening `recordDiagnostics`, `reconcileScanDiagnostics` and
555
- * `reconcileCorrelatedScanDiagnostics` up through `tools/`. That component is
556
- * constant across one file record — both sides of this comparison are the same
557
- * file — so it would discriminate nothing. `normalizeMessage` is the shared
558
- * derivation from `clients/finding-identity.ts` that the anchor uses, imported
559
- * rather than re-spelled.
560
- *
561
- * #3183: this identity is OCCURRENCE-BLIND, and for a STRICT (`false-positive`)
562
- * row that is not enough on its own. Two findings of one rule with identical
563
- * messages on different lines collapse onto it, so a scan reporting only the
564
- * UNMARKED one matched the marked row and replaced it — the chip lost a mark
565
- * that was still applying. A collision is therefore resolved by the second half
566
- * of {@link WidgetDiagnostic.anchorSpan}'s contract, in `retainSuppressedRows`:
567
- * only a colliding incoming finding sitting ON the marked line is the same
568
- * finding. That test, not this one, is what keeps the round-2 F2 case above
569
- * closed — the marked line's TEXT moved, so its span still matches.
570
- */
571
- function retentionIdentity(d) {
572
- return JSON.stringify([
573
- d.tool ?? "",
574
- d.rule ?? "",
575
- normalizeMessage(d.message),
576
- ]);
577
- }
578
- /** `filePath`'s current content, or `undefined` when it cannot be read. */
579
- function readContentOrUndefined(filePath) {
580
- try {
581
- return readFileSync(filePath, "utf8");
582
- }
583
- catch {
584
- // Only reached once this module has established there IS a span-carrying row
585
- // to ask about, so the record is bounded by that population — and worth
586
- // making: with no content both callers fall back to the pre-#3183 rules (the
587
- // per-entry mtime gate, the coarse retention identity), which is the
588
- // conservative direction but can retire a mark that still stands.
589
- recordDegradationOnce({
590
- kind: "widget-mark-anchor-unreadable",
591
- subject: filePath,
592
- reason: "the marked file could not be read, so its suppressed rows fall back to the file-mtime retirement gate and the footer chip may under-count it",
593
- });
594
- return undefined;
595
- }
596
- }
597
- /**
598
- * {@link strictAnchorSpansIn} over `filePath`'s current content (#3183), or
599
- * `undefined` when it cannot be read.
600
- *
601
- * Synchronous in both callers, matching `applyCachedDispositions`
602
- * (`tools/lens-diagnostics.ts`) — the seam that asks the very same question on
603
- * the very same read path, with `statSync`/`readFileSync`. An async read here
604
- * would add two unbounded awaits to a module a registered hook handler imports
605
- * directly (`tests/config/hook-await-bounds.test.ts`) for no behavioural gain.
606
- */
607
- function readAnchorSpans(filePath) {
608
- const content = readContentOrUndefined(filePath);
609
- return content === undefined ? undefined : strictAnchorSpansIn(content);
610
- }
611
- /**
612
- * #3158: carry the record's disposition-tagged rows across a whole-replace
613
- * write when the incoming set no longer reports them, so a `false-positive` /
614
- * `suppress` mark keeps contributing to the footer's `suppressed: N` chip. See
615
- * {@link WidgetDiagnostic.suppressedRetained} for the full lifetime statement.
616
- *
617
- * Retained rows are APPENDED after the incoming ones: `capStoredDiagnostics`
618
- * fills the TUI's display list from the front, so a retained row never evicts a
619
- * live finding from the rendered set.
620
- *
621
- * The tagged-row check comes FIRST so a project with no disposition marks — the
622
- * overwhelmingly common case, and this runs on the per-edit write path — pays
623
- * one predicate per stored row and never builds the identity set.
624
- */
625
- function retainSuppressedRows(previous, incoming, filePath) {
626
- const tagged = previous.filter((d) => d.disposition !== undefined);
627
- if (tagged.length === 0)
628
- return incoming;
629
- const reported = new Map();
630
- for (const d of incoming) {
631
- const id = retentionIdentity(d);
632
- const bucket = reported.get(id);
633
- if (bucket)
634
- bucket.push(d);
635
- else
636
- reported.set(id, [d]);
637
- }
638
- // #3183: the ONE file read on this path, and only for the state the coarse
639
- // identity genuinely cannot resolve — a strict-anchored row whose identity an
640
- // incoming finding collides with. Every other write (no marks at all, a mark
641
- // with no collision, a weak `suppress` mark) stays zero-I/O, which is what
642
- // keeps this off the per-edit `recordDiagnostics` cost.
643
- const content = tagged.some((d) => d.anchorSpan !== undefined && reported.has(retentionIdentity(d)))
644
- ? readContentOrUndefined(filePath)
645
- : undefined;
646
- const spans = content === undefined ? undefined : strictAnchorSpansIn(content);
647
- const retained = tagged.filter((d) => {
648
- const collisions = reported.get(retentionIdentity(d));
649
- // Nothing re-reported this finding, so retention rule 2 has nothing to say.
650
- if (collisions === undefined)
651
- return true;
652
- // No span to ask (a weak `suppress` mark, or a row from an older build), or
653
- // the file is unreadable: fall back to the pre-#3183 coarse rule. The safe
654
- // direction is to UNDER-count, never to leave a phantom duplicate standing
655
- // beside a live finding.
656
- if (d.anchorSpan === undefined || spans === undefined)
657
- return false;
658
- // The mark stopped applying — its line is gone from the file — so the
659
- // colliding finding IS this one, re-reported now that the filter no longer
660
- // drops it (retirement rule 2, and the same verdict the sweep reaches).
661
- if (!spans.has(d.anchorSpan))
662
- return false;
663
- // The mark still applies. Only a collision sitting ON the marked line is
664
- // the same finding (its line number may have shifted under an edit — #3158
665
- // round 2 F2); any other is a DIFFERENT occurrence this strict anchor never
666
- // covered, and replacing the row with it loses a live mark (#3183).
667
- return !collisions.some((i) => strictAnchorSpan(content, i.line) === d.anchorSpan);
668
- });
669
- if (retained.length > MAX_RETAINED_SUPPRESSED_PER_FILE) {
670
- // Freshest observations win the cap. One record per FILE, never one per
671
- // dropped row (AGENTS.md "bounded observability"): the chip under-counts
672
- // by exactly `dropped` for this file until its content changes.
673
- retained.sort((a, b) => (b.observedAt ?? 0) - (a.observedAt ?? 0));
674
- recordDegradationOnce({
675
- kind: "widget-suppressed-retention-capped",
676
- subject: filePath,
677
- reason: `${retained.length - MAX_RETAINED_SUPPRESSED_PER_FILE} suppressed row(s) beyond the ${MAX_RETAINED_SUPPRESSED_PER_FILE}-row per-file retention cap are no longer counted by the footer chip`,
678
- });
679
- retained.length = MAX_RETAINED_SUPPRESSED_PER_FILE;
680
- }
681
- return [
682
- ...incoming,
683
- ...retained.map((d) => d.suppressedRetained ? d : { ...d, suppressedRetained: true }),
684
- ];
685
- }
686
- /** Store `normalized` as the record's complete diagnostic set: recompute counts,
687
- * cap the display list, stamp `touchedAt` (at `observedAt` when given, else now
688
- * — #1093), persist, and re-render. The caller decides what `normalized`
689
- * contains (a full replace, or a merge — see `reconcileCascadeNeighborLspErrors`). */
690
- function commitDiagnostics(rec, filePath, incoming, observedAt, key = fileMapKey(filePath),
691
- /**
692
- * #3158: apply the suppressed-row retention rule to this write. True for
693
- * every seam that hands in a fresh SCAN of the file (`recordDiagnostics` and
694
- * both reconciles), false for `reconcileWidgetDisposition`, which re-maps the
695
- * record's own current rows and is the seam that RETIRES a retained row —
696
- * re-adding what it just dropped would make retirement rule 3 inert.
697
- */
698
- retainSuppressed = true) {
699
- const normalized = retainSuppressed
700
- ? retainSuppressedRows(rec.allDiagnostics, incoming, filePath)
701
- : incoming;
702
- rec.diagnosticCounts = countDiagnostics(normalized);
703
- rec.diagnostics = capStoredDiagnostics(normalized);
704
- rec.allDiagnostics = normalized;
705
- rec.hasFinalDiagnosticsSnapshot = true;
706
- // Record-level `touchedAt` is the FRESHEST per-entry observation in the merged
707
- // set (#1186) — drives render recency and the empty-record stale gate. On a
708
- // merge (`reconcileCascadeNeighborLspErrors`) this is the newest of the
709
- // preserved + incoming entries, not the (possibly aging) incoming stamp, so a
710
- // record holding a fresh preserved entry doesn't sort/gate as stale. Empty set
711
- // falls back to the passed `observedAt` (or now).
712
- rec.touchedAt = freshestObservation(normalized, observedAt ?? Date.now());
713
- files.set(key, rec);
714
- requestRender();
715
- }
716
- export function classifyDiagnosticTier(d) {
717
- if (d.severity === "error")
718
- return "error";
719
- if (d.severity === "warning")
720
- return "warning";
721
- if (d.severity === "hint" || d.severity === "info")
722
- return "advisory";
723
- return undefined;
724
- }
725
- /** Recompute the {blocking, errors, warnings} tally for a diagnostic set.
726
- * `hint`/`info` tier findings are tallied separately — see
727
- * {@link classifyDiagnosticTier} and {@link countAdvisories} — and never
728
- * inflate `warnings` (#2414). */
729
- function countDiagnostics(diags) {
730
- let blocking = 0;
731
- let errors = 0;
732
- let warnings = 0;
733
- for (const diagnostic of diags) {
734
- if (diagnostic.disposition)
735
- continue;
736
- if (isBlocking(diagnostic))
737
- blocking++;
738
- // A past-EOF stale entry keeps its severity for display purposes but is
739
- // excluded from the error/warning tallies alongside blocking — its cited
740
- // coordinate is no longer trustworthy, same reasoning as `isBlocking`.
741
- // A dependency-drift demotion (#1631 criterion 3) is different: the
742
- // finding itself is still real evidence, only its BLOCKING authority is
743
- // revoked until re-confirmed, so it stays in the error/warning tally.
744
- if (diagnostic.stale &&
745
- (diagnostic.staleReason ?? "past-eof") === "past-eof")
746
- continue;
747
- const tier = classifyDiagnosticTier(diagnostic);
748
- if (tier === "error")
749
- errors++;
750
- else if (tier === "warning")
751
- warnings++;
752
- // tier === "advisory" (hint/info): intentionally excluded from the
753
- // footer's warning tally (#2414) — see `countAdvisories` for the
754
- // parallel count `getFileDiagnosticSummaries` exposes to
755
- // `lens_diagnostics` so a hint-only file still shows up as "has issues".
756
- }
757
- return { blocking, errors, warnings };
758
- }
759
- /** Count `hint`/`info` tier findings using the same past-EOF exclusion as
760
- * {@link countDiagnostics} (#2414). Kept as a standalone accessor rather than
761
- * a new `FileRecord.diagnosticCounts` field — the footer never renders this
762
- * number, only `getFileDiagnosticSummaries` (mode=all) needs it, to keep a
763
- * hint-only file from being silently dropped from a detailed listing. */
764
- function countAdvisories(diags) {
765
- let advisories = 0;
766
- for (const diagnostic of diags) {
767
- if (diagnostic.stale &&
768
- (diagnostic.staleReason ?? "past-eof") === "past-eof")
769
- continue;
770
- if (classifyDiagnosticTier(diagnostic) === "advisory")
771
- advisories++;
772
- }
773
- return advisories;
774
- }
775
- /**
776
- * Apply the #1641 past-EOF gate to `rec` in place: demote (never drop) any
777
- * stored diagnostic whose cited line exceeds the file's CURRENT on-disk line
778
- * count, then recompute the capped/full diagnostic lists and the counts so
779
- * every reader (the TUI render loop, `getFileDiagnosticSummaries`,
780
- * `getFileDiagnostics`) sees one consistent, already-gated record instead of
781
- * each re-deriving its own verdict. Cheap on the common case: one memoized
782
- * stat per record (see `getCachedLineCount`), a full recount only when the
783
- * file's mtime moved since the last check.
784
- *
785
- * No `resync` callback here deliberately: this gate runs on the TUI's render
786
- * path and the bus-publish/mark-tool read accessors, all of which fire far
787
- * more often than an agent's own edit/tool cadence. Triggering a document
788
- * resync from every one of those reads would storm the LSP with didOpen calls
789
- * for a file whose drift hasn't yet been fixed. `tools/lens-diagnostics.ts`'s
790
- * `formatAllMode` — an explicit, agent-invoked tool call — is the resync
791
- * trigger point (#1641 criterion 2); this gate still demotes/logs on its own.
792
- *
793
- * The gate RE-DERIVES every entry's `stale` flag on every call — it is never
794
- * a one-way latch. A transient shrink that later restores (truncate-then-
795
- * write, a formatter pass, a checkout) un-demotes on its own next read, so
796
- * this always persists the freshly-derived array back into the record, not
797
- * only when `demotedCount` (which counts RISING edges only, for telemetry)
798
- * is nonzero.
799
- */
800
- function applyPastEofGate(rec, lineCountCache) {
801
- if (rec.allDiagnostics.length === 0)
802
- return;
803
- const { diagnostics } = demotePastEofDiagnostics({
804
- store: "widget-state",
805
- cwd: process.cwd(),
806
- filePath: rec.filePath,
807
- diagnostics: rec.allDiagnostics,
808
- lineCountCache,
809
- });
810
- rec.allDiagnostics = diagnostics;
811
- rec.diagnostics = capStoredDiagnostics(diagnostics);
812
- rec.diagnosticCounts = countDiagnostics(diagnostics);
813
- }
814
- /** The newest per-entry `observedAt` in `diags`, or `fallback` when empty (or no
815
- * entry carries a stamp). Used as the record-level `touchedAt` (#1186). */
816
- function freshestObservation(diags, fallback) {
817
- let newest;
818
- for (const d of diags) {
819
- if (d.observedAt != null &&
820
- (newest === undefined || d.observedAt > newest)) {
821
- newest = d.observedAt;
822
- }
823
- }
824
- return newest ?? fallback;
825
- }
826
- /**
827
- * A stored diagnostic that came from a language SERVER (tsserver, pyright, …)
828
- * AND is an error/blocking finding. `convertLspDiagnostics` tags every
829
- * language-server diagnostic with `tool: "lsp"` and `retagAuxiliaryDiagnostics`
830
- * re-tags auxiliary-LSP findings (opengrep/ast-grep/zizmor/typos) to their real
831
- * tool id, so `tool === "lsp"` uniquely identifies a genuine language-server
832
- * entry. Used by the cascade merge below to decide which existing entries an
833
- * errors-only LSP re-check is entitled to replace.
834
- */
835
- function isLspErrorEntry(d) {
836
- return (d.tool === "lsp" && (d.semantic === "blocking" || d.severity === "error"));
837
- }
838
- /**
839
- * Reconcile a CONFIRMED cascade neighbor re-check (#1093) into the footer,
840
- * MERGING rather than whole-replacing (unlike `recordDiagnostics`).
841
- *
842
- * The cascade only re-checks a neighbor through its LANGUAGE SERVER, and only
843
- * for ERRORS (`severity === 1`; see `clients/dispatch/integration.ts`). A plain
844
- * full-replace would therefore erase the neighbor's live findings from OTHER
845
- * sources that the cascade never re-examined — biome/ruff/ast-grep runner
846
- * findings, and even the language server's own WARNINGS — turning a
847
- * cross-file-error re-check into a silent false-clean for everything else
848
- * (#533). So we replace ONLY the existing LSP-error entries
849
- * (`isLspErrorEntry`) with the cascade's fresh LSP errors and preserve
850
- * everything else verbatim. A stale LSP warning or biome finding therefore
851
- * survives an errors-only cascade — correct, because this check never looked at
852
- * it; it self-corrects on the next per-edit dispatch or a `lens_diagnostics`
853
- * scan (which DO re-examine every source).
854
- *
855
- * Only ever call this for a CONFIRMED result (a valid passive snapshot or a
856
- * completed, NON-inconclusive active touch — #571). `writeIndex` and
857
- * `observedAt` behave exactly as in `recordDiagnostics`.
858
- */
859
- export function reconcileCascadeNeighborLspErrors(filePath, lspErrorDiagnostics, writeIndex, observedAt) {
860
- const key = fileMapKey(filePath);
861
- if (!diagnosticsWriteGuard.shouldWrite(key, writeIndex))
862
- return;
863
- runnerWriteGuard.shouldWrite(key, writeIndex);
864
- // #1186: the INCOMING LSP errors are stamped at THIS observation time
865
- // (`observedAt`, e.g. an aging passive snapshot's `entry.ts`, or now for a
866
- // fresh active touch). The PRESERVED entries keep their OWN prior per-entry
867
- // `observedAt` — a fresh per-edit finding preserved through this errors-only
868
- // merge is NOT re-aged to the incoming stamp. That per-entry split is exactly
869
- // what lets `reconcileStaleWidgetFiles` drop the stale incoming entry while
870
- // keeping the newer preserved one, instead of dropping the whole record.
871
- const observedTs = observedAt ?? Date.now();
872
- const rec = getOrCreate(filePath);
873
- const incoming = normalizeDiagnostics(filePath, lspErrorDiagnostics, observedTs);
874
- const preserved = rec.allDiagnostics.filter((d) => !isLspErrorEntry(d));
875
- commitDiagnostics(rec, filePath, [...incoming, ...preserved], observedTs);
876
- }
877
- /**
878
- * Reconcile a diagnostics result obtained OUTSIDE the per-edit dispatch
879
- * pipeline — a `lens_diagnostics` mode=full workspace scan, or a standalone
880
- * `lsp_diagnostics` on-demand check — into the footer cache (#571).
881
- *
882
- * `recordDiagnostics` is otherwise only reachable from `pipeline.ts`'s
883
- * per-edit dispatch, so a file that becomes stale/fresh purely because of a
884
- * change to some OTHER file it depends on (and is never itself re-edited
885
- * through pi-lens) has no path to correct the footer — a full scan proves
886
- * the fresher truth but had nowhere to put it. This is that path, shared by
887
- * both call sites so there's exactly one place that decides whether a scan
888
- * result is trustworthy enough to write.
889
- *
890
- * `confirmed` MUST be false for any result the caller can't vouch for — a
891
- * timed-out/inconclusive LSP check (see #570) must never present as
892
- * "confirmed clean" in the footer, and must not clobber a real prior
893
- * confirmed-dirty entry either. Non-confirmed results are silently skipped,
894
- * leaving whatever the footer already had (stale-but-real beats
895
- * fresh-but-fabricated).
896
- *
897
- * `writeIndex` should be a freshly-drawn token from the same monotonic
898
- * source the per-edit pipeline uses (`RuntimeCoordinator.nextWriteIndex()`)
899
- * so `recordDiagnostics`'s existing `WriteOrderingGuard` (#555) can tell a
900
- * scan-originated write apart from a concurrent, genuinely newer per-edit
901
- * write for the same file — an omitted `writeIndex` always proceeds (same
902
- * version-less fallback `recordDiagnostics` already documents), which is
903
- * only safe for callers with no ordering token to give (e.g. tests).
904
- *
905
- * `observedAt` (#1093) is the wall-clock time the diagnostics were actually
906
- * OBSERVED — pass it whenever the reconciled result is a replay of an older
907
- * CACHED observation (the workspace-diagnostics cache-hit branch), so
908
- * `touchedAt` records when the truth was seen, not when it was written. Omit
909
- * it for genuinely fresh observations (a just-completed touch/scan), which are
910
- * observed now.
911
- */
912
- export function reconcileScanDiagnostics(filePath, diagnostics, confirmed, writeIndex, observedAt) {
913
- if (!confirmed)
914
- return false;
915
- return recordDiagnostics(filePath, diagnostics, writeIndex, observedAt);
916
- }
917
- /**
918
- * Commit the already-correlated result of a full diagnostics scan.
919
- *
920
- * Unlike {@link reconcileScanDiagnostics}, this seam accepts widget diagnostics
921
- * that have already been merged across producing lanes. `lens_diagnostics`
922
- * uses it after correlating the confirmed LSP sweep, the cheap project scan,
923
- * and retained widget rows. This prevents a broken LSP lane from hiding
924
- * independently-produced `ast-grep-napi` findings in the widget count (#1888).
925
- * Existing per-entry observation times survive the merge; newly-added project
926
- * rows inherit the scan's observation time.
927
- */
928
- export function reconcileCorrelatedScanDiagnostics(filePath, diagnostics, writeIndex, observedAt) {
929
- const key = fileMapKey(filePath);
930
- if (!diagnosticsWriteGuard.shouldWrite(key, writeIndex))
931
- return;
932
- runnerWriteGuard.shouldWrite(key, writeIndex);
933
- const observedTs = observedAt ?? Date.now();
934
- const correlated = diagnostics.map((diagnostic) => ({
935
- ...diagnostic,
936
- observedAt: diagnostic.observedAt ?? observedTs,
937
- }));
938
- commitDiagnostics(getOrCreate(filePath, key), filePath, correlated, observedTs, key);
939
- }
940
- /**
941
- * Drop widget entries whose file changed on disk after pi-lens last recorded
942
- * them (`mtimeMs > touchedAt` → the recorded diagnostics predate the current
943
- * content → stale) or that no longer exist. Keeps `lens_diagnostics` from
944
- * surfacing findings the agent already fixed (or that an external edit
945
- * invalidated). Async with concurrent stats — call on read, never on the typing
946
- * path. Returns how many entries were dropped (so callers can tell the agent
947
- * those files changed and need a `mode=full` rescan rather than reading as
948
- * clean).
949
- */
950
- export async function reconcileStaleWidgetFiles() {
951
- const entries = [...files.entries()];
952
- const verdicts = await Promise.all(
953
- // `mapKey` is the normalized `files` key (used for deletion); stat the
954
- // record's real display path, not the lowercased key (#1020).
955
- entries.map(async ([mapKey, rec]) => {
956
- let mtimeMs;
957
- try {
958
- mtimeMs = (await stat(rec.filePath)).mtimeMs;
959
- }
960
- catch {
961
- return { mapKey, action: "drop" }; // deleted / unreadable → drop
962
- }
963
- // A clean record (no findings) has no per-entry stamps to consult —
964
- // gate it on the record's own `touchedAt` exactly as before, so a ✓
965
- // entry for a file that changed on disk still drops.
966
- //
967
- // #1631 review F11 follow-up: a THIRD site hitting the same Windows
968
- // mtime-vs-`Date.now()` skew F2 named for `blocker-freshness.ts` and
969
- // `reconcileStaleWidgetDependencyBlockers` — a file's mtime can lead
970
- // `Date.now()` by up to ~11.4ms measured (#1491/#1498 precedent), so a
971
- // record touched immediately after its own write dropped here at +1ms
972
- // with zero real drift. Found investigating F11: `lens-diagnostics-
973
- // mode-all-freshness.test.ts`'s "control" case (and its sibling) failed
974
- // under this gate, not from cross-test state. Same tolerance, same
975
- // constant, for the same reason.
976
- if (rec.allDiagnostics.length === 0) {
977
- return freshnessFromMtime({ mtimeMs, referenceMs: rec.touchedAt })
978
- .verdict === "stale"
979
- ? { mapKey, action: "drop" }
980
- : { mapKey, action: "keep" };
981
- }
982
- // #1186 per-ENTRY gate: drop only the entries observed BEFORE the file's
983
- // current mtime; keep the rest. A merged record can hold a fresh
984
- // preserved entry beside an entry replayed from an aging snapshot, so a
985
- // per-RECORD gate over-cleared the whole record (the residual documented
986
- // at dispatch/integration.ts). A missing per-entry stamp (a migrated
987
- // pre-#1186 record) inherits the record's `touchedAt`. Tolerance matches
988
- // the Windows host-clock skew rationale above.
989
- const mtimeStale = (d) => freshnessFromMtime({
990
- mtimeMs,
991
- referenceMs: d.observedAt ?? rec.touchedAt,
992
- }).verdict === "stale";
993
- // #3183: a disposition-tagged row carrying a strict anchor span is gated
994
- // on its MARK, not on the file's mtime (retirement rule 1 — see
995
- // `WidgetDiagnostic.suppressedRetained`): it survives while the marked
996
- // LINE is still somewhere in the file, and is retired when it is not.
997
- // The content read is gated on a span-carrying row the mtime gate WOULD
998
- // have dropped — the only state where reading can change an outcome — so
999
- // the debounced render sweep stays off the disk both for a file with no
1000
- // marks and for a marked file nothing has touched.
1001
- const spans = rec.allDiagnostics.some((d) => d.anchorSpan !== undefined && mtimeStale(d))
1002
- ? readAnchorSpans(rec.filePath)
1003
- : undefined;
1004
- const survivors = rec.allDiagnostics.filter((d) => d.anchorSpan !== undefined && spans !== undefined
1005
- ? spans.has(d.anchorSpan)
1006
- : !mtimeStale(d));
1007
- if (survivors.length === rec.allDiagnostics.length) {
1008
- return { mapKey, action: "keep" }; // nothing stale
1009
- }
1010
- if (survivors.length === 0) {
1011
- return { mapKey, action: "drop" }; // every entry stale → drop record
1012
- }
1013
- return { mapKey, action: "prune", survivors };
1014
- }));
1015
- let dropped = 0;
1016
- for (const v of verdicts) {
1017
- if (v.action === "keep")
1018
- continue;
1019
- if (v.action === "drop") {
1020
- files.delete(v.mapKey);
1021
- dropped += 1;
1022
- continue;
1023
- }
1024
- // prune: the file changed and shed its stale entries but retains fresher
1025
- // ones — keep the record, recompute counts/cap from the survivors, and
1026
- // still count it as a changed file so the agent is told to rescan.
1027
- const rec = files.get(v.mapKey);
1028
- if (rec) {
1029
- rec.allDiagnostics = v.survivors;
1030
- rec.diagnostics = capStoredDiagnostics(v.survivors);
1031
- rec.diagnosticCounts = countDiagnostics(v.survivors);
1032
- }
1033
- dropped += 1;
1034
- }
1035
- if (dropped > 0)
1036
- requestRenderFn?.();
1037
- return dropped;
1038
- }
1039
- export async function reconcileStaleWidgetDependencyBlockers(cwd, turnIndex) {
1040
- let demoted = 0;
1041
- let truncatedImports = 0;
1042
- for (const [, rec] of files.entries()) {
1043
- // #1631 review F4: narrow demotion to LSP-sourced findings. The import-closure
1044
- // drift check only knows how to reason about IMPORT graphs — an ast-grep
1045
- // hardcoded-secret or a govulncheck CVE finding doesn't stop being true because
1046
- // a file this diagnostic's file imports changed; only a language-server verdict
1047
- // (`tool === "lsp"`, the same predicate `isLspErrorEntry` above uses) is actually
1048
- // invalidated by that shape of drift. Documented trade: a non-LSP blocking
1049
- // finding on a file with drifted imports stays fully authoritative until its own
1050
- // content changes or it is explicitly re-run.
1051
- if (!rec.allDiagnostics.some((d) => isBlocking(d) && d.tool === "lsp"))
1052
- continue;
1053
- let importMtimes = [];
1054
- try {
1055
- const result = await collectForwardImportMtimes(cwd, rec.filePath, undefined, turnIndex);
1056
- importMtimes = result.mtimes;
1057
- if (result.truncated)
1058
- truncatedImports += 1;
1059
- }
1060
- catch {
1061
- importMtimes = [];
1062
- }
1063
- if (importMtimes.length === 0)
1064
- continue;
1065
- let changed = false;
1066
- for (const d of rec.allDiagnostics) {
1067
- if (!isBlocking(d) || d.tool !== "lsp")
1068
- continue;
1069
- const baseline = d.observedAt ?? rec.touchedAt;
1070
- // +50ms tolerance (#1631 review F2): a whole-millisecond `Date.now()`
1071
- // baseline vs. sub-millisecond mtime precision only needs +1ms, but on
1072
- // Windows a file's mtime can LEAD `Date.now()` by up to ~11.4ms (measured
1073
- // across 200 writes; #1491/#1498 precedent for the same host-clock skew).
1074
- // +1ms produced 42 false demotions in 50 runs on Windows; +50ms clears the
1075
- // measured skew while staying far below the gap between real edits.
1076
- if (importMtimes.some((im) => freshnessFromMtime({
1077
- mtimeMs: im.mtimeMs,
1078
- referenceMs: baseline,
1079
- }).verdict === "stale")) {
1080
- d.stale = true;
1081
- d.staleReason = "dependency-drift";
1082
- changed = true;
1083
- demoted += 1;
1084
- }
1085
- }
1086
- if (changed) {
1087
- rec.diagnosticCounts = countDiagnostics(rec.allDiagnostics);
1088
- rec.diagnostics = capStoredDiagnostics(rec.allDiagnostics);
1089
- }
1090
- }
1091
- if (demoted > 0)
1092
- requestRenderFn?.();
1093
- return { demoted, truncatedImports };
1094
- }
1095
- /**
1096
- * #1790: sweep-population feed for `sweepInlineBlockerFreshness` (the turn-end
1097
- * gate in `clients/blocker-freshness.ts`). That sweep's population was built
1098
- * solely from `RuntimeCoordinator`'s inline-blocker map — a live-dispatch-only
1099
- * store. A workspace-diagnostics CACHE HIT never touches that map; it calls
1100
- * `reconcileScanDiagnostics` straight into `files` here (the cache-serve branch
1101
- * in `tools/lsp-diagnostics.ts`), so a cache-served blocking row could render in
1102
- * the widget while the turn-end sweep's `total` count never saw it (the live
1103
- * 2026-08-20 dogfood: `total:1 kept:1` against five stale cache-served blocking
1104
- * rows on screen).
1105
- *
1106
- * One entry per file with at least one CURRENTLY blocking (non-stale),
1107
- * LSP-sourced diagnostic — the same provenance narrowing
1108
- * `reconcileStaleWidgetDependencyBlockers` already applies, so every row this
1109
- * feeds into the sweep is one its `isLspSourced` gate would keep anyway.
1110
- * `recordedAtMs` is the EARLIEST `observedAt` among them: the conservative
1111
- * baseline, since using the latest could hide drift that predates an earlier
1112
- * diagnostic's own observation.
1113
- */
1114
- export function getWidgetBlockingFilesForSweep() {
1115
- const out = [];
1116
- for (const rec of files.values()) {
1117
- let earliest;
1118
- for (const d of rec.allDiagnostics) {
1119
- if (!isBlocking(d) || d.tool !== "lsp")
1120
- continue;
1121
- const observedAt = d.observedAt ?? rec.touchedAt;
1122
- if (earliest === undefined || observedAt < earliest)
1123
- earliest = observedAt;
1124
- }
1125
- if (earliest !== undefined)
1126
- out.push({ filePath: rec.filePath, recordedAtMs: earliest });
1127
- }
1128
- return out;
1129
- }
1130
- /**
1131
- * #1790: demote every currently-blocking, LSP-sourced diagnostic for `filePath`
1132
- * to stale (#1419 demote-not-drop). Called by the turn-end sweep once its own
1133
- * drift check (`detectDrift`/`collectForwardImportMtimes`, shared with the
1134
- * inline-blocker branch) independently confirms drift for a file that reached
1135
- * the sweep's population through `getWidgetBlockingFilesForSweep` rather than an
1136
- * inline blocker. Same store and same write `reconcileStaleWidgetDependencyBlockers`
1137
- * already performs per-diagnostic — this is the single-file entry point the
1138
- * sweep drives once ITS check (not a second implementation of the check) says
1139
- * to. Returns true iff something changed.
1140
- *
1141
- * #1790 review F4 (latent): unlike `reconcileStaleWidgetDependencyBlockers`, which
1142
- * checks drift per-diagnostic against EACH diagnostic's own `observedAt`, this
1143
- * demotes every currently-blocking LSP diagnostic for the file against the ONE
1144
- * file-level baseline `getWidgetBlockingFilesForSweep` computed (the earliest
1145
- * `observedAt` among them) — because the sweep's `detectDrift` contract is
1146
- * file-level, not per-diagnostic. That is safe ONLY as long as every LSP
1147
- * diagnostic on a `FileRecord` shares (or is more conservative than) that
1148
- * earliest stamp, which holds today because every writer that populates
1149
- * `allDiagnostics` (`recordDiagnostics`, `reconcileCascadeNeighborLspErrors`)
1150
- * replaces the file's LSP errors wholesale with one `observedAt` per write. If a
1151
- * future writer ever merges LSP diagnostics with genuinely MIXED per-entry
1152
- * stamps on one record, this function would over-demote entries observed after
1153
- * the earliest one drifted — re-derive per-diagnostic (mirroring
1154
- * `reconcileStaleWidgetDependencyBlockers`) rather than assume the single
1155
- * baseline still holds.
1156
- */
1157
- export function markWidgetFileBlockersStale(filePath, reason) {
1158
- const rec = files.get(fileMapKey(filePath));
1159
- if (!rec)
1160
- return false;
1161
- let changed = false;
1162
- for (const d of rec.allDiagnostics) {
1163
- if (!isBlocking(d) || d.tool !== "lsp")
1164
- continue;
1165
- d.stale = true;
1166
- d.staleReason = reason;
1167
- changed = true;
1168
- }
1169
- if (changed) {
1170
- rec.diagnosticCounts = countDiagnostics(rec.allDiagnostics);
1171
- rec.diagnostics = capStoredDiagnostics(rec.allDiagnostics);
1172
- requestRenderFn?.();
1173
- }
1174
- return changed;
1175
- }
1176
- /**
1177
- * #2275: a dependency-drift demotion the footer is still allowed to draw —
1178
- * the one predicate the render marker, the delivery counter and the
1179
- * footer-hide all ask, so "which rows does this cap govern" cannot drift
1180
- * apart between them.
1181
- */
1182
- function isDependencyDriftDemoted(d) {
1183
- return (d.stale === true &&
1184
- d.staleReason === "dependency-drift" &&
1185
- d.footerRetired !== true);
1186
- }
1187
- /**
1188
- * #2275 review F1: take (and clear) the files whose dependency-drift
1189
- * demotions the footer actually RENDERED since the last call — the delivery
1190
- * population `runtime-turn.ts`'s per-turn cap step walks at turn end.
1191
- *
1192
- * Deliberately NOT a store-wide query over everything currently demoted.
1193
- * `renderWidget` serves one record per pass, so a store-wide walk charged a
1194
- * delivery to every demoted file every turn — including files the footer had
1195
- * never shown and one demoted moments earlier by the same turn end, which
1196
- * also made the ledger's "capped after N deliveries" overstate N by one.
1197
- * Draining here is what makes many repaints within one turn count as the one
1198
- * delivery they are, and a turn with no repaint count as none.
1199
- */
1200
- export function drainRenderedDependencyDriftFilePaths() {
1201
- const out = [...renderedDependencyDriftFiles];
1202
- renderedDependencyDriftFiles.clear();
1203
- return out;
1204
- }
1205
- /**
1206
- * #2275: commit one more RENDERED delivery of `filePath`'s
1207
- * dependency-drift-demoted diagnostics. Every qualifying entry on the record
1208
- * advances together — `markWidgetFileBlockersStale` demotes them as one
1209
- * batch per file, so they share one delivery history, mirroring that
1210
- * write's own file-level scope. Returns the new count, or 0 when the file
1211
- * has nothing this cap governs (never demoted, or already footer-retired —
1212
- * a hidden row is delivered no more, so it must stop advancing).
1213
- */
1214
- export function incrementWidgetDependencyDriftDelivery(filePath) {
1215
- const rec = files.get(fileMapKey(filePath));
1216
- if (!rec)
1217
- return 0;
1218
- let next = 0;
1219
- for (const d of rec.allDiagnostics) {
1220
- if (isDependencyDriftDemoted(d)) {
1221
- next = (d.staleDeliveryCount ?? 0) + 1;
1222
- d.staleDeliveryCount = next;
1223
- }
1224
- }
1225
- return next;
1226
- }
1227
- /**
1228
- * #2275 review F2: retire every dependency-drift-demoted diagnostic on
1229
- * `filePath` once its delivery cap is reached — by HIDING it from the
1230
- * footer, not by dropping it.
1231
- *
1232
- * `RuntimeCoordinator.retireDemotedDependencyDriftBlocker` can delete its
1233
- * whole `_pendingInlineBlockers` record because that store backs exactly one
1234
- * surface (the turn-end advisory text). This store does not: `allDiagnostics`
1235
- * is also what `getFileDiagnosticSummaries` serves to `lens_diagnostics
1236
- * mode=all` and what `lens_diagnostic_mark` cross-checks to reanchor. Removing
1237
- * the entry there turned an unconfirmed LSP error into a CLEAN read and took
1238
- * it out of the error tally #1631 requires it to stay in. So the cap marks
1239
- * `footerRetired` and leaves everything else — severity, counts, the
1240
- * `stale`/`dependency-drift` demotion — exactly as it was; mode=all carries
1241
- * the "capped, still confirmable" wording in its own note. Returns true iff
1242
- * something changed (idempotent on a second call).
1243
- */
1244
- export function retireWidgetDependencyDriftBlockers(filePath) {
1245
- const rec = files.get(fileMapKey(filePath));
1246
- if (!rec)
1247
- return false;
1248
- let changed = false;
1249
- for (const d of rec.allDiagnostics) {
1250
- if (isDependencyDriftDemoted(d)) {
1251
- d.footerRetired = true;
1252
- changed = true;
1253
- }
1254
- }
1255
- if (!changed)
1256
- return false;
1257
- // The record's severity tallies are unchanged by design (the finding is
1258
- // still an error, still non-blocking) — only what the footer draws moves.
1259
- // Fix-round 3 (#2275 review F1): this delete is live only for the
1260
- // multi-file drain re-render case — `drainRenderedDependencyDriftFilePaths`
1261
- // already clears the whole Set before this loop starts, so by the time a
1262
- // given `wPath` reaches this call its own entry is normally long gone. It
1263
- // only does something when a render for a DIFFERENT file interleaves
1264
- // between the drain and this retire (adding `rec.filePath` back to the
1265
- // Set mid-loop) or a render fires between retirement and the caller's next
1266
- // drain — either way, defensive: it keeps a just-retired file from being
1267
- // double-drained (and double-charged) on the next pass.
1268
- renderedDependencyDriftFiles.delete(rec.filePath);
1269
- requestRenderFn?.();
1270
- return true;
1271
- }
1272
- /**
1273
- * Keep the TUI honest (#298 follow-up). `reconcileStaleWidgetFiles` drops
1274
- * widget entries whose file changed on disk after they were last recorded
1275
- * (i.e. diagnostics the agent already fixed) — but it was only ever wired
1276
- * into the `lens_diagnostics` tool, so the widget rendered cached diagnostics
1277
- * verbatim and kept showing fixed errors until `lens_diagnostics` was run by
1278
- * hand. This debounced scheduler fires it from the widget render path (see
1279
- * `mountLensWidget` in index.ts) so stale entries self-correct. The debounce
1280
- * collapses the burst of renders that accompany a save into a single sweep.
1281
- */
1282
- let staleReconcileTimer = null;
1283
- export const STALE_RECONCILE_DEBOUNCE_MS = 1500;
1284
- export function scheduleStaleReconcile() {
1285
- if (staleReconcileTimer !== null)
1286
- return;
1287
- staleReconcileTimer = setTimeout(() => {
1288
- staleReconcileTimer = null;
1289
- void reconcileStaleWidgetFiles().catch(() => { });
1290
- }, STALE_RECONCILE_DEBOUNCE_MS);
1291
- // Don't keep the process alive solely for this background sweep.
1292
- staleReconcileTimer?.unref?.();
1293
- }
1294
- /**
1295
- * Return current diagnostics for every file pi-lens has seen this session.
1296
- * Used by lens_diagnostics tool (mode: "all"). Exposes the FULL per-file
1297
- * diagnostic set — decoupled from the widget's display cap — so the agent sees
1298
- * everything, not just the 12 the TUI keeps for rendering.
1299
- *
1300
- * #3183: "everything" means every row a live scan stands behind.
1301
- * `disposition`-tagged rows are the store's own bookkeeping for a mark — the
1302
- * scan that produced this record did not report them — and they are excluded
1303
- * here. That is the same predicate `countDiagnostics` and `isBlocking` have
1304
- * always used, so this makes the projected array agree with the counts beside
1305
- * it; before #3183 the array disagreed, and once retirement rule 1 let a row
1306
- * outlive the file's mtime, `applyCachedDispositions` reached it in the
1307
- * WEAK-fallback branch — which can never match a STRICT `false-positive`
1308
- * anchor — and `mode=all` served a marked finding as a live warning (#3158 AC3).
1309
- * The exclusion lives HERE, at the one projection every cache-only surface and
1310
- * the mode=full merge read, rather than as a second disposition rule inside
1311
- * `summarizeDiagnostics`' tally: that tally still skips `stale` only, and the
1312
- * disposition filter still reaches it pre-applied through the shared
1313
- * `applyCachedDispositions` seam.
1314
- *
1315
- * Deliberately NOT applied to {@link getFileDiagnostics}, the per-file read:
1316
- * `reconcileWidgetDisposition` needs the tagged rows to enforce retirement
1317
- * rule 3, and `lens_diagnostic_mark`'s cross-check re-marks through them.
1318
- */
1319
- export function getFileDiagnosticSummaries() {
1320
- return [...files.values()].map((rec) => {
1321
- applyPastEofGate(rec);
1322
- const live = rec.allDiagnostics.filter((d) => d.disposition === undefined);
1323
- return {
1324
- filePath: rec.filePath,
1325
- blocking: rec.diagnosticCounts.blocking,
1326
- errors: rec.diagnosticCounts.errors,
1327
- warnings: rec.diagnosticCounts.warnings,
1328
- advisories: countAdvisories(live),
1329
- hasFinalSnapshot: rec.hasFinalDiagnosticsSnapshot,
1330
- diagnostics: live.map((d) => ({ ...d })),
1331
- };
1332
- });
1333
- }
1334
- /**
1335
- * How many {@link WidgetDiagnostic.suppressedRetained} rows this file's record
1336
- * is currently carrying (#3158 round 2 F4).
1337
- *
1338
- * The success-path counterpart of `widget-suppressed-retention-capped`: the
1339
- * ledger only hears about retention when the per-file cap TRUNCATES it, so
1340
- * nothing observed the healthy case. `tools/lsp-diagnostics.ts` folds this into
1341
- * the `lsp_probe_disposition_filter` phase it already emits once per filtered
1342
- * file per scan, so the count is bounded by that record's existing cardinality
1343
- * — never one row per retained finding.
1344
- *
1345
- * Deliberately not {@link getFileDiagnostics}: that applies the past-EOF gate,
1346
- * which can `stat` the file. This is a map lookup plus a count over rows the
1347
- * caller's own scan just filtered, so it adds no I/O to the probe path.
1348
- */
1349
- export function countRetainedSuppressedRows(filePath) {
1350
- const rec = files.get(fileMapKey(filePath));
1351
- if (!rec)
1352
- return 0;
1353
- let n = 0;
1354
- for (const d of rec.allDiagnostics)
1355
- if (d.suppressedRetained)
1356
- n += 1;
1357
- return n;
1358
- }
1359
- /**
1360
- * Return the current FULL (uncapped) diagnostic set for a single file, as
1361
- * last recorded by {@link recordDiagnostics} — the same `allDiagnostics`
1362
- * store `getFileDiagnosticSummaries` exposes per-file, without paying for a
1363
- * whole-session snapshot. Used by the #502 `pilens:diagnostics` bus producer
1364
- * (`clients/bus-publish.ts`), which reads this immediately after
1365
- * `recordDiagnostics` writes it so the emitted event reflects the write
1366
- * batch's FINAL diagnostic state (post-format, post-autofix, post-dispatch —
1367
- * see pipeline.ts call order). Returns `undefined` when the file has never
1368
- * been recorded (caller must not confuse "never seen" with "seen and clean";
1369
- * an explicit `[]` from `recordDiagnostics` is a real empty array here).
1370
- *
1371
- * The `files` map key is normalized through `fileMapKey` (#1020), so any path
1372
- * form of the same file — forward-slash, backslash, or a different Windows
1373
- * drive-letter case — resolves to the same record. This read-side fold MUST
1374
- * stay identical to the write-side fold, or a file recorded under one form
1375
- * would silently read as `undefined` under another (e.g. via bus-publish).
1376
- */
1377
- export function getFileDiagnostics(filePath) {
1378
- const rec = files.get(fileMapKey(filePath));
1379
- if (!rec)
1380
- return undefined;
1381
- applyPastEofGate(rec);
1382
- return rec.allDiagnostics.map((d) => ({ ...d }));
1383
- }
1384
- /** @internal Test-only helpers. Do not use in production code. */
1385
- export const __testing = {
1386
- getWidgetStateSnapshot() {
1387
- return {
1388
- files: [...files.values()].map((rec) => ({
1389
- filePath: rec.filePath,
1390
- storedDiagnostics: rec.diagnostics.length,
1391
- blocking: rec.diagnosticCounts.blocking,
1392
- errors: rec.diagnosticCounts.errors,
1393
- warnings: rec.diagnosticCounts.warnings,
1394
- })),
1395
- };
1396
- },
1397
- };
1398
- export function recordLsp(serverId, root, status, durationMs) {
1399
- const normalizedRoot = normalizeMapKey(root);
1400
- const key = `${serverId}@${normalizedRoot}`;
1401
- const mapped = status === "spawn_start"
1402
- ? "spawning"
1403
- : status === "spawn_success"
1404
- ? "ready"
1405
- : "failed";
1406
- lspServers.set(key, { serverId, root, status: mapped, durationMs });
1407
- requestRender();
1408
- }
1409
- // ── Render ────────────────────────────────────────────────────────────────────
1410
- const HORIZONTAL_MIN_WIDTH = 70;
1411
- export function renderWidget(width, theme) {
1412
- const dim = (s) => theme.fg("dim", s);
1413
- const red = (s) => theme.fg("error", s);
1414
- const yellow = (s) => theme.fg("warning", s);
1415
- const green = (s) => theme.fg("success", s);
1416
- const cyan = (s) => theme.fg("accent", s);
1417
- const w = Math.max(1, width || 80);
1418
- const useHorizontal = w >= HORIZONTAL_MIN_WIDTH;
1419
- if (files.size === 0 && lspServers.size === 0)
1420
- return [];
1421
- const lines = [];
1422
- // Header — counts from deduplicated files only
1423
- const deduped = dedupeByBasename([...files.values()]);
1424
- const recencySorted = deduped.filter(shouldRenderFile).slice(0, 5);
1425
- const langStr = sessionLanguages.slice(0, 6).join(" ");
1426
- const totalBlocking = countBlockingIn(deduped);
1427
- const totalErrors = countTotalIn("error", deduped);
1428
- const totalWarnings = countTotalIn("warning", deduped);
1429
- const totalSuppressed = countSuppressedIn(deduped);
1430
- const hasPendingAnalysis = deduped.some(isPendingAnalysis);
1431
- const errorChunk = totalErrors > 0
1432
- ? (totalBlocking > 0 ? red : yellow)(`●${totalErrors}E`)
1433
- : "";
1434
- const warningChunk = totalWarnings > 0 ? yellow(`!${totalWarnings}W`) : "";
1435
- const summary = errorChunk
1436
- ? errorChunk + (warningChunk ? " " + warningChunk : "")
1437
- : warningChunk
1438
- ? warningChunk
1439
- : files.size > 0 && !hasPendingAnalysis
1440
- ? green("✓ clean")
1441
- : "";
1442
- // LSP spawning — folded into the header in horizontal mode, tail line otherwise
1443
- const spawning = [...lspServers.values()].filter((s) => s.status === "spawning");
1444
- const lspChip = useHorizontal && spawning.length > 0 ? " " + dim("LSP↑") : "";
1445
- const header = ` ${cyan("pi-lens")}${langStr ? " " + dim(langStr) : ""}${lspChip}${summary ? " " + summary : ""}`;
1446
- lines.push(fitLine(header, w));
1447
- if (totalSuppressed > 0) {
1448
- lines.push(fitLine(` ${dim(`suppressed: ${totalSuppressed}`)}`, w));
1449
- }
1450
- // File list — display order varies by mode
1451
- if (useHorizontal) {
1452
- const displayOrder = sortByTierThenRecency(recencySorted);
1453
- const rowLine = packHorizontalRow(displayOrder, w, theme);
1454
- if (rowLine.length > 0)
1455
- lines.push(rowLine);
1456
- }
1457
- else {
1458
- for (const rec of recencySorted) {
1459
- lines.push(fitLine(formatFileRowVertical(rec, theme), w));
1460
- }
1461
- }
1462
- // Diagnostics — blocking (or dependency-drift-demoted) only, from the most
1463
- // recently touched file that has them. Vertical mode keeps the divider/filename
1464
- // context; horizontal already shows the filename on the packed row above, so
1465
- // we drop the extra header noise there.
1466
- //
1467
- // #1631 review F10: `isBlocking` answers false for a demoted (`stale`) finding
1468
- // by design (#1419 demote-not-drop) — but this render loop used that same
1469
- // predicate to pick which findings to SHOW, so a demoted finding vanished from
1470
- // the footer entirely instead of demoting visibly. `isBlockingOrDemoted` keeps
1471
- // it in the list; the per-entry render below distinguishes the two with a
1472
- // dimmed marker instead of the red dot.
1473
- //
1474
- // #2275: a demotion that reached its delivery cap is hidden from THIS
1475
- // surface only (`footerRetired`) — it stays in `allDiagnostics` for
1476
- // mode=all and lens_diagnostic_mark, which is where the agent is told it
1477
- // was capped and can still be confirmed by a re-run.
1478
- const isBlockingOrDemoted = (d) => (isBlocking(d) || d.stale === true) && d.footerRetired !== true;
1479
- const withBlocking = recencySorted.filter((r) => r.diagnostics.some(isBlockingOrDemoted));
1480
- if (withBlocking.length > 0) {
1481
- const rec = withBlocking[0];
1482
- // #1641: gate only the ONE record whose line numbers are about to be
1483
- // rendered, not the whole (potentially session-long) `deduped` list the
1484
- // header counts read — one memoized stat per redraw, never a per-file
1485
- // scan of every file pi-lens has touched this session. A past-EOF
1486
- // citation demoted here can leave the header's aggregate blocking count
1487
- // one turn stale; `reconcileStaleWidgetFiles`'s existing debounced sweep
1488
- // (scheduleStaleReconcile) already re-derives that aggregate on its own
1489
- // cadence, so this is a bounded, self-correcting gap, not a silent one.
1490
- applyPastEofGate(rec);
1491
- if (!useHorizontal) {
1492
- lines.push(fitLine(dim("─".repeat(Math.min(w, 60))), w));
1493
- lines.push(fitLine(` ${dim(path.basename(rec.filePath))}`, w));
1494
- }
1495
- const blockers = rec.diagnostics.filter(isBlockingOrDemoted).slice(0, 5);
1496
- // #2275 review F1: THIS is the point where a demoted row is delivered to
1497
- // a human — after the `withBlocking[0]` pick and the top-five slice, not
1498
- // before them. Mark the file so the next turn end can charge exactly one
1499
- // delivery against its cap. File-level, matching
1500
- // `markWidgetFileBlockersStale`'s own file-level demotion batch (all of a
1501
- // file's drift demotions share one delivery history); a file holding more
1502
- // than five demoted rows delivers the visible ones and counts the batch,
1503
- // which is the conservative direction — it retires no LATER than the rows
1504
- // the agent actually read.
1505
- if (blockers.some(isDependencyDriftDemoted)) {
1506
- renderedDependencyDriftFiles.add(rec.filePath);
1507
- }
1508
- for (const d of blockers) {
1509
- // A past-EOF demotion's coordinate is untrustworthy — render the
1510
- // marker instead of the line (#1641); drift demotions keep theirs.
1511
- const pastEof = d.stale && (d.staleReason ?? "past-eof") === "past-eof";
1512
- // No link for a past-EOF demotion: the anchor would carry the same
1513
- // untrustworthy line the marker exists to replace.
1514
- const loc = pastEof
1515
- ? PAST_EOF_STALE_MARKER
1516
- : d.line != null
1517
- ? osc8(d.uri ?? "", `L${d.line}`)
1518
- : "";
1519
- const rule = d.rule ? dim(` ${d.rule}`) : "";
1520
- const staleTag = d.stale ? dim(` ${STALE_LINE_MARKER}`) : "";
1521
- const prefix = ` ${d.stale ? dim("○") : red("●")} ${loc}${rule} `;
1522
- const msgWidth = Math.max(1, w - visibleWidth(prefix) - visibleWidth(staleTag));
1523
- const msg = fitLine(d.message, msgWidth, "…");
1524
- lines.push(fitLine(`${prefix}${msg}${staleTag}`, w));
1525
- }
1526
- }
1527
- // LSP status tail — only in vertical mode; horizontal folds into header
1528
- if (!useHorizontal && spawning.length > 0) {
1529
- const ids = spawning.map((s) => s.serverId).join(" ");
1530
- lines.push(fitLine(` ${dim(`LSP spawning: ${ids}`)}`, w));
1531
- }
1532
- return lines;
1533
- }
1534
- function classifyFileTier(rec) {
1535
- if (rec.diagnosticCounts.blocking > 0)
1536
- return "blocking";
1537
- if (rec.diagnosticCounts.errors > 0 || rec.diagnosticCounts.warnings > 0) {
1538
- return "warning";
1539
- }
1540
- return "clean";
1541
- }
1542
- function sortByTierThenRecency(recs) {
1543
- const order = {
1544
- blocking: 0,
1545
- warning: 1,
1546
- clean: 2,
1547
- };
1548
- return [...recs].sort((a, b) => {
1549
- const ta = order[classifyFileTier(a)];
1550
- const tb = order[classifyFileTier(b)];
1551
- if (ta !== tb)
1552
- return ta - tb;
1553
- return b.touchedAt - a.touchedAt;
1554
- });
1555
- }
1556
- function formatFileRowVertical(rec, theme) {
1557
- const dim = (s) => theme.fg("dim", s);
1558
- const red = (s) => theme.fg("error", s);
1559
- const yellow = (s) => theme.fg("warning", s);
1560
- const green = (s) => theme.fg("success", s);
1561
- const base = path.basename(rec.filePath);
1562
- const blocking = rec.diagnosticCounts.blocking;
1563
- const errors = rec.diagnosticCounts.errors;
1564
- const warnings = rec.diagnosticCounts.warnings;
1565
- const formatterFailed = hasFailedFormatter(rec);
1566
- // Diagnostic severity outranks formatter failure (#1348 review): a file
1567
- // with blocking diagnostics shows the blocking dot even if a format also
1568
- // failed -- same precedence as the horizontal renderer.
1569
- const dot = blocking > 0
1570
- ? red("●")
1571
- : formatterFailed
1572
- ? red("x")
1573
- : warnings > 0 || errors > 0
1574
- ? yellow("!")
1575
- : green("✓");
1576
- const runnerNames = [...rec.runners.entries()]
1577
- .filter(([, r]) => r.status !== "skipped")
1578
- .map(([id]) => id)
1579
- .join(" ");
1580
- const counts = errors > 0
1581
- ? " " +
1582
- (blocking > 0 ? red : yellow)(`${errors}E`) +
1583
- (warnings > 0 ? " " + yellow(`${warnings}W`) : "")
1584
- : warnings > 0
1585
- ? " " + yellow(`${warnings}W`)
1586
- : " " + dim("clean");
1587
- const changedFormatters = [...rec.formatters.entries()]
1588
- .filter(([, f]) => f.changed && f.success)
1589
- .map(([name]) => name);
1590
- const failedFormatters = [...rec.formatters.entries()]
1591
- // An `unavailable` tool is not a code failure (#2413): it never earns a red
1592
- // `fmt-failed` marker. Its result already carries success:true, so this
1593
- // guard is a belt that keeps the intent explicit at the render seam.
1594
- .filter(([, f]) => !f.success && f.outcome !== "unavailable")
1595
- .map(([name]) => name);
1596
- const formatMark = (failedFormatters.length > 0
1597
- ? red(` fmt-failed:${failedFormatters.join(",")}`)
1598
- : "") +
1599
- (changedFormatters.length > 0
1600
- ? dim(` fmt:${changedFormatters.join(",")}`)
1601
- : "");
1602
- return ` ${dot} ${base} ${dim(runnerNames)}${formatMark}${counts}`;
1603
- }
1604
- function packHorizontalRow(recs, totalWidth, theme) {
1605
- if (recs.length === 0)
1606
- return "";
1607
- const dim = (s) => theme.fg("dim", s);
1608
- const indent = " ";
1609
- const sep = " ";
1610
- // Reserve worst-case overflow space upfront so the marker always fits.
1611
- // " +NN" — 4 visible chars covers up to two-digit overflow.
1612
- const overflowReserve = 4;
1613
- let used = visibleWidth(indent);
1614
- const parts = [indent];
1615
- const addedTokenWidths = [];
1616
- let droppedAt = -1;
1617
- for (let i = 0; i < recs.length; i++) {
1618
- const sepWidth = parts.length > 1 ? visibleWidth(sep) : 0;
1619
- const willOverflow = i < recs.length - 1;
1620
- const reserve = willOverflow ? overflowReserve : 0;
1621
- const remaining = totalWidth - used - sepWidth - reserve;
1622
- if (remaining < 4) {
1623
- droppedAt = i;
1624
- break;
1625
- }
1626
- const token = formatFileTokenHorizontal(recs[i], remaining, theme);
1627
- const tokenWidth = visibleWidth(token);
1628
- if (token.length === 0 || used + sepWidth + tokenWidth > totalWidth) {
1629
- droppedAt = i;
1630
- break;
1631
- }
1632
- if (sepWidth > 0) {
1633
- parts.push(sep);
1634
- used += sepWidth;
1635
- }
1636
- parts.push(token);
1637
- used += tokenWidth;
1638
- addedTokenWidths.push(tokenWidth + sepWidth);
1639
- }
1640
- if (droppedAt >= 0) {
1641
- let dropped = recs.length - droppedAt;
1642
- let overflow = " " + dim(`+${dropped}`);
1643
- // If reservation was insufficient (e.g. last token grew because no
1644
- // reserve was applied), shed accepted tokens until overflow fits.
1645
- while (used + visibleWidth(overflow) > totalWidth &&
1646
- addedTokenWidths.length > 0) {
1647
- const lastWidth = addedTokenWidths.pop();
1648
- used -= lastWidth;
1649
- parts.pop(); // token
1650
- if (parts.length > 1)
1651
- parts.pop(); // preceding separator
1652
- dropped++;
1653
- overflow = " " + dim(`+${dropped}`);
1654
- }
1655
- if (used + visibleWidth(overflow) <= totalWidth) {
1656
- parts.push(overflow);
1657
- }
1658
- }
1659
- return fitLine(parts.join(""), totalWidth);
1660
- }
1661
- function formatFileTokenHorizontal(rec, remainingWidth, theme) {
1662
- const dim = (s) => theme.fg("dim", s);
1663
- const red = (s) => theme.fg("error", s);
1664
- const yellow = (s) => theme.fg("warning", s);
1665
- const blocking = rec.diagnosticCounts.blocking;
1666
- const errors = rec.diagnosticCounts.errors;
1667
- const warnings = rec.diagnosticCounts.warnings;
1668
- const formatterChanged = hasChangedFormatter(rec);
1669
- const formatterFailed = hasFailedFormatter(rec);
1670
- let dotChar;
1671
- if (blocking > 0)
1672
- dotChar = red("●");
1673
- else if (errors > 0 || warnings > 0)
1674
- dotChar = yellow("!");
1675
- else if (formatterChanged)
1676
- dotChar = dim("✎");
1677
- else
1678
- dotChar = dim("·");
1679
- if (formatterFailed && blocking === 0 && errors === 0 && warnings === 0) {
1680
- dotChar = red("x");
1681
- }
1682
- let countsStyled = "";
1683
- if (errors > 0 && warnings > 0) {
1684
- const eColor = blocking > 0 ? red : yellow;
1685
- countsStyled = " " + eColor(`${errors}E`) + yellow(`${warnings}W`);
1686
- }
1687
- else if (errors > 0) {
1688
- const eColor = blocking > 0 ? red : yellow;
1689
- countsStyled = " " + eColor(`${errors}E`);
1690
- }
1691
- else if (warnings > 0) {
1692
- countsStyled = " " + yellow(`${warnings}W`);
1693
- }
1694
- const fullBasename = path.basename(rec.filePath);
1695
- const fixedWidth = visibleWidth(dotChar) + 1 + visibleWidth(countsStyled);
1696
- const basenameBudget = remainingWidth - fixedWidth;
1697
- if (basenameBudget < 3)
1698
- return "";
1699
- const truncated = truncateBasename(fullBasename, basenameBudget);
1700
- const linked = osc8(pathToFileURL(rec.filePath).href, truncated);
1701
- return `${dotChar} ${linked}${countsStyled}`;
1702
- }
1703
- function truncateBasename(name, maxWidth) {
1704
- if (visibleWidth(name) <= maxWidth)
1705
- return name;
1706
- if (maxWidth < 2)
1707
- return "…";
1708
- const ext = path.extname(name);
1709
- const stem = name.slice(0, name.length - ext.length);
1710
- const keep = maxWidth - ext.length - 1;
1711
- if (keep < 1) {
1712
- // Extension alone wouldn't fit; truncate the whole name.
1713
- return name.slice(0, maxWidth - 1) + "…";
1714
- }
1715
- return stem.slice(0, keep) + "…" + ext;
1716
- }
1717
- // ── Helpers ──────────────────────────────────────────────────────────────────
1718
- function getOrCreate(filePath, key = fileMapKey(filePath)) {
1719
- maybePruneInactiveFileRecords();
1720
- // Look up by the normalized key so mixed path forms of the same file share
1721
- // ONE record (#1020); keep the caller's verbatim path as the display path.
1722
- return (files.get(key) ?? {
1723
- filePath,
1724
- runners: new Map(),
1725
- formatters: new Map(),
1726
- diagnostics: [],
1727
- allDiagnostics: [],
1728
- diagnosticCounts: { blocking: 0, errors: 0, warnings: 0 },
1729
- hasFinalDiagnosticsSnapshot: false,
1730
- touchedAt: Date.now(),
1731
- });
1732
- }
1733
- function hasChangedFormatter(rec) {
1734
- return [...rec.formatters.values()].some((f) => f.changed && f.success);
1735
- }
1736
- function hasFailedFormatter(rec) {
1737
- // `unavailable` (#2413) is success:true and must never count as a failure —
1738
- // the explicit guard keeps that true even if a future path mislabels it.
1739
- return [...rec.formatters.values()].some((f) => !f.success && f.outcome !== "unavailable");
1740
- }
1741
- function shouldRenderFile(rec) {
1742
- return (rec.hasFinalDiagnosticsSnapshot ||
1743
- hasChangedFormatter(rec) ||
1744
- hasFailedFormatter(rec));
1745
- }
1746
- function hasLiveDiagnostic(rec) {
1747
- return rec.hasFinalDiagnosticsSnapshot && rec.diagnostics.length > 0;
1748
- }
1749
- function isPendingAnalysis(rec) {
1750
- return rec.runners.size > 0 && !rec.hasFinalDiagnosticsSnapshot;
1751
- }
1752
- function capStoredDiagnostics(diagnostics) {
1753
- if (diagnostics.length <= MAX_STORED_DIAGNOSTICS_PER_FILE)
1754
- return diagnostics;
1755
- const blockers = diagnostics.filter(isBlocking);
1756
- if (blockers.length >= MAX_STORED_DIAGNOSTICS_PER_FILE) {
1757
- return blockers.slice(0, MAX_STORED_DIAGNOSTICS_PER_FILE);
1758
- }
1759
- const rest = diagnostics.filter((d) => !isBlocking(d));
1760
- return [
1761
- ...blockers,
1762
- ...rest.slice(0, MAX_STORED_DIAGNOSTICS_PER_FILE - blockers.length),
1763
- ];
1764
- }
1765
- function countTotalIn(severity, recs) {
1766
- let n = 0;
1767
- for (const rec of recs) {
1768
- if (severity === "error")
1769
- n += rec.diagnosticCounts.errors;
1770
- else
1771
- n += rec.diagnosticCounts.warnings;
1772
- }
1773
- return n;
1774
- }
1775
- function countSuppressedIn(recs) {
1776
- let n = 0;
1777
- for (const rec of recs) {
1778
- for (const diagnostic of rec.allDiagnostics) {
1779
- if (diagnostic.disposition)
1780
- n++;
1781
- }
1782
- }
1783
- return n;
1784
- }
1785
- function countBlockingIn(recs) {
1786
- let n = 0;
1787
- for (const rec of recs)
1788
- n += rec.diagnosticCounts.blocking;
1789
- return n;
1790
- }
1791
- function requestRender() {
1792
- requestRenderFn?.();
1793
- }
1794
- function osc8(uri, label) {
1795
- if (!uri)
1796
- return label;
1797
- return `\x1b]8;;${uri}\x1b\\${label}\x1b]8;;\x1b\\`;
1798
- }
1799
- // Dual-signature truncateToWidth handling lives in tui-fit.ts (shared with the
1800
- // turn-summary message renderer, which learned the hard way that pi-tui crashes
1801
- // the host on over-width lines — #513).
1802
- function dedupeByBasename(recs) {
1803
- const seen = new Map();
1804
- for (const r of [...recs].sort((a, b) => a.touchedAt - b.touchedAt)) {
1805
- seen.set(path.basename(r.filePath), r);
1806
- }
1807
- return [...seen.values()].sort((a, b) => b.touchedAt - a.touchedAt);
1808
- }