pi-lens 4.4.0 → 4.4.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 (150) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +1 -0
  3. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  4. package/dist/{chunk-IXEBJSUE.js → chunk-23WOO7TX.js} +11 -10
  5. package/dist/{chunk-E2WFBRHM.js → chunk-2B73ZPOK.js} +3 -3
  6. package/dist/{chunk-DQ2NZ7C4.js → chunk-2BBLFX75.js} +139 -11
  7. package/dist/{chunk-ZMOL6RQC.js → chunk-2CD5RS6M.js} +2 -2
  8. package/dist/{chunk-VD4JQCQC.js → chunk-2ZGIZPDG.js} +38 -37
  9. package/dist/{chunk-DIULIWKU.js → chunk-34WSVGEM.js} +29 -3
  10. package/dist/chunk-3JNKMP3B.js +17 -0
  11. package/dist/{chunk-HVUZXSWK.js → chunk-3UUPUE3O.js} +45 -41
  12. package/dist/{chunk-5RXBNSW2.js → chunk-3VPVG5SU.js} +16 -8
  13. package/dist/chunk-3WA4HP4I.js +55 -0
  14. package/dist/chunk-466Z26MG.js +30 -0
  15. package/dist/{chunk-34U7PLGE.js → chunk-4UNXBNE2.js} +12 -11
  16. package/dist/{chunk-E4MAEDPM.js → chunk-4W4VLAPJ.js} +12 -11
  17. package/dist/{chunk-GUOGVTCQ.js → chunk-52FUZI6K.js} +4 -2
  18. package/dist/chunk-5JM2524W.js +84 -0
  19. package/dist/{chunk-VHQLPIRJ.js → chunk-5NGR67U5.js} +2 -2
  20. package/dist/{chunk-DFVMIKMQ.js → chunk-66SXUJCA.js} +45 -9
  21. package/dist/{chunk-OQGFCZJ4.js → chunk-66VMUL7Q.js} +16 -16
  22. package/dist/{chunk-HWBZIGMJ.js → chunk-67WMNWEE.js} +9 -8
  23. package/dist/{chunk-6QIUX4EP.js → chunk-6C7JWPCP.js} +7 -7
  24. package/dist/{chunk-47D2BO4S.js → chunk-6GFRNVYR.js} +11 -10
  25. package/dist/{chunk-ZAWW3AF6.js → chunk-6SDEP3AJ.js} +9 -8
  26. package/dist/{chunk-TGPXLY2J.js → chunk-724UL2TJ.js} +38 -2
  27. package/dist/{chunk-YLF3PPT5.js → chunk-76MWC2K6.js} +3 -3
  28. package/dist/{chunk-F3P7VQ7H.js → chunk-7DYBLXYA.js} +3 -3
  29. package/dist/{chunk-AJ3QXTRQ.js → chunk-7ULNCDCO.js} +37 -35
  30. package/dist/{chunk-UK3CMEAL.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-F3AU2TMM.js → chunk-ARJ6PLFH.js} +44 -28
  32. package/dist/{chunk-U3VGR4VU.js → chunk-AW7R2GWG.js} +65 -8
  33. package/dist/{chunk-FJNKANOS.js → chunk-BKE7E7TC.js} +10 -9
  34. package/dist/{chunk-4W2PLC4J.js → chunk-BRGDSBJE.js} +13 -12
  35. package/dist/{chunk-VN2AXLEX.js → chunk-C34ZXZ7O.js} +102 -16
  36. package/dist/{chunk-T4ERT73Y.js → chunk-CF24OIYI.js} +21 -20
  37. package/dist/{chunk-NMABWBZN.js → chunk-CHOW74WE.js} +1 -1
  38. package/dist/{chunk-ARFCXG7S.js → chunk-CO2G5PHH.js} +2 -2
  39. package/dist/{chunk-AS44CFQ4.js → chunk-EETY4LF7.js} +24 -10
  40. package/dist/{chunk-I3R7GKDR.js → chunk-EJIUXPBX.js} +1 -1
  41. package/dist/{chunk-B3I2UUT4.js → chunk-ESBQHFMJ.js} +653 -251
  42. package/dist/{chunk-O5WILGMI.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-LNAKEO63.js → chunk-GAEGEPOC.js} +4 -4
  44. package/dist/{chunk-V7R425XG.js → chunk-GBPP5P7X.js} +4 -4
  45. package/dist/{chunk-XK4CIILI.js → chunk-GHB6AYCP.js} +27 -26
  46. package/dist/{chunk-7FBF7NPR.js → chunk-GOYRYZ2R.js} +29 -26
  47. package/dist/{chunk-JOLITGCC.js → chunk-GXW6K4YJ.js} +1 -1
  48. package/dist/{chunk-NXPS2L6U.js → chunk-GZYBXISR.js} +9 -9
  49. package/dist/{chunk-OAXNDZJ7.js → chunk-IBGIOBEE.js} +8 -4
  50. package/dist/{chunk-IIXOAGHR.js → chunk-IBRALRBR.js} +19 -15
  51. package/dist/{chunk-7F72PXY2.js → chunk-IXXDFY73.js} +3 -3
  52. package/dist/chunk-IZSHAIJ7.js +26 -0
  53. package/dist/{chunk-N3YQJI6O.js → chunk-JAU72XNY.js} +20 -15
  54. package/dist/chunk-JMYYOTQG.js +77 -0
  55. package/dist/{chunk-NL5GLMFI.js → chunk-JSQFTSC6.js} +12 -11
  56. package/dist/{chunk-IVP5KAB7.js → chunk-JWA5I3SR.js} +35 -21
  57. package/dist/{chunk-Y4T27COV.js → chunk-KBG47GHP.js} +4 -4
  58. package/dist/{chunk-PWFHBTSF.js → chunk-L3BLXZG7.js} +2 -2
  59. package/dist/chunk-LOP6GRPH.js +31 -0
  60. package/dist/chunk-MQLUAEOC.js +162 -0
  61. package/dist/{chunk-RPD5K5NC.js → chunk-MRXKYPCS.js} +2 -2
  62. package/dist/{chunk-Q5U6FMDI.js → chunk-N2H3ZPZY.js} +59 -7
  63. package/dist/{chunk-27BBHSZC.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-UY752QSN.js → chunk-NW7BOILN.js} +19 -17
  65. package/dist/{chunk-TLGFBM3F.js → chunk-ODBNXWT4.js} +302 -113
  66. package/dist/{chunk-TT22N74J.js → chunk-OG5UUPN7.js} +13 -13
  67. package/dist/{chunk-C2AUYXQH.js → chunk-OXXKQCSV.js} +6 -6
  68. package/dist/{chunk-PAK6IDME.js → chunk-P7266HLM.js} +2 -2
  69. package/dist/{chunk-52H5B5TC.js → chunk-PFNSLWU5.js} +31 -30
  70. package/dist/{chunk-5ZEJHV35.js → chunk-PHDMFAKM.js} +12 -2
  71. package/dist/{chunk-3I5QFT3N.js → chunk-PNQDPDRC.js} +6 -3
  72. package/dist/{chunk-TVN5N6GY.js → chunk-PV6LR5P5.js} +6057 -1311
  73. package/dist/{chunk-AHDW3GPE.js → chunk-Q6ZWTCLY.js} +24 -22
  74. package/dist/{chunk-RUOG5D2C.js → chunk-Q7DUK4LX.js} +5 -5
  75. package/dist/{chunk-YSNFMJKN.js → chunk-QGQ5CCCG.js} +2 -2
  76. package/dist/{chunk-LW6XKDZQ.js → chunk-QHJ6DQ2R.js} +287 -38
  77. package/dist/{chunk-GVXA6CCE.js → chunk-QILIYSJF.js} +1 -1
  78. package/dist/{chunk-22AVIGXS.js → chunk-QOBUHWHX.js} +12 -7
  79. package/dist/{chunk-5THXD6X5.js → chunk-QQNQ7SJW.js} +2 -75
  80. package/dist/{chunk-4RERDEXR.js → chunk-QZVW3AAK.js} +50 -41
  81. package/dist/{chunk-AWE5TMPO.js → chunk-R3PCY3YS.js} +14 -13
  82. package/dist/{chunk-HHYAELLJ.js → chunk-RPLINGVB.js} +7 -7
  83. package/dist/{chunk-EOKRQ3SA.js → chunk-RWWYJ623.js} +10 -1
  84. package/dist/{chunk-YGVYKNSN.js → chunk-SAEFPSB6.js} +59 -12
  85. package/dist/{chunk-K6VYLB2U.js → chunk-TYM6OUIT.js} +13 -12
  86. package/dist/{chunk-AJXODZY6.js → chunk-UXGETRP4.js} +13 -12
  87. package/dist/{chunk-I7SPGOWI.js → chunk-UZ3TUTRV.js} +1 -1
  88. package/dist/{chunk-TO6WL3GM.js → chunk-VCHXCQFG.js} +12 -2
  89. package/dist/{chunk-BRJKOBZP.js → chunk-VITUCP5A.js} +17 -6
  90. package/dist/{chunk-WONERJTP.js → chunk-VRZP76YM.js} +1 -1
  91. package/dist/{chunk-OS32RNR2.js → chunk-VSSBEUFT.js} +16 -15
  92. package/dist/{chunk-VDBPVIKD.js → chunk-VWCXW2E7.js} +1 -1
  93. package/dist/{chunk-NKOUFPQF.js → chunk-WOA4LN2R.js} +2 -2
  94. package/dist/{chunk-RIISMHKN.js → chunk-WOKKRCJ5.js} +15 -13
  95. package/dist/{chunk-XECLWQZN.js → chunk-WU567RSC.js} +13 -10
  96. package/dist/{chunk-V5PXFSZK.js → chunk-XCJN5IJU.js} +4 -4
  97. package/dist/{chunk-ORTMY4Q4.js → chunk-XKIZ2HNN.js} +22 -14
  98. package/dist/{chunk-6VCD4VKZ.js → chunk-XMSXXAHW.js} +2 -2
  99. package/dist/chunk-XPZPXCBV.js +28 -0
  100. package/dist/{chunk-O6TQT6RI.js → chunk-XTMJ7XDT.js} +1 -1
  101. package/dist/{chunk-VNMT7C64.js → chunk-XVF2VVXP.js} +5 -3
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-NTUJEGDZ.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-6P62QVEA.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-ODEEOXPH.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-AJVLWNFN.js → chunk-ZXYDQ3TZ.js} +5 -5
  107. package/dist/index.js +102097 -97778
  108. package/dist/mcp/analyze-cli.js +18 -8
  109. package/dist/mcp/cli.js +48 -37
  110. package/dist/mcp/server.js +695 -426
  111. package/dist/mcp/worker.js +66 -55
  112. package/dist/probes/installer.js +20 -19
  113. package/docs/ast-grep_rules_catalog.md +2 -2
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +38 -2
  116. package/docs/custom-rules.md +25 -2
  117. package/docs/dependencies.md +3 -0
  118. package/docs/environment-variables.md +34 -6
  119. package/docs/features.md +39 -3
  120. package/docs/globalconfig.md +3 -1
  121. package/docs/language-coverage.md +2 -1
  122. package/docs/lsp-capability-matrix.md +29 -6
  123. package/docs/lsp-idle-eviction.md +38 -47
  124. package/docs/pi-lens-fixer.md +13 -5
  125. package/docs/pi-lens-merge-policy.md +48 -1
  126. package/docs/pi-lens-monitor.md +3 -1
  127. package/docs/pi-lens-retro.md +2 -1
  128. package/docs/pi-lens-reviewer.md +19 -3
  129. package/docs/pi-lens-subagent.md +2 -0
  130. package/docs/real-harness.md +17 -0
  131. package/docs/schema/pi-lens-config-v1.json +565 -0
  132. package/docs/servercapabilities.md +18 -4
  133. package/docs/settings.md +2 -1
  134. package/docs/word-index.md +6 -0
  135. package/package.json +1 -1
  136. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  137. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  138. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  139. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  140. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  141. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  142. package/dist/chunk-54B42YRX.js +0 -27
  143. package/dist/chunk-CGGAIP7J.js +0 -29
  144. package/dist/chunk-D6QS3U6M.js +0 -30
  145. package/dist/chunk-HMFXILPV.js +0 -83
  146. package/dist/chunk-LSMJ6KBE.js +0 -4139
  147. package/dist/chunk-NTHH2J74.js +0 -27
  148. package/dist/chunk-RPD4ZSB7.js +0 -25
  149. package/dist/chunk-T6D4Q4AQ.js +0 -54
  150. package/dist/chunk-VCSRSYLF.js +0 -81
@@ -2,74 +2,79 @@
2
2
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
3
3
  import {
4
4
  analyzeFile
5
- } from "../chunk-TLGFBM3F.js";
6
- import "../chunk-C2AUYXQH.js";
7
- import "../chunk-I3R7GKDR.js";
5
+ } from "../chunk-ODBNXWT4.js";
6
+ import "../chunk-OXXKQCSV.js";
7
+ import "../chunk-EJIUXPBX.js";
8
8
  import "../chunk-FC4RP4I2.js";
9
- import "../chunk-DFVMIKMQ.js";
10
- import "../chunk-TO6WL3GM.js";
11
- import "../chunk-7F72PXY2.js";
12
- import "../chunk-RPD5K5NC.js";
13
- import "../chunk-ZMOL6RQC.js";
14
- import "../chunk-I7SPGOWI.js";
15
- import "../chunk-AS44CFQ4.js";
16
- import "../chunk-7FBF7NPR.js";
17
- import "../chunk-HHYAELLJ.js";
18
- import "../chunk-V5PXFSZK.js";
19
- import "../chunk-YLF3PPT5.js";
20
- import "../chunk-JOLITGCC.js";
21
- import "../chunk-E2WFBRHM.js";
22
- import "../chunk-6VCD4VKZ.js";
23
- import "../chunk-F3P7VQ7H.js";
24
- import "../chunk-LSMJ6KBE.js";
9
+ import "../chunk-66SXUJCA.js";
10
+ import "../chunk-VCHXCQFG.js";
11
+ import "../chunk-IXXDFY73.js";
12
+ import "../chunk-MRXKYPCS.js";
13
+ import "../chunk-2CD5RS6M.js";
14
+ import "../chunk-UZ3TUTRV.js";
15
+ import "../chunk-EETY4LF7.js";
16
+ import "../chunk-GOYRYZ2R.js";
17
+ import "../chunk-RPLINGVB.js";
18
+ import "../chunk-XCJN5IJU.js";
19
+ import "../chunk-76MWC2K6.js";
20
+ import "../chunk-GXW6K4YJ.js";
21
+ import "../chunk-2B73ZPOK.js";
22
+ import "../chunk-XMSXXAHW.js";
23
+ import "../chunk-7DYBLXYA.js";
24
+ import "../chunk-PV6LR5P5.js";
25
25
  import "../chunk-3MED75HT.js";
26
- import "../chunk-3I5QFT3N.js";
27
- import "../chunk-TVN5N6GY.js";
28
- import "../chunk-ARFCXG7S.js";
29
- import "../chunk-OAXNDZJ7.js";
26
+ import "../chunk-CO2G5PHH.js";
27
+ import "../chunk-IBGIOBEE.js";
28
+ import "../chunk-PNQDPDRC.js";
30
29
  import "../chunk-XIMYZ3JA.js";
31
- import "../chunk-B3I2UUT4.js";
32
- import "../chunk-DIULIWKU.js";
33
- import "../chunk-VDBPVIKD.js";
30
+ import "../chunk-ESBQHFMJ.js";
31
+ import "../chunk-34WSVGEM.js";
32
+ import "../chunk-VWCXW2E7.js";
33
+ import "../chunk-ZXYDQ3TZ.js";
34
34
  import "../chunk-5W3R6LFT.js";
35
- import "../chunk-LW6XKDZQ.js";
35
+ import "../chunk-QHJ6DQ2R.js";
36
36
  import "../chunk-Z54ZSOBZ.js";
37
- import "../chunk-TGPXLY2J.js";
37
+ import "../chunk-724UL2TJ.js";
38
38
  import "../chunk-QOZXJ4NA.js";
39
- import "../chunk-V7R425XG.js";
40
- import "../chunk-PAK6IDME.js";
41
- import "../chunk-GUOGVTCQ.js";
42
- import "../chunk-AJVLWNFN.js";
43
- import "../chunk-YGVYKNSN.js";
44
- import "../chunk-LNAKEO63.js";
39
+ import "../chunk-GBPP5P7X.js";
40
+ import "../chunk-P7266HLM.js";
41
+ import "../chunk-MQLUAEOC.js";
42
+ import "../chunk-52FUZI6K.js";
43
+ import "../chunk-SAEFPSB6.js";
44
+ import "../chunk-GAEGEPOC.js";
45
45
  import "../chunk-Z3CCVDUB.js";
46
- import "../chunk-PWFHBTSF.js";
46
+ import "../chunk-L3BLXZG7.js";
47
47
  import "../chunk-2BEMIWEW.js";
48
- import "../chunk-OQGFCZJ4.js";
49
- import "../chunk-RUOG5D2C.js";
50
- import "../chunk-U3VGR4VU.js";
51
- import "../chunk-NTUJEGDZ.js";
52
- import "../chunk-EOKRQ3SA.js";
53
- import "../chunk-WONERJTP.js";
54
- import "../chunk-22AVIGXS.js";
55
- import "../chunk-GVXA6CCE.js";
56
- import "../chunk-6QIUX4EP.js";
57
- import "../chunk-VHQLPIRJ.js";
58
- import "../chunk-VN2AXLEX.js";
48
+ import "../chunk-66VMUL7Q.js";
49
+ import "../chunk-Q7DUK4LX.js";
50
+ import "../chunk-AW7R2GWG.js";
51
+ import "../chunk-RWWYJ623.js";
52
+ import "../chunk-VRZP76YM.js";
53
+ import "../chunk-QOBUHWHX.js";
54
+ import "../chunk-QILIYSJF.js";
55
+ import "../chunk-6C7JWPCP.js";
56
+ import "../chunk-5NGR67U5.js";
57
+ import "../chunk-C34ZXZ7O.js";
59
58
  import "../chunk-BW5KFIQC.js";
60
59
  import "../chunk-QYF5S6P6.js";
61
- import "../chunk-5ZEJHV35.js";
62
- import "../chunk-DQ2NZ7C4.js";
63
- import "../chunk-VNMT7C64.js";
60
+ import "../chunk-PHDMFAKM.js";
61
+ import "../chunk-2BBLFX75.js";
62
+ import "../chunk-XVF2VVXP.js";
63
+ import {
64
+ setProjectTrustState
65
+ } from "../chunk-ZANLILYD.js";
64
66
  import "../chunk-2EECBNC5.js";
65
- import "../chunk-N3YQJI6O.js";
66
- import "../chunk-O6TQT6RI.js";
67
- import "../chunk-5THXD6X5.js";
68
- import "../chunk-UK3CMEAL.js";
67
+ import "../chunk-JAU72XNY.js";
68
+ import {
69
+ logExtension
70
+ } from "../chunk-XTMJ7XDT.js";
71
+ import "../chunk-JMYYOTQG.js";
72
+ import "../chunk-QQNQ7SJW.js";
73
+ import "../chunk-AOHZU2BM.js";
69
74
  import "../chunk-LJDODBBZ.js";
70
- import "../chunk-NMABWBZN.js";
75
+ import "../chunk-CHOW74WE.js";
71
76
  import "../chunk-ABBOA7UD.js";
72
- import "../chunk-Q5U6FMDI.js";
77
+ import "../chunk-N2H3ZPZY.js";
73
78
  import "../chunk-NXL4FFQQ.js";
74
79
 
75
80
  // dist/mcp/worker.js
@@ -79,6 +84,12 @@ function arg(name) {
79
84
  return found ? found.slice(prefix.length) : void 0;
80
85
  }
81
86
  async function main() {
87
+ setProjectTrustState("trusted");
88
+ logExtension({
89
+ subsystem: "project-trust",
90
+ level: "debug",
91
+ message: "mcp-worker: workspace trust belongs to the MCP client; pi trust not consulted"
92
+ });
82
93
  const file = arg("file");
83
94
  const cwd = arg("cwd") ?? process.cwd();
84
95
  if (!file) {
@@ -53,30 +53,31 @@ import {
53
53
  verifyNpmPackageEntry,
54
54
  verifyToolBinary,
55
55
  wasLastResolveTransient
56
- } from "../chunk-OQGFCZJ4.js";
57
- import "../chunk-RUOG5D2C.js";
58
- import "../chunk-U3VGR4VU.js";
59
- import "../chunk-NTUJEGDZ.js";
60
- import "../chunk-EOKRQ3SA.js";
61
- import "../chunk-WONERJTP.js";
62
- import "../chunk-22AVIGXS.js";
63
- import "../chunk-GVXA6CCE.js";
64
- import "../chunk-6QIUX4EP.js";
65
- import "../chunk-VHQLPIRJ.js";
66
- import "../chunk-VN2AXLEX.js";
56
+ } from "../chunk-66VMUL7Q.js";
57
+ import "../chunk-Q7DUK4LX.js";
58
+ import "../chunk-AW7R2GWG.js";
59
+ import "../chunk-RWWYJ623.js";
60
+ import "../chunk-VRZP76YM.js";
61
+ import "../chunk-QOBUHWHX.js";
62
+ import "../chunk-QILIYSJF.js";
63
+ import "../chunk-6C7JWPCP.js";
64
+ import "../chunk-5NGR67U5.js";
65
+ import "../chunk-C34ZXZ7O.js";
67
66
  import "../chunk-BW5KFIQC.js";
68
67
  import "../chunk-QYF5S6P6.js";
69
- import "../chunk-5ZEJHV35.js";
70
- import "../chunk-DQ2NZ7C4.js";
71
- import "../chunk-VNMT7C64.js";
68
+ import "../chunk-PHDMFAKM.js";
69
+ import "../chunk-2BBLFX75.js";
70
+ import "../chunk-XVF2VVXP.js";
71
+ import "../chunk-ZANLILYD.js";
72
72
  import "../chunk-2EECBNC5.js";
73
- import "../chunk-N3YQJI6O.js";
74
- import "../chunk-O6TQT6RI.js";
75
- import "../chunk-5THXD6X5.js";
76
- import "../chunk-UK3CMEAL.js";
73
+ import "../chunk-JAU72XNY.js";
74
+ import "../chunk-XTMJ7XDT.js";
75
+ import "../chunk-JMYYOTQG.js";
76
+ import "../chunk-QQNQ7SJW.js";
77
+ import "../chunk-AOHZU2BM.js";
77
78
  import "../chunk-LJDODBBZ.js";
78
79
  import "../chunk-ABBOA7UD.js";
79
- import "../chunk-Q5U6FMDI.js";
80
+ import "../chunk-N2H3ZPZY.js";
80
81
  import "../chunk-NXL4FFQQ.js";
81
82
  export {
82
83
  GITHUB_TOOLS,
@@ -231,7 +231,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
231
231
  | `prefer-structured-clone-js` | warning | no | pi-lens | Use structuredClone() instead of JSON.parse(JSON.stringify()) for deep cloning |
232
232
  | `strict-equality-js` | warning | yes | pi-lens | Use === instead of == |
233
233
  | `strict-inequality-js` | warning | yes | pi-lens | Use !== instead of != |
234
- | `throw-new-error-js` | error | no | pi-lens | Use new Error() not Error() — explicit construction is clearer |
234
+ | `throw-new-error-js` | warning | no | pi-lens | Use new Error() not Error() — explicit construction is clearer |
235
235
  | `unchecked-throwing-call-js` | error | no | pi-lens | $CALL without try/catch — throws on invalid input |
236
236
  | `weak-rsa-key-js` | error | no | pi-lens | Weak RSA key size — use at least 2048 bits |
237
237
 
@@ -547,7 +547,7 @@ See [`docs/custom-rules.md`](custom-rules.md) to add your own. Rule sources: `ru
547
547
  | `strict-equality` | warning | yes | pi-lens | Use === instead of == |
548
548
  | `strict-inequality` | warning | yes | pi-lens | Use !== instead of != |
549
549
  | `switch-without-default` | warning | no | pi-lens | switch has no default clause — values matching no case are silently ignored; add a default (even one that throws) |
550
- | `throw-new-error` | error | no | pi-lens | Use new Error() not Error() — explicit construction is clearer |
550
+ | `throw-new-error` | warning | no | pi-lens | Use new Error() not Error() — explicit construction is clearer |
551
551
  | `ts-array-concat-spread` | warning | no | pi-lens | arr.concat([...items]) - use arr.push(...items) or [...arr, ...items] |
552
552
  | `ts-array-every-some` | warning | no | pi-lens | arr.map(x => x.prop).every(Boolean) - use arr.every(x => !!x.prop) |
553
553
  | `ts-boolean-return-if-else` | warning | no | pi-lens | if/else returning true/false - simplify to return the condition directly |
@@ -17,7 +17,7 @@ The shipped rule set runs in two engines:
17
17
 
18
18
  | Path | Engine | Source |
19
19
  |------|--------|--------|
20
- | ast-grep LSP (the live path) | upstream binary, auto-installed by `ensureTool` | the binary's `ast-grep` version may be ahead of `npx ast-grep@x` |
20
+ | ast-grep LSP (the live path) | upstream binary, auto-installed by `ensureTool` | the binary's `ast-grep` version may be ahead of the npm `@ast-grep/cli` version |
21
21
  | ast-grep napi runner (fallback) | `@ast-grep/napi` npm package, version-pinned in `package.json` | can lag the binary by a release |
22
22
 
23
23
  The CLI test in `tests/clients/dispatch/runners/ast-grep-catalog-rules.test.ts`
@@ -10,6 +10,21 @@ There are **two** pi-lens config files:
10
10
  Both files have the same shape, with one exception noted below the example:
11
11
  everything LSP-related lives under an `lsp` namespace inside them.
12
12
 
13
+ Executable LSP fields from a project config are admitted only when pi has
14
+ marked the project trusted. This includes custom `command` values, command
15
+ overrides, `env`, and `initializationOptions`; an untrusted or older host is
16
+ fail-closed and emits one bounded notice telling you to mark the project
17
+ trusted in pi or upgrade pi. Global config remains trusted, and a project can
18
+ always disable a server. pi-lens does not maintain a second trust setting.
19
+
20
+ The editor-facing JSON Schema is published at
21
+ [`docs/schema/pi-lens-config-v1.json`](schema/pi-lens-config-v1.json). LSP
22
+ server entries also accept canonical argv arrays, optional `name` (defaulting
23
+ to the map id), `enabled`, `role` (`language` or `auxiliary`), `args`, and
24
+ `rootMarkers`. These new fields are experimental; reserved role, root-policy,
25
+ and timing semantics are inert until their planned catalog slices land, so this
26
+ schema slice does not change server launch behavior.
27
+
13
28
  ```jsonc
14
29
  {
15
30
  "$schema": "https://raw.githubusercontent.com/apmantza/pi-lens/master/docs/schema/pi-lens-config-v1.json",
@@ -23,8 +38,7 @@ everything LSP-related lives under an `lsp` namespace inside them.
23
38
  "my-server": {
24
39
  "name": "My Custom LSP",
25
40
  "extensions": [".myext"],
26
- "command": "my-lsp-server",
27
- "args": ["--stdio"],
41
+ "command": ["my-lsp-server", "--stdio"],
28
42
  "covers": ["shellcheck"]
29
43
  }
30
44
  },
@@ -37,6 +51,10 @@ everything LSP-related lives under an `lsp` namespace inside them.
37
51
  }
38
52
  ```
39
53
 
54
+ An override may also provide an experimental launcher `command` and `env`.
55
+ The built-in server ID and strategy remain in force; only the launcher is
56
+ replaced, and project-sourced launcher fields follow the same pi trust gate.
57
+
40
58
  Each model-facing tool accepts `tools.<name>.enabled` in the config file. Valid
41
59
  names include `ast_grep_search`, `ast_grep_replace`, `ast_grep_outline`,
42
60
  `lsp_navigation`, `lens_diagnostics`,
@@ -46,6 +64,24 @@ names include `ast_grep_search`, `ast_grep_replace`, `ast_grep_outline`,
46
64
  lifecycle tools `session_start`, `turn_end`, and `session_end` remain enabled
47
65
  because their host protocols require them.
48
66
 
67
+ To see the resolved tool surface, use `/lens-health`; the `effective_config`
68
+ tool provides the full configuration provenance. Bundled skills are managed by
69
+ pi's package-manifest filters.
70
+
71
+ ### Session-start orientation
72
+
73
+ The session-start orientation names only the tools pi-lens actually registered.
74
+ A tool disabled with `tools.<name>.enabled: false` is never advertised, so the
75
+ orientation cannot direct the agent to a tool it cannot call. The tool roster is
76
+ the same registry that drives registration; there is no second list.
77
+
78
+ `contextInjection.requirePreExistingFixes` (default `true`) controls one
79
+ sentence in that orientation: whether pre-existing blocking errors must be
80
+ fixed. Set it to `false` when the task's scope excludes pre-existing debt. The
81
+ orientation then requires fixes only for blocking errors the agent's own edits
82
+ introduce; pre-existing ones are reported, not demanded. It is a global setting,
83
+ like every `contextInjection` key.
84
+
49
85
  ### `lsp.servers.<id>.covers` — declaring which runners a custom server subsumes
50
86
 
51
87
  A custom server entry accepts an optional `covers` array naming dispatch
@@ -1,6 +1,6 @@
1
1
  # Custom Rules
2
2
 
3
- pi-lens picks up project-local rules automatically alongside its built-ins.
3
+ pi-lens picks up project-local and user-level rules automatically alongside its built-ins.
4
4
  Drop YAML files in the right directory and they are active on the next file dispatch — no config required.
5
5
 
6
6
  > For the full list of bundled rules per language, see the generated catalogs:
@@ -20,7 +20,30 @@ your-project/
20
20
  my-rule.yml ← ast-grep rule, overrides built-in with same id
21
21
  ```
22
22
 
23
- Project ast-grep rules are fingerprinted by relative path and contents, so in-place edits, renames, additions, and removals take effect within one tool call even when mtimes are preserved. Tree-sitter rules retain their directory-mtime cache.
23
+ Both rule families are fingerprinted by relative path and contents, so in-place edits, renames, additions, and removals are picked up without a restart, even when mtimes are preserved.
24
+
25
+ ### When an edit takes effect
26
+
27
+ A per-file dispatch reads the rule corpus fresh every time: its cache key is a
28
+ content fingerprint of every rule file the language's rule set is built from,
29
+ so an edit, addition, or removal is live on the next dispatched file.
30
+
31
+ Whole-project surfaces — a project scan and a structural search — reuse a
32
+ corpus fingerprint computed once per turn, so a rule edit lands on the next
33
+ turn, or immediately if any file is dispatched in between (a dispatch
34
+ republishes the fresh fingerprint for the rest of that turn). This is what
35
+ keeps a warm scan at the cost of a string comparison instead of re-reading
36
+ every rule file on every call.
37
+
38
+ ### User-level rules
39
+
40
+ Rules shared across projects live under the relocatable machine-data directory:
41
+ `<PI_LENS_HOME>/rules/` (or the platform default pi-lens directory when
42
+ `PI_LENS_HOME` is unset). Use the same `tree-sitter-queries/` and
43
+ `ast-grep-rules/` layout shown above. Precedence is project > user > bundled;
44
+ when rule IDs collide, the higher tier wins and the lower definition is
45
+ shadowed from execution. User rules are part of the same fingerprints as
46
+ project rules, so they follow the same timing above.
24
47
 
25
48
  ---
26
49
 
@@ -28,6 +28,7 @@ Auto-install behavior depends on gate type:
28
28
  | `shellcheck` | Shell script linting | Yes | GitHub release |
29
29
  | `shfmt` | Shell script formatting | Yes | GitHub release |
30
30
  | `rust-analyzer` | Rust LSP | Yes | GitHub release |
31
+ | `lake serve` | Lean 4 LSP | No | `lake` must be on PATH; uses the project's Lean toolchain |
31
32
  | `golangci-lint` | Go linting | Yes | GitHub release |
32
33
  | `hadolint` | Dockerfile linting | Yes | GitHub release |
33
34
  | `ktlint` | Kotlin linting | Yes | GitHub release |
@@ -51,6 +52,8 @@ Auto-install behavior depends on gate type:
51
52
  | `trivy` | Dependency-CVE session scan | Auto-install | Explicit opt-in (`trivy.enabled`) |
52
53
  | `psscriptanalyzer` | PowerShell linting | Manual | — |
53
54
 
55
+ Lean LSP is launched as `lake serve` from the detected Lake project root, so elan selects the toolchain from that project's `lean-toolchain` file and Lake supplies its project environment. pi-lens does not install or manage Lean/elan; install Lean and Lake separately and ensure `lake` is on `PATH`.
56
+
54
57
  Additional language servers (gopls, ruby-lsp, solargraph, etc.) are auto-detected from PATH or installed via native package managers (`go install`, `gem install`) when their language is detected.
55
58
 
56
59
  ## Pip tool installation order
@@ -104,12 +104,14 @@ without changing the steady-state behaviour of an interactive session.
104
104
 
105
105
  ### `PI_LENS_STARTUP_SCAN_VERDICT_TTL_MS`
106
106
 
107
- How long (ms) a persisted `too-many-source-files` startup-scan verdict is
108
- trusted before the source-file count is re-walked (default 24h). The verdict
109
- is cached in the project snapshot so repeated `pi -p` runs in a very large
110
- repo skip the counting walk entirely; a repo that shrinks below the threshold
111
- recovers when the TTL expires. Other verdicts use content-based freshness and
112
- ignore this setting.
107
+ How long (ms) a persisted size-skip startup-scan verdict (`too-many-source-files`
108
+ or `too-many-entries`) is trusted before the project is re-walked (default
109
+ 24h). The verdict is cached in the project snapshot so repeated `pi -p` runs in
110
+ a very large repo skip the counting walk entirely; a repo that shrinks below
111
+ the threshold recovers when the TTL expires. The verdict also records the
112
+ bounds that produced it (`maxProjectFiles`, `PI_LENS_STARTUP_SCAN_MAX_ENTRIES`),
113
+ so raising either one invalidates it at once and the next session re-walks.
114
+ Other verdicts use content-based freshness and ignore this setting.
113
115
 
114
116
  ## Scale and limits
115
117
 
@@ -144,6 +146,24 @@ Minimum wall-clock budget (ms) for every dispatch runner; the effective timeout
144
146
  is `max(runner budget, floor)`. **Default:** `0` (no floor). Also settable via
145
147
  the `dispatch.runnerTimeoutFloorMs` config key, which wins when both are set.
146
148
 
149
+ ### `PI_LENS_WORD_INDEX_MEMO_BACKSTOP_MS`
150
+
151
+ Backstop (ms) for the word index's serialized memo, which is also its
152
+ incremental-serialize cache: it is kept while a run edits (so each per-edit
153
+ persist stays O(dirty tokens)) and released once per run at `agent_settled`.
154
+ The backstop releases it when no persist has refreshed it for this long, for a
155
+ host that never emits `agent_settled` and for a stalled run. It is re-armed by
156
+ every serialize and never keeps the process alive. **Default:** `600000` (10
157
+ minutes). `0`, a negative or a non-numeric value means the default (a backstop
158
+ that fired at once would switch the memo off), and a value above `2147483647`
159
+ (about 24.8 days, Node's largest timer delay) is clamped to it. The default is
160
+ deliberately conservative: the maintainer's
161
+ `latency.log` held only two intra-run gaps between `project_snapshot_persist`
162
+ records (5.8 s), too few to derive a percentile. A release leaves one
163
+ `word_index_memo_released` row in `latency.log` (`trigger`: `settle`,
164
+ `post_settle_persist` or `backstop`). Releasing costs the next persist a full
165
+ re-serialize.
166
+
147
167
  ## Install control
148
168
 
149
169
  ### `PI_LENS_DISABLE_LSP_INSTALL`
@@ -268,6 +288,14 @@ nightly measurement behind each declaration, is in
268
288
  When unset or invalid, `PI_LENS_TS_IDLE_EVICT_MS` (below) is read.
269
289
  **Default:** 20 minutes (`1200000`).
270
290
 
291
+ ### `PI_LENS_EPHEMERAL_LSP_IDLE_EVICT_MS`
292
+
293
+ Idle window (ms) for language-service clients whose workspace is inside a
294
+ temporary or ephemeral Git checkout. This overrides the shared window above
295
+ for those roots; when unset or invalid, the default is 60 seconds (`60000`).
296
+ The nightly idle-eviction measurement temporarily sets this variable alongside
297
+ `PI_LENS_LSP_IDLE_EVICT_MS` so both root policies are measured.
298
+
271
299
  ### `PI_LENS_TS_IDLE_EVICT_MS`
272
300
 
273
301
  The original, TypeScript-named spelling of the same window, retained for
package/docs/features.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### LSP Support
4
4
 
5
- pi-lens includes **48 language server definitions** (including four cross-cutting *auxiliary* scanners that attach alongside the file's language server — Opengrep, ast-grep, zizmor, and typos — see below). LSP is **enabled by default**; use `--no-lsp` to disable it for a session. Servers are auto-discovered from PATH, project `node_modules`, and managed installs. When a server is not installed, pi-lens offers an interactive install prompt.
5
+ pi-lens includes **49 language server definitions** (including four cross-cutting *auxiliary* scanners that attach alongside the file's language server — Opengrep, ast-grep, zizmor, and typos — see below). LSP is **enabled by default**; use `--no-lsp` to disable it for a session. Servers are auto-discovered from PATH, project `node_modules`, and managed installs. When a server with a managed installer is not installed, pi-lens offers an interactive install prompt; toolchain-bundled servers, such as Lean's `lake`, are PATH-only, and pi-lens never installs or manages them.
6
6
 
7
7
  **LSP Idle Management:** LSP servers shut down after 240 seconds of inactivity (no files modified) to free resources. The timer resets when you resume editing, preventing cold-start penalties during active development.
8
8
 
@@ -19,7 +19,7 @@ pi-lens includes **48 language server definitions** (including four cross-cuttin
19
19
  - **`capabilities`** — shows which operations are supported by the active LSP server(s) for a file, read directly from the cached `initialize` response (no round-trip).
20
20
  - **Symbol column resolution** — passing `symbol: "myFunc"` instead of an exact `character` position resolves the correct column automatically. Use `symbol: "foo#2"` for the second occurrence of `foo` on the line.
21
21
 
22
- LSP servers for: TypeScript, Deno, Python (pyright/basedpyright + jedi), Go, Rust, Ruby (ruby-lsp + solargraph), PHP, PowerShell, C# (omnisharp), F#, Java (JDT LS, with Lombok javaagent support when a Lombok jar is available), Kotlin, Swift, Dart, Lua, C/C++, Zig, Haskell, Elixir, Gleam, Typst (tinymist), Markdown (marksman), OCaml, Clojure, CUE (syntax and parse diagnostics; evaluation errors via the cue-vet auxiliary runner), Terraform, Nix, Bash, Shuck (zsh-aware shell language server), Fish, CMake, Docker, YAML, JSON, HTML, TOML, Prisma, Vue, Svelte, CSS.
22
+ LSP servers for: TypeScript, Deno, Python (pyright/basedpyright + jedi), Go, Rust, Ruby (ruby-lsp + solargraph), PHP, PowerShell, C# (omnisharp), F#, Java (JDT LS, with Lombok javaagent support when a Lombok jar is available), Kotlin, Lean, Swift, Dart, Lua, C/C++, Zig, Haskell, Elixir, Gleam, Typst (tinymist), Markdown (marksman), OCaml, Clojure, CUE (syntax and parse diagnostics; evaluation errors via the cue-vet auxiliary runner), Terraform, Nix, Bash, Shuck (zsh-aware shell language server), Fish, CMake, Docker, YAML, JSON, HTML, TOML, Prisma, Vue, Svelte, CSS.
23
23
 
24
24
  ### Formatters
25
25
 
@@ -183,7 +183,7 @@ pi-lens MCP server expose the same shape to Claude Code / any MCP client.
183
183
 
184
184
  At `turn_end`, pi-lens writes `<project-data-dir>/cache/actionable-warnings.json` summarizing fixable warnings introduced by the current turn. This powers the optional conservative autofix at `agent_end`.
185
185
 
186
- `<project-data-dir>` is whatever `getProjectDataDir(cwd)` resolves to: `<project>/.pi-lens` only when that legacy directory already exists, otherwise `~/.pi-lens/projects/<project-slug>` (or a `PILENS_DATA_DIR` location). The turn-end advisory points at `lens_diagnostics mode=delta` first and names the resolved file second, so you never have to work the layout out by hand (#2521).
186
+ `<project-data-dir>` is whatever `getProjectDataDir(cwd)` resolves to: `<project>/.pi-lens` only when that legacy directory already exists when the process first resolves the root (a `.pi-lens` created mid-process takes effect at the next process start), otherwise `~/.pi-lens/projects/<project-slug>` (or a `PILENS_DATA_DIR` location). The turn-end advisory points at `lens_diagnostics mode=delta` first and names the resolved file second, so you never have to work the layout out by hand (#2521).
187
187
 
188
188
  **Report contents:**
189
189
 
@@ -234,6 +234,42 @@ One event per logical write batch (not per file) — e.g. a single eslint `--fix
234
234
 
235
235
  **Kill switch:** `PI_LENS_BUS_PUBLISH=0` disables publishing entirely (see `docs/environment-variables.md`). Publishing is fire-and-forget — a disabled/unavailable/throwing bus never affects the write path's own success or latency.
236
236
 
237
+ ### Bus Events — `pilens:format:*` (#673)
238
+
239
+ Deferred formatting has a separate lifecycle on the same `pi.events` bus:
240
+
241
+ ```
242
+ pilens:format:queued { v: 1, source: "pi-lens", filePath, cwd, tool, kinds,
243
+ ownerSessionId?, turnIndex?, batchId? }
244
+ pilens:format:start { v: 1, source: "pi-lens", cwd, paths, fileCount, kinds,
245
+ ownerSessionId?, turnIndex?, batchId? }
246
+ pilens:format:done { v: 1, source: "pi-lens", cwd, paths, fileCount, kinds,
247
+ ownerSessionId?, turnIndex?, batchId?, settled? }
248
+ ```
249
+
250
+ Paths and `cwd` are absolute, normalized strings. `queued` is emitted when a
251
+ file first enters the deferred queue; `start` is emitted when a non-empty
252
+ claimed format batch begins; and `done` is emitted after all formatters in that
253
+ batch and any late-write resyncs have settled. `paths: []` on `done` means the
254
+ batch changed no bytes. `batchId` is process-unique and monotonic across
255
+ coordinator generations, so
256
+ two same-millisecond reloads cannot share a lifecycle identity. A normal
257
+ `pilens:format:done` has `settled: true` (or omits the additive field for old
258
+ producers). If the formatter remains unsettled after the bounded hook/format
259
+ drain, pi-lens emits one terminal `pilens:format:done` with `settled: false`
260
+ and the still-pending paths, and records one `deferred-format-unsettled`
261
+ degradation for that batch. A later formatter write is resynced by the late
262
+ continuation and does not emit a second done event.
263
+ The session and turn fields let a listener match the lifecycle to its own
264
+ session and turn; consumers must not infer ownership from paths or timing.
265
+
266
+ These are visibility events, not a flush API. They are fire-and-forget and can
267
+ be absent when the bus is disabled, unwired, stale, or throws. Replaced-session
268
+ and aborted drains publish only for work they actually claim and finish; work
269
+ requeued for its owner is not
270
+ announced as done by the replacing session. The events are additive v1 schemas
271
+ and may gain optional fields without a version bump.
272
+
237
273
  **Fix provenance (#502):** `FilesTouchedPayload` gained an additive, optional `fixes` field so a diff/review consumer can distinguish a pi-lens-mechanical hunk from an agent edit:
238
274
 
239
275
  ```
@@ -112,6 +112,8 @@ Hide the diagnostics widget by default, run formatting immediately after write/e
112
112
 
113
113
  `contextInjection.enabled` (default `true`) controls whether pi-lens prepends automatic findings — session-start guidance, turn-end findings, and test findings — into the next model turn. Set it to `false` (or use `--no-lens-context` / `PI_LENS_NO_CONTEXT_INJECTION=1` / `/lens-context-toggle`) to keep tools, LSP, read-guard, and formatting running while avoiding the prompt-cache invalidation that injected messages cause in long, cache-sensitive sessions. Findings are still cached, so `lens_diagnostics` and `/lens-health` keep working.
114
114
 
115
+ `contextInjection.requirePreExistingFixes` (default `true`) controls whether the session-start orientation tells the agent to fix pre-existing blocking errors; set `false` to scope that demand to errors its own edits introduce.
116
+
115
117
  `actionableWarnings.enabled` gates the turn_end report. `includeLspCodeActions` fetches LSP code actions for each warning (requires an active language server). `deltaOnly` (default `true`) limits the report to lines touched in the current turn. `autoFix.enabled` applies conservative LSP quickfixes at `agent_end`; `autoFix.maxFixes` caps the number applied per turn (default `5`, must be a non-negative whole number). `maxFixes: 0` keeps the report and applies nothing, which is a useful halfway step before enabling `autoFix` for real. Unlike the toggles in the table above there is no CLI counterpart, since it takes a number rather than being on or off.
116
118
 
117
119
  `turnEnd.madge.enabled` (default `false`) runs the per-turn-end madge circular-dependency check on import-changed files. It is off by default because that pass only writes debug output — user-facing madge diagnostics come from the session-start scan cache and the `lens_diagnostics` extractor — and its subprocess spawns dominated the turn-end latency tail (#766). Enable it with `--lens-turn-end-madge` or `turnEnd.madge.enabled: true` if you want the per-edit circular-dependency note.
@@ -263,7 +265,7 @@ not corrupt delta baselines.
263
265
 
264
266
  ### `maxProjectFiles`
265
267
 
266
- Single scale knob (default `2000`) that a large-but-healthy repo can raise to scale five independent size budgets together instead of tripping each one separately: the project-diagnostics scanner (0.25×, default 500 files), the review graph (0.5× up to a point, default 1,000 files — see below), the startup scan (1×, default 2,000 source files), jscpd (3×, default 6,000 directory entries), and the word index (3×, default 6,000 files). Raising it (e.g. to `5000`) scales all five proportionally. Each subsystem's own environment-variable override (e.g. `PI_LENS_REVIEW_GRAPH_MAX_FILES`, `PI_LENS_STARTUP_SCAN_MAX_ENTRIES`) still takes precedence over this knob when set, and a separate `PI_LENS_MAX_PROJECT_FILES` environment variable sits below `maxProjectFiles` but above the built-in default. See `clients/project-scale.ts` for the full ratio table and precedence order.
268
+ Single scale knob (default `2000`) that a large-but-healthy repo can raise to scale five independent size budgets together instead of tripping each one separately: the project-diagnostics scanner (0.25×, default 500 files), the review graph (0.5× up to a point, default 1,000 files — see below), the startup scan (1.25×, default 2,500 source files), jscpd (3×, default 6,000 directory entries), and the word index (3×, default 6,000 files). Raising it (e.g. to `5000`) scales all five proportionally. Each subsystem's own environment-variable override (e.g. `PI_LENS_REVIEW_GRAPH_MAX_FILES`, `PI_LENS_STARTUP_SCAN_MAX_ENTRIES`) still takes precedence over this knob when set, and a separate `PI_LENS_MAX_PROJECT_FILES` environment variable sits below `maxProjectFiles` but above the built-in default. See `clients/project-scale.ts` for the full ratio table and precedence order.
267
269
 
268
270
  Unlike the other four, the review graph's budget stops scaling flatly at 0.5× once `maxProjectFiles` climbs past 4,000 — it tapers toward a 5,000-file ceiling instead of growing unbounded (#775). Its higher per-file cold-build cost bounds this default file budget independently of the graph's disk-persist circuit-breaker, whose measured default is now 500,000 elements (#936; override with `PI_LENS_GRAPH_PERSIST_MAX_ELEMENTS`). An over-cap graph persists a centrality-ranked partial snapshot with explicit total-versus-persisted coverage; read-only orientation may use it, but incremental builds never reuse it as a complete base. Below the 4,000 threshold (which covers every typical/default-sized project) it's the same flat 0.5× ratio as before. See `reviewGraph.maxFiles` below for an explicit opt-in past the taper.
269
271
 
@@ -1,6 +1,6 @@
1
1
  # Language Coverage
2
2
 
3
- pi-lens supports **36+ languages** through dispatch runners and LSP integration.
3
+ pi-lens supports **37+ languages** through dispatch runners and LSP integration.
4
4
 
5
5
  Formatting uses a single selected formatter per file: explicit project config wins, otherwise pi-lens uses a smart default where supported, and config-first ecosystems do not autoformat without config.
6
6
 
@@ -33,6 +33,7 @@ Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen i
33
33
  | Java | ✓ | lsp, javac | google-java-format |
34
34
  | Java + Lombok | ✓ | JDT LS launched with `-javaagent:<lombok.jar>` when Lombok is detected and a jar is found (`PI_LENS_LOMBOK_JAR` / `LOMBOK_JAR`, project `.lombok/lombok.jar`, or Maven/Gradle cache) | google-java-format |
35
35
  | Kotlin | ✓ | lsp, ktlint, detekt | ktlint |
36
+ | Lean 4 | ✓ (Lake `serve`) | lsp; uses each project's Lake toolchain and environment | — |
36
37
  | Swift | ✓ | lsp, swiftlint | swiftformat |
37
38
  | Dart | ✓ | lsp, dart-analyze | dart format |
38
39
  | Lua | ✓ | lsp | stylua |
@@ -1,5 +1,22 @@
1
1
  # LSP capability matrix — affirmative-clean-signal strategy
2
2
 
3
+ ## Project-code trust class (#4269)
4
+
5
+ Some global language-server binaries execute repository-owned build, plugin, or
6
+ macro code while starting or indexing. The built-in registry marks these with
7
+ `executesProjectCode`; the launch gate refuses them unless pi reports the
8
+ project as trusted. Unknown trust is a `spawn-failed` refusal, not
9
+ `tool-not-found`, and does not install or retry the server. The class currently
10
+ includes rust-analyzer (Cargo `build.rs` and proc macros), Ruby LSP
11
+ (Bundler/Gemfile composed-bundle setup), PowerShell Editor Services (workspace
12
+ modules), C# / F# servers (MSBuild targets), JDT LS and Kotlin (Gradle/Maven),
13
+ SourceKit-LSP (SwiftPM plugins), Haskell Language Server (Cabal/Stack and
14
+ Template Haskell), ElixirLS / Expert (`mix compile`), and the project-configured
15
+ analysis servers whose startup evaluates workspace configuration: Dart, ZLS,
16
+ OCaml, Clojure, Terraform, Nix, CMake, Vue, and Svelte. Lean is tracked by
17
+ #4122 and will declare itself when that change lands. gopls is excluded: its
18
+ startup/index discovery uses `go list`; cgo is not compiled by gopls startup.
19
+
3
20
  How pi-lens knows a just-edited file is **clean** (no diagnostics) vs the server
4
21
  simply **hasn't answered yet** (cold/crashed/silent). pi-lens waits *synchronously*
5
22
  for a verdict, so — unlike an editor, which renders asynchronously and never needs
@@ -68,8 +85,10 @@ discriminator either.
68
85
 
69
86
  `tests/config/lsp-first-publish-census.test.ts` compares this column against the
70
87
  `emptyFirstPublish` markers and reds in BOTH directions, which is the expiry
71
- check: when the nightly re-measures a server into a different class, the docs
72
- refresh cannot merge until the marker is updated with it.
88
+ check: when two consecutive nights re-measure a server into a different class,
89
+ the docs refresh commits the confirmed class and the marker must move with it.
90
+ A single-night flip remains in the refresh-state JSON below and does not change
91
+ the census's confirmed population.
73
92
 
74
93
  ## The strategies
75
94
 
@@ -148,7 +167,7 @@ carries the named admission until then.
148
167
  | deno | deno (alt of typescript) | pull | — | n/a (pull) | 1 | dev+ci |
149
168
  | ruby | ruby-lsp | pull | — | n/a (pull) | 1 | ci |
150
169
  | csharp | csharp-ls | pull | — | n/a (pull) | 1 | ci |
151
- | typescript | typescript-language-server | push-only | silent | direct | 3 | dev+ci |
170
+ | typescript | typescript-language-server | push-only | silent | empty-first | 3 | dev+ci |
152
171
  | markdown | marksman | push-only | silent | direct | 3 | ci |
153
172
  | lua | lua-language-server | push-only | silent | direct | 3 | dev+ci |
154
173
  | python | pyright | push-only | publishes-versioned | direct | 2 | dev+ci |
@@ -156,6 +175,7 @@ carries the named admission until then.
156
175
  | yaml | yaml-language-server | push-only | publishes-unversioned | direct | 2* | dev+ci |
157
176
  | shell | bash-language-server | push-only | publishes-versioned | direct | 2 | dev+ci |
158
177
  | dockerfile | docker-langserver | push-only | publishes-unversioned | direct | 2* | dev+ci |
178
+ | dockerfile-official | docker-language-server (official) | push-only | publishes-versioned | direct | 2 | dev+ci |
159
179
  | toml | taplo | push-only | publishes-unversioned | direct | 2* | dev+ci |
160
180
  | terraform | terraform-ls | push-only | publishes-unversioned | empty-first | 2* | dev+ci |
161
181
  | prisma | @prisma/language-server | push-only | publishes-unversioned | direct | 2* | dev+ci |
@@ -210,6 +230,9 @@ and were characterized `push-only` in the run above).
210
230
  `first-publish` measurements on the dev box, 2026-09-23: php `empty-first`,
211
231
  typescript / opengrep / ast-grep / marksman `direct` (so the class does not
212
232
  extend to them on the measurement, whatever their comments suggest).
233
+ - The official Docker row's clean-gate witness requires the BuildKit-backed
234
+ `JSONArgsRecommended` rule and skips when `docker buildx` is unavailable;
235
+ its first-publish class is the nightly's measured `direct`.
213
236
  - **#458's learned-deadline target set = the tier-3 rows only.** 2\* rows resolve
214
237
  the wait at runtime and must NOT be given learned deadlines.
215
238
  - **Tier 3 is budget-bound by necessity**, not laziness: a silent server's silence is
@@ -255,10 +278,10 @@ _None observed as of the last probe run._
255
278
 
256
279
  ## Capability matrix refresh state (nightly-generated)
257
280
 
258
- Bookkeeping for the date-based `direct` `first-publish` expiry (#3401), the
259
- two-run `clean-behavior` hysteresis and the consecutive-night `idle-eviction`
281
+ Bookkeeping for the two-run `first-publish`/`clean-behavior` hysteresis, the
282
+ date-based `direct` expiry (#3401) and consecutive-night `idle-eviction`
260
283
  counts (#3989). Regenerated every run; never a measurement.
261
284
 
262
285
  ```json
263
- {"idle-eviction":{"docker":{"nights":[{"day":"2026-10-07","rssMb":64,"coldMs":566}]},"json":{"nights":[{"day":"2026-10-07","rssMb":67,"coldMs":1116}]},"powershell":{"nights":[{"day":"2026-10-07","rssMb":155,"coldMs":2601}]},"python-jedi":{"nights":[{"day":"2026-10-07","rssMb":51,"coldMs":1796}]},"zizmor":{"nights":[{"day":"2026-10-07","rssMb":58,"coldMs":608}]}}}
286
+ {"first-publish":{"dockerfile":{"firstMissed":"2026-10-10"},"prisma":{"firstMissed":"2026-10-10"},"python":{"firstMissed":"2026-10-10"},"shell":{"firstMissed":"2026-10-10"},"terraform":{"pendingFirstPublish":"direct","runs":1},"yaml":{"firstMissed":"2026-10-10"}}}
264
287
  ```