@herbertgao/pi-extensions 2026.10.0 → 2026.10.2

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 (769) hide show
  1. package/README.md +16 -12
  2. package/THIRD_PARTY_NOTICES.md +3 -3
  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/.opencode/plugins/index.js +4 -0
  10. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +43 -3
  11. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +142 -59
  12. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  13. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  14. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  15. package/node_modules/@dietrichgebert/ponytail/README.md +52 -242
  16. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +3 -3
  17. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +4 -4
  18. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +70 -16
  19. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +8 -7
  20. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  21. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  22. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +46 -23
  23. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +39 -7
  24. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +20 -3
  25. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +3 -3
  26. package/node_modules/@dietrichgebert/ponytail/package.json +3 -3
  27. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +47 -17
  28. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +21 -7
  29. package/node_modules/@dietrichgebert/ponytail/skills/plugin.json +5 -0
  30. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  31. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -27
  32. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  33. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +21 -18
  34. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +12 -13
  35. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -52
  36. package/node_modules/@herbertgao/pi-agency/LICENSE +21 -0
  37. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  38. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  39. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  45. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  46. package/node_modules/@narumitw/pi-caffeinate/package.json +4 -4
  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 +1064 -0
  67. package/node_modules/pi-lens/README.md +115 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-22AVIGXS.js +560 -0
  70. package/node_modules/pi-lens/dist/chunk-27BBHSZC.js +106 -0
  71. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  72. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  73. package/node_modules/pi-lens/dist/chunk-34U7PLGE.js +23 -0
  74. package/node_modules/pi-lens/dist/chunk-3I5QFT3N.js +181 -0
  75. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  76. package/node_modules/pi-lens/dist/chunk-47D2BO4S.js +87 -0
  77. package/node_modules/pi-lens/dist/chunk-4RERDEXR.js +348 -0
  78. package/node_modules/pi-lens/dist/chunk-4W2PLC4J.js +27 -0
  79. package/node_modules/pi-lens/dist/chunk-52H5B5TC.js +202 -0
  80. package/node_modules/pi-lens/dist/chunk-54B42YRX.js +27 -0
  81. package/node_modules/pi-lens/dist/chunk-5RXBNSW2.js +266 -0
  82. package/node_modules/pi-lens/dist/chunk-5THXD6X5.js +201 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-5ZEJHV35.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6P62QVEA.js +118 -0
  86. package/node_modules/pi-lens/dist/chunk-6QIUX4EP.js +459 -0
  87. package/node_modules/pi-lens/dist/chunk-6VCD4VKZ.js +78 -0
  88. package/node_modules/pi-lens/dist/chunk-7F72PXY2.js +326 -0
  89. package/node_modules/pi-lens/dist/chunk-7FBF7NPR.js +998 -0
  90. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  91. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  92. package/node_modules/pi-lens/dist/chunk-AHDW3GPE.js +50 -0
  93. package/node_modules/pi-lens/dist/chunk-AJ3QXTRQ.js +756 -0
  94. package/node_modules/pi-lens/dist/chunk-AJVLWNFN.js +1610 -0
  95. package/node_modules/pi-lens/dist/chunk-AJXODZY6.js +112 -0
  96. package/node_modules/pi-lens/dist/chunk-ARFCXG7S.js +84 -0
  97. package/node_modules/pi-lens/dist/chunk-AS44CFQ4.js +708 -0
  98. package/node_modules/pi-lens/dist/chunk-AWE5TMPO.js +38 -0
  99. package/node_modules/pi-lens/dist/chunk-B3I2UUT4.js +4692 -0
  100. package/node_modules/pi-lens/dist/chunk-BRJKOBZP.js +331 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-C2AUYXQH.js +149 -0
  103. package/node_modules/pi-lens/dist/chunk-CGGAIP7J.js +29 -0
  104. package/node_modules/pi-lens/dist/chunk-D6QS3U6M.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-DFVMIKMQ.js +349 -0
  106. package/node_modules/pi-lens/dist/chunk-DIULIWKU.js +169 -0
  107. package/node_modules/pi-lens/dist/chunk-DQ2NZ7C4.js +2317 -0
  108. package/node_modules/pi-lens/dist/chunk-E2WFBRHM.js +213 -0
  109. package/node_modules/pi-lens/dist/chunk-E4MAEDPM.js +50 -0
  110. package/node_modules/pi-lens/dist/chunk-EOKRQ3SA.js +30 -0
  111. package/node_modules/pi-lens/dist/chunk-F3AU2TMM.js +2626 -0
  112. package/node_modules/pi-lens/dist/chunk-F3P7VQ7H.js +170 -0
  113. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  114. package/node_modules/pi-lens/dist/chunk-FJNKANOS.js +32 -0
  115. package/node_modules/pi-lens/dist/chunk-GUOGVTCQ.js +146 -0
  116. package/node_modules/pi-lens/dist/chunk-GVXA6CCE.js +18 -0
  117. package/node_modules/pi-lens/dist/chunk-HHYAELLJ.js +2334 -0
  118. package/node_modules/pi-lens/dist/chunk-HMFXILPV.js +83 -0
  119. package/node_modules/pi-lens/dist/chunk-HVUZXSWK.js +421 -0
  120. package/node_modules/pi-lens/dist/chunk-HWBZIGMJ.js +62 -0
  121. package/node_modules/pi-lens/dist/chunk-I3R7GKDR.js +17 -0
  122. package/node_modules/pi-lens/dist/chunk-I7SPGOWI.js +55 -0
  123. package/node_modules/pi-lens/dist/chunk-IIXOAGHR.js +38 -0
  124. package/node_modules/pi-lens/dist/chunk-IVP5KAB7.js +2250 -0
  125. package/node_modules/pi-lens/dist/chunk-IXEBJSUE.js +22 -0
  126. package/node_modules/pi-lens/dist/chunk-JOLITGCC.js +27 -0
  127. package/node_modules/pi-lens/dist/chunk-K6VYLB2U.js +145 -0
  128. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  129. package/node_modules/pi-lens/dist/chunk-LNAKEO63.js +78 -0
  130. package/node_modules/pi-lens/dist/chunk-LSMJ6KBE.js +4139 -0
  131. package/node_modules/pi-lens/dist/chunk-LW6XKDZQ.js +5565 -0
  132. package/node_modules/pi-lens/dist/chunk-N3YQJI6O.js +899 -0
  133. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  134. package/node_modules/pi-lens/dist/chunk-NKOUFPQF.js +659 -0
  135. package/node_modules/pi-lens/dist/chunk-NL5GLMFI.js +211 -0
  136. package/node_modules/pi-lens/dist/chunk-NMABWBZN.js +327 -0
  137. package/node_modules/pi-lens/dist/chunk-NTHH2J74.js +27 -0
  138. package/node_modules/pi-lens/dist/chunk-NTUJEGDZ.js +59 -0
  139. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  140. package/node_modules/pi-lens/dist/chunk-NXPS2L6U.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-O5WILGMI.js +42 -0
  142. package/node_modules/pi-lens/dist/chunk-O6TQT6RI.js +48 -0
  143. package/node_modules/pi-lens/dist/chunk-OAXNDZJ7.js +323 -0
  144. package/node_modules/pi-lens/dist/chunk-ODEEOXPH.js +44 -0
  145. package/node_modules/pi-lens/dist/chunk-OQGFCZJ4.js +4533 -0
  146. package/node_modules/pi-lens/dist/chunk-ORTMY4Q4.js +282 -0
  147. package/node_modules/pi-lens/dist/chunk-OS32RNR2.js +442 -0
  148. package/node_modules/pi-lens/dist/chunk-PAK6IDME.js +189 -0
  149. package/node_modules/pi-lens/dist/chunk-PWFHBTSF.js +406 -0
  150. package/node_modules/pi-lens/dist/chunk-Q5U6FMDI.js +2352 -0
  151. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  152. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  153. package/node_modules/pi-lens/dist/chunk-RIISMHKN.js +36 -0
  154. package/node_modules/pi-lens/dist/chunk-RPD4ZSB7.js +25 -0
  155. package/node_modules/pi-lens/dist/chunk-RPD5K5NC.js +79 -0
  156. package/node_modules/pi-lens/dist/chunk-RUOG5D2C.js +262 -0
  157. package/node_modules/pi-lens/dist/chunk-T4ERT73Y.js +599 -0
  158. package/node_modules/pi-lens/dist/chunk-T6D4Q4AQ.js +54 -0
  159. package/node_modules/pi-lens/dist/chunk-TGPXLY2J.js +391 -0
  160. package/node_modules/pi-lens/dist/chunk-TLGFBM3F.js +12116 -0
  161. package/node_modules/pi-lens/dist/chunk-TO6WL3GM.js +88 -0
  162. package/node_modules/pi-lens/dist/chunk-TT22N74J.js +517 -0
  163. package/node_modules/pi-lens/dist/chunk-TVN5N6GY.js +21216 -0
  164. package/node_modules/pi-lens/dist/chunk-U3VGR4VU.js +308 -0
  165. package/node_modules/pi-lens/dist/chunk-UK3CMEAL.js +1250 -0
  166. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  167. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  168. package/node_modules/pi-lens/dist/chunk-UY752QSN.js +45 -0
  169. package/node_modules/pi-lens/dist/chunk-V5PXFSZK.js +188 -0
  170. package/node_modules/pi-lens/dist/chunk-V7R425XG.js +398 -0
  171. package/node_modules/pi-lens/dist/chunk-VCSRSYLF.js +81 -0
  172. package/node_modules/pi-lens/dist/chunk-VD4JQCQC.js +162 -0
  173. package/node_modules/pi-lens/dist/chunk-VDBPVIKD.js +62 -0
  174. package/node_modules/pi-lens/dist/chunk-VHQLPIRJ.js +114 -0
  175. package/node_modules/pi-lens/dist/chunk-VN2AXLEX.js +5414 -0
  176. package/node_modules/pi-lens/dist/chunk-VNMT7C64.js +478 -0
  177. package/node_modules/pi-lens/dist/chunk-WONERJTP.js +309 -0
  178. package/node_modules/pi-lens/dist/chunk-XECLWQZN.js +85 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-XK4CIILI.js +144 -0
  181. package/node_modules/pi-lens/dist/chunk-Y4T27COV.js +562 -0
  182. package/node_modules/pi-lens/dist/chunk-YGVYKNSN.js +1860 -0
  183. package/node_modules/pi-lens/dist/chunk-YLF3PPT5.js +308 -0
  184. package/node_modules/pi-lens/dist/chunk-YSNFMJKN.js +14 -0
  185. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  186. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  187. package/node_modules/pi-lens/dist/chunk-ZAWW3AF6.js +97 -0
  188. package/node_modules/pi-lens/dist/chunk-ZMOL6RQC.js +167 -0
  189. package/node_modules/pi-lens/dist/index.js +16456 -8335
  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 +22589 -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 +3 -2
  200. package/node_modules/pi-lens/docs/configuration.md +20 -1
  201. package/node_modules/pi-lens/docs/environment-variables.md +52 -2
  202. package/node_modules/pi-lens/docs/features.md +8 -4
  203. package/node_modules/pi-lens/docs/globalconfig.md +1 -0
  204. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  205. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +36 -4
  206. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  207. package/node_modules/pi-lens/docs/mcp.md +21 -5
  208. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  209. package/node_modules/pi-lens/docs/pi-lens-fixer.md +156 -88
  210. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  211. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +694 -0
  212. package/node_modules/pi-lens/docs/pi-lens-monitor.md +73 -7
  213. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  214. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +188 -48
  215. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  216. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  217. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  218. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  219. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  220. package/node_modules/pi-lens/docs/servercapabilities.md +53 -49
  221. package/node_modules/pi-lens/docs/settings.md +1 -0
  222. package/node_modules/pi-lens/docs/usage.md +5 -1
  223. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  224. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  225. package/node_modules/pi-lens/package.json +18 -6
  226. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  227. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  228. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  229. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  230. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  231. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  232. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  233. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  234. package/node_modules/pi-typesafe/README.md +1 -1
  235. package/node_modules/pi-typesafe/dist/backends.d.ts +15 -5
  236. package/node_modules/pi-typesafe/dist/backends.js +18 -0
  237. package/node_modules/pi-typesafe/dist/client.d.ts +4 -3
  238. package/node_modules/pi-typesafe/dist/client.js +22 -7
  239. package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
  240. package/node_modules/pi-typesafe/dist/index.js +1 -1
  241. package/node_modules/pi-typesafe/dist/usage.d.ts +14 -4
  242. package/node_modules/pi-typesafe/dist/usage.js +147 -42
  243. package/node_modules/pi-typesafe/package.json +1 -1
  244. package/node_modules/pi-web-access/CHANGELOG.md +48 -0
  245. package/node_modules/pi-web-access/README.md +57 -11
  246. package/node_modules/pi-web-access/brave-rate-limit.ts +8 -2
  247. package/node_modules/pi-web-access/curator-page.ts +5 -1
  248. package/node_modules/pi-web-access/curator-server.ts +2 -0
  249. package/node_modules/pi-web-access/declared-web-links.ts +2 -2
  250. package/node_modules/pi-web-access/degoog.ts +323 -0
  251. package/node_modules/pi-web-access/dist/index.js +3483 -2745
  252. package/node_modules/pi-web-access/dist/mcp-cli.js +27443 -0
  253. package/node_modules/pi-web-access/exa.ts +5 -1
  254. package/node_modules/pi-web-access/extract.ts +41 -3
  255. package/node_modules/pi-web-access/firecrawl.ts +35 -4
  256. package/node_modules/pi-web-access/gemini-search.ts +34 -11
  257. package/node_modules/pi-web-access/gemini-web.ts +14 -9
  258. package/node_modules/pi-web-access/index.ts +153 -1202
  259. package/node_modules/pi-web-access/keenable.ts +221 -0
  260. package/node_modules/pi-web-access/mcp-cli.ts +40 -0
  261. package/node_modules/pi-web-access/mcp-server.ts +81 -0
  262. package/node_modules/pi-web-access/mcp-tool-definitions.ts +117 -0
  263. package/node_modules/pi-web-access/openai-search.ts +34 -1
  264. package/node_modules/pi-web-access/package.json +23 -5
  265. package/node_modules/pi-web-access/perplexity.ts +64 -56
  266. package/node_modules/pi-web-access/tool-arguments.ts +20 -0
  267. package/node_modules/pi-web-access/video-extract.ts +1 -1
  268. package/node_modules/pi-web-access/web-tool-contract.ts +86 -0
  269. package/node_modules/pi-web-access/web-tool-core.ts +1502 -0
  270. package/node_modules/pi-web-access/youtube-extract.ts +2 -5
  271. package/package.json +17 -12
  272. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -329
  273. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -346
  274. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +0 -21
  275. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +0 -62
  276. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  277. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +0 -115
  278. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +0 -27
  279. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +0 -27
  280. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  281. package/node_modules/@dietrichgebert/ponytail/assets/retriever-icon.png +0 -0
  282. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-dark.svg +0 -4
  283. package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-light.svg +0 -4
  284. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  285. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  286. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  287. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  288. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  289. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  290. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  291. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  292. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  293. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  294. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  295. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  296. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  297. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  298. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  299. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  300. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  301. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  302. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  303. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  304. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  305. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  306. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  307. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  308. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  309. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  310. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  311. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  312. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  313. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  314. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  315. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  316. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  317. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  318. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  319. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  320. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  321. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  322. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  323. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  324. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  325. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  326. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  327. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  328. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  329. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  330. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  331. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  332. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  333. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  334. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  335. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  336. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  337. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  338. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  339. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  340. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  341. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  342. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  343. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  344. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  345. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  346. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  347. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  348. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  349. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  350. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  351. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  352. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  353. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  354. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  355. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  356. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  357. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  358. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  359. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  360. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  361. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  362. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  363. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  364. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  365. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  366. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  367. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  368. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  369. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  370. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  371. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  372. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  373. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  374. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  375. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  376. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  377. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  378. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  379. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  380. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  381. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  382. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  383. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  384. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  385. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  386. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  387. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  388. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  389. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  390. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  391. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  392. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  393. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  394. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  395. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  396. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  397. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  398. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  399. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  400. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  401. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  402. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  403. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  404. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  405. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  406. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  407. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  447. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  448. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  449. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  450. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  451. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  452. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  453. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  454. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  455. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  456. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  457. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  458. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  459. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  460. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  461. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  462. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  463. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  464. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  465. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  466. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  467. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  468. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  469. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  470. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  471. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  472. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  473. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  474. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  475. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  476. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  477. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  478. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  479. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  480. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  481. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  482. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  483. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  484. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  485. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  486. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  487. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  488. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  489. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  490. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  491. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  492. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  493. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  494. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  495. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  496. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  497. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  498. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  499. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  500. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  501. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  502. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  503. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  504. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  505. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  506. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  507. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  508. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  509. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  510. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  511. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  512. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  513. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  514. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  515. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  516. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  517. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  518. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  519. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  520. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  521. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  522. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  523. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  524. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  525. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  526. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  527. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  528. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  529. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  530. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  531. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  532. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  533. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  534. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  535. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  536. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  537. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  538. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  539. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  540. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  541. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  542. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  543. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  544. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  545. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  546. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  547. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  548. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  549. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  550. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  551. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  552. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  553. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  554. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  555. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  556. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  557. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  558. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  559. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  560. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  561. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  562. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  563. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  564. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  565. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  566. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  567. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  568. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  569. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  570. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  571. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  572. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  573. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  574. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  575. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  576. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  577. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  578. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  579. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  580. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  581. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  582. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  583. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  584. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  585. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  586. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  587. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  588. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  589. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  590. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  591. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  592. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  593. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  594. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  595. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  596. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  597. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  598. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  599. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  600. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  601. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  602. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  603. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  604. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  605. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  606. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  607. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  608. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  609. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  610. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  611. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  612. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  613. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  614. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  615. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  616. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  617. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  618. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  619. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  620. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  621. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  623. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  624. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  625. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  626. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  627. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  628. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  629. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  630. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  631. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  632. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  633. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  634. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  635. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  636. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  637. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  638. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  639. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  640. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  641. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  642. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  643. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  644. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  645. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  646. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  647. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  648. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  649. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  650. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  651. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  652. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  653. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  654. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  655. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  656. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  657. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  658. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  659. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  660. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  661. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  662. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  663. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  664. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  665. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  666. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  667. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  668. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  669. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  670. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  671. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  672. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  673. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  674. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  675. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  676. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  677. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  678. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  679. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  680. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  681. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  682. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  683. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  684. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  685. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  686. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  687. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  688. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  689. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  690. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  691. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  692. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  693. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  694. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  695. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  696. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  697. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  698. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  699. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  700. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  701. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  702. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  703. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  704. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  705. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  706. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  707. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  708. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  709. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  710. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  711. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  712. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  713. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  714. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  715. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  716. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  717. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  718. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  719. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  720. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  721. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  722. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  723. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  724. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  725. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  726. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  727. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  728. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  729. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  730. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  731. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  732. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  733. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  734. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  735. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  736. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  737. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  738. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  739. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  740. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  741. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  742. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  743. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  744. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  745. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  746. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  747. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  748. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  749. package/node_modules/pi-lens/dist/i18n.js +0 -69
  750. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  751. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  752. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  753. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  754. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  755. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  756. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  757. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  758. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  759. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  760. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  761. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  762. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  763. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  764. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  765. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  766. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  767. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  768. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  769. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
@@ -1,1244 +0,0 @@
1
- /**
2
- * Path utilities for pi-lens
3
- *
4
- * Handles cross-platform path normalization, particularly
5
- * Windows case-insensitivity issues when using paths as Map keys.
6
- *
7
- * Approach (inspired by OpenCode's Filesystem.normalizePath):
8
- * - On Windows: try realpathSync.native() for canonical casing
9
- * - Falls back to lowercase for files that don't exist yet
10
- * - On non-Windows: adopt the on-disk casing of an existing path (#3098 — a
11
- * case-insensitive POSIX filesystem otherwise derives two Map keys for one
12
- * file); a path that does not exist is returned as-is, case-preserving
13
- * - Always convert backslashes to forward slashes for Map key consistency
14
- */
15
- import { existsSync, readFileSync, realpathSync, statSync, } from "node:fs";
16
- import * as os from "node:os";
17
- import * as path from "node:path";
18
- import { win32 } from "node:path";
19
- import { fileURLToPath, pathToFileURL } from "node:url";
20
- import { minimatch } from "./deps/minimatch.js";
21
- /**
22
- * Detect a positively Windows-shaped path, regardless of the host OS.
23
- *
24
- * A backslash anywhere in a path is not enough: it is a legal character in a
25
- * POSIX filename. Only a drive-letter prefix (`X:`), a UNC root (`\\`), or a
26
- * rooted backslash at position zero (`\`) selects Windows parsing.
27
- */
28
- export function isWindowsPath(filePath) {
29
- return /^[A-Za-z]:/.test(filePath) || filePath.startsWith("\\");
30
- }
31
- /**
32
- * Canonical backslash→forward-slash fold — the single sanctioned form of the
33
- * `p.replace(/\\/g, "/")` idiom otherwise hand-rolled across the codebase
34
- * (~138 sites, #1193). PURE separator normalization: it does NOT resolve,
35
- * canonicalize, lowercase, or collapse repeated slashes — reach for
36
- * `normalizeFilePath`/`normalizeMapKey`/`normalizeEphemeralMapKey` when a
37
- * canonical map *key* (case-fold / realpath) is what you need. Consolidating on
38
- * this funnels the scattered transform and makes a shape-2 lint/ast-grep rule
39
- * possible for the first time: today a bare inline `.replace(/\\/g, "/")` is
40
- * byte-identical to the sanctioned use so it can't be ruled (#1158); once
41
- * everything routes through `toPosix`, an *un-migrated* inline `.replace`
42
- * becomes detectable.
43
- */
44
- export function toPosix(filePath) {
45
- return filePath.replace(/\\/g, "/");
46
- }
47
- /** Return whether `filePath` is fully qualified under Windows semantics. */
48
- export function isFullyQualifiedWin32(filePath) {
49
- return win32.isAbsolute(filePath) && win32.parse(filePath).root.length > 1;
50
- }
51
- /** Return whether `filePath` is fully qualified under POSIX semantics. */
52
- export function isFullyQualifiedPosix(filePath) {
53
- return path.posix.isAbsolute(filePath) && !isWindowsPath(filePath);
54
- }
55
- /**
56
- * Return whether `filePath` is fully qualified under the host's semantics.
57
- *
58
- * In particular, `/foo` is rooted-relative under Win32 (ambient-drive
59
- * dependent) but fully qualified under POSIX.
60
- */
61
- export function isFullyQualified(filePath) {
62
- return process.platform === "win32"
63
- ? isFullyQualifiedWin32(filePath)
64
- : isFullyQualifiedPosix(filePath);
65
- }
66
- /**
67
- * Shape-based absolute check (mirrors `toProjectRelativePath`'s idiom): a
68
- * Windows-shaped path (drive-letter/UNC) is parsed with `win32.isAbsolute`
69
- * regardless of host OS, since the host-default `path.isAbsolute` returns
70
- * FALSE for a Windows-shaped path on POSIX (#1150 class) and would let a
71
- * cross-platform-persisted relative path slip past this guard on Linux CI.
72
- * MUST run on the RAW `filePath`, before `normalizeMapKey`: on Windows,
73
- * `normalizeMapKey`'s nonexistent-path fallback (`resolveNonExisting`) calls
74
- * `win32.resolve`, which silently makes any relative path absolute against
75
- * `process.cwd()` — checking the normalized value would defeat this guard
76
- * entirely on Windows.
77
- *
78
- * Promoted from a private copy in `clients/review-graph/service.ts` (#2477)
79
- * so `clients/dispatch/dispatcher.ts`'s baseline-key guard (#2489) can share
80
- * the same sanctioned check instead of hand-rolling a second one.
81
- */
82
- export function isAbsoluteFilePath(filePath) {
83
- const p = isWindowsPath(filePath) ? win32 : path;
84
- return p.isAbsolute(filePath);
85
- }
86
- /**
87
- * Split a path into its non-empty segments on EITHER separator (`\` or `/`),
88
- * regardless of the running OS — the shape-safe form of `p.split(path.sep)` /
89
- * an inline `p.split(/[\\/]+/)`, which #1161/#1163 showed must not assume the
90
- * host separator for a possibly-cross-shaped path. Drops empty segments
91
- * (leading slash, drive-root, doubled separators).
92
- */
93
- export function splitPathSegments(filePath) {
94
- return filePath.split(/[\\/]+/).filter(Boolean);
95
- }
96
- /**
97
- * Adopt `canonical`'s CASING for the trailing segments of `held`, stopping at
98
- * the first segment that differs by more than case.
99
- *
100
- * `realpathSync.native` answers a different question than the one the POSIX
101
- * arm of `normalizeFilePath` asks: it resolves symlinks AND reports on-disk
102
- * casing, and we want only the second half. Walking from the tail and halting
103
- * at the first structural divergence separates them with no extra syscall:
104
- *
105
- * held /var/folders/T/x/SUB/a.ts (macOS tmpdir, mis-cased segment)
106
- * canonical /private/var/folders/T/x/sub/a.ts
107
- * result /var/folders/T/x/sub/a.ts (case fixed, symlink prefix kept)
108
- *
109
- * The symlink prefix matters: macOS's `os.tmpdir()` is `/var/folders/...`,
110
- * a symlink into `/private/var`, so a whole-string "is this a case variant"
111
- * test would decline to canonicalize exactly the paths the #1024 regression
112
- * test (and any macOS temp-dir workflow) runs on. Keeping the prefix is also
113
- * what bounds this change: on POSIX, `normalizeFilePath` still never resolves
114
- * a symlink, so a symlinked monorepo package keeps keying under the path the
115
- * caller held (refs #2490 — a cwd fold for its own sake broke every monorepo).
116
- *
117
- * PURE string algebra: this pins what the rewrite DOES, never what the kernel
118
- * reports — and from two strings alone it cannot tell "the same file, spelled
119
- * with different case" from "a different file whose name happens to be a case
120
- * variant". Any symlink whose BASENAME is a case variant of its target's
121
- * basename (`<root>/MyProject` → `work/myproject`, `node_modules/Foo` →
122
- * `../pkgs/foo`) has its name replaced while its own parent is kept, so the
123
- * result names a different place — or no place at all. Measured in #3159
124
- * review round 2 (F1): two different inodes collapsed onto ONE key (the #1024
125
- * defect inverted — false suppression), and a symlinked package keyed under a
126
- * path that does not exist on disk, breaking the #2490 bound above. The caller
127
- * therefore CONFIRMS every rewrite against the filesystem before adopting it
128
- * (see `normalizeFilePath`); do not use this function without that step.
129
- */
130
- function adoptCanonicalCasing(held, canonical) {
131
- const heldParts = held.split("/");
132
- const realParts = canonical.split("/");
133
- let i = heldParts.length - 1;
134
- let j = realParts.length - 1;
135
- let changed = false;
136
- while (i >= 0 && j >= 0) {
137
- const a = heldParts[i];
138
- const b = realParts[j];
139
- if (a !== b) {
140
- if (a.toLowerCase() !== b.toLowerCase())
141
- break;
142
- heldParts[i] = b;
143
- changed = true;
144
- }
145
- i--;
146
- j--;
147
- }
148
- // Identity-preserving when nothing moved — this runs on every POSIX map-key
149
- // derivation and the overwhelmingly common answer is "already canonical".
150
- // NOT a behavioural branch (both arms produce an equal string): it is the
151
- // same allocation guard `normalizeEphemeralMapKey` documents below.
152
- return changed ? heldParts.join("/") : held;
153
- }
154
- /**
155
- * Normalize a file path for consistent Map key usage.
156
- *
157
- * On Windows:
158
- * - If the file exists: uses realpathSync.native() to get the canonical
159
- * filesystem path (actual casing, resolved symlinks)
160
- * - If the file doesn't exist: resolves the path and lowercases
161
- * (needed for new files where we haven't written yet)
162
- *
163
- * On POSIX:
164
- * - Folds `.`, `..` and duplicate separators first (#3184, see below).
165
- * - If the file exists: adopts the on-disk CASING of the trailing segments
166
- * (see `adoptCanonicalCasing`) — no lowercasing, no symlink resolution.
167
- * - If the file doesn't exist: returns the path as-is, case-PRESERVING. On a
168
- * case-sensitive filesystem `SUB/a.ts` and `sub/a.ts` are two different
169
- * files, and nothing may fold one into the other; only the filesystem's own
170
- * answer for a path that EXISTS can tell the two apart, and for a path that
171
- * does not exist there is no such answer to ask for.
172
- *
173
- * Why the POSIX arm folds dot segments (#3184): the casing arm returns the
174
- * caller's own spelling whenever `adoptCanonicalCasing` changes nothing, and
175
- * for `<base>/src/../src/a.ts` it always changes nothing — `realpath` answers
176
- * a string with FEWER segments, which a casing-only rewrite cannot express, so
177
- * it declines and the caller's un-folded spelling came back as the map key.
178
- * Every canonical writer keys through `path.resolve` first (`ctx.filePath` =
179
- * `normalizeMapKey(resolveAgainstAncestors(...))`, `clients/dispatch/
180
- * runner-context.ts:49`), so a consumer that passes an ALREADY-absolute
181
- * agent-typed path straight in (`tools/lens-diagnostic-mark.ts`,
182
- * `clients/mcp/analyze.ts`) derived an orphan key that no reader could reach.
183
- * Folding here is pure string algebra — no cwd, no filesystem — so a relative
184
- * path stays relative (`src/../x` → `x`, `../x` → `../x`, never resolved
185
- * against `process.cwd()`; refs #2490, where a cwd fold broke every monorepo)
186
- * and a symlinked package still keys under the path the caller held. These
187
- * are `path.resolve`'s own TEXTUAL `..` semantics, which is exactly what
188
- * makes a folded reader key equal to the canonical writer's key: where a
189
- * `..` sits right after a symlinked directory, textual folding and the
190
- * kernel disagree, and both sides of every comparison take the textual
191
- * answer because every canonical writer already resolved that way.
192
- *
193
- * Why POSIX canonicalizes casing at all (#3098, the #1024 defect's live half):
194
- * a case-insensitive POSIX filesystem — macOS's default APFS, `nocase` vfat /
195
- * ntfs3 / cifs mounts — makes `SUB/a.ts` and `sub/a.ts` ONE file, so a raw
196
- * mis-cased write (`lens_diagnostic_mark` anchors under `path.resolve(cwd,
197
- * arg)`) and a `normalizeMapKey` read derived two anchors for one file and the
198
- * agent's own disposition mark silently never applied. A case-preserving POSIX
199
- * arm cannot close that: the two keys only become one if the normalizer asks
200
- * the filesystem which name is really on disk.
201
- *
202
- * `realpathSync.native` is `realpath(3)`. On Darwin it rebuilds every
203
- * component from the filesystem's own `ATTR_CMN_NAME` — Libc-1669.0.4
204
- * `stdlib/FreeBSD/realpath.c:233` (`getattrlist(resolved, &_rp_alist, …,
205
- * FSOPT_NOFOLLOW)` with `ATTR_CMN_NAME`) and `:348-354` ("attrs already has
206
- * the real name") — which is why it returns on-disk casing on APFS/HFS+.
207
- * MEASURED counter-example (#3098): a Linux ext4/tmpfs `chattr +F` casefold
208
- * directory aliases the two spellings but `realpath(3)` there returns the
209
- * spelling the caller asked with, so this arm is a no-op on casefolded Linux
210
- * directories — filed with the transcript as #3154.
211
- *
212
- * Always converts backslashes to forward slashes for consistent Map keys.
213
- */
214
- export function normalizeFilePath(filePath) {
215
- // Convert backslashes to forward slashes first
216
- const normalized = filePath.replace(/\\/g, "/");
217
- if (process.platform !== "win32" && !isWindowsPath(normalized)) {
218
- // #3184. `path.posix`, not the host default: this branch is already
219
- // committed to POSIX parsing of a slash-folded string (shape 2). Only
220
- // the POSIX arm needs this — the win32 arm below reaches `realpath` or
221
- // `win32.resolve`/`win32.normalize` on every path, all of which fold
222
- // dot segments already (measured: `C:\repo\src\..\src\a.ts` →
223
- // `c:/repo/src/a.ts` on this POSIX host, before this change) — and
224
- // folding BEFORE the arms would also move which arm a degenerate
225
- // drive-letter path selects (`path.posix.normalize("C:/repo/../..")`
226
- // is `"."`, no longer Windows-shaped).
227
- // Two inputs keep the caller's spelling instead:
228
- // - "" is a non-path sentinel in this codebase's path-typed fields
229
- // (see `normalizeLoggedPath`'s doc); `posix.normalize("")` invents
230
- // ".", the process cwd.
231
- // - a UNC root (`\\server\share`, slash-folded to `//server/share`)
232
- // reaches THIS arm on a POSIX host, because `isWindowsPath` tests
233
- // the already-folded string and sees no backslash; POSIX
234
- // `normalize` collapses its leading `//` to `/`, renaming the path
235
- // to an unrelated local one.
236
- const folded = normalized === "" || normalized.startsWith("//")
237
- ? normalized
238
- : path.posix.normalize(normalized);
239
- try {
240
- const canonical = realpathSync.native(folded);
241
- // Fast path, not a guard: both arms answer `folded` when the
242
- // strings match, but skipping the two `split`s there is a measured
243
- // 1.9 vs 2.3 microseconds per call on the per-edit seam (#3098).
244
- if (canonical === folded)
245
- return folded;
246
- const adopted = adoptCanonicalCasing(folded, canonical);
247
- if (adopted === folded)
248
- return folded;
249
- // The rewrite is string algebra and can land on a DIFFERENT file
250
- // (#3159 review round 2, F1 — see `adoptCanonicalCasing`). Adopt it
251
- // only once the filesystem agrees it still names the file the caller
252
- // held: `canonical` IS `realpath(normalized)`, so this asks exactly
253
- // "does the rewritten spelling resolve to the same file?". One extra
254
- // syscall, and only on the rare branch where casing actually moved —
255
- // never on an already-canonical path. A throw here (the rewritten
256
- // path does not exist, the #2490 monorepo case) lands in the catch
257
- // below and keeps the caller's spelling, which is the same answer.
258
- return realpathSync.native(adopted) === canonical ? adopted : folded;
259
- }
260
- catch {
261
- // Does not exist (or is unreadable): case-preserving, as above.
262
- return folded;
263
- }
264
- }
265
- // Windows: try realpathSync.native() for canonical casing
266
- // This resolves symlinks and returns the actual filesystem casing
267
- try {
268
- const canonical = realpathSync.native(filePath);
269
- return canonical.replace(/\\/g, "/");
270
- }
271
- catch {
272
- // File doesn't exist yet (new file) — resolve path and lowercase
273
- // We need to walk up the directory tree to find the nearest existing
274
- // parent, resolve its casing, then append the non-existent parts
275
- try {
276
- return resolveNonExisting(filePath);
277
- }
278
- catch {
279
- // Last resort: just lowercase the resolved path
280
- const resolved = win32.normalize(win32.resolve(filePath));
281
- return resolved.replace(/\\/g, "/").toLowerCase();
282
- }
283
- }
284
- }
285
- /** Resolve a filesystem identity once, retaining a usable absolute fallback. */
286
- export function realpathOrResolve(filePath) {
287
- const resolved = path.resolve(filePath);
288
- try {
289
- return realpathSync(resolved);
290
- }
291
- catch {
292
- return resolved;
293
- }
294
- }
295
- /**
296
- * Normalize a logged `filePath`/`cwd` value, but ONLY when it is already a
297
- * fully-qualified path (#2219, the #2141 class's sibling loggers). Several
298
- * NDJSON log-entry types reuse a `filePath`-typed field for non-path
299
- * sentinels alongside genuine paths — `"<quiet-window>"` in
300
- * `cascade-logger.ts`, `"<tree-sitter>"` in `tree-sitter-logger.ts`, a shell
301
- * command or an empty placeholder in `latency-logger.ts`. `normalizeFilePath`
302
- * resolves a relative-looking string against the CURRENT process cwd (see
303
- * `resolveNonExisting` above), so running it over one of those sentinels
304
- * would silently corrupt it into `"<repoRoot>/<quiet-window>"` instead of
305
- * normalizing it. Only a value that is already fully qualified can be the
306
- * #2141 mixed-raw/normalized-path defect; anything else is passed through
307
- * unchanged.
308
- *
309
- * #2229 review round 1, F1: this classifier checks BOTH host shapes
310
- * (`isFullyQualifiedWin32(value) || isFullyQualifiedPosix(value)`), not
311
- * `isFullyQualified(value)` (host-dispatched on `process.platform`). A
312
- * Windows-shaped absolute path (`C:\Users\...`) is exactly the log payload
313
- * this fix exists to normalize, but `isFullyQualified` on Linux CI routes to
314
- * `isFullyQualifiedPosix`, which rejects it (no leading `/`) — so on Linux
315
- * the guard silently no-ops for the very inputs the #2141 defect produces,
316
- * passing the raw backslash form straight through. Checking both shapes
317
- * makes the classifier's answer for a given STRING independent of which OS
318
- * is asking; `normalizeFilePath` itself still branches on `process.platform`
319
- * for how the file is resolved, but whether to normalize at all no longer
320
- * does (AGENTS.md shape 2, the #1024/#1150 OS-divergence class).
321
- */
322
- export function normalizeLoggedPath(value) {
323
- return isFullyQualifiedWin32(value) || isFullyQualifiedPosix(value)
324
- ? normalizeFilePath(value)
325
- : value;
326
- }
327
- /**
328
- * Resolve a non-existing path by finding the nearest existing parent,
329
- * getting its canonical casing, then appending the non-existent parts lowercased.
330
- *
331
- * Example: C:\Users\Foo\newdir\file.ts
332
- * - C:\Users\Foo exists → realpathSync gives C:\Users\Foo
333
- * - newdir\file.ts doesn't exist → lowercased
334
- * - Result: C:/Users/Foo/newdir/file.ts
335
- */
336
- function resolveNonExisting(filePath) {
337
- const resolved = win32.resolve(filePath);
338
- let current = resolved;
339
- const nonExistentParts = [];
340
- // Walk up until we find an existing directory
341
- while (true) {
342
- if (existsSync(current)) {
343
- // Found existing ancestor — get its canonical casing
344
- const canonical = realpathSync.native(current);
345
- if (nonExistentParts.length === 0) {
346
- return canonical.replace(/\\/g, "/");
347
- }
348
- // Append non-existent parts (lowercased for consistency)
349
- const tail = nonExistentParts.reverse().join("/").toLowerCase();
350
- const base = canonical.replace(/\\/g, "/");
351
- return base.endsWith("/") ? base + tail : `${base}/${tail}`;
352
- }
353
- // Use win32.dirname (not the platform-default dirname) so a
354
- // Windows-shaped path is parsed with win32 semantics regardless of the
355
- // running OS — consistent with the win32.resolve/win32.normalize this
356
- // branch already commits to. The platform-default POSIX dirname would
357
- // find no separator in a win32-resolved "C:\repo\..." path (its only
358
- // separators are backslashes), collapse to ".", stop the upward walk at
359
- // cwd, and mangle the key on Linux CI (refs #1150, the #1024
360
- // OS-divergence class).
361
- const parent = win32.dirname(current);
362
- if (parent === current) {
363
- // Reached filesystem root without finding existing dir
364
- // Fall back to full lowercase
365
- throw new Error("No existing parent found");
366
- }
367
- nonExistentParts.push(win32.basename(current));
368
- current = parent;
369
- }
370
- }
371
- /**
372
- * Convert a file:// URI to a normalized path.
373
- * Handles URL decoding and Windows drive letter normalization.
374
- */
375
- export function uriToPath(uri) {
376
- try {
377
- const filePath = fileURLToPath(uri);
378
- return normalizeFilePath(filePath);
379
- }
380
- catch {
381
- // Not a valid file:// URI, treat as plain path
382
- return normalizeFilePath(uri);
383
- }
384
- }
385
- /**
386
- * Decode a file:// URI to an on-disk path WITHOUT map-key normalization.
387
- *
388
- * `uriToPath` runs its result through `normalizeFilePath`, which on win32
389
- * lowercases the nonexistent tail of a path (see `resolveNonExisting`) and
390
- * canonicalizes an existing path to its real casing. That is correct for Map
391
- * keys, but DESTRUCTIVE for a real create/rename target: creating `NewFile.txt`
392
- * would write `newfile.txt`, and a legitimate case-only rename would collapse
393
- * to a no-op ("source and destination must differ"). Disk mutations must honor
394
- * the caller's intended casing, so they resolve their target through this
395
- * decode-only path while confinement/validation keep using the normalized
396
- * `uriToPath`. Non-win32 is unaffected either way (normalizeFilePath is a
397
- * near-identity there).
398
- */
399
- export function uriToDiskPath(uri) {
400
- try {
401
- return fileURLToPath(uri);
402
- }
403
- catch {
404
- // Not a valid file:// URI — treat as a plain path (matches uriToPath).
405
- return uri;
406
- }
407
- }
408
- /**
409
- * Convert a path to a file:// URI.
410
- * Does NOT normalize the path - URIs preserve original casing.
411
- */
412
- export function pathToUri(filePath) {
413
- return pathToFileURL(filePath).href;
414
- }
415
- /**
416
- * Normalize a Map key lookup for file paths.
417
- * Use this when getting/setting values in Maps that use file paths as keys.
418
- */
419
- export function normalizeMapKey(filePath) {
420
- return normalizeFilePath(filePath);
421
- }
422
- /**
423
- * Human-facing path relative to a project root when the file is inside it.
424
- *
425
- * Parses by path SHAPE, not host OS (refs #1150/#1152, shape-2 class #1163):
426
- * a Windows-shaped `filePath` (drive-letter/UNC — e.g. a persisted call-graph
427
- * symbol-key path `C:\repo\src\x.ts` rehydrated on a Linux CI run) is split
428
- * with `win32.*` regardless of `process.platform`. The host-default
429
- * `isAbsolute`/`relative` find no drive-letter anchor in a win32 path on POSIX:
430
- * `path.isAbsolute("C:\\repo\\x.ts")` returns FALSE on Linux, short-circuiting
431
- * to the raw absolute path instead of ever relativizing it — so a file that IS
432
- * under the project root renders as a full absolute path on Linux but the
433
- * expected `src/x.ts` on Windows (green-locally / wrong-on-CI, the #1024
434
- * divergence class). `win32.*` on a native POSIX path (Windows never sees one;
435
- * Linux native paths aren't Windows-shaped) is never selected, so same-OS
436
- * native paths are unchanged either way.
437
- */
438
- export function toProjectRelativePath(filePath, projectRoot) {
439
- const p = isWindowsPath(filePath) ? win32 : path;
440
- if (!p.isAbsolute(filePath))
441
- return filePath.replace(/\\/g, "/");
442
- const relative = p.relative(p.resolve(projectRoot), filePath);
443
- return relative && !relative.startsWith("..") && !p.isAbsolute(relative)
444
- ? relative.replace(/\\/g, "/")
445
- : filePath.replace(/\\/g, "/");
446
- }
447
- /**
448
- * Cheap, syntactic-only Map key normalization: slash-fold + (on Windows)
449
- * lowercase. No `realpathSync` / filesystem I/O.
450
- *
451
- * `normalizeMapKey` (via `normalizeFilePath`) calls `realpathSync.native()` to
452
- * get canonical on-disk casing — correct for maps that key long-lived state
453
- * shared across call sites (e.g. LSP/read-guard caches), but expensive when
454
- * the *point* of the cache is to avoid filesystem calls in the first place:
455
- * for a candidate path that does NOT exist (the common case for sibling-probe
456
- * memos), `normalizeFilePath` walks up the directory tree doing its own
457
- * `existsSync` calls to resolve the nearest existing ancestor — measured at
458
- * ~11x slower than the single `existsSync` probe such a cache is trying to
459
- * save (refs #191).
460
- *
461
- * Safe to use ONLY for ephemeral, single-process, single-walk caches whose
462
- * keys are produced by this process's own `path.join`/`path.resolve` calls
463
- * within the same run (so separators and casing are already consistent
464
- * modulo simple slash direction) — never for state shared across processes,
465
- * persisted, or compared against externally-supplied paths where symlink /
466
- * real-casing resolution actually matters.
467
- */
468
- export function normalizeEphemeralMapKey(filePath) {
469
- // Most hot-path keys on POSIX are already canonical slash-separated strings.
470
- // Preserve that identity instead of allocating a replacement string for each
471
- // file in a large diagnostics reconciliation.
472
- if (process.platform !== "win32" && !filePath.includes("\\"))
473
- return filePath;
474
- const slashed = filePath.replace(/\\/g, "/");
475
- return process.platform === "win32" ? slashed.toLowerCase() : slashed;
476
- }
477
- /**
478
- * Compare two file paths for equality, handling Windows case-insensitivity
479
- * and mixed separators (backslash vs forward slash).
480
- */
481
- export function pathsEqual(a, b) {
482
- return normalizeFilePath(a) === normalizeFilePath(b);
483
- }
484
- /**
485
- * Check if `child` is under `parent` directory.
486
- * Separator-agnostic and case-insensitive on Windows.
487
- */
488
- /**
489
- * Yield each directory from `startDir` up to (and including) the filesystem
490
- * root. Terminates when `path.dirname(current) === current` so it works on
491
- * Windows drive roots and POSIX `/` alike.
492
- *
493
- * Single source of truth for the half-dozen "walk up the directory tree
494
- * looking for X" loops that have accumulated across the codebase. Callers
495
- * that need an "is there a file named Y anywhere on the way up" check
496
- * should use `findNearestContaining` instead.
497
- */
498
- export function* walkUpDirs(startDir) {
499
- let current = path.resolve(startDir);
500
- while (true) {
501
- yield current;
502
- const parent = path.dirname(current);
503
- if (parent === current)
504
- return;
505
- current = parent;
506
- }
507
- }
508
- /**
509
- * Walk up from `startDir` and return the first directory that contains any
510
- * of `candidates` on disk. Returns `undefined` if none match.
511
- *
512
- * @example
513
- * findNearestContaining("/repo/pkg/src", ["package.json", "tsconfig.json"]);
514
- * // → "/repo/pkg" if pkg/package.json exists, "/repo" if only /repo/package.json
515
- */
516
- export function findNearestContaining(startDir, candidates) {
517
- for (const dir of walkUpDirs(startDir)) {
518
- for (const name of candidates) {
519
- if (existsSync(path.join(dir, name)))
520
- return dir;
521
- }
522
- }
523
- return undefined;
524
- }
525
- /**
526
- * Walk up from `startDir` and return the first matching FILE path (not just
527
- * the containing directory) for any of `names`, first-match-wins within each
528
- * directory in `names` order. Single source of truth for the "walk up
529
- * looking for one of these config filenames" loop that `opengrep-config.ts`,
530
- * `typos-config.ts`, `zizmor-config.ts`, and `sgconfig.ts` each hand-rolled
531
- * independently (refs #680), and that `php-cs-fixer-config.ts` now delegates
532
- * to as well (refs #2472 review F2).
533
- *
534
- * UNCEILINGED by default (refs #2472 review round 3, F1) — `options.homeDir`
535
- * is opt-in, not default-on. A prior version applied the SAME `$HOME`
536
- * ceiling as `findNearestMarkerRoot` unconditionally, which broke every one
537
- * of these tools' actual discovery contract: each of them treats a config
538
- * living directly at `$HOME` (`~/typos.toml`, `~/sgconfig.yml`, …) as the
539
- * user's legitimate GLOBAL config, and reads it itself regardless of pi-lens
540
- * — the ceiling didn't stop pi-lens from seeing an unrelated ancestor
541
- * config, it stopped pi-lens from seeing the SAME config the tool was about
542
- * to read on its own, so pi-lens silently fell back to (or, for typos,
543
- * injected and let its own shipped `_typos.toml` merge over) the user's
544
- * config where the tool's own resolver would have honored it. `php-cs-fixer`
545
- * makes the same mismatch concrete: its detection gates
546
- * (`hasPhpCsFixerConfig` via `findNearestContaining`, `phpCsFixerFormatter
547
- * .detect` via its own `findUp`) are both unceilinged, so a ceilinged
548
- * carriage here disagreed with its own gate — "config exists" from the gate,
549
- * "config not found" from the resolver — and dropped the very `--config`
550
- * argv #2472 exists to carry. Pass `options.homeDir` only when a caller
551
- * affirmatively wants the ceiling (a config found at or above THAT directory
552
- * is never returned); omitting it walks all the way to the filesystem root,
553
- * matching `findNearestContaining`'s unceilinged behavior and every
554
- * underlying tool's own discovery.
555
- *
556
- * Distinct from `findNearestContaining`, which returns the containing
557
- * directory rather than the matched file path — use that one when the caller
558
- * only needs "is one of these present nearby", not which file it is.
559
- *
560
- * @example
561
- * findLocalToolConfig(cwd, ["typos.toml", "_typos.toml", ".typos.toml"]);
562
- * // → "/repo/typos.toml" if present, else undefined
563
- */
564
- export function findLocalToolConfig(startDir, names, options = {}) {
565
- const homeDir = options.homeDir !== undefined ? path.resolve(options.homeDir) : undefined;
566
- for (const dir of walkUpDirs(startDir || process.cwd())) {
567
- if (homeDir !== undefined && isAtOrAboveHomeDir(dir, homeDir)) {
568
- return undefined;
569
- }
570
- for (const name of names) {
571
- const candidate = path.join(dir, name);
572
- if (existsSync(candidate))
573
- return candidate;
574
- }
575
- }
576
- return undefined;
577
- }
578
- /**
579
- * Accept only a real Git repository marker: a directory with HEAD, or a
580
- * worktree/submodule marker file whose first line starts with `gitdir:`.
581
- */
582
- export function isRealGitMarker(markerPath) {
583
- try {
584
- const marker = statSync(markerPath);
585
- if (marker.isDirectory())
586
- return existsSync(path.join(markerPath, "HEAD"));
587
- if (!marker.isFile())
588
- return false;
589
- return readFileSync(markerPath, "utf8")
590
- .split(/\r?\n/, 1)[0]
591
- .startsWith("gitdir:");
592
- }
593
- catch {
594
- return false;
595
- }
596
- }
597
- /**
598
- * Walk up from `startDir` looking for a directory containing any of
599
- * `markers`, the same containment-aware climb `knip-client.ts` and
600
- * `dead-code-client.ts` each used to hand-roll independently (refs #625):
601
- *
602
- * - Never resolves at or above `$HOME` (via `isAtOrAboveHomeDir`) — a
603
- * marker found there has escaped the user's workspace.
604
- * - If `options.boundaries` is given and one is found before any `marker`,
605
- * stops and returns `null` rather than continuing past it.
606
- * - Depth-capped at 64 climbs, matching the callers' existing safety bound
607
- * (guards a pathological symlink loop; real depths are ~10).
608
- * - Returns `null` — never `startDir` — when nothing is found. Callers
609
- * must treat `null` as "no project here", not fall back to the start
610
- * directory (a `null`-swallowing fallback was the #250/#296 bug class:
611
- * scanning $HOME wholesale from a bare cwd).
612
- *
613
- * For a plain "find nearest containing directory" with no boundary concept,
614
- * use `findNearestContaining` instead. Distinct from `startup-scan.ts`'s
615
- * `findNearestProjectRoot` (fixed marker list, no boundaries, no home-check —
616
- * that caller applies `isAtOrAboveHomeDir` itself afterward); named
617
- * differently here to avoid confusion between the two.
618
- */
619
- export function findNearestMarkerRoot(startDir, markers, options = {}) {
620
- const boundaries = options.boundaries ?? [];
621
- const homeDir = path.resolve(options.homeDir ?? os.homedir());
622
- const markerPredicate = options.markerPredicate ?? (() => true);
623
- let current = path.resolve(startDir);
624
- for (let depth = 0; depth < 64; depth++) {
625
- if (isAtOrAboveHomeDir(current, homeDir))
626
- return null;
627
- if (markers.some((m) => existsSync(path.join(current, m)) &&
628
- markerPredicate(path.join(current, m))))
629
- return current;
630
- if (boundaries.some((m) => existsSync(path.join(current, m))))
631
- return null;
632
- const parent = path.dirname(current);
633
- if (parent === current)
634
- return null;
635
- current = parent;
636
- }
637
- return null;
638
- }
639
- /**
640
- * True when `dir` is the home directory OR an ancestor of it (`/home`,
641
- * `C:\Users`, the filesystem root, …). A project-root search that climbs to
642
- * such a directory has escaped the user's workspace — walking down from it
643
- * scans unrelated trees (the #250 runaway). Use this as the single shared
644
- * ceiling on any upward project-root resolution, instead of an exact
645
- * `=== os.homedir()` check (which a marker found *above* `$HOME` slips past).
646
- * A normal project *under* home (e.g. `~/code/app`) is NOT at-or-above home,
647
- * so it still resolves fine. Refs #253.
648
- *
649
- * ONE expression, deliberately: `path.relative` alone answers "is home inside
650
- * `dir`, or the same directory as `dir`", and it answers with the PLATFORM's
651
- * path-equality semantics — case-folding on win32, case-sensitive on POSIX.
652
- * The `resolvedDir === resolvedHome` shortcut that used to precede it was
653
- * case-SENSITIVE on every platform, so `c:\Users\jane` (the lowercase-drive
654
- * form VS Code URIs produce, and the form 46 records in a real `latency.log`
655
- * carry) missed the equality test AND was then rejected by a `rel !== ""`
656
- * clause — reporting "not at home" for the home directory itself and letting
657
- * every ceilinged walker climb straight past HOME (#2544 review F1).
658
- *
659
- * rel === "" ⇢ `dir` IS home
660
- * rel relative, no leading `..` ⇢ home is INSIDE `dir` (an ancestor)
661
- * rel starts with `..` ⇢ home is elsewhere — sibling, or the
662
- * prefix trap `C:\Users\jane2` → home
663
- * `C:\Users\jane` gives `..\jane`
664
- * rel absolute ⇢ different Windows drive
665
- *
666
- * Same shape as `isUnderDir` above; keep the two in step.
667
- *
668
- * `pathImpl` exists so the ubuntu Unit tests lane — the authoritative one —
669
- * can exercise the WIN32 semantics this helper's whole bug was about. Every
670
- * assertion that matters here (drive-letter folding, cross-drive `rel`) is
671
- * win32-only, so with the ambient `path` hardcoded the ceiling was enforced
672
- * only on a maintainer's dev box and CI's diff was structurally zero (#2544
673
- * review F2). Production never passes it; `path.win32`/`path.posix` are pure,
674
- * host-independent implementations, so `tests/clients/path-utils.test.ts` runs
675
- * the same table under both on every lane. Lane-level fix tracked as #2536.
676
- */
677
- export function isAtOrAboveHomeDir(dir, homeDir = os.homedir(), pathImpl = path) {
678
- const rel = pathImpl.relative(pathImpl.resolve(dir), pathImpl.resolve(homeDir));
679
- return !rel.startsWith("..") && !pathImpl.isAbsolute(rel);
680
- }
681
- /**
682
- * Rewrite a `$HOME`-anchored path to its `~` form for a DIAGNOSTIC surface.
683
- *
684
- * `~/.pi-lens/config.json` says everything an operator needs about which file
685
- * won, and `C:/Users/jane.doe/.pi-lens/config.json` says that plus the
686
- * account name. The second is the shape a global config path always has, so any
687
- * projection that names one leaks an identifier by default rather than by
688
- * accident (#2440 review finding F5).
689
- *
690
- * Deliberately NOT built on `normalizeFilePath`/`isUnderDir`: those resolve
691
- * through `realpathSync`, and a redaction helper on a diagnostic path must be
692
- * pure, total, and unable to throw for a file that no longer exists. This is a
693
- * string rewrite and nothing else.
694
- *
695
- * A path that is not under home is returned UNCHANGED — separators included.
696
- * Normalizing unrelated paths on the way past would make the helper's blast
697
- * radius every path any projection ever carries, for no redaction benefit.
698
- */
699
- export function homeRelativePath(filePath, homeDir = os.homedir()) {
700
- if (filePath.length === 0)
701
- return filePath;
702
- const candidate = toPosix(filePath);
703
- const home = toPosix(homeDir).replace(/\/+$/, "");
704
- if (home.length === 0)
705
- return filePath;
706
- // Windows paths are case-insensitive, so the COMPARISON folds case there
707
- // while the returned tail keeps the caller's own casing.
708
- const fold = (text) => process.platform === "win32" ? text.toLowerCase() : text;
709
- const foldedHome = fold(home);
710
- const foldedCandidate = fold(candidate);
711
- if (foldedCandidate === foldedHome)
712
- return "~";
713
- if (!foldedCandidate.startsWith(`${foldedHome}/`))
714
- return filePath;
715
- return `~${candidate.slice(home.length)}`;
716
- }
717
- export function isUnderDir(child, parent) {
718
- const normChild = normalizeFilePath(child);
719
- const normParent = normalizeFilePath(parent);
720
- // Ensure parent ends with / for prefix matching
721
- const parentPrefix = normParent.endsWith("/") ? normParent : `${normParent}/`;
722
- return normChild === normParent || normChild.startsWith(parentPrefix);
723
- }
724
- const VENDOR_DIR_NAMES = new Set([
725
- "node_modules",
726
- "vendor",
727
- "vendors",
728
- "third_party",
729
- "third-party",
730
- ]);
731
- /**
732
- * Returns true when a file should be treated as external/vendor and excluded
733
- * from pipelines (LSP, diagnostics, complexity, read-guard, etc.).
734
- *
735
- * Cases:
736
- * 1. Outside the project root entirely (e.g. global npm packages, system files)
737
- * 2. Inside the project but under a vendor directory (node_modules, vendor, third_party, etc.)
738
- */
739
- export function isExternalOrVendorFile(filePath, projectRoot) {
740
- if (!isUnderDir(filePath, projectRoot))
741
- return true;
742
- const normalized = normalizeFilePath(filePath);
743
- const rootNorm = normalizeFilePath(projectRoot);
744
- const rel = normalized.startsWith(rootNorm + "/")
745
- ? normalized.slice(rootNorm.length + 1)
746
- : normalized;
747
- return rel.split("/").some((seg) => VENDOR_DIR_NAMES.has(seg));
748
- }
749
- /**
750
- * Shared marker-glob semantics for every "does this directory contain a file
751
- * matching this glob" probe (#895 review): match against the entry NAME only,
752
- * `dot: true` so dotfile markers match, `nocase` on win32 to match the
753
- * filesystem (and the project ignore matcher). The three marker probes —
754
- * language-profile.ts `hasProjectMarker`, workspace-topology.ts
755
- * `hasBasenameMarker`, lsp/server.ts `markerExists` — must all route their
756
- * glob matching through here rather than call minimatch with hand-copied
757
- * options.
758
- */
759
- export function nameMatchesMarkerGlob(name, pattern) {
760
- return minimatch(name, pattern, {
761
- dot: true,
762
- nocase: process.platform === "win32",
763
- });
764
- }
765
- /**
766
- * Files/symlinks-only marker-glob probe over a directory listing — a
767
- * *directory* named like a marker (e.g. a `Foo.csproj/` dir) is not a project
768
- * file (#201).
769
- */
770
- export function direntsHaveMarkerGlobMatch(entries, pattern) {
771
- return entries.some((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
772
- nameMatchesMarkerGlob(entry.name, pattern));
773
- }
774
- /**
775
- * Cargo `[workspace] members`/`exclude` entries.
776
- *
777
- * KNOWN LIMITATION (#1671 F6, documented rather than implemented, preserved
778
- * byte-for-byte by #2591's fold): a recursive `**` component is NOT supported
779
- * and a pattern containing one never matches — cargo workspaces that rely on
780
- * `**` to pull in an arbitrarily-nested crate tree under-hoist (the crate stays
781
- * independently rooted instead of joining the workspace). Folding cargo onto
782
- * uv's `crosses-components` globstar would silently change Rust-LSP root
783
- * selection, so the divergence is kept as a dialect value, not resolved.
784
- *
785
- * `characterClasses: false` is likewise a preserved divergence, not a
786
- * considered choice: cargo's pre-fold segment compiler escaped `[` and `]`
787
- * into literals, so `crates/[ab]` names a directory literally called `[ab]`.
788
- * Real cargo (the `glob` crate) would read it as a class; changing that here
789
- * would be the same unreviewed Rust-hoisting change.
790
- */
791
- export const CARGO_WORKSPACE_MEMBER_DIALECT = {
792
- name: "cargo",
793
- globstar: "pattern-never-matches",
794
- wildcardCrossesSeparator: false,
795
- characterClasses: false,
796
- normalizePattern: (pattern) => pattern.replace(/\/+$/, ""),
797
- };
798
- /**
799
- * uv `[tool.uv.workspace] members`, pinned to
800
- * `astral-sh/uv@3c979abda4530fe9bf3d92e9bcf5c5575e3b3126`,
801
- * `crates/uv-workspace/src/workspace.rs` `is_included_in_workspace`: the glob is
802
- * `normalize_path`d first (so a leading `./` and any interior `.` component are
803
- * not part of the pattern) and matched with
804
- * `MatchOptions { require_literal_separator: true, ..MatchOptions::new() }`.
805
- */
806
- export const UV_WORKSPACE_MEMBERS_DIALECT = {
807
- name: "uv-members",
808
- globstar: "crosses-components",
809
- wildcardCrossesSeparator: false,
810
- characterClasses: true,
811
- normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
812
- };
813
- /**
814
- * uv `[tool.uv.workspace] exclude`, same upstream file and SHA
815
- * (`WorkspaceExclusions::matches`): an exclusion is matched with
816
- * `Pattern::matches_path`, i.e. `MatchOptions::new()` defaults, where
817
- * `require_literal_separator` is FALSE — so a `*` in an exclusion DOES cross
818
- * `/` (`exclude = ['packages/a*c']` excludes `packages/a/b/c`). Two different
819
- * option sets inside one tool, which is why the dialect is an object and the
820
- * two uv constants are not one.
821
- *
822
- * Before #2591 this was a documented limitation: minimatch cannot express a
823
- * separator-crossing `*`, so pi-lens under-excluded. The dialect object can, so
824
- * it is now implemented rather than documented.
825
- */
826
- export const UV_WORKSPACE_EXCLUDE_DIALECT = {
827
- name: "uv-exclude",
828
- globstar: "crosses-components",
829
- wildcardCrossesSeparator: true,
830
- characterClasses: true,
831
- normalizePattern: (pattern) => path.posix.normalize(toPosix(pattern)),
832
- };
833
- /**
834
- * Index of the `]` closing the character class opened at `open`, or `-1` when
835
- * the run is unterminated (in which case the `[` is a literal). A leading `!`
836
- * or `^` negates, and a `]` in first position is a literal class member — the
837
- * shape both minimatch and rust `glob` accept.
838
- */
839
- function findCharacterClassEnd(segment, open) {
840
- let i = open + 1;
841
- if (segment[i] === "!" || segment[i] === "^")
842
- i += 1;
843
- if (segment[i] === "]")
844
- i += 1;
845
- for (; i < segment.length; i += 1)
846
- if (segment[i] === "]")
847
- return i;
848
- return -1;
849
- }
850
- /**
851
- * May a wildcard with this separator policy consume `ch`?
852
- *
853
- * A separator-confined wildcard (`[^/]` in the regex this replaced) takes
854
- * anything but `/`. A separator-CROSSING one — uv `exclude`'s `*`/`?`, and
855
- * every `**` in either uv dialect — spelled `.` in that regex, which is every
856
- * code unit EXCEPT the four line terminators. That exclusion is preserved
857
- * deliberately rather than "fixed": minimatch's globstar is `.`-based too, so
858
- * a `\n` inside a directory name has never matched across a `**` in either
859
- * implementation, and the differential oracle would flag a change here as a
860
- * divergence rather than an improvement (#2603).
861
- */
862
- function stepAcceptsChar(crossesSeparator, ch) {
863
- return crossesSeparator
864
- ? ch !== "\n" && ch !== "\r" && ch !== "\u2028" && ch !== "\u2029"
865
- : ch !== "/";
866
- }
867
- /**
868
- * Compile a normalized workspace-member pattern into a step list, or
869
- * `undefined` when it cannot be compiled at all.
870
- *
871
- * A `**` component consumes zero or more path components — except as the LAST
872
- * component, where it requires at least one (`a/**` matches `a/b`, not `a`),
873
- * reproducing both minimatch's and rust `glob`'s answer. It is emitted as
874
- * `split → one → star → literal "/"`, i.e. "nothing, or one-or-more characters
875
- * followed by the `/` that ends them", after the separator that precedes it.
876
- * That is the same language as the `(?:/.+)?/` group the previous compiler
877
- * emitted — `X(?:/.+)?/Y` and `X/(?:.+/)?Y` both denote `X/Y` ∪ `X/.+/Y` — in a
878
- * form with no nested quantifier.
879
- */
880
- function compileWorkspaceMemberPattern(pattern, dialect) {
881
- const isGlobstar = (component) => component === "**" && dialect.globstar === "crosses-components";
882
- const components = pattern.split("/");
883
- const steps = [];
884
- let needSeparator = false;
885
- for (let c = 0; c < components.length; c += 1) {
886
- if (needSeparator)
887
- steps.push({ kind: "literal", char: "/" });
888
- needSeparator = false;
889
- const component = components[c];
890
- if (isGlobstar(component)) {
891
- if (c === components.length - 1) {
892
- // `.+` — a trailing `**` requires at least one character.
893
- steps.push({ kind: "one", crossesSeparator: true });
894
- steps.push({ kind: "star", crossesSeparator: true });
895
- continue;
896
- }
897
- // `(?:.+/)?` — zero or more whole components. The group already ends
898
- // at a `/`, so the next component must NOT emit one.
899
- const split = { kind: "split", alternative: -1 };
900
- steps.push(split);
901
- steps.push({ kind: "one", crossesSeparator: true });
902
- steps.push({ kind: "star", crossesSeparator: true });
903
- steps.push({ kind: "literal", char: "/" });
904
- split.alternative = steps.length;
905
- continue;
906
- }
907
- for (let i = 0; i < component.length; i += 1) {
908
- const ch = component[i];
909
- if (ch === "*") {
910
- steps.push({
911
- kind: "star",
912
- crossesSeparator: dialect.wildcardCrossesSeparator,
913
- });
914
- continue;
915
- }
916
- if (ch === "?") {
917
- steps.push({
918
- kind: "one",
919
- crossesSeparator: dialect.wildcardCrossesSeparator,
920
- });
921
- continue;
922
- }
923
- if (ch === "[" && dialect.characterClasses) {
924
- const close = findCharacterClassEnd(component, i);
925
- if (close !== -1) {
926
- // `\` and a first-position `]` are literal class MEMBERS in glob;
927
- // left alone they would be a regex escape and an empty-class
928
- // terminator (`[]ab]` is `[]` + `ab]` in JS), so both are escaped.
929
- const body = component.slice(i + 1, close).replace(/[\\\]]/g, "\\$&");
930
- const source = body.startsWith("!")
931
- ? `[^${body.slice(1)}]`
932
- : `[${body}]`;
933
- let match;
934
- try {
935
- match = new RegExp(`^${source}$`);
936
- }
937
- catch {
938
- // A glob character class is not a JS character class: `[z-a]`
939
- // is a legal glob (matching nothing, since the range is empty)
940
- // and an illegal RegExp ("Range out of order"). Fail CLOSED —
941
- // an uncompilable pattern declares no member and excludes
942
- // nothing — which is also the answer the deleted minimatch call
943
- // gave (#2591 review round 2, F2).
944
- return undefined;
945
- }
946
- steps.push({ kind: "class", match });
947
- i = close;
948
- continue;
949
- }
950
- }
951
- steps.push({ kind: "literal", char: ch });
952
- }
953
- needSeparator = true;
954
- }
955
- return steps;
956
- }
957
- /**
958
- * Does the whole of `subject` match the whole of `steps`?
959
- *
960
- * A memoized (step index, path index) table, filled once, bottom-up:
961
- * `table[s][p]` is "steps `s…` match `subject[p…]`". Every cell reads only
962
- * cells with a larger step index or a larger path index, so one backward
963
- * double loop fills the table with no recursion and no re-entry — the match is
964
- * O(steps x characters) in time and space, with no path through it that can
965
- * take exponential time (#2603).
966
- *
967
- * The compiled whole-path regex this replaced was correct but backtracking:
968
- * every `**` emitted its own nullable `.+`, and N of them explored 2^N splits
969
- * of a non-matching subject. #2591 collapsed CONSECUTIVE `**`s, which is a
970
- * normalization that cannot fire across a separating component, so
971
- * `("**\/*" x12)/zzz` against a 40-component path still took 124900 ms (#2603);
972
- * the same shapes are microseconds here. There is nothing left to collapse for
973
- * speed, so no collapse is done: `a/**\/**\/b` compiles to two adjacent
974
- * `(?:.+/)?` groups, which denote the same language as one and cost the same
975
- * table.
976
- */
977
- function matchesWorkspaceMemberSteps(steps, subject) {
978
- const stepCount = steps.length;
979
- const width = subject.length + 1;
980
- // One byte per (step, position) cell; `1` means "the rest matches from here".
981
- const table = new Uint8Array((stepCount + 1) * width);
982
- for (let p = subject.length; p >= 0; p -= 1) {
983
- const atEnd = p === subject.length;
984
- // The empty step list matches only the empty remainder.
985
- table[stepCount * width + p] = atEnd ? 1 : 0;
986
- for (let s = stepCount - 1; s >= 0; s -= 1) {
987
- const step = steps[s];
988
- const next = (s + 1) * width;
989
- let matched = 0;
990
- switch (step.kind) {
991
- case "literal":
992
- matched =
993
- !atEnd && subject[p] === step.char ? table[next + p + 1] : 0;
994
- break;
995
- case "one":
996
- matched =
997
- !atEnd && stepAcceptsChar(step.crossesSeparator, subject[p])
998
- ? table[next + p + 1]
999
- : 0;
1000
- break;
1001
- case "class":
1002
- matched =
1003
- !atEnd && step.match.test(subject[p]) ? table[next + p + 1] : 0;
1004
- break;
1005
- case "star":
1006
- // Consume nothing, or one more character and stay on this step.
1007
- matched =
1008
- table[next + p] === 1 ||
1009
- (!atEnd &&
1010
- stepAcceptsChar(step.crossesSeparator, subject[p]) &&
1011
- table[s * width + p + 1] === 1)
1012
- ? 1
1013
- : 0;
1014
- break;
1015
- case "split":
1016
- matched =
1017
- table[step.alternative * width + p] === 1 || table[next + p] === 1
1018
- ? 1
1019
- : 0;
1020
- break;
1021
- }
1022
- table[s * width + p] = matched;
1023
- }
1024
- }
1025
- return table[0] === 1;
1026
- }
1027
- /**
1028
- * Does `relativePath` — a `/`-separated path relative to the workspace root —
1029
- * match one declared workspace-member (or workspace-exclude) `pattern` under
1030
- * `dialect`?
1031
- *
1032
- * This is the ONE workspace-member matcher (#2591). `clients/lsp/server.ts`'s
1033
- * `cargoWorkspaceDeclaresMember` and `clients/python-environment.ts`'s
1034
- * `isUvWorkspaceMember` are thin callers of it; neither keeps a private segment
1035
- * compiler or minimatch options block any more.
1036
- *
1037
- * `clients/review-graph/workspace-modules.ts`'s `expandWorkspacePattern` is
1038
- * deliberately NOT a caller: npm/pnpm `workspaces` entries are EXPANDED against
1039
- * the filesystem (one `readdir` of the directory preceding the first `*`,
1040
- * yielding the directories that exist and hold a manifest) rather than tested
1041
- * against a candidate path. It answers "which directories does this pattern
1042
- * name", not "does this pattern name this directory"; folding it in would mean
1043
- * giving this pure function a filesystem.
1044
- *
1045
- * The supported syntax is the intersection of the two upstream dialects: `*`,
1046
- * `?`, `**`, `[abc]`/`[!abc]` (dialect-gated), and literals. minimatch-only
1047
- * extensions the pre-fold uv path inherited by accident — brace expansion,
1048
- * extglobs, leading-`!` negation, leading-`#` comments — are NOT honored, and
1049
- * uv's own `glob` crate does not honor them either (same pinned SHA), so
1050
- * dropping them moves uv toward upstream rather than away from it.
1051
- *
1052
- * Matching is NON-BACKTRACKING (#2603): the pattern compiles to a list of
1053
- * bounded steps and {@link matchesWorkspaceMemberSteps} decides it with one
1054
- * memoized (step, position) table, O(steps x characters), no matter how many
1055
- * `**`s the pattern carries. The anchored whole-path regex this replaced was
1056
- * correct but explored 2^N splits of a non-matching path for N `**`
1057
- * components — 124900 ms for `("**\/*" x12)/zzz` against a 40-component path,
1058
- * on a call `detectPythonEnvironment` awaits with no timeout, i.e. a wedged
1059
- * turn rather than a slow answer. The wall-clock half of that fix lives in
1060
- * `tests/clients/workspace-glob-nonbacktracking-budget.test.ts`; the answers
1061
- * are unchanged, pinned by the dialect table and the minimatch differential in
1062
- * `tests/clients/path-utils.test.ts`.
1063
- *
1064
- * A pattern that cannot be compiled at all answers `false` for every path —
1065
- * it declares no member and excludes nothing (#2591 review round 2, F2). The
1066
- * only such patterns today carry an empty character-class range (`[z-a]`:
1067
- * legal glob, illegal JS RegExp). Reachability note, recorded rather than
1068
- * assumed away: no such pattern can reach here through a manifest right now,
1069
- * because `parseTomlStringArray` (`clients/cargo-manifest.ts`) captures an
1070
- * array body non-greedily up to the FIRST `]`, so any entry containing a `]`
1071
- * loses its closing quote and is dropped before it becomes a pattern. That is
1072
- * a property of the TOML reader, not of this matcher, and it is not this
1073
- * function's to rely on — the character-class axis stays because upstream
1074
- * cargo and uv both support classes, and the reader's gap may be closed later.
1075
- */
1076
- export function matchesWorkspaceMemberPattern(pattern, relativePath, dialect) {
1077
- const normalized = dialect.normalizePattern(pattern);
1078
- if (dialect.globstar === "pattern-never-matches" &&
1079
- normalized.includes("**")) {
1080
- return false;
1081
- }
1082
- const steps = compileWorkspaceMemberPattern(normalized, dialect);
1083
- return (steps !== undefined && matchesWorkspaceMemberSteps(steps, relativePath));
1084
- }
1085
- /**
1086
- * Narrow no-break space, U+202F. macOS writes it before AM/PM in screenshot
1087
- * file names; users type an ordinary space.
1088
- */
1089
- const NARROW_NO_BREAK_SPACE = "\u202F";
1090
- /** Right single quotation mark, U+2019. macOS writes it; users type U+0027. */
1091
- const RIGHT_SINGLE_QUOTE = "\u2019";
1092
- /**
1093
- * The unicode space class pi folds to U+0020, copied character-for-character
1094
- * from `@earendil-works/pi-coding-agent/dist/utils/paths.js:6`
1095
- * (`UNICODE_SPACES`, source `src/utils/paths.ts`). Widening or narrowing this
1096
- * set makes pi-lens resolve a different file than pi does.
1097
- */
1098
- const HOST_UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
1099
- /**
1100
- * Mirror pi's `normalizeWindowsShellPath`
1101
- * (`@earendil-works/pi-coding-agent/dist/utils/paths.js:47-56`): convert Git
1102
- * Bash, MSYS, Cygwin, and WSL drive paths to a form native Windows APIs
1103
- * accept.
1104
- */
1105
- function hostNormalizeWindowsShellPath(filePath) {
1106
- if (!filePath.startsWith("/") ||
1107
- filePath.startsWith("//") ||
1108
- filePath.includes("\\")) {
1109
- return filePath;
1110
- }
1111
- const match = filePath.match(/^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i);
1112
- if (!match)
1113
- return filePath;
1114
- const suffix = match[2]?.replaceAll("/", "\\");
1115
- return `${match[1].toUpperCase()}:\\${suffix ?? ""}`;
1116
- }
1117
- /**
1118
- * Mirror pi's `normalizePath`
1119
- * (`@earendil-works/pi-coding-agent/dist/utils/paths.js:57-79`, source
1120
- * `src/utils/paths.ts`), in pi's order, which is load-bearing: the unicode
1121
- * fold runs BEFORE the `@` strip, and the tilde expansion before the
1122
- * `file://` conversion.
1123
- *
1124
- * The `win32` step is gated on `process.platform`, exactly as pi gates it.
1125
- * This is a deliberate exception to the usual probe-the-filesystem rule
1126
- * (AGENTS.md shape 2): pi-lens runs in pi's own process, so mirroring the
1127
- * host's own platform branch is what keeps the two resolvers in agreement.
1128
- * Shape-based parsing here would DIVERGE from the host, not protect against
1129
- * it.
1130
- */
1131
- export function normalizeHostToolPath(input, options = {}) {
1132
- let normalized = input;
1133
- if (options.normalizeUnicodeSpaces) {
1134
- normalized = normalized.replace(HOST_UNICODE_SPACES, " ");
1135
- }
1136
- if (options.stripAtPrefix && normalized.startsWith("@")) {
1137
- normalized = normalized.slice(1);
1138
- }
1139
- if (process.platform === "win32") {
1140
- normalized = hostNormalizeWindowsShellPath(normalized);
1141
- }
1142
- // `homedir()` is resolved inside the branch, as pi does. This runs on every
1143
- // tool_call and a `~` path is the rare case.
1144
- if (normalized === "~")
1145
- return os.homedir();
1146
- if (normalized.startsWith("~/") ||
1147
- (process.platform === "win32" && normalized.startsWith("~\\"))) {
1148
- return path.join(os.homedir(), normalized.slice(2));
1149
- }
1150
- if (normalized.startsWith("file://")) {
1151
- try {
1152
- return fileURLToPath(normalized);
1153
- }
1154
- catch {
1155
- // pi lets a malformed file: URL throw out of normalizePath, but pi-lens
1156
- // is advisory instrumentation on the same event: a URL pi rejects must
1157
- // degrade to "no path" here, never take down the tool_call handler.
1158
- return normalized;
1159
- }
1160
- }
1161
- return normalized;
1162
- }
1163
- /**
1164
- * Mirror pi's `resolveToCwd`
1165
- * (`@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js:42-44`,
1166
- * source `src/core/tools/path-utils.ts:~44-46`) \u2014 the BASE resolution every
1167
- * read/edit/write path goes through before the variant ladder below ever
1168
- * runs.
1169
- *
1170
- * Two details are pi's, not ours, and both matter:
1171
- * - the input is normalized with `normalizeUnicodeSpaces` + `stripAtPrefix`;
1172
- * the BASE DIR is normalized with neither (`resolvePath`,
1173
- * `dist/utils/paths.js:80-84`).
1174
- * - an already-absolute input is re-resolved on its own, ignoring the cwd.
1175
- */
1176
- export function resolveHostToolPath(input, baseDir) {
1177
- const normalized = normalizeHostToolPath(input, {
1178
- normalizeUnicodeSpaces: true,
1179
- stripAtPrefix: true,
1180
- });
1181
- const normalizedBaseDir = normalizeHostToolPath(baseDir);
1182
- return path.isAbsolute(normalized)
1183
- ? path.resolve(normalized)
1184
- : path.resolve(normalizedBaseDir, normalized);
1185
- }
1186
- /**
1187
- * Mirror pi's read-path fallback ladder (#1655 item 5).
1188
- *
1189
- * pi does NOT open `resolve(cwd, input.path)`. `resolveReadPath`
1190
- * (`@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js:45-70`,
1191
- * source `src/core/tools/path-utils.ts:52-83`) resolves, and when that path
1192
- * does not exist it silently retries four unicode/spacing variants in this
1193
- * exact order:
1194
- *
1195
- * 1. narrow no-break space before `AM.`/`PM.` (`tryMacOSScreenshotPath`)
1196
- * 2. NFD normalization (`tryNFDVariant`) — macOS stores names decomposed
1197
- * 3. U+0027 → U+2019 (`tryCurlyQuoteVariant`)
1198
- * 4. NFD + curly quote combined
1199
- *
1200
- * Each candidate is used only when it DIFFERS from the resolved path and the
1201
- * file exists; otherwise pi falls back to the resolved path. So the file pi
1202
- * actually read can differ from what a naive `path.resolve` produces, and
1203
- * pi-lens keyed its read guard, LSP touch, and dispatch off the naive form —
1204
- * silently doing nothing for exactly those files.
1205
- *
1206
- * Order matters: it is pi's, so pi-lens picks the same file pi did when more
1207
- * than one variant happens to exist.
1208
- *
1209
- * @param resolvedPath an already-resolved absolute path (the naive form)
1210
- * @param fileExists injectable existence probe; defaults to `existsSync`
1211
- */
1212
- export function resolveHostPathVariants(resolvedPath, fileExists = existsSync) {
1213
- if (fileExists(resolvedPath)) {
1214
- return { path: resolvedPath, unresolved: false, triedVariants: [] };
1215
- }
1216
- const nfd = resolvedPath.normalize("NFD");
1217
- const candidates = [
1218
- {
1219
- variant: "narrow-nbsp",
1220
- candidate: resolvedPath.replace(/ (AM|PM)\./gi, `${NARROW_NO_BREAK_SPACE}$1.`),
1221
- },
1222
- { variant: "nfd", candidate: nfd },
1223
- {
1224
- variant: "curly-quote",
1225
- candidate: resolvedPath.replaceAll("'", RIGHT_SINGLE_QUOTE),
1226
- },
1227
- {
1228
- variant: "nfd-curly-quote",
1229
- candidate: nfd.replaceAll("'", RIGHT_SINGLE_QUOTE),
1230
- },
1231
- ];
1232
- const triedVariants = [];
1233
- for (const { variant, candidate } of candidates) {
1234
- // pi skips a candidate identical to the resolved path, so pi-lens does
1235
- // too — otherwise a no-op "variant" would be reported as a match.
1236
- if (candidate === resolvedPath)
1237
- continue;
1238
- triedVariants.push(variant);
1239
- if (fileExists(candidate)) {
1240
- return { path: candidate, variant, unresolved: false, triedVariants };
1241
- }
1242
- }
1243
- return { path: resolvedPath, unresolved: true, triedVariants };
1244
- }