@herbertgao/pi-extensions 2026.10.1 → 2026.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (934) hide show
  1. package/README.md +14 -17
  2. package/THIRD_PARTY_NOTICES.md +2 -2
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  9. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  10. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  11. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  12. package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
  13. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
  14. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  15. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  16. package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
  17. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  18. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
  19. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  20. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
  21. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
  22. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
  23. package/node_modules/{remote-pi → @herbertgao/pi-agency}/LICENSE +1 -1
  24. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  25. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  26. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  34. package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
  35. package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
  38. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
  39. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
  40. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
  41. package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
  43. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
  44. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
  45. package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
  46. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
  47. package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
  48. package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
  49. package/node_modules/@tifan/pi-recap/package.json +1 -1
  50. package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
  51. package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
  52. package/node_modules/pi-antigravity/README.md +23 -2
  53. package/node_modules/pi-antigravity/package.json +5 -3
  54. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
  55. package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
  56. package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +86 -47
  58. package/node_modules/pi-antigravity/src/image/native.ts +69 -0
  59. package/node_modules/pi-antigravity/src/index.ts +52 -137
  60. package/node_modules/pi-antigravity/src/search/search.ts +130 -31
  61. package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
  62. package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
  63. package/node_modules/pi-antigravity/src/types/types.ts +1 -1
  64. package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
  65. package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
  66. package/node_modules/pi-lens/CHANGELOG.md +1159 -0
  67. package/node_modules/pi-lens/README.md +116 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  70. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  71. package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
  72. package/node_modules/pi-lens/dist/chunk-2UMYMRI3.js +118 -0
  73. package/node_modules/pi-lens/dist/chunk-34OCDWOS.js +48 -0
  74. package/node_modules/pi-lens/dist/chunk-3FERWMCH.js +144 -0
  75. package/node_modules/pi-lens/dist/chunk-3JL6IFD7.js +97 -0
  76. package/node_modules/pi-lens/dist/chunk-3JSEWUWI.js +327 -0
  77. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  78. package/node_modules/pi-lens/dist/chunk-3MRXIJ6I.js +79 -0
  79. package/node_modules/pi-lens/dist/chunk-3P4PF2BX.js +2250 -0
  80. package/node_modules/pi-lens/dist/chunk-3PNFWVHT.js +201 -0
  81. package/node_modules/pi-lens/dist/chunk-3TTPKMEF.js +162 -0
  82. package/node_modules/pi-lens/dist/chunk-5JHXUETW.js +326 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-64LU2OVH.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6B5I6ICN.js +44 -0
  86. package/node_modules/pi-lens/dist/chunk-6P54TMWL.js +274 -0
  87. package/node_modules/pi-lens/dist/chunk-6UYA642J.js +145 -0
  88. package/node_modules/pi-lens/dist/chunk-6XX5HCLK.js +398 -0
  89. package/node_modules/pi-lens/dist/chunk-7AXO3ZDF.js +30 -0
  90. package/node_modules/pi-lens/dist/chunk-7BX5DWSA.js +1907 -0
  91. package/node_modules/pi-lens/dist/chunk-7ELY2CSM.js +902 -0
  92. package/node_modules/pi-lens/dist/chunk-7M4T5N3V.js +38 -0
  93. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  94. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  95. package/node_modules/pi-lens/dist/chunk-AFZYMAIV.js +756 -0
  96. package/node_modules/pi-lens/dist/chunk-ALQRYH6O.js +112 -0
  97. package/node_modules/pi-lens/dist/chunk-AXKPQ6ZI.js +1610 -0
  98. package/node_modules/pi-lens/dist/chunk-BHWHXMQ2.js +62 -0
  99. package/node_modules/pi-lens/dist/chunk-BJF25SUG.js +22 -0
  100. package/node_modules/pi-lens/dist/chunk-BNKLYOOO.js +18 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-CBD2B5CP.js +599 -0
  103. package/node_modules/pi-lens/dist/chunk-CFYISHUW.js +42 -0
  104. package/node_modules/pi-lens/dist/chunk-CMU4RMJB.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-CULQYFLU.js +14 -0
  106. package/node_modules/pi-lens/dist/chunk-D65GOCJB.js +289 -0
  107. package/node_modules/pi-lens/dist/chunk-D6YF5HKX.js +59 -0
  108. package/node_modules/pi-lens/dist/chunk-DMS52BEP.js +342 -0
  109. package/node_modules/pi-lens/dist/chunk-ELVWJYRV.js +211 -0
  110. package/node_modules/pi-lens/dist/chunk-EUT3B22Y.js +27 -0
  111. package/node_modules/pi-lens/dist/chunk-F5VLZHUW.js +146 -0
  112. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  113. package/node_modules/pi-lens/dist/chunk-FGALODG3.js +55 -0
  114. package/node_modules/pi-lens/dist/chunk-G3RLJYA2.js +2358 -0
  115. package/node_modules/pi-lens/dist/chunk-GAYGGICG.js +32 -0
  116. package/node_modules/pi-lens/dist/chunk-H2W2AXD3.js +419 -0
  117. package/node_modules/pi-lens/dist/chunk-H5EV3VMF.js +114 -0
  118. package/node_modules/pi-lens/dist/chunk-HREZQCT3.js +106 -0
  119. package/node_modules/pi-lens/dist/chunk-HSIRMK2D.js +517 -0
  120. package/node_modules/pi-lens/dist/chunk-IMSQR4WO.js +184 -0
  121. package/node_modules/pi-lens/dist/chunk-IQZXOZ7S.js +4816 -0
  122. package/node_modules/pi-lens/dist/chunk-IR6XFA5P.js +37 -0
  123. package/node_modules/pi-lens/dist/chunk-IUKPRZER.js +78 -0
  124. package/node_modules/pi-lens/dist/chunk-IUR76K6A.js +262 -0
  125. package/node_modules/pi-lens/dist/chunk-J3FZR3YY.js +98 -0
  126. package/node_modules/pi-lens/dist/chunk-J6DFZDFA.js +562 -0
  127. package/node_modules/pi-lens/dist/chunk-J7WJZ6W7.js +50 -0
  128. package/node_modules/pi-lens/dist/chunk-JONGANYH.js +202 -0
  129. package/node_modules/pi-lens/dist/chunk-KJCWXUUS.js +425 -0
  130. package/node_modules/pi-lens/dist/chunk-KJN4IOEJ.js +17 -0
  131. package/node_modules/pi-lens/dist/chunk-KMGPUKFD.js +25 -0
  132. package/node_modules/pi-lens/dist/chunk-LD7WFTRX.js +4533 -0
  133. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  134. package/node_modules/pi-lens/dist/chunk-LNOMA3MP.js +169 -0
  135. package/node_modules/pi-lens/dist/chunk-MGIWSROG.js +2334 -0
  136. package/node_modules/pi-lens/dist/chunk-MQA2YBAH.js +308 -0
  137. package/node_modules/pi-lens/dist/chunk-MTDRLSI7.js +21720 -0
  138. package/node_modules/pi-lens/dist/chunk-N2IRDBZI.js +2355 -0
  139. package/node_modules/pi-lens/dist/chunk-N77WFUL7.js +50 -0
  140. package/node_modules/pi-lens/dist/chunk-N7YNJE5R.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  142. package/node_modules/pi-lens/dist/chunk-NFXP47RV.js +999 -0
  143. package/node_modules/pi-lens/dist/chunk-NJF2X5XF.js +45 -0
  144. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  145. package/node_modules/pi-lens/dist/chunk-O7HZX7CA.js +309 -0
  146. package/node_modules/pi-lens/dist/chunk-OC7LYAU5.js +708 -0
  147. package/node_modules/pi-lens/dist/chunk-OEX24ECI.js +348 -0
  148. package/node_modules/pi-lens/dist/chunk-OYOB3ZFG.js +78 -0
  149. package/node_modules/pi-lens/dist/chunk-OYTMZXRF.js +41 -0
  150. package/node_modules/pi-lens/dist/chunk-OZJINN4O.js +84 -0
  151. package/node_modules/pi-lens/dist/chunk-PCHDGUKT.js +23 -0
  152. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  153. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  154. package/node_modules/pi-lens/dist/chunk-RJFED73B.js +79 -0
  155. package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
  156. package/node_modules/pi-lens/dist/chunk-S6AMWKPC.js +189 -0
  157. package/node_modules/pi-lens/dist/chunk-T4QZ6FEH.js +308 -0
  158. package/node_modules/pi-lens/dist/chunk-T54VJRAD.js +5456 -0
  159. package/node_modules/pi-lens/dist/chunk-TA5L5KWQ.js +167 -0
  160. package/node_modules/pi-lens/dist/chunk-TQFBZVCG.js +27 -0
  161. package/node_modules/pi-lens/dist/chunk-U52SAGRM.js +62 -0
  162. package/node_modules/pi-lens/dist/chunk-UEAHAMEI.js +5814 -0
  163. package/node_modules/pi-lens/dist/chunk-UGSMCQ4D.js +327 -0
  164. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  165. package/node_modules/pi-lens/dist/chunk-USHG4DLH.js +459 -0
  166. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  167. package/node_modules/pi-lens/dist/chunk-V5MEBST3.js +4139 -0
  168. package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
  169. package/node_modules/pi-lens/dist/chunk-VPWKF3GN.js +87 -0
  170. package/node_modules/pi-lens/dist/chunk-VSMJK3ZJ.js +1250 -0
  171. package/node_modules/pi-lens/dist/chunk-VYKKMYVT.js +560 -0
  172. package/node_modules/pi-lens/dist/chunk-VZHJS6DR.js +442 -0
  173. package/node_modules/pi-lens/dist/chunk-WATC3IQR.js +213 -0
  174. package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
  175. package/node_modules/pi-lens/dist/chunk-WSEWKCXU.js +188 -0
  176. package/node_modules/pi-lens/dist/chunk-WW5BLB74.js +659 -0
  177. package/node_modules/pi-lens/dist/chunk-XGUL4SLQ.js +406 -0
  178. package/node_modules/pi-lens/dist/chunk-XHSCVSOL.js +170 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-YLN4K6WD.js +385 -0
  181. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  182. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  183. package/node_modules/pi-lens/dist/chunk-Z6BB4JLQ.js +478 -0
  184. package/node_modules/pi-lens/dist/chunk-Z7QQA2KT.js +27 -0
  185. package/node_modules/pi-lens/dist/chunk-ZETYB3XG.js +2626 -0
  186. package/node_modules/pi-lens/dist/chunk-ZMOMPX2Z.js +87 -0
  187. package/node_modules/pi-lens/dist/chunk-ZOEYVXSV.js +149 -0
  188. package/node_modules/pi-lens/dist/chunk-ZZUFZUQZ.js +12120 -0
  189. package/node_modules/pi-lens/dist/index.js +39712 -29876
  190. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
  191. package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
  192. package/node_modules/pi-lens/dist/mcp/server.js +22688 -1573
  193. package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
  194. package/node_modules/pi-lens/dist/probes/installer.js +135 -0
  195. package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
  196. package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
  197. package/node_modules/pi-lens/docs/agent-guide.md +13 -4
  198. package/node_modules/pi-lens/docs/agent-tools.md +42 -0
  199. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -4
  200. package/node_modules/pi-lens/docs/configuration.md +28 -2
  201. package/node_modules/pi-lens/docs/custom-rules.md +25 -2
  202. package/node_modules/pi-lens/docs/environment-variables.md +86 -8
  203. package/node_modules/pi-lens/docs/features.md +45 -5
  204. package/node_modules/pi-lens/docs/globalconfig.md +2 -1
  205. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  206. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +37 -4
  207. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  208. package/node_modules/pi-lens/docs/mcp.md +21 -5
  209. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  210. package/node_modules/pi-lens/docs/pi-lens-fixer.md +160 -88
  211. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  212. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +736 -0
  213. package/node_modules/pi-lens/docs/pi-lens-monitor.md +75 -7
  214. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  215. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +189 -48
  216. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  217. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  218. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  219. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  220. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  221. package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
  222. package/node_modules/pi-lens/docs/servercapabilities.md +56 -49
  223. package/node_modules/pi-lens/docs/settings.md +1 -0
  224. package/node_modules/pi-lens/docs/usage.md +5 -1
  225. package/node_modules/pi-lens/docs/word-index.md +6 -0
  226. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  227. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  228. package/node_modules/pi-lens/package.json +18 -6
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  237. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  238. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  239. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  240. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  241. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  242. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  243. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  244. package/node_modules/pi-web-access/README.md +10 -6
  245. package/node_modules/pi-web-access/ceramic.ts +219 -0
  246. package/node_modules/pi-web-access/curator-page.ts +3 -1
  247. package/node_modules/pi-web-access/curator-server.ts +1 -0
  248. package/node_modules/pi-web-access/dist/index.js +846 -588
  249. package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
  250. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  251. package/node_modules/pi-web-access/github-extract.ts +34 -2
  252. package/node_modules/pi-web-access/index.ts +72 -30
  253. package/node_modules/pi-web-access/openai-search.ts +3 -3
  254. package/node_modules/pi-web-access/package.json +2 -2
  255. package/node_modules/pi-web-access/page-query.ts +6 -9
  256. package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
  257. package/node_modules/pi-web-access/query-rewrite.ts +2 -2
  258. package/node_modules/pi-web-access/storage.ts +49 -0
  259. package/node_modules/pi-web-access/summary-review.ts +7 -6
  260. package/node_modules/pi-web-access/utils.ts +21 -6
  261. package/node_modules/pi-web-access/web-tool-core.ts +1 -0
  262. package/package.json +19 -19
  263. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
  264. package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
  265. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
  266. package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
  267. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  268. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  269. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  270. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  271. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  272. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  273. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  274. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  275. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  276. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  277. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  278. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  279. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  280. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  281. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  282. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  283. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  284. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  285. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  286. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  287. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  288. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  289. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  290. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  291. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  292. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  293. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  294. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  295. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  296. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  297. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  298. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  299. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  300. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  301. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  302. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  303. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  304. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  305. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  306. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  307. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  308. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  309. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  310. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  311. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  312. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  313. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  314. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  315. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  316. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  317. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  318. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  319. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  320. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  321. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  322. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  323. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  324. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  325. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  326. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  327. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  328. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  329. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  330. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  331. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  332. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  333. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  334. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  335. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  336. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  337. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  338. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  339. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  340. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  341. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  342. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  343. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  344. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  345. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  346. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  347. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  348. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  349. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  350. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  351. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  352. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  353. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  354. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  355. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  356. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  357. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  358. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  359. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  360. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  361. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  362. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  363. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  364. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  365. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  366. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  367. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  368. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  369. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  370. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  371. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  372. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  373. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  374. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  375. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  376. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  377. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  378. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  379. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  380. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  381. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  382. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  383. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  384. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  385. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  386. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  387. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  388. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  389. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  390. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  391. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  392. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  393. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  394. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  395. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  396. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  397. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  398. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  399. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  400. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  401. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  402. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  403. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  404. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  405. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  406. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  407. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  447. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  448. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  449. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  450. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  451. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  452. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  453. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  454. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  455. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  456. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  457. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  458. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  459. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  460. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  461. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  462. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  463. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  464. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  465. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  466. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  467. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  468. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  469. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  470. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  471. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  472. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  473. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  474. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  475. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  476. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  477. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  478. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  479. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  480. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  481. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  482. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  483. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  484. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  485. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  486. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  487. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  488. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  489. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  490. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  491. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  492. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  493. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  494. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  495. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  496. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  497. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  498. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  499. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  500. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  501. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  502. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  503. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  504. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  505. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  506. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  507. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  508. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  509. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  510. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  511. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  512. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  513. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  514. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  515. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  516. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  517. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  518. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  519. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  520. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  521. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  522. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  523. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  524. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  525. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  526. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  527. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  528. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  529. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  530. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  531. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  532. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  533. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  534. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  535. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  536. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  537. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  538. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  539. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  540. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  541. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  542. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  543. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  544. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  545. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  546. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  547. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  548. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  549. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  550. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  551. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  552. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  553. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  554. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  555. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  556. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  557. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  558. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  559. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  560. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  561. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  562. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  563. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  564. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  565. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  566. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  567. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  568. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  569. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  570. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  571. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  572. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  573. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  574. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  575. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  576. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  577. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  578. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  579. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  580. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  581. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  582. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  583. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  584. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  585. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  586. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  587. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  588. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  589. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  590. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  591. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  592. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  593. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  594. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  595. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  596. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  597. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  598. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  599. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  600. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  601. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  602. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  603. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  604. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  605. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  606. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  607. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  608. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  609. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  610. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  611. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  612. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  613. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  614. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  615. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  616. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  617. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  618. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  619. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  620. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  621. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  623. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  624. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  625. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  626. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  627. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  628. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  629. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  630. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  631. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  632. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  633. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  634. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  635. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  636. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  637. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  638. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  639. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  640. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  641. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  642. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  643. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  644. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  645. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  646. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  647. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  648. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  649. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  650. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  651. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  652. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  653. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  654. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  655. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  656. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  657. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  658. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  659. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  660. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  661. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  662. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  663. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  664. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  665. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  666. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  667. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  668. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  669. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  670. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  671. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  672. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  673. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  674. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  675. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  676. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  677. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  678. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  679. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  680. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  681. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  682. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  683. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  684. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  685. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  686. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  687. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  688. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  689. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  690. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  691. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  692. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  693. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  694. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  695. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  696. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  697. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  698. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  699. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  700. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  701. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  702. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  703. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  704. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  705. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  706. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  707. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  708. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  709. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  710. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  711. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  712. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  713. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  714. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  715. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  716. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  717. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  718. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  719. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  720. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  721. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  722. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  723. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  724. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  725. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  726. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  727. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  728. package/node_modules/pi-lens/dist/i18n.js +0 -69
  729. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  730. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  731. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  732. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  733. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  734. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  735. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  736. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  737. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  738. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  739. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  740. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  741. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  742. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  743. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  744. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  745. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  746. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  747. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  748. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
  749. package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
  750. package/node_modules/remote-pi/README.md +0 -675
  751. package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
  752. package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
  753. package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
  754. package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
  755. package/node_modules/remote-pi/dist/actions/registry.js +0 -58
  756. package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
  757. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
  758. package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
  759. package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
  760. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
  761. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
  762. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
  763. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
  764. package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
  765. package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
  766. package/node_modules/remote-pi/dist/config.d.ts +0 -62
  767. package/node_modules/remote-pi/dist/config.js +0 -115
  768. package/node_modules/remote-pi/dist/config.js.map +0 -1
  769. package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
  770. package/node_modules/remote-pi/dist/daemon/client.js +0 -131
  771. package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
  772. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
  773. package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
  774. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
  775. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
  776. package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
  777. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
  778. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
  779. package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
  780. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
  781. package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
  782. package/node_modules/remote-pi/dist/daemon/id.js +0 -36
  783. package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
  784. package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
  785. package/node_modules/remote-pi/dist/daemon/install.js +0 -547
  786. package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
  787. package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
  788. package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
  789. package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
  790. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
  791. package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
  792. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
  793. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
  794. package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
  795. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
  796. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
  797. package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
  798. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
  799. package/node_modules/remote-pi/dist/index.d.ts +0 -213
  800. package/node_modules/remote-pi/dist/index.js +0 -4996
  801. package/node_modules/remote-pi/dist/index.js.map +0 -1
  802. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
  803. package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
  804. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
  805. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
  806. package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
  807. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
  808. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
  809. package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
  810. package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
  811. package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
  812. package/node_modules/remote-pi/dist/mesh/client.js +0 -125
  813. package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
  814. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
  815. package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
  816. package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
  817. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
  818. package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
  819. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
  820. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
  821. package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
  822. package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
  823. package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
  824. package/node_modules/remote-pi/dist/mesh/types.js +0 -11
  825. package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
  826. package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
  827. package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
  828. package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
  829. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
  830. package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
  831. package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
  832. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
  833. package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
  834. package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
  835. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
  836. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
  837. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
  838. package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
  839. package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
  840. package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
  841. package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
  842. package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
  843. package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
  844. package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
  845. package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
  846. package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
  847. package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
  848. package/node_modules/remote-pi/dist/protocol/types.js +0 -2
  849. package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
  850. package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
  851. package/node_modules/remote-pi/dist/rooms.js +0 -57
  852. package/node_modules/remote-pi/dist/rooms.js.map +0 -1
  853. package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
  854. package/node_modules/remote-pi/dist/session/address.js +0 -58
  855. package/node_modules/remote-pi/dist/session/address.js.map +0 -1
  856. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
  857. package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
  858. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
  859. package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
  860. package/node_modules/remote-pi/dist/session/bridge.js +0 -130
  861. package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
  862. package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
  863. package/node_modules/remote-pi/dist/session/broker.js +0 -507
  864. package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
  865. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
  866. package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
  867. package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
  868. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
  869. package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
  870. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
  871. package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
  872. package/node_modules/remote-pi/dist/session/envelope.js +0 -117
  873. package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
  874. package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
  875. package/node_modules/remote-pi/dist/session/global_config.js +0 -73
  876. package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
  877. package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
  878. package/node_modules/remote-pi/dist/session/ipc.js +0 -22
  879. package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
  880. package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
  881. package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
  882. package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
  883. package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
  884. package/node_modules/remote-pi/dist/session/local_config.js +0 -157
  885. package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
  886. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
  887. package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
  888. package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
  889. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
  890. package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
  891. package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
  892. package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
  893. package/node_modules/remote-pi/dist/session/peer.js +0 -451
  894. package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
  895. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
  896. package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
  897. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
  898. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
  899. package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
  900. package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
  901. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
  902. package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
  903. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
  904. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
  905. package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
  906. package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
  907. package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
  908. package/node_modules/remote-pi/dist/session/tools.js +0 -252
  909. package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
  910. package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
  911. package/node_modules/remote-pi/dist/session/wizard.js +0 -20
  912. package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
  913. package/node_modules/remote-pi/dist/settings.d.ts +0 -15
  914. package/node_modules/remote-pi/dist/settings.js +0 -52
  915. package/node_modules/remote-pi/dist/settings.js.map +0 -1
  916. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
  917. package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
  918. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
  919. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
  920. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
  921. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
  922. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
  923. package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
  924. package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
  925. package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
  926. package/node_modules/remote-pi/dist/ui/footer.js +0 -34
  927. package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
  928. package/node_modules/remote-pi/docs/daemon.md +0 -289
  929. package/node_modules/remote-pi/package.json +0 -92
  930. package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
  931. package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
  932. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
  933. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
  934. package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
@@ -1,2485 +0,0 @@
1
- import * as nodeFs from "node:fs";
2
- import * as path from "node:path";
3
- import { resetBoundedTelemetry } from "./bounded-telemetry.js";
4
- import { rotateMessageEndAttribution } from "./message-end-attribution.js";
5
- import { createDeadline, yieldIfOverBudget } from "./cooperative-budget.js";
6
- import { deadCodeIssueCount } from "./dead-code-client.js";
7
- import { logDeadCodeScan } from "./dead-code-logger.js";
8
- import { incrementDegradationCount, recordDegradationOnce, resetDegradationLedger, } from "./degradation-ledger.js";
9
- import { getDiagnosticTracker } from "./diagnostic-tracker.js";
10
- import { resetPsScriptAnalyzerAvailability } from "./dispatch/runners/psscriptanalyzer.js";
11
- import { resetInstallRetryLatches } from "./dispatch/runners/utils/availability-policy.js";
12
- import { resetLazyInstallAttempts } from "./dispatch/runners/utils/lazy-installer.js";
13
- import { resetDispatchAvailabilityState } from "./dispatch/runners/utils/runner-helpers.js";
14
- import { resetObservedRunnerLatency } from "./dispatch/collect-later-tier.js";
15
- import { resetPendingRunnerFindings } from "./dispatch/pending-runner-findings.js";
16
- import { clearAllSessions as clearFileTimeSessions } from "./file-time.js";
17
- import { drainProjectDataDirMigrations, getGlobalPiLensDir, getKnipIgnorePatterns, getProjectDataDir, resetProjectDataDirSessionState, } from "./file-utils.js";
18
- import { GitleaksClient } from "./gitleaks-client.js";
19
- import { resetGoAvailability } from "./go-client.js";
20
- import { GovulncheckClient, } from "./govulncheck-client.js";
21
- import { sweepAtomicWriteStages } from "./instance-reaper.js";
22
- import { canRunStartupHeavyScans } from "./language-policy.js";
23
- import { detectProjectLanguageProfile, getDefaultStartupTools, } from "./language-profile.js";
24
- import { logLatency } from "./latency-logger.js";
25
- import { runLogCleanup } from "./log-cleanup.js";
26
- import { resetCascadeTierSessionState } from "./lsp/cascade-tier.js";
27
- import { initLSPConfig, loadLSPConfig } from "./lsp/config.js";
28
- import { resetWorkspaceDiagnosticsCacheSession } from "./lsp/workspace-diagnostics-session.js";
29
- import { resetDirectLspCommandAvailability, resetLSPCaseSensitivityState, } from "./lsp/server.js";
30
- import { loadLspService } from "./lsp-lazy.js";
31
- import { resetManagedToolRefreshSession } from "./installer/managed-tool-refresh-session.js";
32
- import { resetResolvedPathCache } from "./installer/index.js";
33
- import { _resetPackageManagerCache } from "./package-manager.js";
34
- import { clearFormatterCache } from "./formatters.js";
35
- import { isAtOrAboveHomeDir } from "./path-utils.js";
36
- import { isPrintMode } from "./print-mode.js";
37
- import { readLatestProjectSequence, readLatestProjectSequenceAsync, } from "./project-changes.js";
38
- import { getProjectSnapshotPath, getProjectSnapshotLegacyPath, hydrateRuntimeFromProjectSnapshot, hydrateRuntimeFromProjectSnapshotIfIdle, isProjectSnapshotFresh, isProjectSnapshotMetaStale, loadProjectSnapshot, loadProjectSnapshotExportsAndRules, PROJECT_SNAPSHOT_VERSION, readProjectSnapshotMeta, saveRuntimeProjectSnapshot, } from "./project-snapshot.js";
39
- import { scanProjectRules } from "./rules-scanner.js";
40
- import { resetRustAvailability } from "./rust-client.js";
41
- import { resetSafeSpawnWindowsCommandCache } from "./safe-spawn.js";
42
- import { resetAnalyzerBootstrapSessionState, } from "./bootstrap.js";
43
- import { getSlowFsVerdict, isSlowFs, slowFsDegradationNotice, } from "./slow-fs.js";
44
- import { countRecentSmells, formatSmellsSessionStartLine, resetSmellsSessionState, } from "./smells-rollup.js";
45
- import { resetSituationalToolTelemetry } from "./situational-tool-telemetry.js";
46
- import { findNearestProjectRoot, getStartupScanMaxEntries, isStartupScanVerdictFresh, resolveStartupScanContext, } from "./startup-scan.js";
47
- import { getSubagentIdentity, isSubagentSession, subagentLightModeNotice, } from "./subagent-mode.js";
48
- import { TrivyClient } from "./trivy-client.js";
49
- import { isWarmAttached } from "./warm-attach.js";
50
- import { setSessionLanguages } from "./widget-state.js";
51
- import { logWordIndex } from "./word-index-logger.js";
52
- import { resetOpaqueMutationState } from "./opaque-mutation-scan.js";
53
- import { primePersistedMutationAttribution, resetMutationAttribution, } from "./mutation-attribution.js";
54
- import { resetObservedMutationNet } from "./observed-mutation.js";
55
- import { resetPendingAuxiliaryCoverage } from "./lsp/pending-aux-coverage.js";
56
- import { resetWorkspaceTopology } from "./workspace-topology.js";
57
- import { resetZizmorTokenAvailability } from "./zizmor-config.js";
58
- import { resetSpawnTimeoutCooldowns } from "./spawn-timeout-cooldown.js";
59
- import { resetTestRunnerDelivery } from "./test-runner-delivery.js";
60
- import { resetLspMutationNoBridgeDbgLatch } from "./lsp-mutation.js";
61
- /**
62
- * `deps` with resolved analyzer clients merged in, or `null` when the
63
- * bootstrap could not be served and the caller must proceed without them.
64
- *
65
- * No cast and no "is one representative field present?" probe: the seam
66
- * answers with all seventeen clients or with `null`, so the two outcomes are
67
- * the two branches. A skipped consumer is already counted in the ledger under
68
- * `analyzer-bootstrap-unavailable`, so it is never mistaken for a clean one.
69
- */
70
- async function demandBootstrapDeps(deps, reason) {
71
- const clients = await deps.bootstrap.request(reason);
72
- if (!clients)
73
- return null;
74
- return { ...deps, ...clients };
75
- }
76
- const HOST_STALL_THRESHOLD_MS = 30_000;
77
- function logHostReadyDelay(deps, cwd) {
78
- if (!deps.emitHostReadyDelay ||
79
- deps.sessionStartMonotonicAt === undefined ||
80
- deps.extensionLoadedAt === undefined) {
81
- return;
82
- }
83
- const durationMs = Math.max(0, deps.sessionStartMonotonicAt - deps.extensionLoadedAt);
84
- logLatency({
85
- type: "phase",
86
- filePath: cwd,
87
- phase: "host_ready_delay",
88
- durationMs,
89
- metadata: {
90
- hostStallSuspected: durationMs > HOST_STALL_THRESHOLD_MS,
91
- sessionStart: "first-process-session",
92
- reason: deps.sessionReason,
93
- },
94
- });
95
- }
96
- function resolveSnapshotRoot(cwd) {
97
- const resolvedCwd = path.resolve(cwd);
98
- const nearest = findNearestProjectRoot(resolvedCwd);
99
- // Reject a root at — or above — $HOME (the #250/#253 escape); fall back to
100
- // the cwd so the snapshot stays scoped to the actual workspace.
101
- if (!nearest || isAtOrAboveHomeDir(nearest)) {
102
- return resolvedCwd;
103
- }
104
- return nearest;
105
- }
106
- /**
107
- * #947: meta-first staleness gate. Both interactive paths used to sync-parse
108
- * the whole `project-snapshot.json` body (110-130ms at 40MB, ~0.5s at the
109
- * observed 112MB) BEFORE checking freshness — wasted work in the 71% of
110
- * sessions where the snapshot turns out stale. Read the tiny meta sidecar
111
- * (`project-snapshot.meta.json`, written on every save) first; when it says
112
- * stale (seq or version mismatch — the exact fields `isProjectSnapshotFresh`
113
- * checks), skip the body parse entirely. Callers already tolerate a missing
114
- * snapshot (fail-open contract). A missing meta file (legacy install, or a
115
- * snapshot written before the sidecar existed) falls through to parsing the
116
- * body exactly as before.
117
- */
118
- /**
119
- * #1019: build the bounded-replay base from the snapshot's tiny meta sidecar.
120
- * The sequence index is mirrored into the meta (not just the body) precisely so
121
- * this read is cheap — reading it does NOT parse the 40-112MB body, preserving
122
- * the #947 skip-stale optimization. Returns `undefined` (→ full replay) for a
123
- * legacy meta with no embedded index, a version-mismatched meta (a foreign
124
- * snapshot generation), or a missing meta. A seq-stale meta is NOT rejected
125
- * here — folding the newer entries onto it is the whole point; the base's
126
- * trustworthiness against a truncated/ahead log is guarded inside
127
- * `readLatestProjectSequence` itself.
128
- */
129
- function snapshotSequenceBase(root) {
130
- const meta = readProjectSnapshotMeta(root);
131
- if (!meta || meta.version !== PROJECT_SNAPSHOT_VERSION)
132
- return undefined;
133
- const index = meta.sequenceIndex;
134
- if (!index)
135
- return undefined;
136
- return {
137
- projectSeq: index.projectSeq,
138
- fileSeqByPath: index.fileSeqByPath,
139
- sinceSeq: meta.seq,
140
- };
141
- }
142
- /**
143
- * #1785: one bounded ledger record per timed-out sequence read, so a real
144
- * occurrence is diagnosable from the ledger alone rather than only from a
145
- * `dbg` line that most hosts never surface. Carries the project root and the
146
- * on-disk snapshot's age/size (when one exists) — the identity AC1 asks for:
147
- * which project, how stale/large the snapshot that got skipped was.
148
- */
149
- function recordSnapshotSequenceTimeout(args) {
150
- let reason = "sequence read timed out; no snapshot on disk";
151
- try {
152
- const stat = nodeFs.statSync(args.snapshotPath);
153
- reason = `sequence read timed out; snapshot age=${Math.round(Date.now() - stat.mtimeMs)}ms size=${stat.size}b`;
154
- }
155
- catch {
156
- // No snapshot on disk yet — the reason above already covers it.
157
- }
158
- incrementDegradationCount({
159
- kind: "snapshot-sequence-read-timeout",
160
- subject: args.snapshotRoot,
161
- reason,
162
- });
163
- }
164
- /**
165
- * #1785: the synchronous freshness check made a conservative call with the
166
- * `UNKNOWN_PROJECT_SEQ` sentinel (never hydrate when the real sequence is
167
- * unknown). Once the deferred read resolves — a deterministic completion
168
- * signal, not a sleep — this re-checks freshness against the now-known
169
- * sequence and hydrates late when the snapshot really was current, instead of
170
- * leaving the runtime without `cachedExports`/`projectRulesScan` for the rest
171
- * of the session.
172
- *
173
- * #1785 review round F5 (design (a), replacing rounds 1-3's synchronous
174
- * capture entirely): quick mode's own cold-start warmup ALREADY loads the
175
- * on-disk snapshot for its own purposes — `cachedSnapshot` at the top of the
176
- * warmup body, used to reuse a still-fresh `startupScan` verdict — strictly
177
- * BEFORE the warmup's own possible save (`saveRuntimeProjectSnapshot`, only
178
- * reached when that verdict was NOT fresh). Reusing THAT already-loaded
179
- * value costs nothing new: the read was always going to happen regardless of
180
- * this fix, and it is guaranteed to predate any overwrite the SAME warmup
181
- * invocation might go on to make — a read-before-write invariant of the
182
- * warmup's own code, not something this function has to defend against with
183
- * a race window of its own. `getWarmupOwnSnapshotRead` is a closure over a
184
- * per-`handleSessionStart`-call LOCAL variable (never module-scope — #1785
185
- * F6 is exactly the class of bug a module-scope holder here would
186
- * reintroduce) that the warmup publishes into once its own read completes.
187
- *
188
- * When the getter still returns `undefined` — the deferred read resolved
189
- * before the warmup got that far (or no warmup was ever armed by this call:
190
- * a later quick-mode call in the same process, print mode, full mode) —
191
- * there is nothing published yet, so this falls back to a live re-load via
192
- * `loadProjectSnapshotExportsAndRules`. That fallback is exactly as safe as
193
- * the primary path: if the warmup hasn't reached its own read yet, it
194
- * certainly hasn't saved yet either (same invariant), so disk still holds
195
- * whatever was there before any warmup activity.
196
- *
197
- * Both the published value and the fallback re-load are the narrow,
198
- * postings-free `ProjectSnapshotExportsAndRules` shape — never the full
199
- * `loadProjectSnapshot`. This path only ever hydrates
200
- * `cachedExports`/`projectRulesScan`; it can NOT hydrate `wordIndex` even
201
- * when the underlying snapshot would otherwise have one, because the narrow
202
- * type never carries it. That is intentional, not a regression: F2's hazard
203
- * was this exact path nulling a `wordIndex` that quick mode's warmup had, in
204
- * the meantime, built for real — the warmup remains the sole source of a
205
- * late-arriving `wordIndex` for the interactive path.
206
- */
207
- function retroactivelyHydrateAfterDeferredSequence(args) {
208
- return (latestSeq) => {
209
- const published = args.getWarmupOwnSnapshotRead();
210
- const snapshot = published !== undefined
211
- ? published
212
- : loadProjectSnapshotExportsAndRules(args.snapshotRoot);
213
- if (!snapshot ||
214
- snapshot.version !== PROJECT_SNAPSHOT_VERSION ||
215
- snapshot.seq !== latestSeq.projectSeq) {
216
- return;
217
- }
218
- const hydrated = hydrateRuntimeFromProjectSnapshotIfIdle(args.runtime, snapshot);
219
- if (hydrated) {
220
- args.dbg(`session_start: deferred sequence read confirmed snapshot freshness — hydrating cachedExports/projectRulesScan late (seq=${snapshot.seq})`);
221
- }
222
- else {
223
- args.dbg(`session_start: deferred sequence read confirmed snapshot freshness (seq=${snapshot.seq}), but the runtime already has live state (warmup or another task got there first) — skipping late hydration to avoid clobbering it`);
224
- }
225
- };
226
- }
227
- function loadSnapshotBodyUnlessStale(args) {
228
- const meta = readProjectSnapshotMeta(args.root);
229
- if (meta && isProjectSnapshotMetaStale(meta, args.currentProjectSeq)) {
230
- args.dbg(`project_snapshot: meta gate stale (metaSeq=${meta.seq} metaVersion=${meta.version} current=${args.currentProjectSeq}) — skipping body parse`);
231
- return { snapshot: null, skippedStale: true };
232
- }
233
- return {
234
- snapshot: loadProjectSnapshot(args.root),
235
- skippedStale: false,
236
- };
237
- }
238
- function describeSnapshotMiss(snapshot, currentProjectSeq, args) {
239
- if (args.skippedStale)
240
- return "stale-meta-gate";
241
- if (!snapshot)
242
- return args.bodyPresent ? "invalid-body" : "missing";
243
- if (snapshot.seq !== currentProjectSeq) {
244
- return `stale(seq=${snapshot.seq}, current=${currentProjectSeq})`;
245
- }
246
- // Defensive-only arm: parseSnapshot rejects incompatible versions, and a
247
- // same-sequence parsed snapshot is fresh. Keep this classification explicit
248
- // so a future loader change cannot collapse it into a generic miss.
249
- return "incompatible";
250
- }
251
- function logProjectSnapshotProbe(args) {
252
- args.dbg(`project_snapshot: probe root=${args.root} path=${getProjectSnapshotPath(args.root)} currentSeq=${args.currentProjectSeq}`);
253
- if (isProjectSnapshotFresh(args.snapshot, args.currentProjectSeq)) {
254
- args.dbg(`project_snapshot: loaded seq=${args.snapshot.seq} exports=${args.snapshot.cachedExports.length} files=${Object.keys(args.snapshot.files ?? {}).length} reverseDeps=${Object.keys(args.snapshot.reverseDeps ?? {}).length} startupScan=${Boolean(args.snapshot.startupScan)} languageProfile=${Boolean(args.snapshot.languageProfile)}`);
255
- }
256
- else {
257
- args.dbg(`project_snapshot: miss reason=${args.missReason}`);
258
- }
259
- }
260
- function resolveStartupMode(projectConfig, globalConfig) {
261
- const envMode = (process.env.PI_LENS_STARTUP_MODE ?? "").trim().toLowerCase();
262
- if (envMode === "full" || envMode === "minimal" || envMode === "quick") {
263
- return envMode;
264
- }
265
- const configMode = projectConfig?.startup?.mode ?? globalConfig?.startup?.mode;
266
- if (configMode)
267
- return configMode;
268
- if (isPrintMode()) {
269
- return "quick";
270
- }
271
- return "full";
272
- }
273
- // --- Session-start helpers ---
274
- // #1162: bound the session_start sequence read to this budget. The default
275
- // mirrors the issue's ~250ms figure; overridable for tests (mirrors the
276
- // `PI_LENS_WARMUP_DELAY_MS` precedent above) so a regression can use a tiny
277
- // budget instead of racing a real 250ms wall-clock wait.
278
- function sequenceReadBudgetMs() {
279
- const raw = Number(process.env.PI_LENS_SEQUENCE_READ_BUDGET_MS ?? 250);
280
- return Number.isFinite(raw) && raw > 0 ? raw : 250;
281
- }
282
- // #1162 review follow-up (P3): the sentinel used ONLY for the snapshot
283
- // freshness check (`isProjectSnapshotFresh`/`isProjectSnapshotMetaStale`,
284
- // both `=== currentProjectSeq` equality) when a sequence read timed out.
285
- // Every real `projectSeq`/`snapshot.seq` is >= 0 (both derive from
286
- // `Math.max(0, ...)`/a monotonic fold starting at 0), so this value can
287
- // never collide with a legitimate seq — including a project's real
288
- // first-ever snapshot persisted at `seq === 0`, which the cold-seed's OWN
289
- // `projectSeq: 0` would otherwise alias. Deliberately kept separate from
290
- // `runtime.projectSeq` (which a timed-out read still seeds to plain `0`):
291
- // the reseed-advancement guard on the deferred continuation below needs
292
- // `runtime.projectSeq` to read exactly 0 immediately after a cold seed so
293
- // `> 0` reliably means "a bump happened in the stall window", which this
294
- // sentinel is never fed into.
295
- const UNKNOWN_PROJECT_SEQ = -1;
296
- /**
297
- * Bound the sequence read that gates snapshot freshness (#1162). Normally
298
- * ~2ms, but the underlying read is a blocking `fs.readFileSync` that can
299
- * balloon under host I/O pressure (2125ms observed) with NO escape hatch —
300
- * and a `setTimeout`/`Promise.race` timeout can never preempt a *synchronous*
301
- * read (the thread only returns to the event loop once the OS call
302
- * returns). The fix is to read asynchronously (`readLatestProjectSequenceAsync`,
303
- * which uses `fs.promises.readFile` and genuinely yields) so a timeout CAN
304
- * win the race.
305
- *
306
- * On the healthy path (the overwhelming common case) the async read settles
307
- * first and this adds ~zero overhead beyond one microtask hop. On a stalled
308
- * read, the timeout wins: the caller proceeds immediately with a cold
309
- * sequence (`{ projectSeq: 0, fileSeqByPath: empty }` — byte-identical to
310
- * what a full replay of a missing/empty log already produces, so this is
311
- * exactly the existing cold-start case, not a new code path) while the real
312
- * read keeps running in the background. When it resolves, it re-seeds the
313
- * runtime so the warm-start optimization is recovered for the NEXT
314
- * session_start/turn instead of lost for the rest of the process — unless
315
- * the session has since moved on (`runtime.isCurrentSession`) or this is a
316
- * one-shot `pi --print` process (`isPrintMode()`), which has no future
317
- * session in-process to benefit and where letting background work outlive
318
- * settle is exactly the referenced-handle retention class #1154/#1153 just
319
- * closed. The timeout timer itself is `.unref()`'d and always cleared on
320
- * both race outcomes, so it never holds the process open either.
321
- *
322
- * Never silent (shape 10): the caller's `session_start_sequence_read`
323
- * latency line always carries `timedOut`, and a successful deferred reseed
324
- * logs its own `session_start_sequence_read_deferred_reseed` phase.
325
- */
326
- async function readSequenceWithBudget(args) {
327
- const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg, onDeferredSequenceResolved, } = args;
328
- const readPromise = readLatestProjectSequenceAsync(snapshotRoot, base);
329
- let timeoutHandle;
330
- const timeoutPromise = new Promise((resolve) => {
331
- timeoutHandle = setTimeout(() => resolve({ timedOut: true }), sequenceReadBudgetMs());
332
- timeoutHandle.unref();
333
- });
334
- const raced = await Promise.race([
335
- readPromise.then((latestSeq) => ({ timedOut: false, latestSeq })),
336
- timeoutPromise,
337
- ]);
338
- if (timeoutHandle)
339
- clearTimeout(timeoutHandle);
340
- if (!raced.timedOut) {
341
- return { latestSeq: raced.latestSeq, timedOut: false };
342
- }
343
- dbg(`session_start: sequence read exceeded ${sequenceReadBudgetMs()}ms budget — falling back to cold-start sequence`);
344
- if (!isPrintMode()) {
345
- readPromise
346
- .then((latestSeq) => {
347
- if (!runtime.isCurrentSession(sessionGeneration))
348
- return;
349
- // #1162 review follow-up (P3): `isCurrentSession` alone catches a
350
- // CROSS-session move-on but not a SAME-session advancement in the
351
- // stall window. Interleaving: timeout -> cold seed sets
352
- // projectSeq=0 -> handler returns -> agent edits file X ->
353
- // `bumpFileSeq(X)` advances `_projectSeq`/`_fileSeq[X]` -> this
354
- // deferred read completes. Reseeding here would blindly `.clear()`
355
- // and repopulate `_fileSeq`/`_fileLastProjectSeq` from the STALE
356
- // pre-edit snapshot, erasing the in-window bump for file X (a
357
- // transient inconsistency that self-heals into a full sweep per
358
- // `seedProjectSequence`'s empty-changed-map invariant — safe, but
359
- // worth not doing). The cold seed always sets `projectSeq` to
360
- // exactly 0, so `runtime.projectSeq > 0` here can ONLY be true if a
361
- // `bumpFileSeq` ran since — i.e. an in-window edit — never a false
362
- // positive from a legitimately-empty log (which leaves it at 0, and
363
- // the reseed below is then still wanted: it recovers the real,
364
- // possibly non-empty, result that arrived too late for the budget).
365
- if (runtime.projectSeq > 0) {
366
- dbg("session_start: deferred sequence read completed, but the session already advanced (in-window edit) — skipping reseed to avoid clobbering it");
367
- return;
368
- }
369
- const reseedStartedAt = Date.now();
370
- runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
371
- logLatency({
372
- type: "phase",
373
- phase: "session_start_sequence_read_deferred_reseed",
374
- filePath: cwd,
375
- startedAt: new Date(reseedStartedAt).toISOString(),
376
- durationMs: Date.now() - reseedStartedAt,
377
- metadata: { entries: latestSeq.fileSeqByPath.size, deferred: true },
378
- });
379
- dbg(`session_start: deferred sequence read completed — reseeded projectSeq=${latestSeq.projectSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
380
- onDeferredSequenceResolved?.(latestSeq);
381
- })
382
- .catch((err) => {
383
- dbg(`session_start: deferred sequence read failed: ${err}`);
384
- });
385
- }
386
- return {
387
- latestSeq: { projectSeq: 0, fileSeqByPath: new Map() },
388
- timedOut: true,
389
- };
390
- }
391
- async function igniteWarmFiles(cwd, warmFiles, runtime, sessionGeneration, dbg) {
392
- try {
393
- dbg(`session_start lsp-warm: ${warmFiles.length} warm file(s) configured`);
394
- await initLSPConfig(cwd);
395
- if (!runtime.isCurrentSession(sessionGeneration))
396
- return;
397
- const lspService = (await loadLspService()).getLSPService();
398
- const total = warmFiles.length;
399
- let loaded = 0;
400
- let errors = 0;
401
- for (const relPath of warmFiles) {
402
- if (!runtime.isCurrentSession(sessionGeneration))
403
- return;
404
- const filePath = path.isAbsolute(relPath)
405
- ? relPath
406
- : path.resolve(cwd, relPath);
407
- if (!nodeFs.existsSync(filePath)) {
408
- dbg(`session_start lsp-warm: not found: ${relPath}`);
409
- errors++;
410
- continue;
411
- }
412
- try {
413
- const content = nodeFs.readFileSync(filePath, "utf-8");
414
- await lspService.touchFile(filePath, content, {
415
- diagnostics: "none",
416
- source: "startup-warm",
417
- clientScope: "primary",
418
- maxClientWaitMs: 2000,
419
- });
420
- loaded++;
421
- }
422
- catch (err) {
423
- dbg(`session_start lsp-warm: error ${relPath}: ${err}`);
424
- errors++;
425
- }
426
- }
427
- dbg(`session_start lsp-warm: ${loaded}/${total} opened (${errors} err)`);
428
- }
429
- catch (err) {
430
- dbg(`session_start lsp-warm: config/init error: ${err}`);
431
- }
432
- }
433
- /**
434
- * Fallback warm when a project has no explicit `warmFiles`: pre-spawn the LSP
435
- * for the project's *dominant* language (highest source-file count) by opening
436
- * one representative file. This eliminates the cold-spawn stall the first edit
437
- * would otherwise pay (`lsp_client_wait_timeout`, observed up to 5s on
438
- * TypeScript/Deno). Only a single server is warmed — launching every detected
439
- * language's server at once (rust-analyzer + gopls + tsserver …) would spike CPU
440
- * and the event loop at startup, working against the very latency we protect.
441
- * Projects that want more pre-warming can list explicit `warmFiles` (#203).
442
- */
443
- async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg) {
444
- try {
445
- await initLSPConfig(cwd);
446
- if (!runtime.isCurrentSession(sessionGeneration))
447
- return;
448
- const lspService = (await loadLspService()).getLSPService();
449
- const { collectSourceFilesAsync } = await import("./source-filter.js");
450
- const { CODE_KINDS, detectFileKind } = await import("./file-kinds.js");
451
- // Async, event-loop-yielding walk (deferred off the interactive path).
452
- // inspectGeneratedHeaders:false keeps the walk to directory reads only — no
453
- // per-file content opens — so we never hold a file handle (cheaper, and it
454
- // can't collide with concurrent fs teardown). Picking a representative file
455
- // doesn't need generated-banner filtering.
456
- const files = await collectSourceFilesAsync(cwd, {
457
- inspectGeneratedHeaders: false,
458
- });
459
- if (!runtime.isCurrentSession(sessionGeneration))
460
- return;
461
- // Rank languages by source-file count. Computed here from the scan rather
462
- // than reused from languageProfile.counts, which is left empty on the
463
- // no-warm-caches startup path (detectProjectLanguageProfile is called with
464
- // an empty file list there).
465
- const counts = new Map();
466
- for (const f of files) {
467
- const kind = detectFileKind(f);
468
- if (kind)
469
- counts.set(kind, (counts.get(kind) ?? 0) + 1);
470
- }
471
- if (counts.size === 0) {
472
- dbg("session_start lsp-warm: no detected languages to auto-warm");
473
- return;
474
- }
475
- const ranked = [...counts.entries()].sort((a, b) => b[1] - a[1]);
476
- // #894 review: data/doc kinds (json/yaml/markdown/…) have builtin LSP
477
- // servers too, and broadened enumeration makes them countable — so a TS
478
- // repo with more .json/.yml than .ts files would otherwise warm the
479
- // json/yaml server while tsserver still pays the ~5s cold-spawn stall
480
- // this warm exists to prevent (#203). Rank CODE_KINDS first; non-code
481
- // kinds stay as a fallback so a pure-config/docs repo still warms its
482
- // dominant server.
483
- const warmOrder = [
484
- ...ranked.filter(([kind]) => CODE_KINDS.has(kind)),
485
- ...ranked.filter(([kind]) => !CODE_KINDS.has(kind)),
486
- ];
487
- // Walk languages by descending count; warm the first that has both an LSP
488
- // server and a representative on-disk file.
489
- for (const [kind] of warmOrder) {
490
- const sample = files.find((f) => detectFileKind(f) === kind && lspService.supportsLSP(f));
491
- if (!sample)
492
- continue;
493
- const content = await nodeFs.promises.readFile(sample, "utf-8");
494
- if (!runtime.isCurrentSession(sessionGeneration))
495
- return;
496
- await lspService.touchFile(sample, content, {
497
- diagnostics: "none",
498
- source: "startup-warm-dominant",
499
- clientScope: "primary",
500
- maxClientWaitMs: 2000,
501
- });
502
- dbg(`session_start lsp-warm: dominant=${kind} via ${path.basename(sample)}`);
503
- return;
504
- }
505
- dbg("session_start lsp-warm: no dominant-language LSP file found to warm");
506
- }
507
- catch (err) {
508
- dbg(`session_start lsp-warm: dominant warm error: ${err}`);
509
- }
510
- }
511
- function firePreinstallDefaults(ensureTool, dbg, startupDefaults) {
512
- for (const tool of startupDefaults) {
513
- const startedAt = Date.now();
514
- dbg(`session_start preinstall ${tool}: start`);
515
- ensureTool(tool)
516
- .then((toolPath) => {
517
- if (toolPath) {
518
- dbg(`session_start: ${tool} ready at ${toolPath}`);
519
- dbg(`session_start preinstall ${tool}: success (${Date.now() - startedAt}ms)`);
520
- }
521
- else {
522
- dbg(`session_start: ${tool} installation unavailable`);
523
- dbg(`session_start preinstall ${tool}: unavailable (${Date.now() - startedAt}ms)`);
524
- }
525
- })
526
- .catch((err) => {
527
- dbg(`session_start: ${tool} pre-install error: ${err}`);
528
- dbg(`session_start preinstall ${tool}: error (${Date.now() - startedAt}ms)`);
529
- });
530
- }
531
- }
532
- /**
533
- * Default delay before the managed-tool version refresh runs (#1730). Long
534
- * enough that `session_start`, the startup scans, and the tool preinstalls have
535
- * all settled — the refresh may spawn one `npm update`, and that must never
536
- * compete with the work the user is waiting on.
537
- */
538
- const MANAGED_TOOL_REFRESH_DELAY_MS = 30_000;
539
- /**
540
- * Schedule the periodic managed-tool refresh (#1730) on an unref'd background
541
- * timer.
542
- *
543
- * `session_start`'s own cost here is a single `setTimeout` registration: the
544
- * refresh module is not even imported until the timer fires. The timer is
545
- * unref'd so a one-shot process exits without waiting for it, matching the
546
- * warmup timer above. Whether a refresh actually runs is decided inside
547
- * `runManagedToolRefresh` against a PERSISTED per-tool stamp, so a launch loop
548
- * cannot escalate the weekly cadence.
549
- */
550
- function scheduleManagedToolRefresh(dbg) {
551
- const delayMs = Number(process.env.PI_LENS_TOOL_REFRESH_DELAY_MS ?? MANAGED_TOOL_REFRESH_DELAY_MS);
552
- const timer = setTimeout(() => {
553
- void (async () => {
554
- try {
555
- const refresh = await import("./installer/managed-tool-refresh.js");
556
- const outcome = await refresh.runManagedToolRefresh();
557
- if (outcome.skipped) {
558
- dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
559
- return;
560
- }
561
- for (const result of outcome.refreshed) {
562
- dbg(`session_start tool-refresh: ${result.toolId} ${result.ok ? "ok" : "failed"}` +
563
- `${result.changed ? ` ${result.previousVersion ?? "unknown"} → ${result.currentVersion}` : " (unchanged)"}`);
564
- }
565
- }
566
- catch (err) {
567
- dbg(`session_start tool-refresh: error ${err}`);
568
- }
569
- })();
570
- }, Number.isFinite(delayMs) ? delayMs : MANAGED_TOOL_REFRESH_DELAY_MS);
571
- timer.unref?.();
572
- }
573
- async function probePrettierInstall(ensureTool, dbg, analysisRoot) {
574
- const pkgPath = path.join(analysisRoot, "package.json");
575
- try {
576
- const raw = await nodeFs.promises.readFile(pkgPath, "utf-8");
577
- const pkg = JSON.parse(raw);
578
- const usesPrettier = !!pkg.devDependencies?.prettier ||
579
- !!pkg.dependencies?.prettier ||
580
- pkg.prettier !== undefined;
581
- if (usesPrettier) {
582
- dbg("session_start: project uses prettier, ensuring install...");
583
- ensureTool("prettier")
584
- .then((p) => {
585
- if (p)
586
- dbg(`session_start: prettier ready at ${p}`);
587
- else
588
- dbg("session_start: prettier install failed silently");
589
- })
590
- .catch((err) => dbg(`session_start: prettier install error: ${err}`));
591
- }
592
- }
593
- catch {
594
- // no package.json at cwd root
595
- }
596
- }
597
- /** Scan one file via the per-file API, pushing any items. Tolerates an
598
- * unreadable file and a scanner without `scanFile` (no-op). */
599
- function scanOneTodoFile(scanner, filePath, items) {
600
- if (typeof scanner.scanFile !== "function")
601
- return;
602
- try {
603
- // scanFile returns TodoItem[] directly (not a { items } result).
604
- const result = scanner.scanFile(filePath);
605
- if (Array.isArray(result))
606
- items.push(...result);
607
- }
608
- catch {
609
- // Per-file error: skip and continue (matches scanDirectory's tolerance).
610
- }
611
- }
612
- /** Collect the TODO baseline without blocking: enumerate source files and scan
613
- * them per-file, yielding to the event loop every 30 files. Falls back to the
614
- * blocking `scanDirectory` if the chunked path can't run (import error or a
615
- * scanner without `scanFile`). */
616
- async function collectTodoBaselineItems(scanner, analysisRoot, stillCurrent) {
617
- const items = [];
618
- try {
619
- const { getSourceFilesAsync } = await import("./scan-utils.js");
620
- // Enumerate with the chunked-yield walker so the file collection itself
621
- // (the previously-synchronous ~1.5s burst on a 2k-file tree) no longer
622
- // blocks the event loop before the per-file scan loop below even starts.
623
- const files = await getSourceFilesAsync(analysisRoot, true);
624
- if (!stillCurrent())
625
- return items;
626
- const deadline = createDeadline(8);
627
- for (const file of files) {
628
- if (!stillCurrent())
629
- return items;
630
- scanOneTodoFile(scanner, file, items);
631
- // scanFile cost scales with the file contents, so a count cadence cannot
632
- // bound the event-loop block across differently-sized corpora.
633
- await yieldIfOverBudget(deadline);
634
- }
635
- }
636
- catch {
637
- const todoResult = scanner.scanDirectory(analysisRoot);
638
- items.push(...todoResult.items);
639
- }
640
- return items;
641
- }
642
- async function buildOrRefreshWordIndex(args) {
643
- const { runtime, sessionGeneration, analysisRoot, snapshotRoot, dbg } = args;
644
- if (!runtime.isCurrentSession(sessionGeneration))
645
- return;
646
- const startMs = Date.now();
647
- let rebuildPreflightFiles;
648
- const latestSeq = readLatestProjectSequence(snapshotRoot, snapshotSequenceBase(snapshotRoot));
649
- const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
650
- const snapshotLoadStartMs = Date.now();
651
- const snapshot = loadProjectSnapshot(snapshotRoot);
652
- const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
653
- if (snapshot?.wordIndex) {
654
- const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries, estimateWordIndexResidentBytes, } = await import("./word-index.js");
655
- const deserializeStartMs = Date.now();
656
- const index = deserializeWordIndex(snapshot.wordIndex);
657
- const deserializeMs = Date.now() - deserializeStartMs;
658
- if (index) {
659
- try {
660
- const result = await refreshWordIndexIncrementally(index, analysisRoot, () => runtime.isCurrentSession(sessionGeneration));
661
- if (!runtime.isCurrentSession(sessionGeneration))
662
- return;
663
- if (result.mode === "full-required") {
664
- rebuildPreflightFiles = result.preflightFiles;
665
- dbg(`session_start word-index: incremental preflight selected full rebuild (${result.reason})`);
666
- logWordIndex({
667
- phase: "incremental_fallback",
668
- cwd: snapshotRoot,
669
- trigger: "session_start",
670
- reason: result.reason,
671
- phaseDurationsMs: {
672
- snapshotLoadMs,
673
- deserializeMs,
674
- ...result.timings,
675
- },
676
- });
677
- }
678
- else {
679
- runtime.wordIndex = index;
680
- let snapshotSaveSyncMs = 0;
681
- // A stale project seq must be advanced even when mtimes prove every
682
- // indexed document reusable. Fresh snapshots with no changes avoid
683
- // an unnecessary rewrite of the large shared snapshot.
684
- if (!isProjectSnapshotFresh(snapshot, effectiveSeq) ||
685
- result.refreshed > 0 ||
686
- result.dropped > 0 ||
687
- snapshot.wordIndex.truncated !== index.truncated) {
688
- const serializeSaveEnqueueStartMs = Date.now();
689
- saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
690
- snapshotSaveSyncMs = Date.now() - serializeSaveEnqueueStartMs;
691
- }
692
- const phases = `snapshot-load=${snapshotLoadMs}ms, deserialize=${deserializeMs}ms, ` +
693
- `source-walk=${result.timings.sourceWalkMs}ms, stat-walk=${result.timings.statWalkMs}ms, ` +
694
- `refresh-reads=${result.timings.refreshReadsMs}ms, ` +
695
- `snapshot-save-sync=${snapshotSaveSyncMs}ms`;
696
- dbg(`session_start word-index: incremental (seq=${effectiveSeq}, refreshed=${result.refreshed}, dropped=${result.dropped}, skipped=${result.skipped}, reused=${result.reused}, ${Date.now() - startMs}ms, phases: ${phases})`);
697
- // M2, #958: the incremental-vs-full decision + honest coverage
698
- // (indexedFileCount/truncated/skipped), independent of host `dbg`.
699
- logWordIndex({
700
- phase: "incremental_refresh",
701
- cwd: snapshotRoot,
702
- trigger: "session_start",
703
- durationMs: Date.now() - startMs,
704
- indexedFileCount: index.docCount,
705
- tokens: index.postings.size,
706
- postingEntries: countWordIndexPostingEntries(index),
707
- residentBytes: estimateWordIndexResidentBytes(index),
708
- truncated: index.truncated,
709
- phaseDurationsMs: {
710
- snapshotLoadMs,
711
- deserializeMs,
712
- ...result.timings,
713
- snapshotSaveSyncMs,
714
- },
715
- refreshed: result.refreshed,
716
- dropped: result.dropped,
717
- skipped: result.skipped,
718
- reused: result.reused,
719
- });
720
- return result;
721
- }
722
- }
723
- catch (err) {
724
- // Supersession is an expected transition, not a failure: a newer
725
- // session_start bumped the generation mid-refresh. Return undefined the
726
- // way master's synchronous path did instead of reporting a fallback and
727
- // re-walking for a rebuild whose result this generation may not publish.
728
- if (!runtime.isCurrentSession(sessionGeneration)) {
729
- dbg("session_start word-index: incremental refresh superseded");
730
- return;
731
- }
732
- dbg(`session_start word-index: incremental refresh failed; falling back to full rebuild (${err})`);
733
- logWordIndex({
734
- phase: "incremental_fallback",
735
- cwd: snapshotRoot,
736
- trigger: "session_start",
737
- reason: err instanceof Error ? err.message : String(err),
738
- phaseDurationsMs: { snapshotLoadMs, deserializeMs },
739
- });
740
- }
741
- }
742
- }
743
- // Shared file-walk-and-read helper (#348): the ONE collectWordIndexDocs
744
- // implementation backs this task, the quick-mode warmup call below, AND the
745
- // stateless cold-query background trigger in word-index.ts — a bound/skip
746
- // -rule change lands once, not in three copies.
747
- const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes, } = await import("./word-index.js");
748
- const docs = await collectWordIndexDocs(analysisRoot, () => runtime.isCurrentSession(sessionGeneration), rebuildPreflightFiles);
749
- if (!runtime.isCurrentSession(sessionGeneration))
750
- return;
751
- // #1197 review finding 2: `buildWordIndexAsync` THROWS on supersession, and
752
- // the quick-mode warmup caller (below) awaits this function OUTSIDE any
753
- // per-step try/catch — an escaping throw there skips the rest of warmup,
754
- // including the #947 LSP pre-warm, and resets `__piLensWarmupScheduled`.
755
- // Supersession is an expected transition (master's synchronous path just
756
- // returned), so absorb it here and let the caller continue.
757
- let rebuiltIndex;
758
- try {
759
- rebuiltIndex = await buildWordIndexAsync(docs, () => runtime.isCurrentSession(sessionGeneration));
760
- }
761
- catch (err) {
762
- if (!runtime.isCurrentSession(sessionGeneration)) {
763
- dbg("session_start word-index: rebuild superseded");
764
- return;
765
- }
766
- throw err;
767
- }
768
- if (!runtime.isCurrentSession(sessionGeneration))
769
- return;
770
- runtime.wordIndex = rebuiltIndex;
771
- saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
772
- dbg(`session_start word-index: rebuilt (absent/stale, seq=${effectiveSeq}) ` +
773
- `${runtime.wordIndex.docCount} files, ${runtime.wordIndex.postings.size} tokens (${Date.now() - startMs}ms)`);
774
- // M2, #958: full-rebuild decision + honest coverage. `docs.skipped` (L1)
775
- // counts files the walk saw but could not index (unreadable / over the byte
776
- // cap) so a partial index is never reported as complete (#533).
777
- logWordIndex({
778
- phase: "full_rebuild",
779
- cwd: snapshotRoot,
780
- trigger: "session_start",
781
- durationMs: Date.now() - startMs,
782
- indexedFileCount: runtime.wordIndex.docCount,
783
- tokens: runtime.wordIndex.postings.size,
784
- postingEntries: countWordIndexPostingEntries(runtime.wordIndex),
785
- residentBytes: estimateWordIndexResidentBytes(runtime.wordIndex),
786
- truncated: runtime.wordIndex.truncated,
787
- skipped: docs.skipped,
788
- });
789
- return {
790
- mode: "full",
791
- refreshed: runtime.wordIndex.docCount,
792
- dropped: 0,
793
- skipped: docs.skipped,
794
- reused: 0,
795
- };
796
- }
797
- // Fire off heavy scans as background tasks — don't block session start.
798
- // Each consumer already handles the "not ready yet" case gracefully
799
- // (cachedExports.size > 0, cache miss paths).
800
- function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg) {
801
- // #2467: the analyzers these scans drive are loaded HERE, on the deferred
802
- // background path, instead of before the handler ran. This function was
803
- // already fire-and-forget ("don't block session start"), so resolving them
804
- // across one await adds no wait to the interactive path and changes no
805
- // ordering its caller depends on — the caller never awaited it.
806
- void (async () => {
807
- const resolved = await demandBootstrapDeps(deps, "session-start-scans");
808
- // Fail open: no analyzers means no scans, said out loud. The demand is
809
- // already counted in the ledger, so this is not a silent skip.
810
- if (!resolved) {
811
- dbg("session_start scans: analyzer bootstrap unavailable — skipped");
812
- return;
813
- }
814
- scheduleStartupScansWithClients(resolved, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg);
815
- })();
816
- }
817
- /** The scan bodies, once the analyzers they need are resolved. */
818
- function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg) {
819
- const { todoScanner, cacheManager, knipClient, jscpdClient, deadCodeClients, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, astGrepClient, depChecker, } = deps;
820
- const analyzerEnabled = (flag) => !deps.getFlag(flag);
821
- if (!analyzerEnabled("no-complexity")) {
822
- dbg("session_start complexity: skipped (disabled by config)");
823
- recordDegradationOnce({
824
- kind: "startup-analyzer-disabled",
825
- subject: "complexity",
826
- reason: "skipped (disabled by config)",
827
- });
828
- }
829
- // Some background scans are CPU-heavy and arrive on the event loop
830
- // just as the user is most likely typing (right after /new). Defer
831
- // those by a few seconds so the perceptible 50-100ms sync bursts they
832
- // contain land during LLM streaming idle time instead. All other
833
- // tasks (those not listed here) run on the next `setImmediate` tick
834
- // as before. The delays are deliberately staggered (200ms apart) so
835
- // two heavy tasks don't both run on the same macrotask.
836
- const taskDeferMsByName = {
837
- "call-graph": 5000,
838
- "codebase-model": 5200,
839
- "ast-grep exports": 5400,
840
- "project index": 5400,
841
- };
842
- const runTask = (name, task) => {
843
- const queuedAt = Date.now();
844
- dbg(`session_start task ${name}: scheduled`);
845
- runtime.markStartupScanInFlight(name, sessionGeneration);
846
- const completion = new Promise((resolve) => {
847
- const fire = () => {
848
- const startedAt = Date.now();
849
- dbg(`session_start task ${name}: start queuedMs=${startedAt - queuedAt}`);
850
- void task()
851
- .then(() => {
852
- dbg(`session_start task ${name}: success runMs=${Date.now() - startedAt} queuedMs=${startedAt - queuedAt}`);
853
- })
854
- .catch((err) => {
855
- dbg(`session_start: ${name} background scan failed: ${err}`);
856
- dbg(`session_start task ${name}: failed runMs=${Date.now() - startedAt} queuedMs=${startedAt - queuedAt}`);
857
- })
858
- .finally(() => {
859
- runtime.clearStartupScanInFlight(name, sessionGeneration);
860
- dbg(`session_start task ${name}: end`);
861
- resolve();
862
- });
863
- };
864
- const delay = taskDeferMsByName[name] ?? 0;
865
- if (delay > 0) {
866
- // #1154: unref to match the repo-wide convention that every
867
- // background timer is `.unref()`'d. Full-mode deferred scans only
868
- // run in long-lived/interactive sessions today (never a one-shot),
869
- // so this is defensive — but a referenced 5s timer would keep a
870
- // process open were a full-mode one-shot ever introduced.
871
- setTimeout(fire, delay).unref?.();
872
- }
873
- else {
874
- setImmediate(fire);
875
- }
876
- });
877
- return completion;
878
- };
879
- const canRunJsTsHeavyScans = canRunStartupHeavyScans(languageProfile, "jsts");
880
- const scanNames = ["todo", "dead-code"];
881
- if (canRunJsTsHeavyScans) {
882
- scanNames.push("knip", "jscpd", "ast-grep exports", "project index");
883
- }
884
- dbg(`session_start: launching background scans (${scanNames.join(", ")})`);
885
- void runTask("todo", async () => {
886
- if (!runtime.isCurrentSession(sessionGeneration))
887
- return;
888
- // The original implementation called todoScanner.scanDirectory(), which
889
- // walks the project synchronously and freezes the TUI for ~3s on a 2k-file
890
- // project. collectTodoBaselineItems re-implements the walk in async chunks
891
- // (per-file scan, yielding every 30 files), falling back to scanDirectory.
892
- const items = await collectTodoBaselineItems(todoScanner, analysisRoot, () => runtime.isCurrentSession(sessionGeneration));
893
- if (!runtime.isCurrentSession(sessionGeneration))
894
- return;
895
- dbg(`session_start TODO scan: ${items.length} items (baseline stored)`);
896
- cacheManager.writeCache("todo-baseline", { items }, analysisRoot);
897
- });
898
- if (!canRunJsTsHeavyScans) {
899
- dbg("session_start: skipping JS/TS startup scans (requires JS/TS language + project config)");
900
- return;
901
- }
902
- // #462: knip/jscpd/madge/dead-code/govulncheck/gitleaks/trivy/opengrep each spawn an
903
- // external CLI that walks the whole project tree on its own — a walk we
904
- // don't control or get to route to an async collector. On a measured-slow
905
- // filesystem that walk reproduces the exact multi-second freeze this
906
- // feature exists to prevent, so skip exactly those seven with a visible
907
- // reason instead of leaving the agent to read an empty/stale cache as
908
- // "clean". The other scans in this function (todo above; call-graph/
909
- // codebase-model/ast-grep-exports/word-index below) walk via
910
- // `collectSourceFilesAsync` or build from cached review-graph data, so
911
- // they stay on.
912
- //
913
- // #449 slice 0: the same gate also fires inside a nicobailon/pi-subagents
914
- // child `pi` process (`PI_SUBAGENT_CHILD=1`) — a fan-out of N subagents in
915
- // the same cwd otherwise pays N full heavyweight-scan fleets for
916
- // short-lived task agents that rarely consult them. In-process scans stay
917
- // on for the same reason as slow-FS: the subagent may still use symbol
918
- // search / word-index.
919
- const isSubagent = isSubagentSession();
920
- const skipHeavyweightScans = isSlowFs(analysisRoot) || isSubagent;
921
- const runHeavyweightTask = (name, task) => {
922
- const flag = `no-${name}`;
923
- if (name !== "opengrep" && name !== "trivy" && !analyzerEnabled(flag)) {
924
- dbg(`session_start ${name}: skipped (disabled by config)`);
925
- recordDegradationOnce({
926
- kind: "startup-analyzer-disabled",
927
- subject: name,
928
- reason: "skipped (disabled by config)",
929
- });
930
- return;
931
- }
932
- if (skipHeavyweightScans)
933
- return;
934
- void runTask(name, task);
935
- };
936
- if (skipHeavyweightScans) {
937
- dbg(`session_start: skipping knip/jscpd/madge/dead-code/govulncheck/gitleaks/trivy/opengrep (${isSubagent ? "subagent" : "slow-fs"})`);
938
- deps.notify(`⏭️ Skipped background code-quality scans (knip/jscpd/madge/dead-code/govulncheck/gitleaks/trivy/opengrep): ${isSubagent ? subagentLightModeNotice() : slowFsDegradationNotice()}`, "info");
939
- }
940
- // Knip — dead code / unused exports
941
- runHeavyweightTask("knip", async () => {
942
- if (!runtime.isCurrentSession(sessionGeneration))
943
- return;
944
- const cached = cacheManager.readCache("knip", analysisRoot);
945
- if (cached) {
946
- if (!runtime.isCurrentSession(sessionGeneration))
947
- return;
948
- dbg(`session_start Knip: cache hit (${Math.round((Date.now() - new Date(cached.meta.timestamp).getTime()) / 1000)}s ago)`);
949
- }
950
- else {
951
- // KnipClient skips before probing/installing when analysisRoot is not a real
952
- // JS/Knip project. This avoids running knip from Unity/C#/generic repos.
953
- const startMs = Date.now();
954
- const knipResult = await knipClient.analyze(analysisRoot, getKnipIgnorePatterns(), { projectSeq: runtime.projectSeq });
955
- if (!runtime.isCurrentSession(sessionGeneration))
956
- return;
957
- if (knipResult.success) {
958
- cacheManager.writeCache("knip", knipResult, analysisRoot, {
959
- scanDurationMs: Date.now() - startMs,
960
- });
961
- }
962
- dbg(`session_start Knip scan done (${Date.now() - startMs}ms)`);
963
- }
964
- });
965
- // jscpd — duplicate code detection
966
- runHeavyweightTask("jscpd", async () => {
967
- if (await jscpdClient.ensureAvailable()) {
968
- if (!runtime.isCurrentSession(sessionGeneration))
969
- return;
970
- // Detect TS projects by tsconfig.json at the analysis root. When
971
- // set, JscpdClient.scan adds **/*.js and **/*.jsx to its ignore
972
- // pattern so compiled artifacts under dist/ aren't flagged as
973
- // duplicates of their TypeScript sources (closes #126's latent
974
- // dist/-as-duplicate bug). Cache scanner key varies by this flag
975
- // so a stale cache built with the wrong setting invalidates on
976
- // first read.
977
- const isTsProject = nodeFs.existsSync(path.join(analysisRoot, "tsconfig.json"));
978
- const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
979
- const cached = cacheManager.readCache(scannerKey, analysisRoot);
980
- if (cached) {
981
- if (!runtime.isCurrentSession(sessionGeneration))
982
- return;
983
- dbg(`session_start jscpd: cache hit (${scannerKey})`);
984
- }
985
- else {
986
- const startMs = Date.now();
987
- const jscpdResult = await jscpdClient.scan(analysisRoot, undefined, undefined, isTsProject);
988
- if (!runtime.isCurrentSession(sessionGeneration))
989
- return;
990
- if (jscpdResult.success) {
991
- cacheManager.writeCache(scannerKey, jscpdResult, analysisRoot, {
992
- scanDurationMs: Date.now() - startMs,
993
- });
994
- }
995
- dbg(`session_start jscpd scan done (${Date.now() - startMs}ms, isTsProject=${isTsProject})`);
996
- }
997
- }
998
- else {
999
- if (!runtime.isCurrentSession(sessionGeneration))
1000
- return;
1001
- dbg("session_start jscpd: not available");
1002
- }
1003
- });
1004
- // dead-code — cross-file dead-code for non-JS/TS languages (#127). Each
1005
- // client self-gates via detect() (a cheap fs marker probe), so only a
1006
- // matching-language project incurs the whole-tree scan cost. Knip remains
1007
- // the JS/TS path (above); these run alongside it for polyglot repos.
1008
- runHeavyweightTask("dead-code", async () => {
1009
- const applicable = deadCodeClients.filter((c) => c.detect(analysisRoot));
1010
- if (applicable.length === 0)
1011
- return;
1012
- await Promise.all(applicable.map(async (client) => {
1013
- if (!runtime.isCurrentSession(sessionGeneration))
1014
- return undefined;
1015
- const cacheKey = `dead-code-${client.id}`;
1016
- const cached = cacheManager.readCache(cacheKey, analysisRoot);
1017
- if (cached) {
1018
- dbg(`session_start dead-code(${client.id}): cache hit`);
1019
- return undefined;
1020
- }
1021
- const startMs = Date.now();
1022
- const result = await client.analyze(analysisRoot);
1023
- if (!runtime.isCurrentSession(sessionGeneration))
1024
- return undefined;
1025
- if (result.success) {
1026
- cacheManager.writeCache(cacheKey, result, analysisRoot, {
1027
- scanDurationMs: Date.now() - startMs,
1028
- });
1029
- }
1030
- logDeadCodeScan({
1031
- language: client.language,
1032
- success: result.success,
1033
- cached: false,
1034
- unusedExports: result.unusedExports.length,
1035
- unusedFiles: result.unusedFiles.length,
1036
- unusedDeps: result.unusedDeps.length,
1037
- unlistedDeps: result.unlistedDeps.length,
1038
- durationMs: result.durationMs ?? Date.now() - startMs,
1039
- ...(!result.success && { reason: result.summary }),
1040
- });
1041
- dbg(`session_start dead-code(${client.id}) done (${Date.now() - startMs}ms, ${deadCodeIssueCount(result)} issues)`);
1042
- }));
1043
- });
1044
- // govulncheck — Go module CVE detection (#132)
1045
- // Skipped silently when the project isn't a Go module or when
1046
- // `govulncheck` isn't installed (no auto-install in this slice).
1047
- runHeavyweightTask("govulncheck", async () => {
1048
- if (!GovulncheckClient.hasGoModule(analysisRoot)) {
1049
- dbg("session_start govulncheck: no go.mod — skipped");
1050
- return;
1051
- }
1052
- if (!(await govulncheckClient.ensureAvailable())) {
1053
- if (!runtime.isCurrentSession(sessionGeneration))
1054
- return;
1055
- dbg("session_start govulncheck: not installed (go install golang.org/x/vuln/cmd/govulncheck@latest)");
1056
- return;
1057
- }
1058
- if (!runtime.isCurrentSession(sessionGeneration))
1059
- return;
1060
- const cached = cacheManager.readCache("govulncheck", analysisRoot);
1061
- if (cached) {
1062
- if (!runtime.isCurrentSession(sessionGeneration))
1063
- return;
1064
- dbg(`session_start govulncheck: cache hit (${cached.data.findings.length} findings)`);
1065
- return;
1066
- }
1067
- const startMs = Date.now();
1068
- const result = await govulncheckClient.analyze(analysisRoot);
1069
- if (!runtime.isCurrentSession(sessionGeneration))
1070
- return;
1071
- if (result.success) {
1072
- cacheManager.writeCache("govulncheck", result, analysisRoot, {
1073
- scanDurationMs: Date.now() - startMs,
1074
- });
1075
- }
1076
- dbg(`session_start govulncheck: ${result.findings.length} reachable findings (${Date.now() - startMs}ms)`);
1077
- });
1078
- // gitleaks — committed-secrets detection (#130)
1079
- // Config-gated: opts in via .gitleaks.toml / .gitleaksignore / git
1080
- // hook / gitleaks dep. Cross-language by design.
1081
- runHeavyweightTask("gitleaks", async () => {
1082
- if (!GitleaksClient.hasGitleaksSignal(analysisRoot)) {
1083
- dbg("session_start gitleaks: no opt-in signal — skipped");
1084
- return;
1085
- }
1086
- if (!(await gitleaksClient.ensureAvailable())) {
1087
- if (!runtime.isCurrentSession(sessionGeneration))
1088
- return;
1089
- dbg("session_start gitleaks: not available (install failed?)");
1090
- return;
1091
- }
1092
- if (!runtime.isCurrentSession(sessionGeneration))
1093
- return;
1094
- const cached = cacheManager.readCache("gitleaks", analysisRoot);
1095
- if (cached) {
1096
- if (!runtime.isCurrentSession(sessionGeneration))
1097
- return;
1098
- dbg(`session_start gitleaks: cache hit (${cached.data.findings.length} findings)`);
1099
- return;
1100
- }
1101
- const startMs = Date.now();
1102
- const result = await gitleaksClient.scan(analysisRoot);
1103
- if (!runtime.isCurrentSession(sessionGeneration))
1104
- return;
1105
- if (result.success) {
1106
- cacheManager.writeCache("gitleaks", result, analysisRoot, {
1107
- scanDurationMs: Date.now() - startMs,
1108
- });
1109
- }
1110
- dbg(`session_start gitleaks: ${result.findings.length} findings (${Date.now() - startMs}ms)`);
1111
- });
1112
- // opengrep — full-workspace security/quality findings via a single CLI scan
1113
- // (#584). Structurally always-on (mirrors the LSP auxiliary's own
1114
- // enablement — see `OpengrepClient.resolveConfig`): the local rule file or
1115
- // `auto` registry ruleset always runs, `resolveOpengrepConfig` only picks
1116
- // which. Replaces the per-file LSP sweep as the source of opengrep
1117
- // findings for `lens_diagnostics mode=full` (`runWorkspaceDiagnostics` now
1118
- // excludes the opengrep server from its per-file "all"-scope touch —
1119
- // clients/lsp/index.ts `WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS`); the per-edit
1120
- // real-time LSP path is untouched.
1121
- runHeavyweightTask("opengrep", async () => {
1122
- if (!(await opengrepClient.ensureAvailable())) {
1123
- if (!runtime.isCurrentSession(sessionGeneration))
1124
- return;
1125
- dbg("session_start opengrep: not available (install failed?)");
1126
- return;
1127
- }
1128
- if (!runtime.isCurrentSession(sessionGeneration))
1129
- return;
1130
- const cached = cacheManager.readCache("opengrep", analysisRoot);
1131
- if (cached) {
1132
- if (!runtime.isCurrentSession(sessionGeneration))
1133
- return;
1134
- dbg(`session_start opengrep: cache hit (${cached.data.findings.length} findings)`);
1135
- return;
1136
- }
1137
- const startMs = Date.now();
1138
- const result = await opengrepClient.scan(analysisRoot);
1139
- if (!runtime.isCurrentSession(sessionGeneration))
1140
- return;
1141
- if (result.success) {
1142
- cacheManager.writeCache("opengrep", result, analysisRoot, {
1143
- scanDurationMs: Date.now() - startMs,
1144
- });
1145
- }
1146
- dbg(`session_start opengrep: ${result.findings.length} findings (${Date.now() - startMs}ms)`);
1147
- });
1148
- // madge — whole-project circular-dependency detection. Session-start + cached
1149
- // (uniform with knip/jscpd/gitleaks) so lens_diagnostics mode=full reads it
1150
- // from the `madge` cache via the extractor registry — never a fresh scan.
1151
- runHeavyweightTask("madge", async () => {
1152
- if (!(await depChecker.ensureAvailable())) {
1153
- if (!runtime.isCurrentSession(sessionGeneration))
1154
- return;
1155
- dbg("session_start madge: not available");
1156
- return;
1157
- }
1158
- if (!runtime.isCurrentSession(sessionGeneration))
1159
- return;
1160
- const cached = cacheManager.readCache("madge", analysisRoot);
1161
- if (cached) {
1162
- dbg(`session_start madge: cache hit (${cached.data.circular.length} cycles)`);
1163
- return;
1164
- }
1165
- const startMs = Date.now();
1166
- const result = await depChecker.scanProject(analysisRoot);
1167
- if (!runtime.isCurrentSession(sessionGeneration))
1168
- return;
1169
- cacheManager.writeCache("madge", result, analysisRoot, {
1170
- scanDurationMs: Date.now() - startMs,
1171
- });
1172
- dbg(`session_start madge: ${result.circular.length} circular dependency chain(s) (${Date.now() - startMs}ms)`);
1173
- });
1174
- // trivy — dependency CVE detection (#131, Phase 1)
1175
- // Explicit opt-in: `trivy.enabled: true` in .pi-lens.json AND a dependency
1176
- // manifest present. The first run downloads Trivy's vuln DB (~30-200 MB);
1177
- // harmless here since this whole task runs in the background session_start
1178
- // wrapper.
1179
- runHeavyweightTask("trivy", async () => {
1180
- if (!TrivyClient.shouldScan(analysisRoot)) {
1181
- dbg("session_start trivy: not enabled / no dependency manifest — skipped");
1182
- return;
1183
- }
1184
- if (!(await trivyClient.ensureAvailable())) {
1185
- if (!runtime.isCurrentSession(sessionGeneration))
1186
- return;
1187
- dbg("session_start trivy: not available (install failed?)");
1188
- return;
1189
- }
1190
- if (!runtime.isCurrentSession(sessionGeneration))
1191
- return;
1192
- const cached = cacheManager.readCache("trivy", analysisRoot);
1193
- if (cached) {
1194
- if (!runtime.isCurrentSession(sessionGeneration))
1195
- return;
1196
- dbg(`session_start trivy: cache hit (${cached.data.findings.length} findings)`);
1197
- return;
1198
- }
1199
- const startMs = Date.now();
1200
- const result = await trivyClient.scan(analysisRoot);
1201
- if (!runtime.isCurrentSession(sessionGeneration))
1202
- return;
1203
- if (result.success) {
1204
- cacheManager.writeCache("trivy", result, analysisRoot, {
1205
- scanDurationMs: Date.now() - startMs,
1206
- });
1207
- }
1208
- dbg(`session_start trivy: ${result.findings.length} CVE findings (${Date.now() - startMs}ms)`);
1209
- });
1210
- // call-graph — build function-level call graph from review graph data
1211
- let callGraphIdentity;
1212
- const callGraphTask = runTask("call-graph", async () => {
1213
- const { FactStore } = await import("./dispatch/fact-store.js");
1214
- const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity, } = await import("./review-graph/builder.js");
1215
- const { buildCallGraph, saveCallGraph, loadCallGraph } = await import("./call-graph.js");
1216
- if (!runtime.isCurrentSession(sessionGeneration))
1217
- return;
1218
- const startMs = Date.now();
1219
- // Build (or hydrate) the canonical review graph first. The call graph is a
1220
- // derived projection of that graph, so its freshness is the review graph's
1221
- // version/signature—not a second source walk and mtime policy.
1222
- // Subject labels this store's capacity-eviction telemetry distinctly
1223
- // from the other five production FactStore instances (#2243 review
1224
- // round 3, F1) — this session-start walk runs BEFORE any dispatch and
1225
- // can visit every file in the project, so a shared subject would let
1226
- // it consume the dispatch store's once-per-session ledger slot first.
1227
- const sessionFacts = new FactStore("runtime-session-call-graph");
1228
- const graph = await buildOrUpdateGraph(analysisRoot, [], sessionFacts);
1229
- if (!runtime.isCurrentSession(sessionGeneration))
1230
- return;
1231
- const identity = getReviewGraphCacheIdentity(analysisRoot, graph);
1232
- if (!identity) {
1233
- dbg("session_start call-graph: canonical review-graph identity unavailable");
1234
- return;
1235
- }
1236
- callGraphIdentity = {
1237
- reviewGraphVersion: identity.version,
1238
- reviewGraphSignature: identity.signature,
1239
- };
1240
- const cached = loadCallGraph(snapshotRoot, {
1241
- reviewGraphVersion: identity.version,
1242
- reviewGraphSignature: identity.signature,
1243
- });
1244
- if (cached) {
1245
- runtime.callGraph = cached.graph;
1246
- dbg(`session_start call-graph: loaded from cache (${cached.graph.edges.length} edges, ${cached.graph.callers.size} callee entries, ${Date.now() - startMs}ms)`);
1247
- return;
1248
- }
1249
- // Build from the canonical review graph (reuses already-parsed data, no
1250
- // duplicate parser or source walk).
1251
- const { allSymbols, allRefs, coverage } = extractSymbolsAndRefsFromGraph(graph);
1252
- const callGraph = buildCallGraph(allSymbols, allRefs, coverage);
1253
- runtime.callGraph = callGraph;
1254
- saveCallGraph(snapshotRoot, callGraph, {
1255
- reviewGraphVersion: identity.version,
1256
- reviewGraphSignature: identity.signature,
1257
- });
1258
- dbg(`session_start call-graph: built ${callGraph.edges.length} edges, ${callGraph.callers.size} callee entries (${Date.now() - startMs}ms)`);
1259
- });
1260
- // codebase-model — build mental model from call graph (internal-only until validated)
1261
- // Keep this deferred, but await the call-graph task's completion rather than
1262
- // racing its independently deferred timer. Otherwise a slow graph build leaves
1263
- // `runtime.callGraph` unset at this task's start and loses the model for the
1264
- // entire session (#1070).
1265
- void runTask("codebase-model", async () => {
1266
- await callGraphTask;
1267
- if (!runtime.isCurrentSession(sessionGeneration))
1268
- return;
1269
- if (!runtime.callGraph)
1270
- return;
1271
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, } = await import("./codebase-model.js");
1272
- if (!callGraphIdentity)
1273
- return;
1274
- const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
1275
- saveCodebaseModel(snapshotRoot, model);
1276
- const top3 = model.entries
1277
- .slice(0, 3)
1278
- .map((e) => e.name)
1279
- .join(", ");
1280
- dbg(`session_start codebase-model: ${model.entries.length} entries, ` +
1281
- `${model.totalTokens} tokens, top symbols: ${top3 || "(none)"}`);
1282
- });
1283
- // ast-grep — export scan for duplicate detection
1284
- void runTask("ast-grep-exports", async () => {
1285
- if (await astGrepClient.ensureAvailable()) {
1286
- if (!runtime.isCurrentSession(sessionGeneration))
1287
- return;
1288
- const exports = await astGrepClient.scanExports(analysisRoot, "typescript");
1289
- if (!runtime.isCurrentSession(sessionGeneration))
1290
- return;
1291
- dbg(`session_start exports scan: ${exports.size} functions found`);
1292
- for (const [name, file] of exports) {
1293
- runtime.cachedExports.set(name, file);
1294
- }
1295
- saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
1296
- }
1297
- });
1298
- // word-index — identifier inverted index + BM25 for ranked symbol search
1299
- // (#162). Load -> rebuild-if-stale -> persist lifecycle (#348), shared with
1300
- // the quick-mode cold-start warmup pass below.
1301
- void runTask("word-index", async () => {
1302
- await buildOrRefreshWordIndex({
1303
- runtime,
1304
- sessionGeneration,
1305
- analysisRoot,
1306
- snapshotRoot,
1307
- dbg,
1308
- });
1309
- });
1310
- }
1311
- function scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
1312
- // #2467: same deferral as `scheduleStartupScans` — the probes were already
1313
- // fire-and-forget, so the analyzers they probe load here rather than before
1314
- // the handler.
1315
- void (async () => {
1316
- const resolved = await demandBootstrapDeps(deps, "session-start-tool-probes");
1317
- if (!resolved) {
1318
- dbg("session_start tools: analyzer bootstrap unavailable — no probes");
1319
- return;
1320
- }
1321
- scheduleDeferredToolProbesWithClients(resolved, languageProfile, startupDefaults, startupScansWillRun, dbg);
1322
- })();
1323
- }
1324
- /** The probe bodies, once the analyzers they probe are resolved. */
1325
- function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
1326
- const { biomeClient, ruffClient, depChecker } = deps;
1327
- const defaultTools = new Set(startupDefaults);
1328
- const probes = [];
1329
- // Do not probe tools already covered by startup preinstall or startup scans.
1330
- // This keeps session_start logs from showing duplicate "ensure X: start" lines
1331
- // while preserving lazy checks for tools that are actually relevant.
1332
- if (languageProfile.present.jsts && !defaultTools.has("biome")) {
1333
- probes.push(["biome", () => biomeClient.ensureAvailable()]);
1334
- }
1335
- if (languageProfile.present.python && !defaultTools.has("ruff")) {
1336
- probes.push(["ruff", () => ruffClient.ensureAvailable()]);
1337
- }
1338
- if (startupScansWillRun) {
1339
- probes.push(["madge", () => depChecker.ensureAvailable()]);
1340
- }
1341
- if (probes.length === 0) {
1342
- dbg("session_start tools: no deferred availability probes needed");
1343
- return;
1344
- }
1345
- void (async () => {
1346
- const warmStart = Date.now();
1347
- const results = await Promise.all(probes.map(async ([name, run]) => {
1348
- try {
1349
- return [name, await run()];
1350
- }
1351
- catch (err) {
1352
- dbg(`session_start: ${name} availability check failed: ${err}`);
1353
- return [name, false];
1354
- }
1355
- }));
1356
- const summary = results
1357
- .map(([name, ready]) => `${name}=${ready}`)
1358
- .join(" ");
1359
- dbg(`session_start tools (deferred probes complete, ${Date.now() - warmStart}ms): ${summary}`);
1360
- })();
1361
- }
1362
- /**
1363
- * Session-start orientation prepended as a context message (gated by the
1364
- * context-injection toggle). Deliberately lean: it names the high-value tools
1365
- * and the distinction between cached reporting and active verification
1366
- * — per-tool argument detail lives in each tool's own registered description, so
1367
- * re-documenting it here would just pay the tokens twice every session.
1368
- */
1369
- export const SESSION_START_GUIDANCE = [
1370
- "📌 pi-lens active — automated checks run on every edit/write; blocking errors (including pre-existing) show inline and must be fixed.\n" +
1371
- "Key tools (see each tool's own description for args):\n" +
1372
- "• lens_diagnostics — source=session reads cache; empty cache ≠ clean; use source=lsp scope=paths for changed files with absent or stale findings (aggregate hosts: lens(action=diagnostics)).\n" +
1373
- "• symbol_search → module_report → read_symbol/read_enclosing — ranked identifier search, then navigable outline/callback handles + exact body reads; cheaper than reading a whole file before editing.\n" +
1374
- "• Situational (activate via pi_lens_activate_tools): lsp_navigation, ast_grep_search, ast_grep_replace. Use ast_grep_search with dump=true to inspect AST nodes.",
1375
- ];
1376
- export async function handleSessionStart(deps) {
1377
- resetDegradationLedger();
1378
- // #2467: re-arm the analyzer bootstrap's shutdown gate. The gate is a
1379
- // per-SESSION claim ("this session is over") held in process-lived storage,
1380
- // so without this a replacement session in the same process would find
1381
- // every analyzer refused for the rest of the process — AGENTS.md defect
1382
- // shape 17. The resident clients themselves are deliberately kept.
1383
- resetAnalyzerBootstrapSessionState();
1384
- resetProjectDataDirSessionState();
1385
- resetTestRunnerDelivery();
1386
- // #2450 fix round 3, catalog shape 17: the "bridge unavailable" dbg latch
1387
- // (`clients/lsp-mutation.ts`) is a process-lifetime once-per-session flag,
1388
- // same shape as the ledger's own once-per-subject bookkeeping above, but
1389
- // kept as its own reset (not folded into `resetDegradationLedger`) to
1390
- // avoid that module reaching back into this one — see the latch's own
1391
- // comment for why.
1392
- resetLspMutationNoBridgeDbgLatch();
1393
- // #1743: the bounded-telemetry per-turn counters. The rising-edge state is
1394
- // NOT here — it is the ledger's own tally, reset on the line above. These
1395
- // counters are keyed by turn index, and a new session restarts turn
1396
- // numbering at 0, so without this a stale count could survive a session
1397
- // boundary that happened to land on the same index.
1398
- resetBoundedTelemetry();
1399
- // #1956 R3: stale message_end events drain after replacement. Rotate the
1400
- // live anchor into one bounded previous slot so the queued event keeps the
1401
- // replaced session's attribution until a newer live message_end is seen.
1402
- rotateMessageEndAttribution();
1403
- // #2526: the once-per-session `config_resolved` claim is NOT re-armed here.
1404
- // index.ts resolves this session's config in `ensureLSPConfigInitialized`
1405
- // BEFORE it calls this handler, so a re-arm on this line would fire between
1406
- // the session's own two resolutions and the deferred `loadLSPConfig` below
1407
- // would write a second row for one session (review round 2, F1). The re-arm
1408
- // lives in index.ts's `session_start` closure, ahead of that ensure and
1409
- // behind the #473 gate — see `resetOncePerSessionPhases`'s doc comment.
1410
- const handlerEnteredAt = Date.now();
1411
- const sessionStartMs = deps.sessionStartFiredAt ?? handlerEnteredAt;
1412
- const cwdForTelemetry = deps.ctxCwd ?? process.cwd();
1413
- if (deps.sessionStartFiredAt !== undefined) {
1414
- logLatency({
1415
- type: "phase",
1416
- filePath: cwdForTelemetry,
1417
- phase: "session_start_prehandler",
1418
- startedAt: new Date(deps.sessionStartFiredAt).toISOString(),
1419
- durationMs: (deps.handlerEnteredAt ?? handlerEnteredAt) - deps.sessionStartFiredAt,
1420
- metadata: { reason: deps.sessionReason },
1421
- });
1422
- }
1423
- // Cold-start input-latency mitigation. The first `session_start` of
1424
- // the process — i.e. the one that fires immediately after the user
1425
- // launches `pi` — must return as fast as possible so the TUI input
1426
- // box becomes responsive. The full startup mode runs several
1427
- // expensive synchronous walks (resolveStartupScanContext,
1428
- // detectProjectLanguageProfile, scanProjectRules, scheduleStartupScans)
1429
- // that together can block the event loop for 3-6s on a 2k-file
1430
- // project, during which keystrokes are dropped or batched.
1431
- //
1432
- // Strategy:
1433
- // - Force the very first invocation to "quick" mode, which exits
1434
- // after a minimal runtime reset and snapshot hydration.
1435
- // - 2 seconds later, schedule a background warmup that walks the
1436
- // project asynchronously (yielding every 100 entries) and
1437
- // populates the in-process memo caches
1438
- // (startupScanContextCache + languageProfileCache).
1439
- // - The user's first /new (or any subsequent session_start) sees
1440
- // a cache hit on both walks and finishes the full path in <50ms.
1441
- //
1442
- // Opt-out: PI_LENS_COLD_START_QUICK=0 disables this behaviour.
1443
- // Override: PI_LENS_STARTUP_MODE explicitly set wins (we honour it).
1444
- // deps.startupModeOverride lets a host (e.g. the MCP server, which has
1445
- // no TUI keystroke latency to protect) skip the quick-mode heuristic
1446
- // entirely — but only when PI_LENS_STARTUP_MODE is unset in the env
1447
- // (an explicit env var still takes highest precedence).
1448
- // Tunable: PI_LENS_WARMUP_DELAY_MS adjusts the warmup delay.
1449
- let startupMode = resolveStartupMode(deps.projectConfig, deps.globalConfig);
1450
- // SAFETY: these two flags are process-lifetime state pi-lens stashes on
1451
- // `globalThis` so a second extension instance in the same process sees the
1452
- // first one's warmup. There is no ambient declaration for them, and adding
1453
- // one would let any module write them. Both are declared OPTIONAL here, so
1454
- // the reads below are `undefined`-safe on a process where nothing set them.
1455
- const processGlobals = globalThis;
1456
- const isFirstSessionOfProcess = !processGlobals.__piLensFirstSessionDone;
1457
- if (isFirstSessionOfProcess &&
1458
- process.env.PI_LENS_COLD_START_QUICK !== "0" &&
1459
- !process.env.PI_LENS_STARTUP_MODE &&
1460
- !deps.projectConfig?.startup?.mode &&
1461
- !deps.globalConfig?.startup?.mode) {
1462
- // Apply host-provided override (e.g. MCP server forces "full") before
1463
- // falling back to the TUI quick-mode heuristic.
1464
- startupMode = deps.startupModeOverride ?? "quick";
1465
- }
1466
- processGlobals.__piLensFirstSessionDone = true;
1467
- // #1785 F5 (round 4, design (a)): the warmup timer body below ALREADY
1468
- // loads the on-disk snapshot for its own purposes (`cachedSnapshot`,
1469
- // reused for the `startupScan` verdict cache) strictly BEFORE any save it
1470
- // might go on to make. This LOCAL variable — per-`handleSessionStart`-call,
1471
- // deliberately NOT module-scope (see #1785 F6) — lets the warmup publish
1472
- // that already-loaded read (narrowed to exports+rules, never the full
1473
- // object with `wordIndex`) for `retroactivelyHydrateAfterDeferredSequence`
1474
- // to reuse at zero additional cost, instead of this call paying for its
1475
- // own separate synchronous capture (rounds 1-3's approach, reverted here:
1476
- // see that function's doc comment for the full history). `undefined`
1477
- // means the warmup hasn't reached its own read yet (or was never armed by
1478
- // this call at all) — the quick-mode block's retroactive-hydration
1479
- // callback then falls back to a live re-load instead.
1480
- let warmupOwnSnapshotRead;
1481
- // #1154: quick mode is entered on BOTH `pi -p`/`--print` (a one-shot that
1482
- // exits right after this turn) and an interactive process's first
1483
- // session_start (forced quick to protect keystroke latency, then warms
1484
- // caches for the NEXT /new). The warmup only benefits a *future* session in
1485
- // the same process — a one-shot print invocation has none. Worse, in a
1486
- // one-shot the warmup is a referenced-handle keep-alive: the +2s timer and
1487
- // the LSP-prewarm children / language-profile source walk it launches
1488
- // outlive settle with no session_shutdown teardown, holding the process
1489
- // open (the #1097/#1110/#1122-hyp-A class). So skip warmup entirely in
1490
- // print mode; interactive first-sessions (not print) still warm.
1491
- if (startupMode === "quick" &&
1492
- !isPrintMode() &&
1493
- process.env.PI_LENS_COLD_START_QUICK !== "0" &&
1494
- !processGlobals.__piLensWarmupScheduled) {
1495
- processGlobals.__piLensWarmupScheduled = true;
1496
- const warmupDelayMs = Number(process.env.PI_LENS_WARMUP_DELAY_MS ?? 2000);
1497
- const warmupCwd = deps.ctxCwd ?? process.cwd();
1498
- const warmupDbg = deps.dbg;
1499
- // #1154: `.unref()` the warmup timer so — even for a warmup that IS
1500
- // scheduled (an interactive first-session) — the pending timer alone can
1501
- // never hold the loop open. Interactive processes stay alive for other
1502
- // reasons, so the warmup still fires; a one-shot never reaches here (the
1503
- // isPrintMode gate above), and repo convention is that every background
1504
- // timer is unref'd (this file previously had none).
1505
- const warmupTimer = setTimeout(() => {
1506
- const warmupStartedAt = Date.now();
1507
- void (async () => {
1508
- try {
1509
- warmupDbg("warmup: starting background warmup");
1510
- const scanContextStartedAt = Date.now();
1511
- // Dynamic imports keep the warmup pipeline off the hot
1512
- // startup path — these modules don't load until the timer
1513
- // fires, well after the TUI is interactive.
1514
- const startupScanModule = await import("./startup-scan.js");
1515
- const languageProfileModule = await import("./language-profile.js");
1516
- const warmupSnapshotRoot = resolveSnapshotRoot(warmupCwd);
1517
- // #699: this background timer is the ONLY place a quick-mode
1518
- // session (i.e. every `-p`/`--print` invocation, which forces
1519
- // quick mode and returns before line ~1253 without ever touching
1520
- // scan-context) computes scan-context — and that process exits
1521
- // right after, discarding the result. Before walking, check for a
1522
- // still-fresh persisted verdict (mirrors the interactive path's
1523
- // snapshot reuse); after walking, persist unconditionally
1524
- // (canWarmCaches true OR false) so the NEXT one-shot process can
1525
- // reuse it instead of re-walking a possibly huge tree from
1526
- // scratch on every single startup.
1527
- const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
1528
- // #1785 F5 (round 4): publish this ALREADY-loaded read — narrowed
1529
- // to exports+rules, never a reference to `cachedSnapshot` itself
1530
- // (which carries `wordIndex`/`files`/`symbols`/`reverseDeps`) — for
1531
- // `retroactivelyHydrateAfterDeferredSequence` to reuse. This read
1532
- // strictly precedes the warmup's own possible save a few lines
1533
- // below, so it is guaranteed to reflect disk as it stood before
1534
- // THIS warmup could have touched it. Zero marginal cost: this read
1535
- // already happens unconditionally for the `startupScan` verdict
1536
- // cache below, with or without this fix.
1537
- warmupOwnSnapshotRead = cachedSnapshot
1538
- ? {
1539
- version: cachedSnapshot.version,
1540
- seq: cachedSnapshot.seq,
1541
- cachedExports: cachedSnapshot.cachedExports,
1542
- projectRulesScan: cachedSnapshot.projectRulesScan,
1543
- }
1544
- : null;
1545
- const cachedVerdict = cachedSnapshot?.startupScan;
1546
- let scan;
1547
- if (cachedVerdict &&
1548
- startupScanModule.isStartupScanVerdictFresh(cachedVerdict)) {
1549
- scan = { ...cachedVerdict, cwd: path.resolve(warmupCwd) };
1550
- warmupDbg(`warmup: scan-context reused from cache (canWarm=${scan.canWarmCaches}${scan.reason ? `, reason=${scan.reason}` : ""})`);
1551
- }
1552
- else {
1553
- scan =
1554
- await startupScanModule.resolveStartupScanContextAsync(warmupCwd);
1555
- logLatency({
1556
- type: "phase",
1557
- phase: "session_start_scan_context_compute",
1558
- filePath: warmupCwd,
1559
- startedAt: new Date(scanContextStartedAt).toISOString(),
1560
- durationMs: Date.now() - scanContextStartedAt,
1561
- metadata: { mode: "quick-background" },
1562
- });
1563
- warmupDbg(`warmup: scan-context done in ${Date.now() - warmupStartedAt}ms (canWarm=${scan.canWarmCaches})`);
1564
- // Best-effort: a save failure must never affect warmup itself —
1565
- // saveRuntimeProjectSnapshot already swallows its own errors.
1566
- saveRuntimeProjectSnapshot({
1567
- cwd: warmupSnapshotRoot,
1568
- runtime: deps.runtime,
1569
- startupScan: scan,
1570
- dbg: warmupDbg,
1571
- });
1572
- }
1573
- logLatency({
1574
- type: "phase",
1575
- phase: "warmup_scan_context",
1576
- filePath: warmupCwd,
1577
- startedAt: new Date(scanContextStartedAt).toISOString(),
1578
- durationMs: Date.now() - scanContextStartedAt,
1579
- });
1580
- // Respect the startup-scan guard (#250): canWarmCaches is false for
1581
- // home-dir / no-project-root / too-many-source-files. Proceeding into
1582
- // the language-profile source walk in those cases lets it root at an
1583
- // ancestor (e.g. a marker in $HOME when pi runs in ~/tmp) and traverse
1584
- // the entire home tree — multi-hour scans. Nothing to warm anyway when
1585
- // the guard says caches can't be warmed.
1586
- if (!scan.canWarmCaches) {
1587
- warmupDbg(`warmup: skipping language-profile (canWarm=false, reason=${scan.reason ?? "unknown"})`);
1588
- return;
1589
- }
1590
- const languageRoot = scan.projectRoot ?? warmupCwd;
1591
- const languageProfileStartedAt = Date.now();
1592
- await languageProfileModule.detectProjectLanguageProfileAsync(languageRoot);
1593
- warmupDbg(`warmup: language-profile done in ${Date.now() - languageProfileStartedAt}ms`);
1594
- logLatency({
1595
- type: "phase",
1596
- phase: "warmup_language_profile",
1597
- filePath: warmupCwd,
1598
- startedAt: new Date(languageProfileStartedAt).toISOString(),
1599
- durationMs: Date.now() - languageProfileStartedAt,
1600
- });
1601
- // #348: fold the word-index build/refresh into this existing
1602
- // cold-start warmup pass so quick-mode (and any session whose very
1603
- // first session_start is forced quick) still ends up with a
1604
- // queryable index — full-mode sessions get it via the "word-index"
1605
- // runTask above; this is the quick-mode equivalent, once per
1606
- // process, off the hot path.
1607
- const wordIndexStartedAt = Date.now();
1608
- const wordIndexResult = await buildOrRefreshWordIndex({
1609
- runtime: deps.runtime,
1610
- sessionGeneration: deps.runtime.sessionGeneration,
1611
- analysisRoot: languageRoot,
1612
- snapshotRoot: warmupSnapshotRoot,
1613
- dbg: warmupDbg,
1614
- });
1615
- warmupDbg(`warmup: word-index done in ${Date.now() - wordIndexStartedAt}ms`);
1616
- logLatency({
1617
- type: "phase",
1618
- phase: "warmup_word_index",
1619
- filePath: warmupCwd,
1620
- startedAt: new Date(wordIndexStartedAt).toISOString(),
1621
- durationMs: Date.now() - wordIndexStartedAt,
1622
- metadata: wordIndexResult ? { ...wordIndexResult } : undefined,
1623
- });
1624
- // #947: fold the dominant-language LSP pre-warm into this
1625
- // warmup pass. The first-session-of-process heuristic forces
1626
- // quick mode, and the pre-warm below is gated on
1627
- // allowBootstrapTasks (full mode only) — so it NEVER ran in
1628
- // practice and every session's first edit paid a cold LSP
1629
- // spawn (~750ms wait + ~2.5s spawn, measured). Fire it here
1630
- // instead: off the interactive path, once per process (the
1631
- // __piLensWarmupScheduled guard above), generation-guarded
1632
- // inside igniteDominantLanguageWarm, and honoring the same
1633
- // skips as the full-mode path — subagent light mode (#449),
1634
- // warm-attach (#822), the no-lsp flag, and the
1635
- // canWarmCaches guard (the early return above). Concurrent
1636
- // secondaries never reach handleSessionStart at all (the
1637
- // #473 guard in index.ts), so they never schedule this
1638
- // warmup in the first place.
1639
- const lspPrewarmStartedAt = Date.now();
1640
- if (deps.getFlag("no-lsp")) {
1641
- warmupDbg("warmup: skipping LSP pre-warm (no-lsp)");
1642
- }
1643
- else if (isSubagentSession()) {
1644
- warmupDbg("warmup: skipping LSP pre-warm (subagent session)");
1645
- }
1646
- else if (isWarmAttached()) {
1647
- warmupDbg("warmup: skipping LSP pre-warm (attached to incumbent)");
1648
- }
1649
- else {
1650
- // #957 review: honor explicit warmFiles (#203) like the full
1651
- // path does — configured projects warm exactly what they
1652
- // asked for; dominant-language warm is the fallback.
1653
- const lspConfig = await loadLSPConfig(warmupCwd).catch(() => ({
1654
- warmFiles: [],
1655
- }));
1656
- const warmFiles = lspConfig.warmFiles ?? [];
1657
- if (warmFiles.length > 0) {
1658
- await igniteWarmFiles(warmupCwd, warmFiles, deps.runtime, deps.runtime.sessionGeneration, warmupDbg);
1659
- }
1660
- else {
1661
- await igniteDominantLanguageWarm(languageRoot, deps.runtime, deps.runtime.sessionGeneration, warmupDbg);
1662
- }
1663
- logLatency({
1664
- type: "phase",
1665
- phase: "warmup_lsp_prewarm",
1666
- filePath: warmupCwd,
1667
- startedAt: new Date(lspPrewarmStartedAt).toISOString(),
1668
- durationMs: Date.now() - lspPrewarmStartedAt,
1669
- });
1670
- warmupDbg(`warmup: lsp-prewarm done in ${Date.now() - lspPrewarmStartedAt}ms`);
1671
- }
1672
- warmupDbg(`warmup: total ${Date.now() - warmupStartedAt}ms`);
1673
- }
1674
- catch (err) {
1675
- warmupDbg(`warmup: error ${err}`);
1676
- // Allow a future session to retry the warmup.
1677
- processGlobals.__piLensWarmupScheduled = false;
1678
- }
1679
- finally {
1680
- logLatency({
1681
- type: "phase",
1682
- phase: "warmup_total",
1683
- filePath: warmupCwd,
1684
- startedAt: new Date(warmupStartedAt).toISOString(),
1685
- durationMs: Date.now() - warmupStartedAt,
1686
- });
1687
- }
1688
- })();
1689
- }, warmupDelayMs);
1690
- warmupTimer.unref?.();
1691
- }
1692
- const allowBootstrapTasks = startupMode === "full";
1693
- const quickMode = startupMode === "quick";
1694
- const { ctxCwd, getFlag, notify, dbg, log, runtime, cacheManager, ensureTool, cleanStaleTsBuildInfo, resetDispatchBaselines, resetLSPService, } = deps;
1695
- // Lightweight phase timer — resets after each call so each log line shows
1696
- // the cost of that phase alone, not cumulative time from session start.
1697
- let _phaseT = Date.now();
1698
- const phase = (name) => {
1699
- dbg(`session_start phase ${name}: ${Date.now() - _phaseT}ms`);
1700
- _phaseT = Date.now();
1701
- };
1702
- // #2467: `peek`, never a load. A metrics client that was never constructed
1703
- // holds no per-session state to re-arm, so the reset is vacuous — and
1704
- // loading the analyzer graph in order to reset nothing is the interactive-
1705
- // path cost this issue removes.
1706
- deps.bootstrap.peek()?.metricsClient.reset();
1707
- getDiagnosticTracker().reset();
1708
- clearFileTimeSessions();
1709
- runtime.complexityBaselines.clear();
1710
- resetDispatchBaselines(ctxCwd);
1711
- // #806: clear the shared per-directory marker index and registered
1712
- // topology-derived caches only at session start. Mid-session edits are NOT
1713
- // detected; #805's tsconfig matcher cache follows the same registry reset.
1714
- resetWorkspaceTopology();
1715
- // #2000: opaque-recovery baselines are keyed cwd:generation (unreachable
1716
- // after reset) and the git-worktree memo must re-probe after a session
1717
- // that may have seen a non-git dir become one.
1718
- resetOpaqueMutationState();
1719
- // #2430: the observational net's pending baselines are keyed by tool-call
1720
- // id and its content ledger describes the previous session's files; both
1721
- // mis-answer forever without this. The learned attribution map is cleared
1722
- // and then RE-PRIMED from this project's persisted file, so a tool an
1723
- // earlier session observed is classified by name with no snapshot at all.
1724
- resetObservedMutationNet();
1725
- resetMutationAttribution();
1726
- primePersistedMutationAttribution(ctxCwd);
1727
- // #2026: pending auxiliary baselines are unreachable after generation bump.
1728
- resetPendingAuxiliaryCoverage();
1729
- // #817/#1199: Windows command resolution is cached per (command, canonical
1730
- // effective child PATH/PATHEXT/cwd/per-drive provenance); drop it each
1731
- // session so environment changes (e.g.
1732
- // a tool installed mid-session or a differently-scoped managed bin) are
1733
- // picked up fresh.
1734
- resetSafeSpawnWindowsCommandCache();
1735
- // #1266: install-failure suppression in dispatch runner availability
1736
- // checkers (`noteInstallFailure`) is meant to last only until "the next
1737
- // session", per #1222 — but nothing called the reset helper, so a
1738
- // transient install failure (npm registry blip, locked file) suppressed
1739
- // the tool for the rest of the process lifetime. Clear it here, same
1740
- // boundary as the other per-session caches on this line.
1741
- resetDispatchAvailabilityState();
1742
- // Runner collect-later observations and their late findings are session
1743
- // scoped. A new session must re-probe rather than inherit a process latch.
1744
- resetObservedRunnerLatency();
1745
- resetPendingRunnerFindings();
1746
- // #1995: a command that TIMED OUT (not merely failed a probe) cools down
1747
- // for the rest of the session so a hot loop of edits cannot hand the same
1748
- // wedged .cmd shim a second budget. A new session may retry: the executable
1749
- // or its environment may have changed.
1750
- resetSpawnTimeoutCooldowns();
1751
- // #1895: formatter PATH verdicts are session-scoped, but they live in
1752
- // formatters.ts and are not covered by the dispatch generation. Re-arm them
1753
- // here so a formatter installed or removed between sessions is observed by
1754
- // the next session. `clearFormatterCache` drops the per-cwd selection cache
1755
- // as well as the which latches, which is what a real re-probe needs: the
1756
- // selection cache answers a same-cwd lookup before any probe runs, so
1757
- // clearing the latches alone would leave the stale verdict standing in the
1758
- // one directory the user is working in.
1759
- clearFormatterCache();
1760
- // #1535: same #1266 pattern, one caller earlier — the `gh auth token` latch
1761
- // zizmor's spawn reads is process-lived storage whose durability contract is
1762
- // per SESSION, not per process. Without this, a user who runs `gh auth
1763
- // login` and starts a fresh session still reads the previous session's
1764
- // stale "no token" verdict until the cooldown (if any) happens to expire.
1765
- resetZizmorTokenAvailability();
1766
- // #2455: same #1496/#1535 process-lifetime-latch shape, one caller later —
1767
- // the `goClient`/`rustClient` singletons each hold their own
1768
- // `createAvailabilityLatch()` outside the dispatch generation counter above
1769
- // (they predate `createCwdCachedProbe`). A "missing" verdict never expires
1770
- // on its own, so without these a go/cargo install between sessions stayed
1771
- // unobserved for the rest of the process's life. Found by hand while
1772
- // auditing #2455's detector widening, NOT surfaced by it: the sweep skips
1773
- // any file exporting no reset at all, and neither client module exported
1774
- // one, so no container predicate could have yielded them (#2455 fix round 4,
1775
- // F5 — an earlier draft of this comment had the causality backwards).
1776
- resetGoAvailability();
1777
- resetRustAvailability();
1778
- // psscriptanalyzer's three latches (interpreter, module, -File exec) are
1779
- // module-local, so the generation counter above does not reach them
1780
- // (#1490, #1540).
1781
- resetPsScriptAnalyzerAvailability();
1782
- // #1497: the install-class retry ceiling is terminal for a SESSION, but the
1783
- // latches holding it live on process-lived client instances (bootstrap builds
1784
- // them once). Same #1266/#1490/#1535 shape — without this line "terminal for
1785
- // the session" is terminal for the process, and a repaired network never
1786
- // re-earns its `go install`.
1787
- resetInstallRetryLatches();
1788
- // #1537: the lazy-install seam's hold (`gem install rubocop`, `rustup
1789
- // component add`) is durable for a SESSION. Its map is module-local, so the
1790
- // generation counter above does not reach it — same #1490/#1497 shape. It
1791
- // belongs on THIS line and not on the turn_end path: the first cut cleared it
1792
- // from `clearFormatterRuntimeState()`, which runs every turn, so a failing
1793
- // install re-spawned every turn instead of once per session.
1794
- resetLazyInstallAttempts();
1795
- // #1897: direct-LSP negative availability and bare installer paths are
1796
- // session facts. A command or PATH entry can appear between sessions.
1797
- resetDirectLspCommandAvailability();
1798
- // #2052: a root may not exist when the case-sensitivity probe first sees it.
1799
- // Re-probe it at the next session boundary after the workspace is created.
1800
- resetLSPCaseSensitivityState();
1801
- resetResolvedPathCache();
1802
- // #1653: pnpm/yarn/bun/npm's availability latches (package-manager.ts) are
1803
- // module-local, same #1490/#1535 shape as the two lines above — the
1804
- // generation counter above does not reach them. Without this line, a
1805
- // pnpm/yarn/bun install done mid-day stayed invisible: a genuine "missing"
1806
- // verdict from one session latched into the next until a process restart.
1807
- _resetPackageManagerCache();
1808
- // #1730: the managed-tool refresh budget is a per-SESSION allowance (at most
1809
- // one `npm update` per session). Left process-lived, one long-running pi
1810
- // would refresh a single tool at launch and never look at the other 21
1811
- // again — the same latch shape as the two lines above. The weekly cadence
1812
- // itself is NOT reset here: it lives in the persisted per-tool stamp, so
1813
- // re-arming the budget only restores the session's right to ask.
1814
- resetManagedToolRefreshSession();
1815
- // #1123 item 3: a fresh session can re-report smells that a prior session
1816
- // already surfaced once (see `checkSmellsAndNoteOnce`'s once-per-session gate).
1817
- resetSmellsSessionState();
1818
- // #2800 item 8: the situational-tool dead-weight observation (which tools
1819
- // this session never activated or called) is session-scoped, so its sets
1820
- // and once-latch re-arm here beside the other registered resets. Both hosts
1821
- // open the session's row through startSituationalToolTelemetrySession()
1822
- // BEFORE this handler runs, and a repeated MCP session_start refresh
1823
- // legitimately re-runs this handler — so the reset only acts when no
1824
- // telemetry session is open; clearing a live session here would wipe the
1825
- // calls recorded before the refresh.
1826
- resetSituationalToolTelemetry();
1827
- // #1782: re-arm the workspace-diagnostics cache session clock. Entries
1828
- // written before this instant assert findings from a session that is over,
1829
- // so they must revalidate before they can be served as current again.
1830
- resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
1831
- // Some embedders inject a capability-shaped Knip client rather than the
1832
- // concrete KnipClient. Session reset is an optional lifecycle capability;
1833
- // its absence must not make session_start fail.
1834
- // #2467: `peek` for the same reason as `metricsClient.reset()` above —
1835
- // an unloaded knip client has no session state to clear.
1836
- deps.bootstrap.peek()?.knipClient.resetSessionState?.();
1837
- // #1910: the tier-3 cascade outstanding-touch registry and its
1838
- // sweep-scoped expired/evicted counters (clients/lsp/cascade-tier.ts) are
1839
- // a per-SESSION claim about touches THIS session fired. #1899 bounded the
1840
- // registry between sweeps but, by its own review, left the session
1841
- // boundary unwired — a session replacement inherited the prior session's
1842
- // outstanding touches and misattributed a stray expiry/eviction to the
1843
- // next session's first reconcile gauge.
1844
- resetCascadeTierSessionState();
1845
- runtime.resetForSession(sessionStartMs);
1846
- logLatency({
1847
- type: "phase",
1848
- phase: "session_start_runtime_reset",
1849
- filePath: ctxCwd ?? process.cwd(),
1850
- startedAt: new Date(handlerEnteredAt).toISOString(),
1851
- durationMs: Date.now() - handlerEnteredAt,
1852
- metadata: { mode: startupMode, reason: deps.sessionReason },
1853
- });
1854
- // #1019: log cleanup is deferred OFF the interactive critical path. It does
1855
- // synchronous fs sweeps (~7ms) whose only consumer is an async notification —
1856
- // nothing on the hot path reads its result — so running it inline just taxed
1857
- // every session start. It still runs every session (correctness unchanged),
1858
- // now on the next tick, and notifies when done. Errors are swallowed to a
1859
- // dbg line: a best-effort log sweep must never surface as a session failure.
1860
- const cleanupCwd = ctxCwd ?? process.cwd();
1861
- setImmediate(() => {
1862
- const logCleanupStartedAt = Date.now();
1863
- try {
1864
- const logCleanup = runLogCleanup(dbg);
1865
- logLatency({
1866
- type: "phase",
1867
- phase: "session_start_log_cleanup",
1868
- filePath: cleanupCwd,
1869
- startedAt: new Date(logCleanupStartedAt).toISOString(),
1870
- durationMs: Date.now() - logCleanupStartedAt,
1871
- metadata: {
1872
- mode: startupMode,
1873
- reason: deps.sessionReason,
1874
- deferred: true,
1875
- },
1876
- });
1877
- if (logCleanup.cleaned > 0 || logCleanup.rotated > 0) {
1878
- notify(`🧹 ${logCleanup.report}`, "info");
1879
- }
1880
- }
1881
- catch (err) {
1882
- dbg(`session_start: deferred log cleanup failed: ${err}`);
1883
- }
1884
- });
1885
- dbg(`session_start startup mode: ${startupMode}`);
1886
- if (!getFlag("no-lsp")) {
1887
- resetLSPService({ fast: true, reason: "session_start" });
1888
- dbg("session_start: LSP service reset");
1889
- dbg("session_start: phase0 workspace diagnostics observation enabled (capability probe only)");
1890
- }
1891
- const hasWorkspaceCwd = typeof ctxCwd === "string" && ctxCwd.length > 0;
1892
- const cwd = ctxCwd ?? process.cwd();
1893
- // #2526 review round 3, S1: this handler no longer predicts whether config
1894
- // will resolve. `loadLSPConfig` itself publishes a `config_resolution_pending`
1895
- // mark, from inside the function, at the instant a resolution is actually
1896
- // attempted — see that function's doc comment (`clients/lsp/config.ts`) for
1897
- // why a prediction mirrored against this handler's own gates drifted from
1898
- // what index.ts and this handler's callers actually schedule.
1899
- // #1228: generic atomic-write stages are shared by several project stores,
1900
- // so the review-graph-specific sweep cannot own this namespace. Sweep the
1901
- // project data roots and machine-global registry root once per session start;
1902
- // this is fire-and-forget and bounded so it never delays startup.
1903
- const projectDataDir = getProjectDataDir(cwd);
1904
- // #2874: `getProjectDataDir` queues one migration per old-slug directory
1905
- // it renames (or finds coexisting with its hashed successor). Drain here
1906
- // so each migration emits one bounded record per session at most.
1907
- for (const migration of drainProjectDataDirMigrations()) {
1908
- const targetName = path.basename(migration.to);
1909
- const hash = targetName.match(/([0-9a-f]{8})$/)?.[1] ?? "unknown";
1910
- recordDegradationOnce({
1911
- kind: "data_dir_migrated",
1912
- subject: hash,
1913
- reason: migration.outcome === "renamed"
1914
- ? "using hashed directory after renaming legacy directory"
1915
- : migration.outcome === "coexisting"
1916
- ? "using hashed directory because legacy and hashed directories both exist"
1917
- : migration.outcome === "rename-failed"
1918
- ? "using legacy directory after hashed-directory rename failure"
1919
- : "using resolved directory after realpath fallback",
1920
- });
1921
- }
1922
- // #1609 review F1: sweepOwnStagingFiles does not recurse, so the installer's
1923
- // bin/ and tools/ subdirectories (clients/installer/index.ts's
1924
- // GITHUB_BIN_DIR / TOOLS_DIR, now atomic-write.js writers too) need their
1925
- // own entries — otherwise an orphaned staging file from a kill mid-install
1926
- // (this PR's own motivating scenario) never gets reaped, and unique
1927
- // pid-thread-seq staging names mean repeated kills ACCUMULATE full-size
1928
- // orphan binaries instead of being cleaned up.
1929
- const globalDir = getGlobalPiLensDir();
1930
- void sweepAtomicWriteStages([
1931
- projectDataDir,
1932
- path.join(projectDataDir, "cache"),
1933
- path.join(projectDataDir, "sessions"),
1934
- globalDir,
1935
- path.join(globalDir, "bin"),
1936
- path.join(globalDir, "tools"),
1937
- ]).catch(() => {
1938
- // best-effort lifecycle cleanup — never fail session_start
1939
- });
1940
- if (quickMode) {
1941
- runtime.projectRoot = cwd;
1942
- const sequenceReadStartedAt = Date.now();
1943
- const snapshotRoot = resolveSnapshotRoot(cwd);
1944
- // #1785 F5 (round 4): the getter closes over `warmupOwnSnapshotRead`
1945
- // directly (a `let` in this same `handleSessionStart` invocation's
1946
- // scope), so it reads whatever value is current AT DEFERRED-RESOLVE
1947
- // TIME — not whatever it was at this wiring instant. See
1948
- // `retroactivelyHydrateAfterDeferredSequence`'s doc comment for the
1949
- // full design.
1950
- const { latestSeq, timedOut } = await readSequenceWithBudget({
1951
- snapshotRoot,
1952
- base: snapshotSequenceBase(snapshotRoot),
1953
- cwd,
1954
- runtime,
1955
- sessionGeneration: runtime.sessionGeneration,
1956
- dbg,
1957
- onDeferredSequenceResolved: retroactivelyHydrateAfterDeferredSequence({
1958
- getWarmupOwnSnapshotRead: () => warmupOwnSnapshotRead,
1959
- snapshotRoot,
1960
- runtime,
1961
- dbg,
1962
- }),
1963
- });
1964
- logLatency({
1965
- type: "phase",
1966
- phase: "session_start_sequence_read",
1967
- filePath: cwd,
1968
- startedAt: new Date(sequenceReadStartedAt).toISOString(),
1969
- durationMs: Date.now() - sequenceReadStartedAt,
1970
- metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
1971
- });
1972
- if (timedOut) {
1973
- recordSnapshotSequenceTimeout({
1974
- snapshotRoot,
1975
- snapshotPath: getProjectSnapshotPath(snapshotRoot),
1976
- });
1977
- }
1978
- runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
1979
- const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
1980
- dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
1981
- // #1162 review follow-up (P3): a timed-out read's cold sentinel is
1982
- // `projectSeq: 0`, which is indistinguishable from a project's real
1983
- // first-ever snapshot (persisted with `seq === 0` before any change was
1984
- // logged). Feeding `effectiveSeq` (0) straight into the freshness check
1985
- // would let that legit seq-0 snapshot match and hydrate as FRESH even
1986
- // though the on-disk log has since moved past it. Use a value the
1987
- // freshness check can never match (`snapshot.seq` is always >= 0) ONLY
1988
- // for this gate when the read timed out — `runtime.projectSeq` itself
1989
- // stays 0 (untouched), which is what Fix 1's advancement guard above
1990
- // relies on to detect an in-window bump.
1991
- const freshnessSeq = timedOut ? UNKNOWN_PROJECT_SEQ : effectiveSeq;
1992
- const snapshotLoadStartedAt = Date.now();
1993
- const snapshotPath = getProjectSnapshotPath(snapshotRoot);
1994
- let snapshotBytes = 0;
1995
- try {
1996
- snapshotBytes = nodeFs.statSync(snapshotPath).size;
1997
- }
1998
- catch {
1999
- // Missing snapshots are the normal cold-start case.
2000
- }
2001
- const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
2002
- nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
2003
- const snapshotGate = loadSnapshotBodyUnlessStale({
2004
- root: snapshotRoot,
2005
- currentProjectSeq: freshnessSeq,
2006
- dbg,
2007
- });
2008
- const snapshot = snapshotGate.snapshot;
2009
- const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
2010
- const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
2011
- skippedStale: snapshotGate.skippedStale,
2012
- bodyPresent: snapshotBodyPresent,
2013
- });
2014
- logLatency({
2015
- type: "phase",
2016
- phase: "session_start_snapshot_load",
2017
- filePath: cwd,
2018
- startedAt: new Date(snapshotLoadStartedAt).toISOString(),
2019
- durationMs: Date.now() - snapshotLoadStartedAt,
2020
- metadata: {
2021
- bytes: snapshotBytes,
2022
- fresh: snapshotFresh,
2023
- seq: snapshot?.seq ?? null,
2024
- reason: snapshotFresh ? undefined : snapshotMissReason,
2025
- ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
2026
- ...(timedOut ? { sequenceUnknown: true } : {}),
2027
- },
2028
- });
2029
- logProjectSnapshotProbe({
2030
- dbg,
2031
- root: snapshotRoot,
2032
- currentProjectSeq: freshnessSeq,
2033
- snapshot,
2034
- missReason: snapshotMissReason,
2035
- });
2036
- if (snapshotFresh) {
2037
- hydrateRuntimeFromProjectSnapshot(runtime, snapshot);
2038
- }
2039
- const quickTools = [];
2040
- if (!getFlag("no-lsp")) {
2041
- quickTools.push("LSP Service");
2042
- }
2043
- log(`Active tools: ${quickTools.join(", ")}`);
2044
- dbg(`session_start tools: ${quickTools.join(", ") || "deferred (quick mode)"}`);
2045
- dbg("session_start: quick mode active - skipping slow tool probes, language profiling, preinstall, scans, and error debt baseline");
2046
- // #1911: the debug line above says WHICH steps were skipped, but nothing
2047
- // bounded or structured said so — quick mode's absence of work and an
2048
- // absent LOGGER read identically in latency.log. This record is that
2049
- // line's structured twin: one bounded gauge per quick-mode session_start,
2050
- // naming exactly the step set skipped, so a reader can tell "quick mode
2051
- // correctly skipped these" from "the probes silently never ran".
2052
- logLatency({
2053
- type: "phase",
2054
- phase: "session_start_skipped_steps",
2055
- filePath: cwd,
2056
- durationMs: 0,
2057
- metadata: {
2058
- mode: startupMode,
2059
- reason: deps.sessionReason,
2060
- // #1911 review F3: this `if (quickMode)` branch is an early return —
2061
- // the whole quick path — so there is no enumerable list of "steps
2062
- // run vs. skipped" this array could be derived from mechanically. It
2063
- // is DESCRIPTIVE documentation of what the early return skips,
2064
- // matching the `dbg()` line above word for word; keep both in sync by
2065
- // hand if either changes. It also does NOT narrow under
2066
- // `getFlag("no-lsp")` — quick mode skips the same five steps either
2067
- // way; the LSP flag instead affects `quickTools` above, a separate
2068
- // record.
2069
- steps: [
2070
- "slow_tool_probes",
2071
- "language_profiling",
2072
- "tool_preinstall",
2073
- "startup_scans",
2074
- "error_debt_baseline",
2075
- ],
2076
- },
2077
- });
2078
- const totalDurationMs = Date.now() - sessionStartMs;
2079
- dbg(`session_start total: ${totalDurationMs}ms (interactive path)`);
2080
- logLatency({
2081
- type: "phase",
2082
- phase: "session_start_total",
2083
- filePath: cwd,
2084
- startedAt: new Date(sessionStartMs).toISOString(),
2085
- durationMs: totalDurationMs,
2086
- metadata: {
2087
- mode: startupMode,
2088
- reason: deps.sessionReason,
2089
- // #2129: the root-identity input decideSessionStart consulted,
2090
- // alongside `mode` — every start reaching this line already
2091
- // classified `primary`/`sequential-replacement` (a `secondary-root`
2092
- // start returns before `handleSessionStart` is ever called).
2093
- classification: deps.sessionStartClassification,
2094
- // `undefined` is omitted by JSON.stringify. Keep unknown explicit so
2095
- // strict log readers can distinguish it from legacy omission.
2096
- sameRoot: deps.sessionStartSameRoot ?? "unknown",
2097
- },
2098
- });
2099
- logHostReadyDelay(deps, cwd);
2100
- emitSmellsSessionStartLine(dbg, sessionStartMs);
2101
- return;
2102
- }
2103
- const tools = [];
2104
- if (!getFlag("no-lsp"))
2105
- tools.push("LSP Service");
2106
- if (allowBootstrapTasks && !getFlag("no-lsp")) {
2107
- const cleaned = cleanStaleTsBuildInfo(ctxCwd ?? process.cwd());
2108
- if (cleaned.length > 0) {
2109
- notify(`🧹 Deleted stale TypeScript build cache (${cleaned.map((f) => path.basename(f)).join(", ")}) — phantom errors suppressed.`, "warning");
2110
- dbg(`session_start: cleaned stale tsbuildinfo: ${cleaned.join(", ")}`);
2111
- }
2112
- }
2113
- // Captured here (not at first use, below) because #1162's bounded read
2114
- // needs it to gate the deferred reseed against a stale/moved-on session —
2115
- // it's stable for the rest of this call (no further `resetForSession`
2116
- // between here and the later uses of the same generation).
2117
- const sessionGeneration = runtime.sessionGeneration;
2118
- const sequenceReadStartedAt = Date.now();
2119
- const snapshotRoot = resolveSnapshotRoot(cwd);
2120
- const { latestSeq, timedOut } = await readSequenceWithBudget({
2121
- snapshotRoot,
2122
- base: snapshotSequenceBase(snapshotRoot),
2123
- cwd,
2124
- runtime,
2125
- sessionGeneration,
2126
- dbg,
2127
- });
2128
- logLatency({
2129
- type: "phase",
2130
- phase: "session_start_sequence_read",
2131
- filePath: cwd,
2132
- startedAt: new Date(sequenceReadStartedAt).toISOString(),
2133
- durationMs: Date.now() - sequenceReadStartedAt,
2134
- metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
2135
- });
2136
- if (timedOut) {
2137
- // #1785: instrumentation only here, deliberately no
2138
- // `onDeferredSequenceResolved` — unlike quick mode's cold path (which
2139
- // does nothing when the snapshot isn't trusted), full mode's cold path
2140
- // actively kicks off a real rescan below to rebuild this same state.
2141
- // Retroactively hydrating from the on-disk snapshot once the deferred
2142
- // read lands could overwrite that fresher, actively-computed state with
2143
- // the stale disk copy — a regression, not a fix. See the PR body for the
2144
- // full reasoning.
2145
- recordSnapshotSequenceTimeout({
2146
- snapshotRoot,
2147
- snapshotPath: getProjectSnapshotPath(snapshotRoot),
2148
- });
2149
- }
2150
- runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
2151
- const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
2152
- dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
2153
- // #1162 review follow-up (P3) — see the matching comment in the quick-mode
2154
- // path above: a timed-out read's cold sentinel (`projectSeq: 0`) must
2155
- // never satisfy the freshness check, or a project's real first-ever
2156
- // snapshot (persisted at `seq === 0`) would hydrate as fresh despite the
2157
- // on-disk log having moved past it. `runtime.projectSeq` itself is left
2158
- // at 0, which Fix 1's advancement guard above depends on.
2159
- const freshnessSeq = timedOut ? UNKNOWN_PROJECT_SEQ : effectiveSeq;
2160
- const snapshotLoadStartedAt = Date.now();
2161
- const snapshotPath = getProjectSnapshotPath(snapshotRoot);
2162
- let snapshotBytes = 0;
2163
- try {
2164
- snapshotBytes = nodeFs.statSync(snapshotPath).size;
2165
- }
2166
- catch {
2167
- // Missing snapshots are the normal cold-start case.
2168
- }
2169
- const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
2170
- nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
2171
- const snapshotGate = loadSnapshotBodyUnlessStale({
2172
- root: snapshotRoot,
2173
- currentProjectSeq: freshnessSeq,
2174
- dbg,
2175
- });
2176
- const snapshot = snapshotGate.snapshot;
2177
- const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
2178
- const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
2179
- skippedStale: snapshotGate.skippedStale,
2180
- bodyPresent: snapshotBodyPresent,
2181
- });
2182
- logLatency({
2183
- type: "phase",
2184
- phase: "session_start_snapshot_load",
2185
- filePath: cwd,
2186
- startedAt: new Date(snapshotLoadStartedAt).toISOString(),
2187
- durationMs: Date.now() - snapshotLoadStartedAt,
2188
- metadata: {
2189
- bytes: snapshotBytes,
2190
- fresh: snapshotFresh,
2191
- seq: snapshot?.seq ?? null,
2192
- reason: snapshotFresh ? undefined : snapshotMissReason,
2193
- ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
2194
- ...(timedOut ? { sequenceUnknown: true } : {}),
2195
- },
2196
- });
2197
- logProjectSnapshotProbe({
2198
- dbg,
2199
- root: snapshotRoot,
2200
- currentProjectSeq: freshnessSeq,
2201
- snapshot,
2202
- missReason: snapshotMissReason,
2203
- });
2204
- const freshSnapshot = snapshotFresh ? snapshot : null;
2205
- if (freshSnapshot) {
2206
- hydrateRuntimeFromProjectSnapshot(runtime, freshSnapshot);
2207
- }
2208
- // #699: a persisted `too-many-source-files` verdict is only reused while
2209
- // still within its TTL (isStartupScanVerdictFresh) — the seq-based
2210
- // freshness check above (isProjectSnapshotFresh) never fires for that
2211
- // reason on its own, since pi-lens never wrote anything while
2212
- // canWarmCaches was false, so the snapshot's seq never advances.
2213
- const cachedStartupScan = freshSnapshot?.startupScan &&
2214
- isStartupScanVerdictFresh(freshSnapshot.startupScan)
2215
- ? freshSnapshot.startupScan
2216
- : undefined;
2217
- const startupScanSource = cachedStartupScan ? "snapshot" : "computed";
2218
- let startupScan;
2219
- if (cachedStartupScan) {
2220
- startupScan = { ...cachedStartupScan, cwd: path.resolve(cwd) };
2221
- }
2222
- else {
2223
- const scanContextStartedAt = Date.now();
2224
- startupScan = resolveStartupScanContext(cwd);
2225
- logLatency({
2226
- type: "phase",
2227
- phase: "session_start_scan_context_compute",
2228
- filePath: cwd,
2229
- startedAt: new Date(scanContextStartedAt).toISOString(),
2230
- durationMs: Date.now() - scanContextStartedAt,
2231
- metadata: { mode: startupMode },
2232
- });
2233
- }
2234
- phase("scan-context");
2235
- dbg(`session_start scan-context source=${startupScanSource}`);
2236
- const scanRoot = startupScan.projectRoot ?? cwd;
2237
- // Both "tree is too big" verdicts still found a real project root — the
2238
- // walk just refused to warm caches over it — so signals stay anchored at
2239
- // that root rather than falling back to cwd (#758 added too-many-entries
2240
- // as the sibling of too-many-source-files).
2241
- const useScanRootForSignals = startupScan.canWarmCaches ||
2242
- startupScan.reason === "too-many-source-files" ||
2243
- startupScan.reason === "too-many-entries";
2244
- const analysisRoot = useScanRootForSignals ? scanRoot : cwd;
2245
- runtime.projectRoot = cwd;
2246
- const languageProfileSource = freshSnapshot?.languageProfile
2247
- ? "snapshot"
2248
- : "computed";
2249
- const languageProfile = freshSnapshot?.languageProfile
2250
- ? freshSnapshot.languageProfile
2251
- : detectProjectLanguageProfile(analysisRoot, startupScan.canWarmCaches ? undefined : []);
2252
- phase("language-profile");
2253
- dbg(`session_start language-profile source=${languageProfileSource}`);
2254
- dbg(`session_start cwd: ${cwd}`);
2255
- dbg(`session_start scan root: ${scanRoot} (warmCaches=${startupScan.canWarmCaches}${startupScan.reason ? `, reason=${startupScan.reason}` : ""})`);
2256
- dbg(`session_start analysis root: ${analysisRoot}`);
2257
- dbg(`session_start workspace root: ${runtime.projectRoot}`);
2258
- dbg(`session_start language profile: ${languageProfile.detectedKinds.join(", ") || "none"}`);
2259
- dbg(`session_start language counts: ${JSON.stringify(languageProfile.counts)} configured=${JSON.stringify(languageProfile.configured)}`);
2260
- dbg(`session_start workspace cwd available: ${hasWorkspaceCwd}`);
2261
- if (useScanRootForSignals && analysisRoot !== cwd) {
2262
- dbg(`session_start: monorepo analysis root override -> ${analysisRoot}`);
2263
- }
2264
- // Slow-FS probe (#462): classify the workspace filesystem by measurement
2265
- // (median fs.statSync cost) before any tree walk runs. WSL 9p mounts cost
2266
- // ~1.3ms/stat vs ~17µs native — a 75x slowdown that turns a 5,000-file sync
2267
- // walk into a multi-second TUI freeze. Logged to the latency log so
2268
- // dogfooding can see the verdict; a visible notice fires when engaged so a
2269
- // degraded scan is never mistaken for a silently-empty one.
2270
- const slowFsVerdict = getSlowFsVerdict(analysisRoot);
2271
- logLatency({
2272
- type: "phase",
2273
- phase: "slow_fs_probe",
2274
- filePath: analysisRoot,
2275
- durationMs: 0,
2276
- metadata: {
2277
- slow: slowFsVerdict.slow,
2278
- medianStatMicros: slowFsVerdict.medianStatMicros,
2279
- samples: slowFsVerdict.samples,
2280
- },
2281
- });
2282
- dbg(`session_start slow-fs probe: slow=${slowFsVerdict.slow} medianStatMicros=${slowFsVerdict.medianStatMicros.toFixed(1)} samples=${slowFsVerdict.samples}`);
2283
- if (slowFsVerdict.slow) {
2284
- notify(`🐢 Slow filesystem detected (median ${slowFsVerdict.medianStatMicros.toFixed(0)}µs/stat) — reduced-scan mode engaged (set PI_LENS_ALLOW_SLOW_FS_SCAN=1 to override).`, "warning");
2285
- }
2286
- // Subagent light mode (#449 slice 0): detected once per session, alongside
2287
- // the slow-FS probe above. Logged to the latency log so dogfooding can see
2288
- // how often subagent fan-outs engage it and what identity they carry.
2289
- const subagentSession = isSubagentSession();
2290
- if (isWarmAttached()) {
2291
- dbg("session_start lsp-warm: skipping pre-warm (attached to incumbent)");
2292
- }
2293
- else if (subagentSession) {
2294
- const identity = getSubagentIdentity();
2295
- logLatency({
2296
- type: "phase",
2297
- phase: "subagent_light_mode",
2298
- filePath: analysisRoot,
2299
- durationMs: 0,
2300
- metadata: {
2301
- runId: identity?.runId,
2302
- agentName: identity?.agentName,
2303
- marker: identity?.marker,
2304
- },
2305
- });
2306
- dbg(`session_start subagent light mode: engaged (marker=${identity?.marker ?? "unknown"} runId=${identity?.runId ?? "unknown"} agentName=${identity?.agentName ?? "unknown"})`);
2307
- }
2308
- const lensLspEnabled = !getFlag("no-lsp");
2309
- const startupDefaults = getDefaultStartupTools(languageProfile).filter((tool) => {
2310
- if ((tool === "typescript-language-server" || tool === "pyright") &&
2311
- !lensLspEnabled) {
2312
- return false;
2313
- }
2314
- return true;
2315
- });
2316
- if (!allowBootstrapTasks) {
2317
- dbg("session_start: skipping tool preinstall (startup mode)");
2318
- }
2319
- else if (startupDefaults.length > 0) {
2320
- dbg(`session_start: pre-install defaults -> ${startupDefaults.join(", ")}`);
2321
- firePreinstallDefaults(ensureTool, dbg, startupDefaults);
2322
- }
2323
- else {
2324
- dbg("session_start: no language defaults selected for pre-install");
2325
- }
2326
- const startupScansEnabled = deps.projectConfig?.startup?.scans?.enabled ??
2327
- deps.globalConfig?.startup?.scans?.enabled ??
2328
- true;
2329
- const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches && startupScansEnabled;
2330
- const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
2331
- if (allowBootstrapTasks) {
2332
- scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
2333
- scheduleManagedToolRefresh(dbg);
2334
- }
2335
- if (allowBootstrapTasks) {
2336
- // Fire-and-forget like other tool probes
2337
- void probePrettierInstall(ensureTool, dbg, analysisRoot);
2338
- }
2339
- else {
2340
- dbg("session_start: skipping prettier preinstall probe (startup mode)");
2341
- }
2342
- // #2467: the first point on the FULL-mode path that genuinely needs the
2343
- // analyzer clients, so it is where the load is paid. Quick mode — the
2344
- // process's first session, the one the user is waiting on — returns above
2345
- // and never reaches here. Fail open: an unavailable bootstrap drops these
2346
- // three entries from the "Active tools" line rather than failing the start.
2347
- const summaryClients = await deps.bootstrap.request("session-start-tools");
2348
- const detectedRunner = summaryClients?.testRunnerClient.detectRunner(analysisRoot);
2349
- phase("test-runner-detect");
2350
- if (detectedRunner)
2351
- tools.push(`Test runner (${detectedRunner.runner})`);
2352
- if (await summaryClients?.goClient.isGoAvailableAsync())
2353
- tools.push("Go (go vet)");
2354
- if (await summaryClients?.rustClient.isAvailableAsync())
2355
- tools.push("Rust (cargo)");
2356
- log(`Active tools: ${tools.join(", ")}`);
2357
- dbg(`session_start tools: ${tools.join(", ")}`);
2358
- const agentStartupGuidance = SESSION_START_GUIDANCE;
2359
- runtime.projectRulesScan = scanProjectRules(analysisRoot);
2360
- saveRuntimeProjectSnapshot({
2361
- cwd: snapshotRoot,
2362
- runtime,
2363
- startupScan,
2364
- languageProfile,
2365
- dbg,
2366
- });
2367
- phase("project-rules");
2368
- if (runtime.projectRulesScan.hasCustomRules) {
2369
- const ruleCount = runtime.projectRulesScan.rules.length;
2370
- const sources = [
2371
- ...new Set(runtime.projectRulesScan.rules.map((r) => r.source)),
2372
- ];
2373
- dbg(`session_start: found ${ruleCount} project rule(s) from ${sources.join(", ")}`);
2374
- }
2375
- else {
2376
- dbg("session_start: no project rules found");
2377
- }
2378
- cacheManager.writeCache("session-start-guidance", { content: agentStartupGuidance.join("\n") }, analysisRoot);
2379
- // `sessionGeneration` was captured earlier (#1162's bounded sequence read
2380
- // needs it before this point) and is stable across this whole call.
2381
- if (!allowBootstrapTasks) {
2382
- dbg("session_start: skipping startup background scans (startup mode)");
2383
- }
2384
- else if (!startupScansEnabled) {
2385
- dbg("session_start: skipping startup background scans (disabled by config)");
2386
- recordDegradationOnce({
2387
- kind: "startup-analyzer-disabled",
2388
- subject: "startup-scans",
2389
- reason: "skipped (disabled by config)",
2390
- });
2391
- }
2392
- else if (!startupScan.canWarmCaches) {
2393
- dbg(`session_start: skipping heavy scans (${startupScan.reason ?? "unknown"})`);
2394
- dbg(`session_start: skipping TODO scan (${startupScan.reason ?? "unknown"})`);
2395
- // #775: mirror the slow-fs notify above — a size-skipped warm pipeline is
2396
- // otherwise silent (debug-log only), so a large project can look like
2397
- // pi-lens just isn't scanning anything. Fires ONCE per session (this
2398
- // `else` branch runs once per handleSessionStart call — the
2399
- // dominant-language LSP pre-warm skip further down reuses this same
2400
- // verdict rather than notifying a second time).
2401
- if (startupScan.reason === "too-many-source-files" ||
2402
- startupScan.reason === "too-many-entries") {
2403
- const overrideHint = startupScan.reason === "too-many-entries"
2404
- ? ` (set PI_LENS_STARTUP_SCAN_MAX_ENTRIES=<n> to override the ${getStartupScanMaxEntries()}-entry cap)`
2405
- : "";
2406
- notify(`📦 Project-size limits disabled background warm scans (heavy scans, TODO scan, LSP pre-warm)${overrideHint}.`, "warning");
2407
- }
2408
- }
2409
- else {
2410
- scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg);
2411
- }
2412
- // LSP warm files — deferred to the next event-loop turn so the config walk
2413
- // (several ENOENT readFile calls up the directory tree) never runs on the
2414
- // interactive path. setImmediate guarantees handleSessionStart has already
2415
- // resolved before loadLSPConfig is even called.
2416
- //
2417
- // #449 slice 0: skip both the explicit warmFiles warm and the
2418
- // dominant-language auto-warm inside a subagent session — a fan-out of N
2419
- // subagents otherwise pays N full LSP pre-warms in the same cwd. Per-edit
2420
- // LSP dispatch is untouched (see `pipeline.ts`), so a subagent that
2421
- // actually edits code still gets diagnostics; it just spawns the server
2422
- // lazily on first edit instead of eagerly at session start.
2423
- if (subagentSession) {
2424
- dbg("session_start lsp-warm: skipping pre-warm (subagent session)");
2425
- }
2426
- else if (!getFlag("no-lsp") && allowBootstrapTasks) {
2427
- setImmediate(() => {
2428
- void loadLSPConfig(cwd).then((lspConfig) => {
2429
- const warmFiles = lspConfig.warmFiles ?? [];
2430
- dbg(`session_start lsp-config: loaded (${warmFiles.length} warm file(s) configured)`);
2431
- if (warmFiles.length > 0) {
2432
- igniteWarmFiles(cwd, warmFiles, runtime, sessionGeneration, dbg).catch((err) => dbg(`session_start lsp-warm: unhandled error: ${err}`));
2433
- }
2434
- else if (startupScan.canWarmCaches) {
2435
- // No explicit warmFiles — pre-spawn just the dominant language's
2436
- // LSP so the first edit doesn't pay the cold-spawn stall (#203).
2437
- // Only do the auto-discovery warm on guarded real project roots; on
2438
- // home/no-project/too-large roots this source walk can become the same
2439
- // delayed background tree scan that the startup-scan guard prevents.
2440
- igniteDominantLanguageWarm(analysisRoot, runtime, sessionGeneration, dbg).catch((err) => dbg(`session_start lsp-warm: unhandled dominant error: ${err}`));
2441
- }
2442
- else {
2443
- dbg(`session_start lsp-warm: skipping dominant-language auto-warm (${startupScan.reason ?? "unknown"})`);
2444
- }
2445
- });
2446
- });
2447
- phase("lsp-config");
2448
- }
2449
- setSessionLanguages(languageProfile.detectedKinds);
2450
- const totalDurationMs = Date.now() - sessionStartMs;
2451
- dbg(`session_start total: ${totalDurationMs}ms (interactive path; background tasks may continue)`);
2452
- logLatency({
2453
- type: "phase",
2454
- phase: "session_start_total",
2455
- filePath: cwd,
2456
- startedAt: new Date(sessionStartMs).toISOString(),
2457
- durationMs: totalDurationMs,
2458
- metadata: {
2459
- mode: startupMode,
2460
- reason: deps.sessionReason,
2461
- // #2129: see the quick-mode session_start_total record above.
2462
- classification: deps.sessionStartClassification,
2463
- // Keep the full path's durable shape identical to quick mode.
2464
- sameRoot: deps.sessionStartSameRoot ?? "unknown",
2465
- },
2466
- });
2467
- logHostReadyDelay(deps, cwd);
2468
- emitSmellsSessionStartLine(dbg, sessionStartMs);
2469
- }
2470
- /**
2471
- * #1123 item 3: one bounded `session_start` line surfacing smells the manual
2472
- * `npm run logs:smells` analyzer would otherwise only catch on demand — see
2473
- * `clients/smells-rollup.ts` for the tail-scan cost bound and threshold
2474
- * gating. Never throws: a rollup miss must not break session_start.
2475
- */
2476
- function emitSmellsSessionStartLine(dbg, sessionStartMs) {
2477
- try {
2478
- const line = formatSmellsSessionStartLine(countRecentSmells(undefined, sessionStartMs));
2479
- if (line)
2480
- dbg(line);
2481
- }
2482
- catch {
2483
- // best-effort — smells rollup must never break session_start
2484
- }
2485
- }