@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,2032 +0,0 @@
1
- /**
2
- * Formatter Definitions for pi-lens
3
- *
4
- * Auto-detects formatters based on:
5
- * - Config files (biome.json, .prettierrc, etc.)
6
- * - Dependencies (package.json, requirements.txt, etc.)
7
- * - Binary availability (which/where)
8
- *
9
- * Inspired by OpenCode's formatter.ts pattern
10
- */
11
- import { logExtension } from "./extension-log.js";
12
- import { AsyncLocalStorage } from "node:async_hooks";
13
- import * as fs from "node:fs/promises";
14
- import * as os from "node:os";
15
- import * as path from "node:path";
16
- import { BoundedLruCache } from "./bounded-cache.js";
17
- import { createGenerationSource } from "./generation-guard.js";
18
- import { findNearestContaining, normalizeMapKey } from "./path-utils.js";
19
- import { resolveToolCwd } from "./tool-cwd.js";
20
- import { resolveCargoPackageEdition } from "./cargo-manifest.js";
21
- import { resolveKtfmtGradleStyle } from "./gradle-ktfmt-style.js";
22
- import { resolvePhpCsFixerConfig } from "./php-cs-fixer-config.js";
23
- import { TERRAGRUNT_FILENAMES } from "./file-kinds.js";
24
- import { stripAnsi } from "./sanitize.js";
25
- import { detectIndentation, hasDetectableIndentation, } from "./dispatch/indent-detect.js";
26
- import { logLatency } from "./latency-logger.js";
27
- import { compareOrdinal } from "./string-utils.js";
28
- import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
29
- import { findGlobalBinary, findLocalBinUpwards, VENDOR_BIN_DIRS, VENV_BIN_DIRS, } from "./package-manager.js";
30
- import { safeSpawnAsync } from "./safe-spawn.js";
31
- import { establishToolAgreement } from "./tool-agreement.js";
32
- import { recordDegradationOnce } from "./degradation-ledger.js";
33
- import { probeToolAsync } from "./tool-probe.js";
34
- import { assertInstallAllowed } from "./project-trust.js";
35
- import { tryLazyInstallForFormatter } from "./dispatch/runners/utils/lazy-installer.js";
36
- import { getToolPath } from "./installer/index.js";
37
- import { findPSScriptAnalyzerConfigPath, getAutoInstallToolIdForFormatter, getFormatterPolicyForFile, getSmartDefaultFormatterName, hasBiomeConfig, hasBlackConfig, hasClangFormatConfig, hasCljfmtConfig, hasCmakeFormatConfig, hasCsharpierConfig, hasFantomasConfig, hasGoogleJavaFormatConfig, hasKtfmtConfig, hasKtlintConfig, hasMixFormatConfig, hasNearestPackageJsonDependency, hasNearestPackageJsonField, hasOcamlformatConfig, hasOrmoluConfig, hasOxfmtConfig, hasOxfmtSvelteConfig, hasPhpCsFixerConfig, hasPrettierConfig, hasPSScriptAnalyzerConfig, hasRubocopConfig, hasRuffConfig, hasSqlfluffConfig, hasStandardrbConfig, hasStyluaConfig, hasSwiftformatConfig, hasTaploConfig, hasTerraformConfig, hasVitePlusConfig, OXFMT_SUPPORTED_EXTENSIONS, } from "./tool-policy.js";
38
- /**
39
- * Lazy-install a formatter's tool, through the shared seam (#1537).
40
- *
41
- * This used to own a second copy of the attempt guard — `_lazyInstallAttempts`,
42
- * a Set keyed before the install ran and never cleared — so a `gem install
43
- * rubocop` that died on a network blip was never retried for the session. The
44
- * state, the transient/durable classification and the retry ladder now live in
45
- * `lazy-installer.ts`; what stays here is the one thing that is formatter
46
- * business: a fresh binary on PATH invalidates every "not found" verdict (#1495).
47
- */
48
- export async function tryLazyInstallFormatterTool(tool, cwd) {
49
- const ok = await tryLazyInstallForFormatter(tool, cwd);
50
- if (ok)
51
- resetWhichLatches();
52
- return ok;
53
- }
54
- // --- Types ---
55
- /**
56
- * Sentinel a `resolveCommand` returns to mean "do not format this file at
57
- * all" — distinct from `null`, which means "fall back to the static
58
- * `command`". #1144's style-preserving resolvers return this when the repo
59
- * has no config AND the file's indentation is undetectable: running the
60
- * stock command there is exactly the stock-style imposition the fix bans.
61
- */
62
- export const SKIP_FORMATTING = "skip-formatting";
63
- /**
64
- * Sentinel a `resolveCommand` returns when it has PROVEN the selected
65
- * formatter has no executable — the binary is absent from every place the
66
- * resolver probed (node_modules/.bin, a venv, a managed install dir, PATH) and
67
- * no alternative resolved (#2413). Distinct from `null` ("no override, fall
68
- * back to the static `command`") and from SKIP_FORMATTING ("style-preserving
69
- * refusal"): the static fallback for these formatters is the SAME bare binary
70
- * the resolver just proved missing, so spawning it only re-triggers the ENOENT
71
- * the resolver already observed — and reporting that as a formatting failure is
72
- * the exact defect this fixes (recurring defect shape 10: an unavailable
73
- * producer collapsed into an ordinary failed result).
74
- */
75
- export const FORMATTER_UNAVAILABLE = "formatter-unavailable";
76
- export const FORMATTER_AGREEMENT_UNAVAILABLE = "formatter-agreement-unavailable";
77
- // --- Utility Functions ---
78
- async function fileExists(filePath) {
79
- try {
80
- await fs.access(filePath);
81
- return true;
82
- }
83
- catch {
84
- return false;
85
- }
86
- }
87
- async function findUp(targets, startDir, stopDir = path.parse(startDir).root) {
88
- const found = [];
89
- let currentDir = startDir;
90
- while (currentDir !== stopDir) {
91
- // One `readdir` per directory instead of one `access` per target: the
92
- // per-target probe made this loop's cost O(targets), so a long target
93
- // list (e.g. `FORMATTER_CONFIG_FILES`) paid for every entry at every
94
- // ancestor directory even when almost none of them exist (#1603).
95
- let entries;
96
- try {
97
- entries = await fs.readdir(currentDir);
98
- }
99
- catch {
100
- entries = [];
101
- }
102
- if (entries.length > 0) {
103
- // On default-case-insensitive platforms the old `fs.access` probe
104
- // matched a config file regardless of on-disk casing; fold so the
105
- // readdir membership check keeps that behavior.
106
- const foldCase = process.platform === "win32" || process.platform === "darwin";
107
- const entrySet = new Set(foldCase ? entries.map((e) => e.toLowerCase()) : entries);
108
- for (const target of targets) {
109
- // Nested candidates (for example node_modules/.bin/biome) do
110
- // not appear as direct readdir members. They retain the old
111
- // bounded candidate probe; direct names use the O(depth + matches)
112
- // directory-membership path above.
113
- if (target.includes("/") || target.includes("\\"))
114
- continue;
115
- if (entrySet.has(foldCase ? target.toLowerCase() : target)) {
116
- const candidate = path.join(currentDir, target);
117
- // Directory membership is only a cheap candidate filter. Keep the
118
- // old access check for matched entries so dangling links and entries
119
- // that cannot be read do not become config evidence (#1603 R2).
120
- if (await fileExists(candidate))
121
- found.push(candidate);
122
- }
123
- }
124
- }
125
- for (const target of targets) {
126
- if (!target.includes("/") && !target.includes("\\"))
127
- continue;
128
- const candidate = path.join(currentDir, target);
129
- if (await fileExists(candidate))
130
- found.push(candidate);
131
- }
132
- const parent = path.dirname(currentDir);
133
- if (parent === currentDir)
134
- break;
135
- currentDir = parent;
136
- }
137
- return found;
138
- }
139
- /** Test-only access to the real filesystem walker and its candidate filter. */
140
- export function _findUpForTests(targets, startDir, stopDir) {
141
- return findUp(targets, startDir, stopDir);
142
- }
143
- const WHICH_BUDGET_MS = 5000;
144
- /**
145
- * PATH lookups, governed by the shared availability policy (#1495).
146
- *
147
- * `which()` is a spawn on a 5 s budget, not a filesystem check, and around a
148
- * dozen `detect*` implementations gate on it. A transient timeout used to drop
149
- * the formatter AND get written to `detectionCache` as an empty enabled-list —
150
- * a cache invalidated only by a config file's mtime or size, never by time. One
151
- * stalled `which rustfmt` therefore disabled Rust formatting for the session
152
- * unless the user happened to edit a config file.
153
- *
154
- * Now: only a genuine absence latches, a stall expires on a cooldown, and the
155
- * detection pass refuses to cache an empty result a stall caused.
156
- */
157
- const whichLatchByCommand = new Map();
158
- /**
159
- * Commands whose CURRENT `which` verdict is transient — a probe that never got a
160
- * fair hearing, still inside its cooldown.
161
- *
162
- * Scoped per command on purpose (#1495 review). The first cut counted transient
163
- * verdicts process-wide and compared the count across a detection pass, which
164
- * over-suppressed: one stalled `which rustfmt` stopped an unrelated Python or
165
- * shell detection from caching, and stamped `reason: "probe-timeout"` on
166
- * selections where nothing had timed out. A command leaves the set as soon as
167
- * its verdict becomes trustworthy — a resolved path or a genuine absence.
168
- */
169
- const whichTransientCommands = new Set();
170
- /**
171
- * The binaries `which()` was asked about inside the current `detect()` call
172
- * (#1539).
173
- *
174
- * This replaces the `command[0]` approximation the poison guard used to run on.
175
- * `command[0]` is the formatter's own binary, which is exact for most
176
- * `detect()`s and wrong for the ones that consult a second binary — and the
177
- * "extras are harmless" argument does not hold: `rustfmt` answering a GENUINE
178
- * absence while `which rustup` stalls skips the lazy install and leaves nothing
179
- * in the transient set for `command[0]` to match. Recording the actual probes
180
- * makes the guard exact in both directions: it also stops a leftover transient
181
- * verdict for some other pass's binary from being blamed on this decision.
182
- *
183
- * `AsyncLocalStorage` because `detect()` is async and two selection passes can
184
- * be in flight at once (`tests/clients/formatters-which-latch.test.ts` runs
185
- * exactly that); a module-level set would let one pass's probes leak into the
186
- * other's verdict. Built eagerly — unlike `extension-log.ts`'s lazy console
187
- * capture, this store is entered on every detection pass, so there is no window
188
- * where paying for it is avoidable.
189
- */
190
- const probedCommandsStorage = new AsyncLocalStorage();
191
- /**
192
- * Run one candidate's `detect()` and read the per-candidate verdict back out of
193
- * the PATH latch (#1539). `detected` is the plain boolean the caller still needs
194
- * for selection; `verdict` is why.
195
- */
196
- async function detectCandidate(formatter, cwd) {
197
- const probed = new Set();
198
- let detected = false;
199
- let error;
200
- try {
201
- detected = await probedCommandsStorage.run(probed, () => formatter.detect(cwd));
202
- }
203
- catch (err) {
204
- // A `detect()` that threw still probed whatever it probed first, and those
205
- // probes are the fact the guard needs. Swallowing them here would let a
206
- // stall inside a throwing detection cache as a clean "not this project".
207
- error = err;
208
- }
209
- const stalledCommands = [...probed].filter((command) => whichTransientCommands.has(command));
210
- return {
211
- detected,
212
- ...(error !== undefined && { error }),
213
- outcome: {
214
- name: formatter.name,
215
- verdict: detected
216
- ? "available"
217
- : stalledCommands.length > 0
218
- ? "unreachable"
219
- : "missing",
220
- stalledCommands,
221
- },
222
- };
223
- }
224
- /**
225
- * Drop the PATH verdicts, so a newly installed binary is visible at once.
226
- *
227
- * Module-private on purpose. Clearing the latches alone does NOT re-arm
228
- * formatter availability: `getFormattersForFile` answers from `detectionCache`
229
- * before it ever probes PATH, so a caller that wants a genuine re-probe must
230
- * drop both. `clearFormatterCache` is that pair, and it is the only reset a
231
- * session boundary should call (#1895 review round).
232
- */
233
- function resetWhichLatches() {
234
- whichLatchByCommand.clear();
235
- whichTransientCommands.clear();
236
- cooldownRecordedForRetryAtMs.clear();
237
- }
238
- /**
239
- * Last `retryAtMs` a cooldown-served record was emitted for, per command.
240
- *
241
- * The bound (#1539): the memo branch used to return before
242
- * `logAvailabilityDecision`, so a formatter held off by a transient cooldown
243
- * produced ONE record for arbitrarily many decisions — a reader counting
244
- * `availability_decision` rows undercounted how long it had been off. Logging
245
- * every cache hit is not an option either: this seam is consulted per save.
246
- * One extra row per cooldown WINDOW is the compromise, and `retryAtMs` is the
247
- * window's identity, so the ladder itself does the rate limiting.
248
- */
249
- const cooldownRecordedForRetryAtMs = new Map();
250
- /**
251
- * Record that a still-cooling verdict was served from the latch, at most once
252
- * per cooldown window (#1539's second defect).
253
- */
254
- function noteCooldownServedVerdict(command, latch) {
255
- if (latch.getOutcome() !== "transient")
256
- return;
257
- const retryAtMs = latch.getRetryAtMs();
258
- if (retryAtMs <= 0)
259
- return;
260
- if (cooldownRecordedForRetryAtMs.get(command) === retryAtMs)
261
- return;
262
- cooldownRecordedForRetryAtMs.set(command, retryAtMs);
263
- const cause = latch.getCause();
264
- if (cause === null) {
265
- // `getOutcome() === "transient"` is only ever set by `noteUnavailable` in
266
- // the same call that sets `cause`, so this is unreachable today. It is
267
- // still not a throw (#1539 review F2): this runs inside `which()`, which
268
- // runs inside `detect()`, and the smart-default branch of the selection
269
- // pass rethrows a detection failure — so an invariant break in a LOGGING
270
- // helper could take down formatting for the file. Nothing is fabricated
271
- // either (#1535's rule: a made-up cause would mislabel WHY a formatter is
272
- // off, in the record this fix exists to make honest). The row is dropped
273
- // and the anomaly is reported, bounded by the same once-per-window gate
274
- // this function already passed.
275
- logExtension({
276
- subsystem: "format",
277
- message: `which latch: transient outcome with no cause for ${command}; cooldown-served record dropped`,
278
- metadata: { tool: command, retryAtMs },
279
- });
280
- return;
281
- }
282
- logAvailabilityDecision({
283
- tool: command,
284
- verdict: "unavailable",
285
- outcome: "transient",
286
- cause,
287
- elapsedMs: 0,
288
- latched: false,
289
- retryAfterMs: Math.max(1, retryAtMs - Date.now()),
290
- budgetMs: WHICH_BUDGET_MS,
291
- servedFromCooldown: true,
292
- // No probe ran here: the latch's own remembered cause is replayed
293
- // as-is, so the call site is the one asserting it (#2209).
294
- classifiedBy: "caller",
295
- });
296
- }
297
- async function which(command) {
298
- probedCommandsStorage.getStore()?.add(command);
299
- let entry = whichLatchByCommand.get(command);
300
- if (!entry) {
301
- entry = { latch: createAvailabilityLatch(), resolved: null };
302
- whichLatchByCommand.set(command, entry);
303
- }
304
- const memo = entry.latch.read();
305
- if (memo !== null) {
306
- if (!memo)
307
- noteCooldownServedVerdict(command, entry.latch);
308
- return memo ? entry.resolved : null;
309
- }
310
- const stallSampler = startHostStallSampler();
311
- const startedAt = Date.now();
312
- const result = await safeSpawnAsync(process.platform === "win32" ? "where" : "which", [command], { timeout: WHICH_BUDGET_MS });
313
- const hostStallMs = stallSampler.stop();
314
- const elapsedMs = Date.now() - startedAt;
315
- const resolved = !result.error && result.status === 0
316
- ? (result.stdout?.trim().split(/\r?\n/)[0] ?? null)
317
- : null;
318
- if (resolved) {
319
- entry.resolved = resolved;
320
- entry.latch.noteAvailable();
321
- whichTransientCommands.delete(command);
322
- cooldownRecordedForRetryAtMs.delete(command);
323
- logAvailabilityDecision({
324
- tool: command,
325
- verdict: "available",
326
- outcome: "success",
327
- cause: "ok",
328
- elapsedMs,
329
- latched: true,
330
- hostStallMs,
331
- budgetMs: WHICH_BUDGET_MS,
332
- classifiedBy: "probe",
333
- });
334
- return resolved;
335
- }
336
- entry.resolved = null;
337
- // A `which`/`where` that RAN and found nothing exits nonzero with NO spawn
338
- // error, and only that is a genuine absence an install would fix.
339
- //
340
- // An UNSPAWNABLE prober is the opposite (#1495 review): EACCES, `spawn
341
- // UNKNOWN`, EMFILE, an unresolvable cwd, or `where`/`which` itself missing
342
- // says nothing about the tool that was asked for. Claiming `missing` there
343
- // was the worst version of this bug, because the prober is shared by every
344
- // which-gated formatter — one EACCES would have latched a dozen of them off
345
- // for the session. So the `missing` override is granted only to a prober that
346
- // ran, and anything that stopped it from running stays transient.
347
- const proberRan = !result.error;
348
- const classified = classifyProbeFailure(result, {
349
- hostStallMs,
350
- ...(proberRan && { unclassifiedFailureOutcome: "missing" }),
351
- });
352
- let { outcome, cause } = classified;
353
- // This call site OVERRIDES classifyProbeFailure's own answer below, so a
354
- // row it forced is a caller assertion, not a probe classification — the
355
- // scrutiny availability-policy.ts:253-258 reserves for "caller" rows must
356
- // not be laundered away by mislabeling this one "probe" (#2226 review F1).
357
- let outcomeForced = false;
358
- if (!proberRan && outcome !== "transient") {
359
- outcome = "transient";
360
- cause = "probe-rejected";
361
- outcomeForced = true;
362
- }
363
- const retryAfterMs = entry.latch.noteUnavailable(outcome, cause);
364
- if (outcome === "transient")
365
- whichTransientCommands.add(command);
366
- else
367
- whichTransientCommands.delete(command);
368
- // A fresh probe opens a new cooldown window (or ends the cooldown), so the
369
- // next cache hit is entitled to its own record.
370
- cooldownRecordedForRetryAtMs.delete(command);
371
- logAvailabilityDecision({
372
- tool: command,
373
- verdict: "unavailable",
374
- outcome,
375
- cause,
376
- elapsedMs,
377
- latched: retryAfterMs === 0,
378
- hostStallMs,
379
- ...(retryAfterMs > 0 && { retryAfterMs }),
380
- budgetMs: WHICH_BUDGET_MS,
381
- classifiedBy: outcomeForced ? "caller" : "probe",
382
- });
383
- return null;
384
- }
385
- async function resolveGoFmtBinary() {
386
- const inPath = await which("gofmt");
387
- if (inPath)
388
- return inPath;
389
- const goCheck = await probeToolAsync("go", ["env", "GOROOT"], {
390
- timeout: 5000,
391
- });
392
- if (goCheck.error || goCheck.status !== 0)
393
- return null;
394
- const goroot = (goCheck.stdout ?? "").trim();
395
- if (!goroot)
396
- return null;
397
- const binary = path.join(goroot, "bin", process.platform === "win32" ? "gofmt.exe" : "gofmt");
398
- return (await fileExists(binary)) ? binary : null;
399
- }
400
- // --- Venv / Local Binary Helpers ---
401
- /**
402
- * Walk up from cwd looking for a binary in .venv or venv.
403
- * Returns the absolute path if found, null otherwise.
404
- *
405
- * Tool-resolution walker, not a config lookup (#2514/#2517 policy): escaping
406
- * the project upward past HOME means STOP, not keep reading — a `.venv`/`venv`
407
- * bin found at or above HOME can never be THIS project's own virtualenv. The
408
- * ceiling lives in `findLocalBinUpwards` and is default-on there; the tests
409
- * that pin a fake HOME drive that walker directly rather than threading a
410
- * `homeDir` no production caller ever passed (#2544 review F3).
411
- *
412
- * On Windows the candidate order is EXT-OUTER, DIR-INNER — `.venv/Scripts/x.exe`,
413
- * `venv/Scripts/x.exe`, then the bare names — which is what the pre-fold copy
414
- * did and what `findLocalBinUpwards` preserves.
415
- */
416
- async function findInVenv(binary, cwd) {
417
- return (findLocalBinUpwards(binary, cwd, {
418
- windowsExt: ".exe",
419
- binDirs: VENV_BIN_DIRS,
420
- }) ?? null);
421
- }
422
- /**
423
- * Check vendor/bin for PHP Composer-managed tools.
424
- * Walks up from cwd to find vendor/bin/<binary>.
425
- *
426
- * Tool-resolution walker, not a config lookup (#2514/#2517 policy): escaping
427
- * the project upward past HOME means STOP, not keep reading — a `vendor/bin`
428
- * match found at or above HOME can never be THIS project's own Composer
429
- * install. Same single walker as `findInVenv` above; only `binDirs` and the
430
- * Windows extension differ.
431
- */
432
- async function findInVendorBin(binary, cwd) {
433
- return (findLocalBinUpwards(binary, cwd, {
434
- windowsExt: ".bat",
435
- binDirs: VENDOR_BIN_DIRS,
436
- }) ?? null);
437
- }
438
- /**
439
- * Check node_modules/.bin for locally installed Node tools.
440
- * Walks up from cwd to find node_modules/.bin/<binary>.
441
- *
442
- * #2514: this WAS a private, byte-for-byte duplicate of
443
- * `findLocalBinUpwards` (`package-manager.ts`) — the same walk `stylua`
444
- * below, `taplo.ts`, `knip-client.ts`, and (via `findNodeToolBinary`)
445
- * `dependency-checker.ts`/`jscpd-client.ts` already reuse. A stray
446
- * `~/node_modules/.bin/oxfmt.cmd` (the home-level pi-extensions manifest
447
- * installs its own bins) was picked up as the project's formatter because
448
- * this copy had no HOME ceiling; fixing only this copy would have left every
449
- * other caller of `findLocalBinUpwards` with the same defect. Delegating
450
- * here instead of hand-rolling a second ceiling keeps there being exactly one
451
- * `node_modules/.bin` walker — see that function for the tool-resolution
452
- * "escaping the project means STOP" policy (#2517).
453
- */
454
- async function findInNodeModules(binary, cwd) {
455
- return findLocalBinUpwards(binary, cwd) ?? null;
456
- }
457
- /**
458
- * Returns true if `bundle exec <gem>` should be used:
459
- * bundle binary is available AND Gemfile.lock exists in the tree.
460
- */
461
- async function canUseBundleExec(cwd) {
462
- if ((await which("bundle")) === null)
463
- return false;
464
- const lockfiles = await findUp(["Gemfile.lock"], cwd);
465
- return lockfiles.length > 0;
466
- }
467
- async function resolveManagedSmartDefaultCommand(formatterName, filePath, args) {
468
- const toolId = getAutoInstallToolIdForFormatter(formatterName);
469
- if (!toolId)
470
- return FORMATTER_UNAVAILABLE;
471
- const { ensureTool } = await import("./installer/index.js");
472
- const installed = await ensureTool(toolId);
473
- if (!installed) {
474
- recordDegradationOnce({
475
- kind: "formatter-unavailable",
476
- subject: formatterName,
477
- reason: "managed formatter lookup and installation returned no executable",
478
- });
479
- return FORMATTER_UNAVAILABLE;
480
- }
481
- return [installed, ...args, filePath];
482
- }
483
- /** Resolve an explicitly selected formatter from the shared managed-tool seam. */
484
- async function resolveManagedFormatterCommand(toolId, filePath, args, findLocal, cwd) {
485
- const local = findLocal && cwd ? await findLocal(toolId, cwd) : null;
486
- const installed = local ?? (await which(toolId)) ?? (await getToolPath(toolId));
487
- return installed ? [installed, ...args, filePath] : FORMATTER_UNAVAILABLE;
488
- }
489
- function managedFormatterResolver(toolId, args, findLocal) {
490
- return (filePath, cwd) => resolveManagedFormatterCommand(toolId, filePath, args, findLocal, cwd);
491
- }
492
- function managedToolDetect(toolId, configDetect, nativeDetect) {
493
- return async (cwd) => {
494
- const available = (nativeDetect && (await nativeDetect(cwd))) ||
495
- Boolean(await getToolPath(toolId));
496
- return available && (configDetect ? await configDetect(cwd) : true);
497
- };
498
- }
499
- /**
500
- * One entry per formatter that can be selected via explicit project config
501
- * (the `formatterPolicy` "explicit-config" branch of `getFormattersForFile`).
502
- * A formatter with NO entry here can never win that branch — the switch this
503
- * table replaced had exactly that failure mode for `psscriptanalyzer-format`
504
- * (#1572: `.ps1`'s policy sets `defaultWhenUnconfigured: false` AND the
505
- * formatter had no config check, so neither selection branch could ever pick
506
- * it).
507
- *
508
- * This table IS the source of truth for "which formatters have an explicit-
509
- * config check" — `FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK` below derives its
510
- * membership from these keys rather than hand-listing them a second time, so
511
- * the coverage guard in formatter-policy-consistency.test.ts (#1572) can never
512
- * drift from what this function actually does.
513
- */
514
- // A `Map`, not an object literal: an object literal's lookup inherits
515
- // `Object.prototype`, so `checks["toString"]` or `checks["constructor"]`
516
- // resolves to a real (non-formatter) function instead of `undefined` —
517
- // a formatter genuinely NAMED one of those prototype properties would read
518
- // as "has an explicit-config check" when it has none (review finding, #1572).
519
- // `Map.prototype.get` has no such inherited-key hazard.
520
- const EXPLICIT_FORMATTER_CONFIG_CHECKS = new Map([
521
- ["biome", (cwd) => hasBiomeConfig(cwd)],
522
- [
523
- "prettier",
524
- (cwd) => hasPrettierConfig(cwd) || hasNearestPackageJsonField(cwd, "prettier"),
525
- ],
526
- // .svelte is conditional beyond "an oxfmt config exists": oxfmt requires the
527
- // `svelte` package installed AND the config's `svelte` flag enabled
528
- // (verified empirically — see hasOxfmtSvelteConfig). The generic checks
529
- // below are NOT sufficient for .svelte — an oxfmt.toml with no svelte flag,
530
- // or an oxfmt dependency alone, both fail at runtime for .svelte
531
- // specifically (other extensions are unaffected by this stricter gate).
532
- [
533
- "oxfmt",
534
- (cwd, ext) => ext === ".svelte"
535
- ? hasOxfmtSvelteConfig(cwd)
536
- : hasOxfmtConfig(cwd) ||
537
- hasVitePlusConfig(cwd) ||
538
- // The published package is `oxfmt`; `@oxc-project/oxfmt` does not
539
- // exist on npm. Accept both (scoped kept for forward-compat).
540
- hasNearestPackageJsonDependency(cwd, "oxfmt") ||
541
- hasNearestPackageJsonDependency(cwd, "@oxc-project/oxfmt"),
542
- ],
543
- ["ruff", (cwd) => hasRuffConfig(cwd)],
544
- ["black", (cwd) => hasBlackConfig(cwd)],
545
- ["sqlfluff", (cwd) => hasSqlfluffConfig(cwd)],
546
- ["rubocop", (cwd) => hasRubocopConfig(cwd)],
547
- ["standardrb", (cwd) => hasStandardrbConfig(cwd)],
548
- ["clang-format", (cwd) => hasClangFormatConfig(cwd)],
549
- ["php-cs-fixer", (cwd) => hasPhpCsFixerConfig(cwd)],
550
- ["stylua", (cwd) => hasStyluaConfig(cwd)],
551
- ["ocamlformat", (cwd) => hasOcamlformatConfig(cwd)],
552
- ["google-java-format", (cwd) => hasGoogleJavaFormatConfig(cwd)],
553
- ["ktfmt", (cwd) => hasKtfmtConfig(cwd)],
554
- ["ktlint", (cwd) => hasKtlintConfig(cwd)],
555
- ["cljfmt", (cwd) => hasCljfmtConfig(cwd)],
556
- ["cmake-format", (cwd) => hasCmakeFormatConfig(cwd)],
557
- ["psscriptanalyzer-format", (cwd) => hasPSScriptAnalyzerConfig(cwd)],
558
- // #1595 sweep — see the comment above hasCsharpierConfig et al. in
559
- // tool-policy.ts for why nixfmt (the 8th formatter #1572 flagged) is NOT
560
- // wired here: it has no config-file convention and no manifest-marker
561
- // equivalent to `.terraform.lock.hcl`, so there is no honest opt-in signal.
562
- ["csharpier", (cwd) => hasCsharpierConfig(cwd)],
563
- ["ormolu", (cwd) => hasOrmoluConfig(cwd)],
564
- ["taplo", (cwd) => hasTaploConfig(cwd)],
565
- ["terraform", (cwd) => hasTerraformConfig(cwd)],
566
- ["swiftformat", (cwd) => hasSwiftformatConfig(cwd)],
567
- ["fantomas", (cwd) => hasFantomasConfig(cwd)],
568
- ["mix", (cwd) => hasMixFormatConfig(cwd)],
569
- ]);
570
- function hasExplicitFormatterConfig(formatterName, cwd, ext) {
571
- return (EXPLICIT_FORMATTER_CONFIG_CHECKS.get(formatterName)?.(cwd, ext) ?? false);
572
- }
573
- // Exported for the "every registered formatter is selectable" coverage guard
574
- // (formatter-policy-consistency.test.ts, #1572): derived from the table above,
575
- // not hand-listed, so it cannot drift from what `hasExplicitFormatterConfig`
576
- // actually checks.
577
- export const FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK = new Set(EXPLICIT_FORMATTER_CONFIG_CHECKS.keys());
578
- // --- Formatter Definitions ---
579
- async function hasEditorConfig(cwd) {
580
- return findNearestContaining(cwd, [".editorconfig"]) !== undefined;
581
- }
582
- async function indentationArgs(filePath, tool, cwd) {
583
- if ((await hasEditorConfig(cwd)) ||
584
- hasExplicitFormatterConfig(tool, cwd, path.extname(filePath))) {
585
- return [];
586
- }
587
- let content;
588
- try {
589
- content = await fs.readFile(filePath, "utf8");
590
- }
591
- catch {
592
- // Resolution tests and callers may probe a path before it exists.
593
- return [];
594
- }
595
- if (!hasDetectableIndentation(content))
596
- return null;
597
- const indentation = detectIndentation(content);
598
- if (!indentation) {
599
- // #3038: the file HAS indentation, but only nested runs, so no unit can be
600
- // proven. The caller turns this into SKIP_FORMATTING — a file the user
601
- // expected to be formatted silently is not — so the refusal gets one
602
- // bounded row. Subject is the tool (four possible values, recorded once
603
- // per session each) rather than the path: a session that edits many such
604
- // files must not write one ledger key per file.
605
- recordDegradationOnce({
606
- kind: "formatter-skip",
607
- subject: tool,
608
- reason: "indentation evidence is ambiguous; formatter style not pinned",
609
- });
610
- return null;
611
- }
612
- if (tool === "shfmt")
613
- return indentation.style === "tab"
614
- ? ["-i", "0"]
615
- : ["-i", String(indentation.width)];
616
- if (tool === "prettier") {
617
- return [
618
- indentation.style === "tab" ? "--use-tabs" : "--no-use-tabs",
619
- "--tab-width",
620
- String(indentation.width),
621
- ];
622
- }
623
- if (tool === "ruff") {
624
- // `ruff format` has NO --indent-style/--indent-width flags (it errors with
625
- // "unexpected argument" and exits 2, which formatFile reported as a silent
626
- // clean no-op back when exit-code strictness was opt-in). Style is pinned
627
- // through inline TOML overrides instead (#1144 follow-up).
628
- return [
629
- "--config",
630
- `indent-width=${indentation.width}`,
631
- "--config",
632
- `format.indent-style='${indentation.style}'`,
633
- ];
634
- }
635
- return [
636
- "--indent-style",
637
- indentation.style,
638
- "--indent-width",
639
- String(indentation.width),
640
- ];
641
- }
642
- /**
643
- * Resolve the cwd for the formatter child, capped at the user's home
644
- * directory. Files outside a project retain the historical file-directory
645
- * cwd. The optional homeDir is only for the ceiling regression test.
646
- */
647
- export function resolveFormatterCwd(absolutePath, formatterName, homeDir = os.homedir()) {
648
- return resolveToolCwd("formatter", formatterName ?? "unknown", absolutePath, {
649
- // Formatter discovery historically walks above the file directory. The
650
- // filesystem root is the neutral dispatch boundary; the seam's `$HOME`
651
- // ceiling prevents it from escaping the user's workspace.
652
- cwd: path.parse(path.resolve(absolutePath)).root,
653
- homeDir,
654
- }).cwd;
655
- }
656
- /**
657
- * Every biome invocation must carry this. Biome exits 1 with "No files were
658
- * processed in the specified paths" when the path is ignored by the repo's own
659
- * biome.json or carries an extension biome does not handle — a benign outcome
660
- * that, under #1337's strict default, would otherwise report a formatting
661
- * failure on every edit under an ignored directory. Verified against biome
662
- * 2.4.12: ignored path → exit 1, +flag → exit 0; unsupported extension → exit
663
- * 1, +flag → exit 0; and a real syntax error still exits 1 WITH the flag, so
664
- * strictness is preserved for actual failures.
665
- *
666
- * `clients/dispatch/runners/biome-check.ts:108` already passes this on the lint
667
- * path; the formatter path did not, which is what made biome the one
668
- * misclassified formatter in the #1337 audit.
669
- */
670
- const BIOME_UNMATCHED_FLAG = "--no-errors-on-unmatched";
671
- export const biomeFormatter = {
672
- name: "biome",
673
- command: [
674
- "npx",
675
- "@biomejs/biome",
676
- "format",
677
- "--write",
678
- BIOME_UNMATCHED_FLAG,
679
- "$FILE",
680
- ],
681
- async resolveCommand(filePath, cwd) {
682
- const editorConfigFlag = (await hasEditorConfig(cwd))
683
- ? ["--use-editorconfig=true"]
684
- : [];
685
- const styleArgs = await indentationArgs(filePath, "biome", cwd);
686
- if (styleArgs === null)
687
- return SKIP_FORMATTING;
688
- const args = [
689
- "format",
690
- "--write",
691
- BIOME_UNMATCHED_FLAG,
692
- ...editorConfigFlag,
693
- ...styleArgs,
694
- ];
695
- const local = await findInNodeModules("biome", cwd);
696
- if (local)
697
- return [local, ...args, filePath];
698
- // Any package manager's global bin dir (npm/pnpm/yarn/bun) before we
699
- // auto-install — catches a `pnpm add -g @biomejs/biome` PATH misses (#375).
700
- const global = await findGlobalBinary("biome");
701
- if (global)
702
- return [global, ...args, filePath];
703
- const toolId = getAutoInstallToolIdForFormatter("biome");
704
- if (!toolId)
705
- return null;
706
- const { ensureTool } = await import("./installer/index.js");
707
- const installed = await ensureTool(toolId);
708
- if (installed)
709
- return [installed, ...args, filePath];
710
- return null;
711
- },
712
- extensions: [
713
- ".js",
714
- ".jsx",
715
- ".mjs",
716
- ".cjs",
717
- ".ts",
718
- ".tsx",
719
- ".mts",
720
- ".cts",
721
- ".json",
722
- ".jsonc",
723
- ".css",
724
- ".scss",
725
- ".sass",
726
- ".vue",
727
- ".svelte",
728
- ".html",
729
- ".htm",
730
- ],
731
- async detect(cwd) {
732
- return (hasBiomeConfig(cwd) ||
733
- hasNearestPackageJsonDependency(cwd, "@biomejs/biome"));
734
- },
735
- };
736
- export const prettierFormatter = {
737
- name: "prettier",
738
- command: ["npx", "prettier", "--write", "$FILE"],
739
- async resolveCommand(filePath, cwd) {
740
- const styleArgs = await indentationArgs(filePath, "prettier", cwd);
741
- if (styleArgs === null)
742
- return SKIP_FORMATTING;
743
- const args = ["--write", ...styleArgs];
744
- const local = await findInNodeModules("prettier", cwd);
745
- if (local)
746
- return [local, ...args, filePath];
747
- // Global bin of any manager (npm/pnpm/yarn/bun) before auto-install (#375).
748
- const global = await findGlobalBinary("prettier");
749
- if (global)
750
- return [global, ...args, filePath];
751
- return resolveManagedSmartDefaultCommand("prettier", filePath, args);
752
- },
753
- extensions: [
754
- ".js",
755
- ".jsx",
756
- ".mjs",
757
- ".cjs",
758
- ".ts",
759
- ".tsx",
760
- ".mts",
761
- ".cts",
762
- ".json",
763
- ".jsonc",
764
- ".css",
765
- ".scss",
766
- ".sass",
767
- ".less",
768
- ".vue",
769
- ".svelte",
770
- ".html",
771
- ".htm",
772
- ".md",
773
- ".mdx",
774
- ".yaml",
775
- ".yml",
776
- ".graphql",
777
- ".gql",
778
- ],
779
- async detect(cwd) {
780
- return (hasPrettierConfig(cwd) ||
781
- hasNearestPackageJsonDependency(cwd, "prettier") ||
782
- hasNearestPackageJsonField(cwd, "prettier"));
783
- },
784
- };
785
- /**
786
- * Turns "no target files left after ignore rules" into a clean exit 0.
787
- *
788
- * oxfmt is offered for every extension in `OXFMT_SUPPORTED_EXTENSIONS` as soon
789
- * as a config exists, but a config's `ignorePatterns` can exclude any of them.
790
- * Handed a path it has been told to ignore, oxfmt 0.64.0 exits 2 with
791
- * "Expected at least one target file. All matched files may have been excluded
792
- * by ignore rules." Under the #1337 strict default that reads as a formatting
793
- * failure, so a user whose oxfmt config ignores (say) Markdown sees an error on
794
- * every Markdown edit.
795
- *
796
- * Same class as biome's "No files were processed in the specified paths", and
797
- * the same fix: ask the tool not to treat an empty target set as an error,
798
- * rather than teaching `formatFile` to forgive an exit code. This is the
799
- * narrower repair — a flag cannot mask anything else, whereas status- or
800
- * message-keyed leniency would have to be trusted to stay tight across oxfmt
801
- * releases. Verified against oxfmt 0.64.0: with the flag, an ignored path exits
802
- * 0 untouched, a normal file is still rewritten, and an unparseable file still
803
- * exits 2. `tests/clients/dispatch/oxfmt-ignored-file-noop.test.ts` pins all
804
- * three against the real binary.
805
- *
806
- * The flag also silences a genuinely missing file, which `formatFile` never
807
- * reaches: it reads the file before it spawns anything, so a missing path
808
- * fails there.
809
- */
810
- const OXFMT_NO_ERROR_ON_UNMATCHED = "--no-error-on-unmatched-pattern";
811
- export const oxfmtFormatter = {
812
- name: "oxfmt",
813
- command: ["oxfmt", OXFMT_NO_ERROR_ON_UNMATCHED, "$FILE"],
814
- // #1337 audit: oxfmt (and the `vp fmt --write` path) publish no exit-code
815
- // table. `--write` is the default and `--check` is the separate verification
816
- // mode, so there is no documented nonzero-on-reformat. Absent a documented
817
- // benign-nonzero mode, it stays strict — the safe direction, since the failure
818
- // mode of guessing wrong the other way is a silent no-op (#1336).
819
- async resolveCommand(filePath, cwd) {
820
- if (hasVitePlusConfig(cwd)) {
821
- // No unmatched-pattern flag here: this is `vp`, a different CLI with
822
- // its own arguments. Whether `vp fmt` has the same empty-target
823
- // behavior is unverified, so nothing is claimed about it.
824
- const localVp = await findInNodeModules("vp", cwd);
825
- if (localVp)
826
- return [localVp, "fmt", filePath, "--write"];
827
- const globalVp = await which("vp");
828
- if (globalVp)
829
- return [globalVp, "fmt", filePath, "--write"];
830
- }
831
- const local = await findInNodeModules("oxfmt", cwd);
832
- if (local)
833
- return [local, OXFMT_NO_ERROR_ON_UNMATCHED, filePath];
834
- const found = await which("oxfmt");
835
- if (found)
836
- return [found, OXFMT_NO_ERROR_ON_UNMATCHED, filePath];
837
- return ((await resolveManagedFormatterCommand("oxfmt", filePath, [OXFMT_NO_ERROR_ON_UNMATCHED], findInNodeModules, cwd)) ?? FORMATTER_UNAVAILABLE);
838
- },
839
- // Single source of truth: OXFMT_SUPPORTED_EXTENSIONS in tool-policy.ts.
840
- // Do not hand-maintain a second copy of this list (#1134 — previously two
841
- // parallel hand-maintained lists, the #883 single-source-of-truth class).
842
- extensions: [...OXFMT_SUPPORTED_EXTENSIONS],
843
- async detect(cwd) {
844
- return (hasOxfmtConfig(cwd) ||
845
- hasVitePlusConfig(cwd) ||
846
- // Published package is `oxfmt` (the scoped name does not exist on npm).
847
- hasNearestPackageJsonDependency(cwd, "oxfmt") ||
848
- hasNearestPackageJsonDependency(cwd, "@oxc-project/oxfmt"));
849
- },
850
- };
851
- export const ruffFormatter = {
852
- name: "ruff",
853
- command: ["ruff", "format", "$FILE"],
854
- extensions: [".py", ".pyi"],
855
- // Strict (the #1337 default): `ruff format` exits 0 on a successful in-place
856
- // rewrite and 2 on argument rejection / syntax error (verified, ruff 0.x:
857
- // well-formed → 0, reformatted → 0, unparseable → 2). The exit-2 no-op is
858
- // exactly what hid the bad --indent-style flags for a full release cycle.
859
- async resolveCommand(filePath, cwd) {
860
- const styleArgs = await indentationArgs(filePath, "ruff", cwd);
861
- if (styleArgs === null)
862
- return SKIP_FORMATTING;
863
- const args = ["format", ...styleArgs];
864
- const venv = await findInVenv("ruff", cwd);
865
- if (venv)
866
- return [venv, ...args, filePath];
867
- const toolId = getAutoInstallToolIdForFormatter("ruff");
868
- if (!toolId)
869
- return null;
870
- const { ensureTool } = await import("./installer/index.js");
871
- const installed = await ensureTool(toolId);
872
- if (installed)
873
- return [installed, ...args, filePath];
874
- // #2413: `ensureTool` already resolves PATH, global bins and the managed
875
- // dir (and attempts an install) before returning falsy, so reaching here
876
- // means ruff is genuinely nowhere. `detect()`'s `hasRuffConfig` branch has
877
- // no binary check, so a config-only project selects ruff with nothing
878
- // installed; the static bare `ruff` would then ENOENT. Prove it unavailable.
879
- return FORMATTER_UNAVAILABLE;
880
- },
881
- async detect(cwd) {
882
- if (hasRuffConfig(cwd))
883
- return true;
884
- // No-config fallback: if Ruff is already available, allow formatter usage.
885
- // This keeps Python default behavior consistent with startup defaults.
886
- return Boolean(await getToolPath("ruff"));
887
- },
888
- };
889
- export const blackFormatter = {
890
- name: "black",
891
- command: ["black", "$FILE"],
892
- extensions: [".py", ".pyi"],
893
- resolveCommand: managedFormatterResolver("black", [], findInVenv),
894
- async detect(cwd) {
895
- return hasBlackConfig(cwd);
896
- },
897
- };
898
- export const sqlfluffFormatter = {
899
- name: "sqlfluff",
900
- command: ["sqlfluff", "fix", "--force", "$FILE"],
901
- extensions: [".sql"],
902
- lenientExitCode: "lint-autofix: `sqlfluff fix` writes the corrected file and then exits 1 " +
903
- "when unfixable violations remain (its documented exit codes are 0 = all " +
904
- "clean, 1 = violations remain, 2 = command/config failure), so a nonzero " +
905
- "exit routinely accompanies a successful rewrite.",
906
- lenientStatuses: [1],
907
- async resolveCommand(filePath, cwd) {
908
- const venv = await findInVenv("sqlfluff", cwd);
909
- if (venv)
910
- return [venv, "fix", "--force", filePath];
911
- return null;
912
- },
913
- async detect(cwd) {
914
- return hasSqlfluffConfig(cwd);
915
- },
916
- };
917
- export const gofmtFormatter = {
918
- name: "gofmt",
919
- command: ["gofmt", "-w", "$FILE"],
920
- extensions: [".go"],
921
- async resolveCommand(filePath, _cwd) {
922
- const gofmtBinary = await resolveGoFmtBinary();
923
- if (!gofmtBinary)
924
- return null;
925
- return [gofmtBinary, "-w", filePath];
926
- },
927
- async detect(_cwd) {
928
- return (await resolveGoFmtBinary()) !== null;
929
- },
930
- };
931
- export const rustfmtFormatter = {
932
- name: "rustfmt",
933
- command: ["rustfmt", "$FILE"],
934
- extensions: [".rs"],
935
- // #2466: rustfmt defaults to an OLDER edition than the file's actual Cargo
936
- // package when invoked bare, so it can reject valid newer-edition syntax
937
- // (e.g. Rust 2024). Carry the nearest package's `edition` (honoring
938
- // `edition.workspace = true` inheritance) through `--edition`; `undefined`
939
- // (unreadable/unparseable manifest) falls back to the static command
940
- // above, unchanged from pre-#2466 behavior.
941
- async resolveCommand(filePath, _cwd) {
942
- const edition = await resolveCargoPackageEdition(filePath);
943
- if (edition === undefined)
944
- return null;
945
- return ["rustfmt", "--edition", edition, filePath];
946
- },
947
- async detect(cwd) {
948
- if ((await which("rustfmt")) !== null)
949
- return true;
950
- // If we're in a Rust project, attempt one lazy install of rustfmt component.
951
- const rustProject = (await findUp(["Cargo.toml"], cwd)).length > 0;
952
- if (!rustProject)
953
- return false;
954
- if ((await which("rustup")) === null)
955
- return false;
956
- await tryLazyInstallFormatterTool("rustfmt", cwd);
957
- return (await which("rustfmt")) !== null;
958
- },
959
- };
960
- export const zigFormatter = {
961
- name: "zig",
962
- command: ["zig", "fmt", "$FILE"],
963
- extensions: [".zig", ".zon"],
964
- async detect(_cwd) {
965
- return (await which("zig")) !== null;
966
- },
967
- };
968
- export const dartFormatter = {
969
- name: "dart",
970
- command: ["dart", "format", "$FILE"],
971
- extensions: [".dart"],
972
- async detect(_cwd) {
973
- return (await which("dart")) !== null;
974
- },
975
- };
976
- export const shfmtFormatter = {
977
- name: "shfmt",
978
- command: ["shfmt", "-w", "$FILE"],
979
- extensions: [".sh", ".bash"],
980
- async resolveCommand(filePath, cwd) {
981
- const styleArgs = await indentationArgs(filePath, "shfmt", cwd);
982
- if (styleArgs === null)
983
- return SKIP_FORMATTING;
984
- const inPath = await which("shfmt");
985
- if (inPath)
986
- return [inPath, "-w", ...styleArgs, filePath];
987
- return resolveManagedSmartDefaultCommand("shfmt", filePath, [
988
- "-w",
989
- ...styleArgs,
990
- ]);
991
- },
992
- detect: managedToolDetect("shfmt", undefined, async () => (await which("shfmt")) !== null),
993
- };
994
- export const nixfmtFormatter = {
995
- name: "nixfmt",
996
- command: ["nixfmt", "$FILE"],
997
- // #1337 audit: nixfmt's README documents in-place formatting but has no
998
- // exit-code section and no change-detection mode, so nothing documents a
999
- // benign nonzero. Strict by default (see oxfmt above for the rationale).
1000
- extensions: [".nix"],
1001
- async detect(_cwd) {
1002
- return (await which("nixfmt")) !== null;
1003
- },
1004
- };
1005
- export const mixFormatter = {
1006
- name: "mix",
1007
- command: ["mix", "format", "$FILE"],
1008
- extensions: [".ex", ".exs", ".eex", ".heex", ".leex"],
1009
- async detect(_cwd) {
1010
- return (await which("mix")) !== null;
1011
- },
1012
- };
1013
- export const ocamlformatFormatter = {
1014
- name: "ocamlformat",
1015
- command: ["ocamlformat", "-i", "$FILE"],
1016
- extensions: [".ml", ".mli"],
1017
- async detect(cwd) {
1018
- const hasBinary = (await which("ocamlformat")) !== null;
1019
- if (!hasBinary)
1020
- return false;
1021
- const configs = [".ocamlformat"];
1022
- const found = await findUp(configs, cwd);
1023
- return found.length > 0;
1024
- },
1025
- };
1026
- export const clangFormatFormatter = {
1027
- name: "clang-format",
1028
- command: ["clang-format", "-i", "$FILE"],
1029
- extensions: [".c", ".cc", ".cpp", ".cxx", ".h", ".hpp", ".ino"],
1030
- async detect(cwd) {
1031
- const hasBinary = (await which("clang-format")) !== null;
1032
- if (!hasBinary)
1033
- return false;
1034
- const configs = [".clang-format", "_clang-format"];
1035
- const found = await findUp(configs, cwd);
1036
- return found.length > 0;
1037
- },
1038
- };
1039
- export const ktlintFormatter = {
1040
- name: "ktlint",
1041
- command: ["ktlint", "-F", "$FILE"],
1042
- extensions: [".kt", ".kts"],
1043
- lenientExitCode: "lint-autofix: `ktlint -F` autocorrects what it can and then exits 1 if " +
1044
- "any lint error remains unfixed — the documented CLI contract (ktlint " +
1045
- "exits nonzero whenever violations are reported, and -F does not suppress " +
1046
- "the ones it cannot correct).",
1047
- lenientStatuses: [1],
1048
- async resolveCommand(filePath, _cwd) {
1049
- const inPath = await which("ktlint");
1050
- if (inPath)
1051
- return [inPath, "-F", filePath];
1052
- return resolveManagedSmartDefaultCommand("ktlint", filePath, ["-F"]);
1053
- },
1054
- detect: managedToolDetect("ktlint", undefined, async () => (await which("ktlint")) !== null),
1055
- };
1056
- export const ktfmtFormatter = {
1057
- name: "ktfmt",
1058
- // ktfmt formats in place when given a file path (no flag needed).
1059
- command: ["ktfmt", "$FILE"],
1060
- extensions: [".kt", ".kts"],
1061
- // #2468: ktfmt's CLI never reads a project's Gradle `ktfmt { googleStyle()
1062
- // | kotlinLangStyle() }` selection — style is CLI-flag-only
1063
- // (`--google-style`/`--kotlinlang-style`, verified against ktfmt v0.63's
1064
- // own arg parser). Carry the nearest module's declared style through;
1065
- // `undefined` (no declaration, unreadable/unparseable manifest, or an
1066
- // unsupported style like the removed `dropboxStyle()`) falls back to the
1067
- // bare invocation, unchanged from pre-#2468 behavior.
1068
- async resolveCommand(filePath, _cwd) {
1069
- const styleFlag = await resolveKtfmtGradleStyle(filePath);
1070
- const inPath = await which("ktfmt");
1071
- if (inPath)
1072
- return styleFlag ? [inPath, styleFlag, filePath] : [inPath, filePath];
1073
- const { ensureTool } = await import("./installer/index.js");
1074
- const installed = await ensureTool("ktfmt");
1075
- // #2413: which() and ensureTool (PATH/global/managed + install) both
1076
- // failed, and `detect()` gates only on hasKtfmtConfig — no binary probe —
1077
- // so a config-only project reaches here with ktfmt absent. The static
1078
- // command is bare `ktfmt`; spawning it reproduces the ENOENT class.
1079
- if (!installed)
1080
- return FORMATTER_UNAVAILABLE;
1081
- return styleFlag ? [installed, styleFlag, filePath] : [installed, filePath];
1082
- },
1083
- async detect(cwd) {
1084
- // Opt-in only: ktfmt becomes the formatter when the project elects it,
1085
- // otherwise ktlint stays the Kotlin smart-default (#129).
1086
- return hasKtfmtConfig(cwd);
1087
- },
1088
- };
1089
- export const rubocopFormatter = {
1090
- name: "rubocop",
1091
- command: ["rubocop", "-a", "--no-color", "$FILE"],
1092
- extensions: [".rb", ".rake", ".gemspec", ".ru"],
1093
- lenientExitCode: "lint-autofix: `rubocop -a` exits 1 whenever ANY offense remains after it " +
1094
- "has already rewritten the file. Verified locally (rubocop on Ruby 3.4): a " +
1095
- "file with an unfixable Lint/UselessAssignment exits 1, and even a " +
1096
- "tidy file exits 1 on an unfixable Style/Documentation offense — nonzero " +
1097
- "is the normal outcome, not a failure.",
1098
- lenientStatuses: [1],
1099
- async resolveCommand(filePath, cwd) {
1100
- if (await canUseBundleExec(cwd))
1101
- return ["bundle", "exec", "rubocop", "-a", "--no-color", filePath];
1102
- return null;
1103
- },
1104
- async detect(cwd) {
1105
- if (!hasRubocopConfig(cwd))
1106
- return false;
1107
- if ((await which("rubocop")) !== null)
1108
- return true;
1109
- await tryLazyInstallFormatterTool("rubocop", cwd);
1110
- return (await which("rubocop")) !== null;
1111
- },
1112
- };
1113
- export const standardrbFormatter = {
1114
- name: "standardrb",
1115
- command: ["standardrb", "--fix", "$FILE"],
1116
- extensions: [".rb", ".rake"],
1117
- lenientExitCode: "lint-autofix: standardrb is a RuboCop wrapper and inherits its exit " +
1118
- "contract — `--fix` exits 1 when offenses remain after the rewrite.",
1119
- lenientStatuses: [1],
1120
- async resolveCommand(filePath, cwd) {
1121
- if (await canUseBundleExec(cwd))
1122
- return ["bundle", "exec", "standardrb", "--fix", filePath];
1123
- return null;
1124
- },
1125
- async detect(cwd) {
1126
- if (!hasStandardrbConfig(cwd))
1127
- return false;
1128
- return (await which("standardrb")) !== null;
1129
- },
1130
- };
1131
- export const gleamFormatter = {
1132
- name: "gleam",
1133
- command: ["gleam", "format", "$FILE"],
1134
- extensions: [".gleam"],
1135
- async detect(cwd) {
1136
- // Present if gleam.toml exists (any Gleam project)
1137
- const found = await findUp(["gleam.toml"], cwd);
1138
- if (found.length > 0)
1139
- return (await which("gleam")) !== null;
1140
- return false;
1141
- },
1142
- };
1143
- export const typstyleFormatter = {
1144
- name: "typstyle",
1145
- command: ["typstyle", "-i", "$FILE"],
1146
- extensions: [".typ", ".typc"],
1147
- async resolveCommand(filePath) {
1148
- const inPath = await which("typstyle");
1149
- if (inPath)
1150
- return [inPath, "-i", filePath];
1151
- return resolveManagedSmartDefaultCommand("typstyle", filePath, ["-i"]);
1152
- },
1153
- detect: managedToolDetect("typstyle", undefined, async () => (await which("typstyle")) !== null),
1154
- };
1155
- export const terraformFormatter = {
1156
- name: "terraform",
1157
- command: ["terraform", "fmt", "$FILE"],
1158
- extensions: [".tf", ".tfvars"],
1159
- async detect(_cwd) {
1160
- return (await which("terraform")) !== null;
1161
- },
1162
- };
1163
- export const terragruntHclFormatter = {
1164
- name: "terragrunt-hcl",
1165
- command: ["terragrunt", "hcl", "fmt", "--file", "$FILE"],
1166
- extensions: [],
1167
- filenames: TERRAGRUNT_FILENAMES,
1168
- // Strict (the #1337 default): verified exit 0 on a successful in-place format
1169
- // against terragrunt v1.1.2. A binary predating the `hcl` command group exits
1170
- // nonzero and touches nothing, which unguarded reads as "already formatted".
1171
- async detect(_cwd) {
1172
- return (await which("terragrunt")) !== null;
1173
- },
1174
- };
1175
- export const phpCsFixerFormatter = {
1176
- name: "php-cs-fixer",
1177
- command: ["php-cs-fixer", "fix", "$FILE"],
1178
- extensions: [".php"],
1179
- // #2472: php-cs-fixer does NOT walk up parent directories looking for its
1180
- // own config the way prettier/biome/eslint do (verified against upstream
1181
- // `computeConfigFiles()` — see `resolvePhpCsFixerConfig`'s doc comment),
1182
- // and `formatFile` spawns with cwd = the FILE's own directory, which is
1183
- // not necessarily where the ancestor config `detect()` found actually
1184
- // lives. Always resolve the binary explicitly here (vendor/bin first,
1185
- // then global) rather than falling through to the static `command` above
1186
- // — that static command can never carry `--config`, so a config found at
1187
- // an ancestor would silently be dropped whenever the vendor lookup here
1188
- // missed but a global binary still resolved. `--config` is attached
1189
- // whenever a config resolves, even when it sits in the file's own
1190
- // directory (AC3): unlike the pre-#2472 code, correctness no longer
1191
- // depends on php-cs-fixer's own (nonexistent) upward search.
1192
- async resolveCommand(filePath, cwd) {
1193
- const configPath = resolvePhpCsFixerConfig(filePath);
1194
- const binary = (await findInVendorBin("php-cs-fixer", cwd)) ??
1195
- (await which("php-cs-fixer"));
1196
- const managed = await resolveManagedFormatterCommand("php-cs-fixer", filePath, [], findInVendorBin, cwd);
1197
- if (managed === FORMATTER_UNAVAILABLE)
1198
- return FORMATTER_UNAVAILABLE;
1199
- const resolved = binary ?? managed[0];
1200
- // #2413/#2472 review F4: both probes (vendor/bin, then PATH) have
1201
- // PROVEN the binary is absent — returning `null` here would fall back
1202
- // to the static `command` above, which is the SAME bare
1203
- // `php-cs-fixer` this just failed to find, spawning it only to
1204
- // re-observe the ENOENT already known. Report the proven-missing
1205
- // state instead so `formatFile` skips the wasted spawn.
1206
- if (!resolved)
1207
- return FORMATTER_UNAVAILABLE;
1208
- return configPath
1209
- ? [resolved, "fix", "--config", configPath, filePath]
1210
- : [resolved, "fix", filePath];
1211
- },
1212
- detect: managedToolDetect("php-cs-fixer", async (cwd) => (await findUp([".php-cs-fixer.php", ".php-cs-fixer.dist.php"], cwd))
1213
- .length > 0, async (cwd) => Boolean((await findInVendorBin("php-cs-fixer", cwd)) ||
1214
- (await which("php-cs-fixer")))),
1215
- };
1216
- export const csharpierFormatter = {
1217
- name: "csharpier",
1218
- // CSharpier ≥1.0 is a standalone `csharpier format <file>`; the `dotnet
1219
- // csharpier <file>` form was removed (a bare `dotnet csharpier` now errors
1220
- // "a dotnet-prefixed executable with this name could not be found"). Keep the
1221
- // legacy form as a fallback for CSharpier 0.x via resolveCommand.
1222
- command: ["csharpier", "format", "$FILE"],
1223
- extensions: [".cs"],
1224
- async resolveCommand(filePath, _cwd) {
1225
- if ((await which("csharpier")) !== null) {
1226
- return ["csharpier", "format", filePath];
1227
- }
1228
- // CSharpier 0.x: invoked through the dotnet driver.
1229
- if ((await which("dotnet")) !== null) {
1230
- const legacy = await probeToolAsync("dotnet", ["csharpier", "--version"], { timeout: 5000 });
1231
- if (!legacy.error && legacy.status === 0) {
1232
- return ["dotnet", "csharpier", filePath];
1233
- }
1234
- }
1235
- return null;
1236
- },
1237
- async detect(_cwd) {
1238
- // CSharpier ≥1.0 standalone binary …
1239
- if ((await which("csharpier")) !== null)
1240
- return true;
1241
- // … or the legacy dotnet-driver form (CSharpier 0.x).
1242
- if ((await which("dotnet")) === null)
1243
- return false;
1244
- const result = await probeToolAsync("dotnet", ["csharpier", "--version"], {
1245
- timeout: 5000,
1246
- });
1247
- return !result.error && result.status === 0;
1248
- },
1249
- };
1250
- export const fantomasFormatter = {
1251
- name: "fantomas",
1252
- command: ["fantomas", "$FILE"],
1253
- extensions: [".fs", ".fsi", ".fsx"],
1254
- async detect(_cwd) {
1255
- return (await which("fantomas")) !== null;
1256
- },
1257
- };
1258
- export const swiftformatFormatter = {
1259
- name: "swiftformat",
1260
- command: ["swiftformat", "$FILE"],
1261
- extensions: [".swift"],
1262
- async detect(_cwd) {
1263
- return (await which("swiftformat")) !== null;
1264
- },
1265
- };
1266
- export const styluaFormatter = {
1267
- name: "stylua",
1268
- command: ["stylua", "$FILE"],
1269
- extensions: [".lua"],
1270
- resolveCommand: managedFormatterResolver("stylua", [], findLocalBinUpwards),
1271
- detect: managedToolDetect("stylua", async (cwd) => (await findUp(["stylua.toml", ".stylua.toml"], cwd)).length > 0, async (cwd) => Boolean(findLocalBinUpwards("stylua", cwd) || (await which("stylua")))),
1272
- };
1273
- export const ormoluFormatter = {
1274
- name: "ormolu",
1275
- command: ["ormolu", "--mode", "inplace", "$FILE"],
1276
- extensions: [".hs", ".lhs"],
1277
- async detect(_cwd) {
1278
- return (await which("ormolu")) !== null;
1279
- },
1280
- };
1281
- export const taploFormatter = {
1282
- name: "taplo",
1283
- command: ["taplo", "fmt", "$FILE"],
1284
- extensions: [".toml"],
1285
- async resolveCommand(filePath, _cwd) {
1286
- const inPath = await which("taplo");
1287
- if (inPath)
1288
- return [inPath, "fmt", filePath];
1289
- return resolveManagedSmartDefaultCommand("taplo", filePath, ["fmt"]);
1290
- },
1291
- detect: managedToolDetect("taplo", undefined, async () => (await which("taplo")) !== null),
1292
- };
1293
- export const googleJavaFormatFormatter = {
1294
- name: "google-java-format",
1295
- command: ["google-java-format", "--replace", "$FILE"],
1296
- extensions: [".java"],
1297
- resolveCommand: managedFormatterResolver("google-java-format", ["--replace"]),
1298
- detect: managedToolDetect("google-java-format", hasGoogleJavaFormatConfig, async () => (await which("google-java-format")) !== null),
1299
- };
1300
- export const cljfmtFormatter = {
1301
- name: "cljfmt",
1302
- command: ["cljfmt", "fix", "$FILE"],
1303
- extensions: [".clj", ".cljc", ".cljs"],
1304
- resolveCommand: managedFormatterResolver("cljfmt", ["fix"]),
1305
- detect: managedToolDetect("cljfmt", hasCljfmtConfig, async () => (await which("cljfmt")) !== null),
1306
- };
1307
- export const cmakeFormatFormatter = {
1308
- name: "cmake-format",
1309
- command: ["cmake-format", "-i", "$FILE"],
1310
- extensions: [".cmake"],
1311
- resolveCommand: managedFormatterResolver("cmake-format", ["-i"]),
1312
- detect: managedToolDetect("cmake-format", hasCmakeFormatConfig, async () => (await which("cmake-format")) !== null),
1313
- };
1314
- export const cueFormatter = {
1315
- name: "cue",
1316
- // `cue fmt <file>` rewrites in place already. There is no `-w`: the only
1317
- // flags are --check, -d/--diff and --files, so `cue fmt -w` aborts with
1318
- // "unknown shorthand flag: 'w'" on every .cue write (verified on cue
1319
- // v0.17.1).
1320
- command: ["cue", "fmt", "$FILE"],
1321
- extensions: [".cue"],
1322
- async detect(_cwd) {
1323
- return (await which("cue")) !== null;
1324
- },
1325
- };
1326
- /**
1327
- * The PowerShell one-liner behind `psscriptanalyzer-format`.
1328
- *
1329
- * #1337 audit finding: without `$ErrorActionPreference = 'Stop'`, a failing
1330
- * `Invoke-Formatter` is a NON-TERMINATING error — pwsh still exits 0, so this
1331
- * formatter could never report a failure through the strict seam and the #1336
1332
- * silent-no-op class survived intact for `.ps1/.psm1/.psd1`. Verified: an
1333
- * `Invoke-Formatter` argument-validation failure exits 0 under the old script.
1334
- *
1335
- * Two more defects fixed here rather than left as landmines:
1336
- * - the old script ran `Set-Content -Value $formatted` even when `$formatted`
1337
- * was `$null`, which TRUNCATES the file it was asked to format;
1338
- * - single-quoted interpolation broke on any path containing an apostrophe.
1339
- * An empty/whitespace-only file exits 0 without touching anything, so "nothing
1340
- * to format" stays a clean no-op rather than a reported failure.
1341
- *
1342
- * `settingsPath`, when the project has one (#1572 review F2), is passed
1343
- * straight to `Invoke-Formatter -Settings`. Gating selection on the file's
1344
- * presence without ever reading its rules would run the project through the
1345
- * stock `CodeFormatting` ruleset regardless of what it declared — the same
1346
- * stock-style imposition #1144 banned for the other config-first formatters.
1347
- */
1348
- function psScriptAnalyzerCommand(filePath, settingsPath) {
1349
- // PowerShell single-quoted strings escape an apostrophe by doubling it.
1350
- const quoted = filePath.replace(/'/g, "''");
1351
- const settingsArg = settingsPath
1352
- ? ` -Settings '${settingsPath.replace(/'/g, "''")}'`
1353
- : "";
1354
- return [
1355
- "$ErrorActionPreference = 'Stop'",
1356
- `$p = '${quoted}'`,
1357
- "$content = Get-Content -Raw -LiteralPath $p",
1358
- "if ([string]::IsNullOrWhiteSpace($content)) { exit 0 }",
1359
- `$formatted = Invoke-Formatter -ScriptDefinition $content${settingsArg}`,
1360
- "if ($null -eq $formatted) { throw 'Invoke-Formatter returned no output' }",
1361
- "Set-Content -LiteralPath $p -Value $formatted",
1362
- ].join("; ");
1363
- }
1364
- export const psscriptanalyzerFormatFormatter = {
1365
- name: "psscriptanalyzer-format",
1366
- command: ["pwsh", "-NoProfile", "-Command", psScriptAnalyzerCommand("$FILE")],
1367
- extensions: [".ps1", ".psm1", ".psd1"],
1368
- async resolveCommand(filePath, cwd) {
1369
- const pwsh = (await which("pwsh")) ?? (await which("powershell"));
1370
- if (!pwsh)
1371
- return null;
1372
- const settingsPath = findPSScriptAnalyzerConfigPath(cwd);
1373
- return [
1374
- pwsh,
1375
- "-NoProfile",
1376
- "-Command",
1377
- psScriptAnalyzerCommand(filePath, settingsPath),
1378
- ];
1379
- },
1380
- async detect(_cwd) {
1381
- const pwsh = (await which("pwsh")) ?? (await which("powershell"));
1382
- if (!pwsh)
1383
- return false;
1384
- // Check PSScriptAnalyzer module is available
1385
- const result = await probeToolAsync(pwsh, [
1386
- "-NoProfile",
1387
- "-Command",
1388
- "Get-Module -ListAvailable PSScriptAnalyzer | Select-Object -First 1 -ExpandProperty Name",
1389
- ], { timeout: 5_000 });
1390
- return (result.stdout ?? "").includes("PSScriptAnalyzer");
1391
- },
1392
- };
1393
- // --- Registry ---
1394
- // Exported for the formatter/policy drift guard (tests/clients/
1395
- // formatter-policy-consistency.test.ts, #1135): the test cross-checks these
1396
- // definitions against tool-policy.ts's FORMATTER_POLICY_BY_EXTENSION so the two
1397
- // hand-maintained inverse mappings can never silently diverge.
1398
- export const ALL_FORMATTERS = [
1399
- biomeFormatter,
1400
- prettierFormatter,
1401
- oxfmtFormatter,
1402
- ruffFormatter,
1403
- blackFormatter,
1404
- sqlfluffFormatter,
1405
- gofmtFormatter,
1406
- rustfmtFormatter,
1407
- zigFormatter,
1408
- dartFormatter,
1409
- shfmtFormatter,
1410
- nixfmtFormatter,
1411
- mixFormatter,
1412
- ocamlformatFormatter,
1413
- clangFormatFormatter,
1414
- ktlintFormatter,
1415
- ktfmtFormatter,
1416
- terraformFormatter,
1417
- terragruntHclFormatter,
1418
- phpCsFixerFormatter,
1419
- csharpierFormatter,
1420
- fantomasFormatter,
1421
- swiftformatFormatter,
1422
- styluaFormatter,
1423
- ormoluFormatter,
1424
- rubocopFormatter,
1425
- standardrbFormatter,
1426
- gleamFormatter,
1427
- typstyleFormatter,
1428
- taploFormatter,
1429
- googleJavaFormatFormatter,
1430
- cljfmtFormatter,
1431
- cmakeFormatFormatter,
1432
- cueFormatter,
1433
- psscriptanalyzerFormatFormatter,
1434
- ];
1435
- // Basenames claimed by a filename-keyed formatter, e.g. terragrunt.hcl.
1436
- const FILENAME_FORMATTER_BASENAMES = new Set(ALL_FORMATTERS.flatMap((f) => f.filenames ?? []));
1437
- // Cache for detection results - stores array of enabled formatter names per cwd+ext
1438
- const detectionCache = new BoundedLruCache(32);
1439
- // The signature is immutable for a cache generation. This memo is separate
1440
- // from detectionCache because a cwd can have several extension entries, and a
1441
- // warm lookup must not repeat the ancestor walk or stat matched configs.
1442
- const formatterSignatureFlights = new BoundedLruCache(32);
1443
- const formatterCacheGeneration = createGenerationSource("formatter-cache");
1444
- // These are the formatter configuration files consulted by the policy helpers
1445
- // above. Their metadata is captured by the cold detection signature. The
1446
- // write-result seam invalidates that signature when a config path changes, so
1447
- // detection re-runs even when PATH and installed tools are unchanged. A
1448
- // filename a `has*Config` check reads but this list omits is invisible to the
1449
- // cache: the signature never moves when that file is added, so a project that
1450
- // opts in AFTER the first `getFormattersForFile` call for its cwd keeps
1451
- // getting the stale (pre-opt-in) cached answer for the rest of the session
1452
- // (#1572 review F1 — proved for psscriptanalyzer-format's settings file;
1453
- // swept against every `EXPLICIT_FORMATTER_CONFIG_CHECKS` entry below, which
1454
- // turned up the same gap for google-java-format, cljfmt, cmake-format, the
1455
- // Kotlin/Spotless gradle files, sqlfluff's setup.cfg, and oxfmt's
1456
- // vite-plus.json / additional vite.config extensions).
1457
- const FORMATTER_CONFIG_FILES = [
1458
- "package.json",
1459
- "biome.json",
1460
- "biome.jsonc",
1461
- ".prettierrc",
1462
- ".prettierrc.json",
1463
- ".prettierrc.yml",
1464
- ".prettierrc.yaml",
1465
- ".prettierrc.js",
1466
- ".prettierrc.cjs",
1467
- ".prettierrc.mjs",
1468
- "prettier.config.js",
1469
- "prettier.config.cjs",
1470
- "prettier.config.mjs",
1471
- "prettier.config.ts",
1472
- "pyproject.toml",
1473
- "ruff.toml",
1474
- ".ruff.toml",
1475
- "black.toml",
1476
- ".black",
1477
- "tox.ini",
1478
- "setup.cfg",
1479
- "requirements.txt",
1480
- "Pipfile",
1481
- ".sqlfluff",
1482
- ".rubocop.yml",
1483
- ".rubocop.yaml",
1484
- "Gemfile",
1485
- ".clang-format",
1486
- "_clang-format",
1487
- ".php-cs-fixer.php",
1488
- ".php-cs-fixer.dist.php",
1489
- "stylua.toml",
1490
- ".stylua.toml",
1491
- ".ocamlformat",
1492
- ".editorconfig",
1493
- ".google-java-format",
1494
- ".ktfmt",
1495
- ".ktfmt.kts",
1496
- "build.gradle.kts",
1497
- "build.gradle",
1498
- "settings.gradle.kts",
1499
- "settings.gradle",
1500
- ".cljfmt.edn",
1501
- "cljfmt.edn",
1502
- ".cljfmt",
1503
- ".cmake-format",
1504
- ".cmake-format.yaml",
1505
- ".cmake-format.yml",
1506
- ".cmake-format.json",
1507
- ".cmake-format.py",
1508
- "cmake-format.py",
1509
- "cmake-format.yaml",
1510
- "cmake-format.yml",
1511
- "oxfmt.toml",
1512
- ".oxfmtrc.json",
1513
- "vite-plus.json",
1514
- "vite.config.ts",
1515
- "vite.config.mts",
1516
- "vite.config.cts",
1517
- "vite.config.js",
1518
- "vite.config.mjs",
1519
- "vite.config.cjs",
1520
- "PSScriptAnalyzerSettings.psd1",
1521
- "ScriptAnalyzerSettings.psd1",
1522
- // #1595 sweep additions.
1523
- ".csharpierrc",
1524
- ".csharpierrc.json",
1525
- ".csharpierrc.yaml",
1526
- ".csharpierrc.yml",
1527
- ".ormolu",
1528
- "taplo.toml",
1529
- ".taplo.toml",
1530
- ".terraform.lock.hcl",
1531
- ".swiftformat",
1532
- ".fantomasignore",
1533
- ".formatter.exs",
1534
- ];
1535
- async function formatterConfigSignature(cwd) {
1536
- const paths = await findUp(FORMATTER_CONFIG_FILES, cwd);
1537
- const parts = await Promise.all(paths.sort(compareOrdinal).map(async (filePath) => {
1538
- try {
1539
- const stat = await fs.stat(filePath);
1540
- return `${filePath}:${stat.mtimeMs}:${stat.size}`;
1541
- }
1542
- catch {
1543
- return `${filePath}:missing`;
1544
- }
1545
- }));
1546
- return parts.join("|");
1547
- }
1548
- async function getFormatterConfigSignature(cwd, normalizedCwd) {
1549
- const existing = formatterSignatureFlights.get(normalizedCwd);
1550
- if (existing)
1551
- return existing.promise;
1552
- const promise = formatterConfigSignature(cwd).finally(() => {
1553
- const current = formatterSignatureFlights.get(normalizedCwd);
1554
- if (current?.promise === promise)
1555
- formatterSignatureFlights.delete(normalizedCwd);
1556
- });
1557
- formatterSignatureFlights.set(normalizedCwd, { promise });
1558
- return promise;
1559
- }
1560
- // --- Public API ---
1561
- export async function getFormattersForFile(filePath, cwd) {
1562
- const ext = path.extname(filePath).toLowerCase();
1563
- const base = path.basename(filePath).toLowerCase();
1564
- // Filename-keyed formatters (e.g. terragrunt.hcl) can share an extension
1565
- // with unrelated files in the same dir (.terraform.lock.hcl next to
1566
- // terragrunt.hcl). Fold the basename into the cache key only when a
1567
- // filename-based formatter actually applies, so a plain .hcl file cached
1568
- // first doesn't poison the cache for terragrunt.hcl/root.hcl, or vice versa.
1569
- const normalizedCwd = normalizeMapKey(cwd);
1570
- const cacheKey = FILENAME_FORMATTER_BASENAMES.has(base)
1571
- ? `${normalizedCwd}:${ext}:${base}`
1572
- : `${normalizedCwd}:${ext}`;
1573
- // A warm entry is authoritative until the write-result seam invalidates its
1574
- // config path. This is the only way to make the warm path free of config
1575
- // polling while still reacting immediately to pi-authored create/remove/
1576
- // change events. External editor changes remain a documented session-boundary
1577
- // limitation, like other write-result-owned freshness seams.
1578
- let cached = detectionCache.get(normalizedCwd);
1579
- if (cached?.entries.has(cacheKey)) {
1580
- const enabledNames = cached.entries.get(cacheKey);
1581
- const selectedFormatterName = enabledNames && enabledNames.length > 0 ? enabledNames[0] : null;
1582
- logLatency({
1583
- type: "phase",
1584
- phase: "formatter_selected",
1585
- filePath,
1586
- durationMs: 0,
1587
- metadata: {
1588
- formatter: selectedFormatterName,
1589
- reason: "cache",
1590
- cached: true,
1591
- outcome: "hit",
1592
- cwd,
1593
- },
1594
- });
1595
- if (!enabledNames || enabledNames.length === 0)
1596
- return [];
1597
- return ALL_FORMATTERS.filter((f) => enabledNames.includes(f.name));
1598
- }
1599
- if (!cached) {
1600
- const generation = formatterCacheGeneration.capture();
1601
- const configSignature = await getFormatterConfigSignature(cwd, normalizedCwd);
1602
- // An invalidation can happen while the first signature walk is in flight.
1603
- // Do not publish a pre-invalidation signature into the new generation.
1604
- if (!generation.isCurrent()) {
1605
- return getFormattersForFile(filePath, cwd);
1606
- }
1607
- // Another cold caller for this cwd can finish the shared signature flight
1608
- // first. Re-read the LRU before installing an object so the later caller
1609
- // merges its extension entry into the existing object instead of replacing
1610
- // the earlier entry.
1611
- cached = detectionCache.get(normalizedCwd);
1612
- if (!cached) {
1613
- cached = { signature: configSignature, entries: new Map() };
1614
- detectionCache.set(normalizedCwd, cached);
1615
- }
1616
- }
1617
- // Detect formatters for this extension (or exact filename, e.g. terragrunt.hcl)
1618
- const matching = ALL_FORMATTERS.filter((f) => f.extensions.includes(ext) || f.filenames?.includes(base));
1619
- const formatterPolicy = getFormatterPolicyForFile(filePath);
1620
- const smartDefaultFormatterName = getSmartDefaultFormatterName(filePath);
1621
- const candidateFormatters = formatterPolicy?.formatterNames?.length
1622
- ? matching.filter((f) => formatterPolicy.formatterNames.includes(f.name))
1623
- : matching;
1624
- let selected;
1625
- /**
1626
- * One entry per candidate this pass actually ASKED, in the order it asked
1627
- * them (#1539). Candidates eliminated without a probe — an explicit-config
1628
- * decision is pure filesystem — contribute nothing, which is the point: a
1629
- * decision that never probed cannot have been degraded by a probe.
1630
- */
1631
- const candidateOutcomes = [];
1632
- if (formatterPolicy) {
1633
- const explicitlyConfigured = candidateFormatters.filter((formatter) => hasExplicitFormatterConfig(formatter.name, cwd, ext));
1634
- if (explicitlyConfigured.length > 0) {
1635
- // A formatter with explicit project config was found — use it.
1636
- // Prefer the policy's defaultFormatter only if it has explicit config,
1637
- // otherwise pick the first explicitly-configured formatter.
1638
- selected = formatterPolicy.defaultFormatter
1639
- ? (explicitlyConfigured.find((f) => f.name === formatterPolicy.defaultFormatter) ?? explicitlyConfigured[0])
1640
- : explicitlyConfigured[0];
1641
- }
1642
- else if (smartDefaultFormatterName) {
1643
- // Reached only when explicitlyConfigured is empty, so no candidate
1644
- // has explicit config. Safe to activate the smart-default.
1645
- const smartDefaultFormatter = candidateFormatters.find((f) => f.name === smartDefaultFormatterName);
1646
- if (smartDefaultFormatter) {
1647
- const autoInstallToolId = getAutoInstallToolIdForFormatter(smartDefaultFormatter.name);
1648
- if (autoInstallToolId) {
1649
- selected = smartDefaultFormatter;
1650
- }
1651
- else {
1652
- const { detected, error, outcome } = await detectCandidate(smartDefaultFormatter, cwd);
1653
- candidateOutcomes.push(outcome);
1654
- // This branch never caught a throwing detection, and still
1655
- // does not: `detectCandidate` catches only so the probes survive.
1656
- if (error !== undefined)
1657
- throw error;
1658
- if (detected)
1659
- selected = smartDefaultFormatter;
1660
- }
1661
- }
1662
- }
1663
- }
1664
- else {
1665
- for (const formatter of candidateFormatters) {
1666
- const { detected, error, outcome } = await detectCandidate(formatter, cwd);
1667
- candidateOutcomes.push(outcome);
1668
- if (error !== undefined) {
1669
- // pi-lens-ignore: missing-error-propagation — optional formatter detection, skip on failure
1670
- logExtension({
1671
- subsystem: "format",
1672
- message: `Detection failed for ${formatter.name}: ${error instanceof Error ? error.message : String(error)}`,
1673
- metadata: { formatter: formatter.name, cwd },
1674
- });
1675
- continue;
1676
- }
1677
- if (detected) {
1678
- selected = formatter;
1679
- break;
1680
- }
1681
- }
1682
- }
1683
- const enabled = selected ? [selected] : [];
1684
- // The #925/#1467 `wouldPoisonCache` shape: an empty result produced while a
1685
- // PATH probe was timing out is not a finding about this project. Caching it
1686
- // would survive until a config file's mtime or size changed, so leave the
1687
- // cache untouched and let the next turn re-detect.
1688
- // Which of THIS file's candidates are currently un-answered? Read off the
1689
- // binaries the candidates' own `detect()`s actually probed (#1539), so a
1690
- // stalled `which rustfmt` cannot stop a shell or Python detection from
1691
- // caching (#1495 review) AND a leftover transient verdict for a binary this
1692
- // pass never consulted cannot be blamed on this decision.
1693
- //
1694
- // This replaces the `command[0]` approximation, which missed both shapes of
1695
- // extra probe: a co-equal ALTERNATIVE (`pwsh` ?? `powershell`) and — the
1696
- // case the old comment wrongly called harmless — an install FALLBACK reached
1697
- // after the primary answered a GENUINE absence. `rustfmt` missing plus a
1698
- // stalled `which rustup` skips the lazy install, and nothing named `rustfmt`
1699
- // is transient, so the empty result used to cache for the session.
1700
- //
1701
- // Residual: the record covers this module's own `which()` only. A detection
1702
- // that reaches PATH some other way is invisible to it — ktlint's `detect()`
1703
- // falls back to the installer's `getToolPath("ktlint")`, which has its own
1704
- // probe and its own 24-hour cache. Widening the record to those is deferred to
1705
- // the installer-side follow-up rather than bolted on here.
1706
- const stalledCandidateCommands = [
1707
- ...new Set(candidateOutcomes.flatMap((outcome) => outcome.stalledCommands)),
1708
- ];
1709
- const poisonedByTransientProbe = enabled.length === 0 && stalledCandidateCommands.length > 0;
1710
- // A NON-empty result the poison guard cannot see (#1539): the winner won only
1711
- // because a candidate ahead of it was never asked. `candidateOutcomes` is in
1712
- // ask order and the loop stops at the winner, so every entry before the
1713
- // winner's is a candidate that lost — and an `unreachable` one did not lose
1714
- // on the merits. Caching this would hand the session to the runner-up until
1715
- // a config file's mtime or size changed.
1716
- const winnerIndex = selected
1717
- ? candidateOutcomes.findIndex((outcome) => outcome.name === selected.name)
1718
- : -1;
1719
- const unreachablePreferred = candidateOutcomes
1720
- .slice(0, winnerIndex === -1 ? 0 : winnerIndex)
1721
- .filter((outcome) => outcome.verdict === "unreachable")
1722
- .map((outcome) => outcome.name);
1723
- const degradedSelection = unreachablePreferred.length > 0;
1724
- let selectionReason;
1725
- if (poisonedByTransientProbe) {
1726
- selectionReason = "probe-timeout";
1727
- }
1728
- else if (degradedSelection) {
1729
- // The reason now describes how the winner WON, not just what the config
1730
- // looks like. "explicit-config" on a selection whose preferred candidate
1731
- // was never reachable was the most misleading record in this seam.
1732
- selectionReason = "preferred-unreachable";
1733
- }
1734
- else if (!selected) {
1735
- selectionReason = "none";
1736
- }
1737
- else if (!formatterPolicy) {
1738
- selectionReason = "detect";
1739
- }
1740
- else {
1741
- selectionReason = candidateFormatters.some((f) => hasExplicitFormatterConfig(f.name, cwd, ext))
1742
- ? "explicit-config"
1743
- : "smart-default";
1744
- }
1745
- const provisional = poisonedByTransientProbe || degradedSelection;
1746
- logLatency({
1747
- type: "phase",
1748
- phase: "formatter_selected",
1749
- filePath: filePath,
1750
- durationMs: 0,
1751
- metadata: {
1752
- formatter: selected?.name ?? null,
1753
- reason: selectionReason,
1754
- outcome: "miss",
1755
- cwd,
1756
- ...(provisional && {
1757
- cached: false,
1758
- stalledProbes: stalledCandidateCommands,
1759
- }),
1760
- ...(degradedSelection && { unreachablePreferred }),
1761
- },
1762
- });
1763
- // Provisional either way: not cached, so the next pass re-detects once the
1764
- // cooldown expires and the preferred formatter's recovery takes effect
1765
- // without waiting on a config-file edit.
1766
- if (provisional)
1767
- return enabled;
1768
- // Store the list of enabled formatter names in cache
1769
- const enabledNames = enabled.map((f) => f.name);
1770
- // An invalidation may have removed or replaced this object while detection
1771
- // awaited a tool probe. Never repopulate the old generation.
1772
- if (detectionCache.get(normalizedCwd) === cached) {
1773
- cached.entries.set(cacheKey, enabledNames);
1774
- }
1775
- return enabled;
1776
- }
1777
- /**
1778
- * Re-arm formatter availability: drop both the per-cwd selection cache and the
1779
- * PATH latches.
1780
- *
1781
- * #1895: this is the session-boundary reset. Both halves are required. The
1782
- * latches decide whether `which` runs at all, but `detectionCache` short-
1783
- * circuits ahead of them — a same-cwd lookup returns the previous verdict's
1784
- * formatter names without reaching a probe. Clearing only the latches
1785
- * therefore re-arms every directory EXCEPT the one the user is working in.
1786
- */
1787
- export function clearFormatterCache() {
1788
- formatterCacheGeneration.bump();
1789
- formatterSignatureFlights.clear();
1790
- detectionCache.clear();
1791
- resetWhichLatches();
1792
- }
1793
- const formatterConfigBasenames = new Set(FORMATTER_CONFIG_FILES.map((fileName) => fileName.toLowerCase()));
1794
- /** Invalidate selection when the write-result seam reports a config path. */
1795
- export function invalidateFormatterCacheForPath(filePath) {
1796
- const basename = filePath.includes("\\")
1797
- ? path.win32.basename(filePath)
1798
- : path.basename(filePath);
1799
- if (!formatterConfigBasenames.has(basename.toLowerCase()))
1800
- return;
1801
- clearFormatterCache();
1802
- }
1803
- /**
1804
- * Test-only internals access for the session-state registry probe (#1895):
1805
- * the conformance suite must arm all four pieces of state this reset claims
1806
- * to cover and prove none survives.
1807
- */
1808
- export function _getFormatterResetStateForTests() {
1809
- return {
1810
- whichLatchByCommand,
1811
- whichTransientCommands,
1812
- cooldownRecordedForRetryAtMs,
1813
- detectionCache,
1814
- formatterSignatureFlights,
1815
- };
1816
- }
1817
- export function clearFormatterRuntimeState() {
1818
- clearFormatterCache();
1819
- // NO `resetLazyInstallAttempts()` here (#1537 review F1). This function runs
1820
- // from `resetFormatService()`, which `handleTurnEnd` calls every turn — so
1821
- // clearing the lazy-install hold here made "held for the session" mean "held
1822
- // for a turn", and a failing install re-ran every turn. The hold's only reset
1823
- // is `session_start`'s block in runtime-session.ts.
1824
- }
1825
- const BOX_DRAWING_GLOBAL = /[\u2500-\u257F]/g;
1826
- /** Blank, or a rule made of box-drawing characters and spacing alone. */
1827
- const DECORATION_RULE = /^[\u2500-\u257F\s]*$/;
1828
- /** The renderer's section heading: one word, then box-drawing to end of line. */
1829
- const DECORATION_HEADING = /^\S+[ \t]+[\u2500-\u257F][\u2500-\u257F\s]*$/;
1830
- /**
1831
- * First line of `text` that actually carries a diagnostic.
1832
- *
1833
- * #1337 made nonzero exits user-visible, which put whatever this returns in
1834
- * front of the agent (`clients/pipeline.ts`) and in the end-of-turn summary
1835
- * (`clients/runtime-agent-end.ts`) — so "first line of stderr" is no longer
1836
- * good enough. Biome opens stderr with a decorated section rule
1837
- * (`format ━━━━━━━━━━━`), which as an error message is pure noise.
1838
- *
1839
- * Skips blank lines, pure box-drawing rules, and short banner headings; strips
1840
- * ANSI colour so the message stays readable in a plain-text surface.
1841
- */
1842
- export function firstDiagnosticLine(text) {
1843
- for (const raw of (text ?? "").split("\n")) {
1844
- const line = stripAnsi(raw).trimEnd();
1845
- if (isDecorativeLine(line))
1846
- continue;
1847
- return line.replace(BOX_DRAWING_GLOBAL, "").trim().slice(0, 300);
1848
- }
1849
- return undefined;
1850
- }
1851
- /**
1852
- * The two decoration SHAPES a renderer emits, and nothing else: a rule made of
1853
- * box-drawing characters alone (blank lines included), and a `<word> ━━━━`
1854
- * section heading whose remainder after the word is box-drawing only.
1855
- *
1856
- * The single normalization both readers above and below share, so the bounded
1857
- * tail cannot surface what the first-line reader already rejects (#3312 review
1858
- * R3-4: the tail put biome's `format ━━━━` banner back in front of the strict
1859
- * #1337 seam). Matching SHAPES rather than "contains a box character and is
1860
- * short" is #3312 review R4-1: the length heuristic deleted
1861
- * `━ traceback source excerpt`, a diagnostic whose own text opens with a box
1862
- * character. A line carrying any non-decorative text is never decoration.
1863
- */
1864
- function isDecorativeLine(line) {
1865
- return DECORATION_RULE.test(line) || DECORATION_HEADING.test(line);
1866
- }
1867
- const FORMATTER_ERROR_TAIL_LINES = 20;
1868
- /**
1869
- * Preserve a bounded tail of a formatter's diagnostic output.
1870
- *
1871
- * A traceback's first line is not actionable on its own (#3312). Keep the
1872
- * final lines, which include the exception and its message, while bounding
1873
- * the model-facing error when a formatter emits an unexpectedly large log.
1874
- *
1875
- * Decorative lines are dropped FIRST, by the same predicate `firstDiagnosticLine`
1876
- * uses: a tail that kept them would hand the strict #1337 seam the banner this
1877
- * module already decided is not a diagnostic, and every banner line it kept
1878
- * would evict a real traceback line from the bound.
1879
- */
1880
- export function diagnosticTail(text, maxLines = FORMATTER_ERROR_TAIL_LINES) {
1881
- const lines = [];
1882
- for (const raw of (text ?? "").split("\n")) {
1883
- const line = stripAnsi(raw).trimEnd();
1884
- if (isDecorativeLine(line))
1885
- continue;
1886
- lines.push(line.slice(0, 300));
1887
- }
1888
- if (lines.length === 0)
1889
- return undefined;
1890
- return lines.slice(-maxLines).join("\n");
1891
- }
1892
- /**
1893
- * Resolve a formatter command without allowing the static command fallback to
1894
- * bypass a resolver's style-preservation refusal (#1345). `null` means the
1895
- * primary command is unavailable; the explicit sentinel means formatting is
1896
- * forbidden for this file and must be returned before the static command is
1897
- * materialized.
1898
- */
1899
- async function resolveFormatterCommand(formatter, absolutePath, cwd) {
1900
- const agreement = establishToolAgreement(formatter.name, cwd);
1901
- if (agreement.decision === "decline") {
1902
- recordDegradationOnce({
1903
- kind: "formatter-agreement-unavailable",
1904
- subject: agreement.subject,
1905
- reason: agreement.reason,
1906
- });
1907
- return FORMATTER_AGREEMENT_UNAVAILABLE;
1908
- }
1909
- const resolved = formatter.resolveCommand
1910
- ? await formatter.resolveCommand(absolutePath, cwd)
1911
- : null;
1912
- if (resolved === SKIP_FORMATTING)
1913
- return SKIP_FORMATTING;
1914
- // The resolver proved the executable absent — DO NOT fall through to the
1915
- // static bare command, which is the same binary it just probed and missing
1916
- // (#2413). The caller turns this into a typed `unavailable` outcome, never a
1917
- // spawn and never a failure.
1918
- if (resolved === FORMATTER_UNAVAILABLE)
1919
- return FORMATTER_UNAVAILABLE;
1920
- if (resolved !== null)
1921
- return resolved;
1922
- const fallback = formatter.command.map((c) => c.replace("$FILE", absolutePath));
1923
- // Trust gate on the install-capable static fallback (#1334 S5): npx can
1924
- // DOWNLOAD packages, so an untrusted project treats the fallback as
1925
- // unavailable -- a skip, not a formatter failure; may converge next turn.
1926
- if (fallback[0] === "npx" &&
1927
- !assertInstallAllowed(`formatter npx fallback: ${formatter.name}`)) {
1928
- // No second ledger entry here (#1366 review): assertInstallAllowed just
1929
- // recorded the trust-refusal with this formatter's context — recording
1930
- // formatter-skip too would count one user-visible degradation twice.
1931
- return SKIP_FORMATTING;
1932
- }
1933
- return fallback;
1934
- }
1935
- export async function formatFile(filePath, formatter) {
1936
- try {
1937
- const absolutePath = path.resolve(filePath);
1938
- const cwd = path.dirname(absolutePath);
1939
- const formatterCwd = resolveToolCwd("formatter", formatter.name, absolutePath, { cwd: path.parse(absolutePath).root }).cwd;
1940
- const contentBefore = await fs.readFile(absolutePath, "utf-8");
1941
- // Resolve command: prefer local (venv/vendor/node_modules) over global.
1942
- // The shared seam must honor SKIP_FORMATTING before selecting the static
1943
- // command, including its npx fallback (#1345).
1944
- const cmd = await resolveFormatterCommand(formatter, absolutePath, cwd);
1945
- if (cmd === SKIP_FORMATTING) {
1946
- // Style-preserving refusal (#1144): no repo config and no detectable
1947
- // indentation to pin — formatting would impose the tool's stock style.
1948
- return { success: true, changed: false, outcome: "skipped" };
1949
- }
1950
- if (cmd === FORMATTER_UNAVAILABLE) {
1951
- // The resolver proved the executable absent (#2413): the oxfmt ENOENT
1952
- // trap and its siblings. This is unavailable infrastructure, not a code
1953
- // failure — success stays true so it is never counted as a failed file
1954
- // or requeued, and the typed outcome keeps it out of the failure bucket.
1955
- return {
1956
- success: true,
1957
- changed: false,
1958
- outcome: "unavailable",
1959
- error: `${formatter.name}: formatter executable not found`,
1960
- };
1961
- }
1962
- if (cmd === FORMATTER_AGREEMENT_UNAVAILABLE) {
1963
- return {
1964
- success: true,
1965
- changed: false,
1966
- outcome: "unavailable",
1967
- error: `${formatter.name}: project tool agreement could not be established`,
1968
- };
1969
- }
1970
- // Run formatter without blocking the event loop.
1971
- const result = await safeSpawnAsync(cmd[0], cmd.slice(1), {
1972
- timeout: 15000,
1973
- cwd: formatterCwd,
1974
- });
1975
- // A resolver that could NOT prove absence (it never probed PATH — e.g.
1976
- // black/sqlfluff, which only look in a venv) legitimately falls back to the
1977
- // bare static command. When that bare command is missing, the spawn boundary
1978
- // reports a typed `tool-not-found` failure — never a nonzero exit. Treat it
1979
- // as unavailable, not a formatting failure, closing the same #2413 class for
1980
- // the formatters whose `null` correctly means "static untried" (no
1981
- // oxfmt-specific string matching — this reads the typed spawn-failure kind).
1982
- if (result.spawnFailure?.kind === "tool-not-found") {
1983
- return {
1984
- success: true,
1985
- changed: false,
1986
- outcome: "unavailable",
1987
- error: result.spawnFailure.message,
1988
- };
1989
- }
1990
- // Strict by default (#1337): only a formatter with a documented
1991
- // benign-nonzero mode (`lenientExitCode`) may exit nonzero and still be
1992
- // read as a successful run. Everything else that exits nonzero never
1993
- // rewrote the file, and reporting {success: true, changed: false} there is
1994
- // indistinguishable from "already formatted" — the #1336 silent no-op.
1995
- const lenientOk = formatter.lenientExitCode !== undefined &&
1996
- result.status !== null &&
1997
- (formatter.lenientStatuses ?? []).includes(result.status);
1998
- if (result.error || (result.status !== 0 && !lenientOk)) {
1999
- return {
2000
- success: false,
2001
- changed: false,
2002
- outcome: "failed",
2003
- error: result.error?.message ||
2004
- diagnosticTail(result.stderr) ||
2005
- // biome, ktlint and `mix format` report on STDOUT; without this
2006
- // their diagnostic is discarded and the user is told only that
2007
- // the tool "exited with status 1".
2008
- diagnosticTail(result.stdout) ||
2009
- `${formatter.name} exited with status ${result.status}`,
2010
- };
2011
- }
2012
- // Check if content changed
2013
- const contentAfter = await fs.readFile(absolutePath, "utf-8");
2014
- const changed = contentBefore !== contentAfter;
2015
- return {
2016
- success: true,
2017
- changed,
2018
- outcome: changed ? "formatted" : "unchanged",
2019
- };
2020
- }
2021
- catch (err) {
2022
- return {
2023
- success: false,
2024
- changed: false,
2025
- outcome: "failed",
2026
- error: err instanceof Error ? err.message : String(err),
2027
- };
2028
- }
2029
- }
2030
- export function listAllFormatters() {
2031
- return ALL_FORMATTERS.map((f) => f.name);
2032
- }