@mrclrchtr/supi-code-intelligence 4.10.0 → 6.0.0

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 (269) hide show
  1. package/README.md +4 -3
  2. package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  3. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  6. package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  8. package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  9. package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  10. package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  11. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  12. package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  13. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
  14. package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  15. package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  16. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  17. package/node_modules/@mrclrchtr/supi-lsp/README.md +111 -5
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  21. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  22. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  23. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  24. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  26. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  27. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  28. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  29. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
  30. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  31. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  32. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  33. package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
  34. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +22 -1
  35. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +245 -0
  36. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
  37. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +171 -0
  38. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +413 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +15 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
  42. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +690 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
  44. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +97 -16
  45. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
  46. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +448 -298
  47. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +70 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +231 -0
  49. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
  50. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +529 -119
  51. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
  52. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +12 -3
  53. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
  54. package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
  55. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
  56. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
  57. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
  58. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
  59. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
  60. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +42 -0
  61. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
  62. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
  63. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +17 -7
  64. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +350 -26
  65. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
  66. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +586 -96
  67. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
  68. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
  69. package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
  70. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
  71. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
  72. package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
  73. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
  74. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +42 -0
  75. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +92 -0
  76. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +165 -118
  77. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
  78. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +17 -7
  80. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  81. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  82. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  83. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  84. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  85. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  86. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  87. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -2
  88. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
  89. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  90. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  91. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
  92. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
  96. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
  97. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
  98. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
  99. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
  100. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
  101. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
  102. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
  103. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
  104. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
  105. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
  106. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
  107. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
  108. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
  109. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
  110. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
  111. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -3
  112. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
  113. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
  114. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
  115. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
  116. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
  117. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
  118. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
  119. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
  120. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
  121. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
  122. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
  123. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
  124. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
  125. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
  126. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
  127. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
  128. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
  129. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
  130. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
  131. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
  132. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
  133. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
  134. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
  135. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
  136. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
  137. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
  138. package/package.json +5 -5
  139. package/src/analysis/health/diagnostics.ts +221 -77
  140. package/src/analysis/health/file-scope.ts +184 -0
  141. package/src/analysis/health/recovery.ts +14 -3
  142. package/src/analysis/provider.ts +58 -0
  143. package/src/analysis/readiness.ts +34 -9
  144. package/src/analysis/refactor/apply.ts +45 -1
  145. package/src/analysis/refactor/mutation-authority.ts +143 -0
  146. package/src/analysis/relations/callees.ts +9 -1
  147. package/src/analysis/relations/types.ts +5 -2
  148. package/src/analysis/search/ast-scan-timing.ts +9 -2
  149. package/src/analysis/search/ast-scan.ts +5 -2
  150. package/src/analysis/search/deadline.ts +2 -2
  151. package/src/analysis/search/pattern-analysis.ts +54 -33
  152. package/src/analysis/search/pattern.ts +18 -3
  153. package/src/analysis/target/anchored.ts +20 -11
  154. package/src/analysis/target/file.ts +18 -11
  155. package/src/analysis/target/identity.ts +19 -3
  156. package/src/analysis/target/symbol.ts +36 -15
  157. package/src/api.ts +4 -0
  158. package/src/config.ts +35 -1
  159. package/src/diagnostics/evidence.ts +51 -0
  160. package/src/extension.ts +57 -14
  161. package/src/overview/overview-data.ts +9 -2
  162. package/src/overview/overview.ts +49 -30
  163. package/src/overview/types.ts +2 -0
  164. package/src/session/capability-adapter.ts +12 -3
  165. package/src/session/find-types.ts +2 -2
  166. package/src/session/find-workflow.ts +20 -9
  167. package/src/session/graph/collect.ts +18 -2
  168. package/src/session/graph-workflow.ts +19 -12
  169. package/src/session/health-refresh.ts +175 -0
  170. package/src/session/health-types.ts +43 -15
  171. package/src/session/health-workflow.ts +69 -54
  172. package/src/session/input/workflows.ts +2 -2
  173. package/src/session/inspect/collect.ts +20 -3
  174. package/src/session/inspect-workflow.ts +12 -6
  175. package/src/session/orientation/collect.ts +12 -3
  176. package/src/session/orientation/context-facts.ts +3 -1
  177. package/src/session/orientation/context-sections.ts +20 -7
  178. package/src/session/orientation/gather.ts +17 -11
  179. package/src/session/orientation-types.ts +4 -1
  180. package/src/session/orientation-workflow.ts +23 -10
  181. package/src/session/refactor-plans.ts +2 -0
  182. package/src/session/refactor-workflow.ts +61 -15
  183. package/src/session/session.ts +29 -2
  184. package/src/session/target-workflow.ts +54 -23
  185. package/src/session/workflow-control.ts +7 -3
  186. package/src/substrate/lsp/lifecycle.ts +26 -46
  187. package/src/substrate/lsp/maintenance.ts +284 -28
  188. package/src/substrate/lsp/recovery.ts +11 -5
  189. package/src/substrate/lsp/state.ts +2 -2
  190. package/src/substrate/workspace-provider-host.ts +25 -3
  191. package/src/tool/{find → code_find}/execute.ts +2 -16
  192. package/src/tool/code_find/guidance.ts +8 -0
  193. package/src/tool/{find → code_find}/render.ts +21 -4
  194. package/src/tool/{result/find.ts → code_find/result.ts} +78 -9
  195. package/src/tool/code_find/spec.ts +37 -0
  196. package/src/tool/code_find/tui.ts +41 -0
  197. package/src/tool/code_graph/execute.ts +24 -0
  198. package/src/tool/code_graph/guidance.ts +6 -0
  199. package/src/tool/{graph → code_graph}/markdown.ts +1 -1
  200. package/src/tool/{result/graph.ts → code_graph/result.ts} +111 -3
  201. package/src/tool/code_graph/spec.ts +39 -0
  202. package/src/tool/code_graph/tui.ts +117 -0
  203. package/src/tool/{health → code_health}/execute.ts +2 -13
  204. package/src/tool/code_health/guidance.ts +8 -0
  205. package/src/tool/{health → code_health}/markdown.ts +119 -27
  206. package/src/tool/code_health/refresh-status.ts +105 -0
  207. package/src/tool/{result/health.ts → code_health/result.ts} +80 -3
  208. package/src/tool/code_health/spec.ts +41 -0
  209. package/src/tool/{health → code_health}/tui.ts +95 -98
  210. package/src/tool/{inspect → code_inspect}/execute.ts +2 -16
  211. package/src/tool/code_inspect/guidance.ts +6 -0
  212. package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
  213. package/src/tool/{result/inspect.ts → code_inspect/result.ts} +143 -2
  214. package/src/tool/code_inspect/spec.ts +25 -0
  215. package/src/tool/code_inspect/tui.ts +44 -0
  216. package/src/tool/code_orientation/execute.ts +25 -0
  217. package/src/tool/code_orientation/guidance.ts +9 -0
  218. package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
  219. package/src/tool/code_orientation/result.ts +248 -0
  220. package/src/tool/code_orientation/spec.ts +25 -0
  221. package/src/tool/{orientation → code_orientation}/tui.ts +51 -35
  222. package/src/tool/code_refactor_apply/execute.ts +17 -0
  223. package/src/tool/code_refactor_apply/guidance.ts +5 -0
  224. package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +8 -11
  225. package/src/tool/code_refactor_apply/spec.ts +26 -0
  226. package/src/tool/code_refactor_apply/tui.ts +73 -0
  227. package/src/tool/code_refactor_plan/execute.ts +27 -0
  228. package/src/tool/code_refactor_plan/guidance.ts +8 -0
  229. package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +13 -22
  230. package/src/tool/code_refactor_plan/spec.ts +25 -0
  231. package/src/tool/code_refactor_plan/tui.ts +54 -0
  232. package/src/tool/code_resolve/execute.ts +19 -0
  233. package/src/tool/code_resolve/guidance.ts +8 -0
  234. package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
  235. package/src/tool/{result/resolve.ts → code_resolve/result.ts} +79 -29
  236. package/src/tool/code_resolve/spec.ts +25 -0
  237. package/src/tool/code_resolve/tui.ts +60 -0
  238. package/src/tool/guidance.ts +71 -52
  239. package/src/tool/infra/truncate.ts +40 -0
  240. package/src/tool/infra/workflow-control.ts +8 -0
  241. package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
  242. package/src/tool/register.ts +105 -88
  243. package/src/tool/result/display.ts +107 -0
  244. package/src/tool/result/errors.ts +20 -17
  245. package/src/tool/result/refactor.ts +37 -8
  246. package/src/tool/result/types.ts +20 -0
  247. package/src/tool/schemas.ts +26 -157
  248. package/src/tool/specs.ts +40 -126
  249. package/src/types/execution.ts +18 -5
  250. package/src/types/index.ts +8 -1
  251. package/src/ui/status-command.ts +5 -3
  252. package/src/ui/status-overlay.ts +4 -0
  253. package/src/ui/tui/common.ts +108 -10
  254. package/src/ui/tui/display.ts +48 -0
  255. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -351
  256. package/src/tool/find/tui.ts +0 -39
  257. package/src/tool/graph/execute.ts +0 -62
  258. package/src/tool/graph/tui.ts +0 -104
  259. package/src/tool/inspect/tui.ts +0 -36
  260. package/src/tool/orientation/execute.ts +0 -62
  261. package/src/tool/refactor-apply/tui.ts +0 -55
  262. package/src/tool/refactor-plan/tui.ts +0 -41
  263. package/src/tool/resolve/execute.ts +0 -36
  264. package/src/tool/resolve/tui.ts +0 -42
  265. package/src/tool/result/orientation.ts +0 -132
  266. /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
  267. /package/src/tool/{find → code_find}/markdown.ts +0 -0
  268. /package/src/tool/{find → code_find}/modes.ts +0 -0
  269. /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
@@ -7,6 +7,10 @@
7
7
 
8
8
  import * as path from "node:path";
9
9
  import ts from "typescript";
10
+ import { collectExtendedProjectConfigs } from "./tsconfig-extends.ts";
11
+ import { normalizeTsconfigPath as normalizePath } from "./tsconfig-path.ts";
12
+
13
+ export { isProjectConfigFileName } from "./tsconfig-extends.ts";
10
14
 
11
15
  interface ParsedProjectConfig {
12
16
  configPath: string;
@@ -19,8 +23,39 @@ interface ParsedProjectConfig {
19
23
  usesDefaultInclude: boolean;
20
24
  }
21
25
 
26
+ interface ParsedProjectConfigCacheEntry {
27
+ parsed: ParsedProjectConfig | null;
28
+ dependencies: Set<string>;
29
+ }
30
+
31
+ /** Whether a file is inside the compilation scope of its nearest project config. */
32
+ export type FileScopeStatus = "included" | "excluded" | "no-config" | "out-of-tree";
33
+
34
+ /**
35
+ * The mechanism that produced a file scope decision.
36
+ *
37
+ * `extension` covers unsupported file extensions (checked before any pattern).
38
+ */
39
+ export type ScopeDecisionBasis =
40
+ | "fileNames"
41
+ | "explicit"
42
+ | "include-pattern"
43
+ | "default-include"
44
+ | "exclude-pattern"
45
+ | "extension";
46
+
47
+ /** An explainable tsconfig scope decision for one file. */
48
+ export interface FileScopeDecision {
49
+ status: FileScopeStatus;
50
+ /** Decision mechanism; null when no decision applies (no-config, out-of-tree). */
51
+ basis: ScopeDecisionBasis | null;
52
+ /** Absolute path of the config that decided the file, when one exists. */
53
+ configPath: string | null;
54
+ caseSensitiveFileNames: boolean;
55
+ }
56
+
22
57
  const nearestConfigCache = new Map<string, string | null>();
23
- const parsedConfigCache = new Map<string, ParsedProjectConfig | null>();
58
+ const parsedConfigCache = new Map<string, ParsedProjectConfigCacheEntry>();
24
59
 
25
60
  const tsInternal = ts as typeof ts & {
26
61
  getFileMatcherPatterns?: (
@@ -47,14 +82,63 @@ const tsInternal = ts as typeof ts & {
47
82
  * @returns `true` if the file is excluded from compilation scope
48
83
  */
49
84
  export function isFileExcludedByTsconfig(filePath: string, cwd: string): boolean {
85
+ // Legacy filter semantics: only a resolved "excluded" decision filters a
86
+ // file. Out-of-tree and no-config files stay unfiltered; the decision API
87
+ // still reports those statuses honestly for consumers that want them.
88
+ return getFileScopeDecision(filePath, cwd).status === "excluded";
89
+ }
90
+
91
+ /**
92
+ * Compute the explainable tsconfig scope decision for one file.
93
+ *
94
+ * The decision carries the mechanism that produced it (the scope decision
95
+ * basis) so consumers can report why a file is inside or outside the nearest
96
+ * project config without re-deriving the scope logic.
97
+ */
98
+ export function getFileScopeDecision(filePath: string, cwd: string): FileScopeDecision {
99
+ const caseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
50
100
  const absolutePath = path.resolve(cwd, filePath);
101
+ if (isOutOfTree(cwd, absolutePath)) {
102
+ return {
103
+ status: "out-of-tree",
104
+ basis: null,
105
+ configPath: null,
106
+ caseSensitiveFileNames,
107
+ };
108
+ }
109
+
51
110
  const configPath = findNearestProjectConfig(path.dirname(absolutePath), cwd);
52
- if (!configPath) return false;
111
+ if (!configPath) {
112
+ return {
113
+ status: "no-config",
114
+ basis: null,
115
+ configPath: null,
116
+ caseSensitiveFileNames,
117
+ };
118
+ }
53
119
 
54
120
  const parsed = parseProjectConfig(configPath);
55
- if (!parsed) return false;
121
+ if (!parsed) {
122
+ return {
123
+ status: "no-config",
124
+ basis: null,
125
+ configPath,
126
+ caseSensitiveFileNames,
127
+ };
128
+ }
56
129
 
57
- return !isFileInProjectScope(parsed, absolutePath);
130
+ const decision = decideFileScope(parsed, absolutePath);
131
+ return {
132
+ status: decision.included ? "included" : "excluded",
133
+ basis: decision.basis,
134
+ configPath: parsed.configPath,
135
+ caseSensitiveFileNames,
136
+ };
137
+ }
138
+
139
+ function isOutOfTree(cwd: string, absolutePath: string): boolean {
140
+ const relative = path.relative(path.resolve(cwd), absolutePath);
141
+ return relative.startsWith(`..${path.sep}`) || relative === "..";
58
142
  }
59
143
 
60
144
  /**
@@ -104,11 +188,15 @@ function getLocalProjectConfig(directory: string): string | null {
104
188
  function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
105
189
  const normalizedConfigPath = normalizePath(configPath);
106
190
  const cached = parsedConfigCache.get(normalizedConfigPath);
107
- if (cached !== undefined) return cached;
191
+ if (cached !== undefined) return cached.parsed;
108
192
 
193
+ const cacheEntry: ParsedProjectConfigCacheEntry = {
194
+ parsed: null,
195
+ dependencies: collectExtendedProjectConfigs(configPath),
196
+ };
109
197
  const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, createParseConfigHost());
110
198
  if (!parsed) {
111
- parsedConfigCache.set(normalizedConfigPath, null);
199
+ parsedConfigCache.set(normalizedConfigPath, cacheEntry);
112
200
  return null;
113
201
  }
114
202
 
@@ -119,7 +207,10 @@ function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
119
207
  configDir,
120
208
  parsed.raw.include,
121
209
  parsed.raw.exclude,
122
- usesDefaultInclude,
210
+ {
211
+ useDefaultInclude: usesDefaultInclude,
212
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
213
+ },
123
214
  );
124
215
  const supportedExtensions = new Set(getSupportedExtensions(parsed.options));
125
216
  if (parsed.options.resolveJsonModule) supportedExtensions.add(".json");
@@ -134,7 +225,8 @@ function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
134
225
  supportedExtensions,
135
226
  usesDefaultInclude,
136
227
  } satisfies ParsedProjectConfig;
137
- parsedConfigCache.set(normalizedConfigPath, result);
228
+ cacheEntry.parsed = result;
229
+ parsedConfigCache.set(normalizedConfigPath, cacheEntry);
138
230
  return result;
139
231
  }
140
232
 
@@ -147,15 +239,21 @@ function extractExplicitFiles(rawFiles: unknown, configDir: string): Set<string>
147
239
  );
148
240
  }
149
241
 
242
+ interface FileMatcherOptions {
243
+ useDefaultInclude: boolean;
244
+ useCaseSensitiveFileNames: boolean;
245
+ }
246
+
150
247
  function createFileMatchers(
151
248
  configDir: string,
152
249
  rawInclude: unknown,
153
250
  rawExclude: unknown,
154
- useDefaultInclude: boolean,
251
+ options: FileMatcherOptions,
155
252
  ): {
156
253
  includeFilePattern: RegExp | null;
157
254
  excludePattern: RegExp | null;
158
255
  } {
256
+ const { useDefaultInclude, useCaseSensitiveFileNames } = options;
159
257
  const includeSpecs = Array.isArray(rawInclude)
160
258
  ? rawInclude.filter((entry): entry is string => typeof entry === "string")
161
259
  : undefined;
@@ -167,29 +265,45 @@ function createFileMatchers(
167
265
  excludeSpecs,
168
266
  useDefaultInclude ? ["**/*"] : includeSpecs,
169
267
  );
268
+ // normalizePath() lowercases paths when the filesystem is case-insensitive,
269
+ // so the include/exclude regexes must match case-insensitively too. Otherwise
270
+ // files created after the parse (absent from fileNames, tested against the
271
+ // include pattern) are wrongly declared excluded.
272
+ const regexFlags = useCaseSensitiveFileNames ? undefined : "i";
170
273
 
171
274
  return {
172
275
  includeFilePattern: matcherPatterns.includeFilePattern
173
- ? new RegExp(matcherPatterns.includeFilePattern)
276
+ ? new RegExp(matcherPatterns.includeFilePattern, regexFlags)
174
277
  : null,
175
278
  excludePattern: matcherPatterns.excludePattern
176
- ? new RegExp(matcherPatterns.excludePattern)
279
+ ? new RegExp(matcherPatterns.excludePattern, regexFlags)
177
280
  : null,
178
281
  };
179
282
  }
180
283
 
181
- function isFileInProjectScope(parsed: ParsedProjectConfig, absolutePath: string): boolean {
284
+ function decideFileScope(
285
+ parsed: ParsedProjectConfig,
286
+ absolutePath: string,
287
+ ): { included: boolean; basis: ScopeDecisionBasis } {
182
288
  const normalizedPath = normalizePath(absolutePath);
183
- if (parsed.fileNames.has(normalizedPath)) return true;
289
+ if (parsed.fileNames.has(normalizedPath)) return { included: true, basis: "fileNames" };
184
290
 
185
291
  const extension = path.extname(absolutePath).toLowerCase();
186
- if (!parsed.supportedExtensions.has(extension)) return false;
292
+ if (!parsed.supportedExtensions.has(extension)) return { included: false, basis: "extension" };
187
293
 
188
- if (parsed.explicitFiles) return parsed.explicitFiles.has(normalizedPath);
189
- if (!isWithinOrEqual(parsed.configDir, absolutePath)) return false;
190
- if (parsed.excludePattern?.test(normalizedPath)) return false;
191
- if (parsed.usesDefaultInclude) return true;
192
- return parsed.includeFilePattern ? parsed.includeFilePattern.test(normalizedPath) : false;
294
+ if (parsed.explicitFiles) {
295
+ return { included: parsed.explicitFiles.has(normalizedPath), basis: "explicit" };
296
+ }
297
+ // Include patterns are rooted at the config directory; a file outside it can
298
+ // only be in scope through the parse-time file set or an explicit files list.
299
+ if (!isWithinOrEqual(parsed.configDir, absolutePath)) {
300
+ return { included: false, basis: "include-pattern" };
301
+ }
302
+ if (parsed.excludePattern?.test(normalizedPath))
303
+ return { included: false, basis: "exclude-pattern" };
304
+ if (parsed.usesDefaultInclude) return { included: true, basis: "default-include" };
305
+ if (!parsed.includeFilePattern) return { included: false, basis: "include-pattern" };
306
+ return { included: parsed.includeFilePattern.test(normalizedPath), basis: "include-pattern" };
193
307
  }
194
308
 
195
309
  function getSupportedExtensions(options: ts.CompilerOptions): string[] {
@@ -229,11 +343,6 @@ function isWithinOrEqual(root: string, target: string): boolean {
229
343
  return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== "..");
230
344
  }
231
345
 
232
- function normalizePath(target: string): string {
233
- const resolved = path.resolve(target).replaceAll("\\", "/");
234
- return ts.sys.useCaseSensitiveFileNames ? resolved : resolved.toLowerCase();
235
- }
236
-
237
346
  /**
238
347
  * Clear cached nearest-config lookups and parsed project config state.
239
348
  * Useful for testing and after workspace file changes.
@@ -242,3 +351,46 @@ export function clearTsconfigCache(): void {
242
351
  nearestConfigCache.clear();
243
352
  parsedConfigCache.clear();
244
353
  }
354
+
355
+ /**
356
+ * Invalidate cached state for one config file that changed, was created, or
357
+ * was deleted.
358
+ *
359
+ * Removes the changed config and every cached root config that depends on it,
360
+ * so the next decision re-reads current config data. The wholesale
361
+ * {@link clearTsconfigCache} stays for lifecycle events; change signals use
362
+ * this targeted invalidation instead.
363
+ */
364
+ export function invalidateTsconfigCacheForConfig(configPath: string): void {
365
+ const normalizedConfigPath = normalizePath(configPath);
366
+ const invalidated = new Set([normalizedConfigPath]);
367
+
368
+ for (const [cachedConfigPath, cacheEntry] of parsedConfigCache) {
369
+ if (cacheEntry.dependencies.has(normalizedConfigPath)) invalidated.add(cachedConfigPath);
370
+ }
371
+
372
+ for (const cachedConfigPath of invalidated) {
373
+ parsedConfigCache.delete(cachedConfigPath);
374
+ }
375
+
376
+ for (const [key, value] of nearestConfigCache) {
377
+ if (value !== null && invalidated.has(normalizePath(value))) nearestConfigCache.delete(key);
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Invalidate nearest-config lookups that found nothing under `dir`.
383
+ *
384
+ * Used when a config is created: directories below the new config that
385
+ * previously resolved to no config may now resolve to it.
386
+ */
387
+ export function invalidateTsconfigCacheForConfigDir(dir: string): void {
388
+ const normalizedDir = normalizePath(dir);
389
+ for (const [key, value] of nearestConfigCache) {
390
+ if (value !== null) continue;
391
+ const keyDir = key.split("::", 1)[0];
392
+ if (keyDir === normalizedDir || keyDir.startsWith(`${normalizedDir}/`)) {
393
+ nearestConfigCache.delete(key);
394
+ }
395
+ }
396
+ }
@@ -0,0 +1,45 @@
1
+ // Bounded identity vocabulary for LSP debug telemetry.
2
+ //
3
+ // LSP debug events may carry server, workspace, file, method, and root
4
+ // identity for local protocol diagnosis. Every emitted identity string is
5
+ // bounded to MAX_IDENTITY_STRING UTF-16 code units (truncation marker
6
+ // included) and every identity list is bounded to MAX_SERVERS entries.
7
+
8
+ /** Maximum number of server entries in one bounded LSP debug payload. */
9
+ export const MAX_SERVERS = 16;
10
+
11
+ /** Maximum UTF-16 code-unit length of one emitted identity string in an LSP debug payload. */
12
+ export const MAX_IDENTITY_STRING = 512;
13
+
14
+ /** Marker appended to an identity string truncated to MAX_IDENTITY_STRING. */
15
+ export const IDENTITY_TRUNCATION_MARKER = "…";
16
+
17
+ /**
18
+ * JSON-RPC error code recorded for client-local request timeouts.
19
+ *
20
+ * The JSON-RPC and LSP vocabularies define no timeout code. This constant
21
+ * uses the JSON-RPC implementation-defined error range (-32099..-32000) at a
22
+ * value that vscode-jsonrpc transport codes (-32099, -32098, -32097, -32096,
23
+ * -32002, -32001) and LSP error codes never use.
24
+ */
25
+ export const LSP_REQUEST_TIMEOUT_ERROR_CODE = -32_095;
26
+
27
+ /**
28
+ * Truncate one identity string so the emitted value (marker included) never
29
+ * exceeds MAX_IDENTITY_STRING UTF-16 code units.
30
+ */
31
+ export function truncateIdentity(value: string): string {
32
+ if (value.length <= MAX_IDENTITY_STRING) return value;
33
+ return `${value.slice(0, MAX_IDENTITY_STRING - 1)}${IDENTITY_TRUNCATION_MARKER}`;
34
+ }
35
+
36
+ /** Bound an optional workspace root for event-level identity; undefined stays absent. */
37
+ export function boundCwd(cwd: string | undefined): string | undefined {
38
+ if (cwd === undefined) return undefined;
39
+ return truncateIdentity(cwd);
40
+ }
41
+
42
+ /** Bound a server-name list to MAX_SERVERS entries, truncating each name. */
43
+ export function boundServerNames(names: readonly string[]): string[] {
44
+ return names.slice(0, MAX_SERVERS).map(truncateIdentity);
45
+ }
@@ -0,0 +1,42 @@
1
+ /** Reason for a partial or unavailable single-file result when the push stays tentative. */
2
+ export const TENTATIVE_PUSH_UNAVAILABLE_REASON =
3
+ "The current push publication is tentative: a later diagnostic republish for the same document synchronization is required.";
4
+
5
+ /** A final evidence state for one tracked document. */
6
+ export type DiagnosticEvidenceStatus = "confirmed" | "unconfirmed" | "failed" | "removed";
7
+
8
+ /** Freshness evidence for one tracked document. */
9
+ export interface DiagnosticEvidenceDocument {
10
+ readonly file: string;
11
+ readonly status: DiagnosticEvidenceStatus;
12
+ }
13
+
14
+ /** Exact document coverage from a diagnostic snapshot or refresh attempt. */
15
+ export interface DiagnosticEvidenceSummary {
16
+ readonly requested: number;
17
+ readonly confirmed: number;
18
+ readonly unconfirmed: number;
19
+ readonly failed: number;
20
+ readonly removed: number;
21
+ readonly documents: readonly DiagnosticEvidenceDocument[];
22
+ }
23
+
24
+ /** Build exact coverage counts from one document outcome per requested document. */
25
+ export function summarizeDiagnosticEvidence(
26
+ documents: readonly DiagnosticEvidenceDocument[],
27
+ ): DiagnosticEvidenceSummary {
28
+ const counts = {
29
+ requested: documents.length,
30
+ confirmed: 0,
31
+ unconfirmed: 0,
32
+ failed: 0,
33
+ removed: 0,
34
+ };
35
+ for (const document of documents) counts[document.status]++;
36
+ return { ...counts, documents: [...documents] };
37
+ }
38
+
39
+ /** Return an empty evidence summary for a refresh with no tracked documents. */
40
+ export function emptyDiagnosticEvidence(): DiagnosticEvidenceSummary {
41
+ return summarizeDiagnosticEvidence([]);
42
+ }
@@ -1,20 +1,32 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { FileChangeType, type FileEvent } from "../config/types.ts";
4
- import { fileToUri } from "../utils.ts";
4
+ import { fileToUri, uriToFile } from "../utils.ts";
5
5
 
6
6
  const IGNORED_DIRECTORIES = new Set(["node_modules", ".pnpm", ".git", "dist", "coverage"]);
7
7
  const ROOT_LOCKFILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"];
8
8
 
9
+ export interface WorkspaceSentinelScanOptions {
10
+ /**
11
+ * Also track every regular source file (not just sentinels). The workspace
12
+ * refresh path uses this to discover files created since the last refresh;
13
+ * the diff then reports them as `sourceChanges` so the caller can pull them.
14
+ */
15
+ includeSourceFiles?: boolean;
16
+ }
17
+
9
18
  /** Build a fresh snapshot of workspace sentinel files and their mtimes. */
10
- export function scanWorkspaceSentinels(cwd: string): Map<string, number> {
19
+ export function scanWorkspaceSentinels(
20
+ cwd: string,
21
+ options: WorkspaceSentinelScanOptions = {},
22
+ ): Map<string, number> {
11
23
  const resolvedCwd = path.resolve(cwd);
12
24
  const snapshot = new Map<string, number>();
13
25
 
14
26
  if (!fs.existsSync(resolvedCwd)) return snapshot;
15
27
 
16
28
  try {
17
- walkWorkspace(resolvedCwd, resolvedCwd, snapshot);
29
+ walkWorkspace(resolvedCwd, resolvedCwd, snapshot, options.includeSourceFiles ?? false);
18
30
  } catch {
19
31
  return snapshot;
20
32
  }
@@ -48,15 +60,34 @@ export function diffWorkspaceSentinelSnapshot(
48
60
  return changes.sort((a, b) => a.uri.localeCompare(b.uri));
49
61
  }
50
62
 
51
- /** Refresh a previous snapshot and return the new snapshot plus change events. */
63
+ /**
64
+ * Refresh a previous snapshot and return the new snapshot plus change events.
65
+ *
66
+ * When `includeSourceFiles` is set, the returned `changes` list keeps
67
+ * sentinel-file events only; source-file events are returned separately in
68
+ * `sourceChanges` so the caller can act on them without interpreting every
69
+ * regular file edit as a config-level workspace change.
70
+ */
52
71
  export function syncWorkspaceSentinelSnapshot(
53
72
  cwd: string,
54
73
  previous: Map<string, number>,
55
- ): { snapshot: Map<string, number>; changes: FileEvent[] } {
56
- const snapshot = scanWorkspaceSentinels(cwd);
74
+ options: WorkspaceSentinelScanOptions = {},
75
+ ): { snapshot: Map<string, number>; changes: FileEvent[]; sourceChanges: FileEvent[] } {
76
+ const snapshot = scanWorkspaceSentinels(cwd, options);
77
+ const changes = diffWorkspaceSentinelSnapshot(previous, snapshot);
78
+ if (!options.includeSourceFiles) return { snapshot, changes, sourceChanges: [] };
79
+
80
+ const sourceChanges = changes.filter((change) => {
81
+ const filePath = uriToFile(change.uri);
82
+ return !isWorkspaceSentinelPath(filePath, cwd);
83
+ });
57
84
  return {
58
85
  snapshot,
59
- changes: diffWorkspaceSentinelSnapshot(previous, snapshot),
86
+ changes: changes.filter((change) => {
87
+ const filePath = uriToFile(change.uri);
88
+ return isWorkspaceSentinelPath(filePath, cwd);
89
+ }),
90
+ sourceChanges,
60
91
  };
61
92
  }
62
93
 
@@ -66,7 +97,12 @@ export function isWorkspaceRecoveryTrigger(filePath: string, cwd: string): boole
66
97
  return isWorkspaceSentinelPath(path.resolve(root, filePath), root);
67
98
  }
68
99
 
69
- function walkWorkspace(root: string, directory: string, snapshot: Map<string, number>): void {
100
+ function walkWorkspace(
101
+ root: string,
102
+ directory: string,
103
+ snapshot: Map<string, number>,
104
+ includeSourceFiles: boolean,
105
+ ): void {
70
106
  let entries: fs.Dirent[];
71
107
  try {
72
108
  entries = fs.readdirSync(directory, { withFileTypes: true });
@@ -80,14 +116,14 @@ function walkWorkspace(root: string, directory: string, snapshot: Map<string, nu
80
116
  for (const entry of entries) {
81
117
  if (entry.isDirectory()) {
82
118
  if (IGNORED_DIRECTORIES.has(entry.name)) continue;
83
- walkWorkspace(root, path.join(directory, entry.name), snapshot);
119
+ walkWorkspace(root, path.join(directory, entry.name), snapshot, includeSourceFiles);
84
120
  continue;
85
121
  }
86
122
 
87
123
  if (!entry.isFile()) continue;
88
124
 
89
125
  const filePath = path.join(directory, entry.name);
90
- if (!isWorkspaceSentinelPath(filePath, root)) continue;
126
+ if (!includeSourceFiles && !isWorkspaceSentinelPath(filePath, root)) continue;
91
127
  try {
92
128
  snapshot.set(filePath, fs.statSync(filePath).mtimeMs);
93
129
  } catch {
@@ -1,5 +1,16 @@
1
1
  import * as path from "node:path";
2
+ import {
3
+ type CodeRequestControl,
4
+ isCodeRequestInterruption,
5
+ } from "@mrclrchtr/supi-code-runtime/api";
2
6
  import type { LspClient } from "../client/client.ts";
7
+ import { getDiagnosticFileState } from "../client/client-file-state.ts";
8
+ import {
9
+ type DiagnosticEvidenceDocument,
10
+ type DiagnosticEvidenceSummary,
11
+ summarizeDiagnosticEvidence,
12
+ } from "../diagnostics/evidence.ts";
13
+ import { uriToFile } from "../utils.ts";
3
14
 
4
15
  export function closeFileAcrossClients(clients: Iterable<LspClient>, filePath: string): void {
5
16
  const resolvedPath = path.resolve(filePath);
@@ -21,14 +32,73 @@ export function pruneMissingFilesFromClients(clients: Iterable<LspClient>): stri
21
32
 
22
33
  export async function refreshOpenDiagnosticsForClients(
23
34
  clients: Iterable<LspClient>,
24
- options?: { maxWaitMs?: number; quietMs?: number },
25
- ): Promise<void> {
26
- const refreshes = Array.from(clients)
27
- .filter((client) => client.status === "running")
28
- .map((client) =>
29
- client.refreshOpenDiagnostics(options).catch(() => {
30
- // Soft-fail: LSP errors must not prevent agent startup
31
- }),
32
- );
35
+ options?: { maxWaitMs?: number; quietMs?: number } & CodeRequestControl,
36
+ ): Promise<DiagnosticEvidenceSummary> {
37
+ const documents: DiagnosticEvidenceDocument[] = [];
38
+ const refreshes = Array.from(clients).map(async (client) => {
39
+ const trackedFiles = [...client.openFiles];
40
+ if (client.status !== "running") {
41
+ documents.push(...failedCoverageForClient(client, trackedFiles));
42
+ return;
43
+ }
44
+
45
+ try {
46
+ const result = await client.refreshOpenDiagnostics(options);
47
+ const reportedFiles = new Set(result.documents.map((document) => document.file));
48
+ documents.push(...result.documents);
49
+ documents.push(
50
+ ...trackedFiles
51
+ .filter((file) => !reportedFiles.has(file))
52
+ .map((file) => ({ file, status: "failed" as const })),
53
+ );
54
+ } catch (error) {
55
+ // A cancelled refresh must not degrade into failed coverage evidence.
56
+ if (isCodeRequestInterruption(error, options)) throw error;
57
+ // Keep failed coverage for every document requested by the client.
58
+ documents.push(...failedCoverageForClient(client, trackedFiles));
59
+ }
60
+ });
33
61
  await Promise.all(refreshes);
62
+ return summarizeDiagnosticEvidence(deduplicateEvidenceDocuments(documents));
63
+ }
64
+
65
+ function failedCoverageForClient(
66
+ client: LspClient,
67
+ trackedFiles: readonly string[],
68
+ ): DiagnosticEvidenceDocument[] {
69
+ const snapshot = client.getDiagnosticSnapshot();
70
+ const files = new Set([
71
+ ...trackedFiles,
72
+ ...snapshot.documents.map((document) => uriToFile(document.uri)),
73
+ ]);
74
+ return Array.from(files).map((file) => ({
75
+ file,
76
+ status: getDiagnosticFileState(file) === "removed" ? ("removed" as const) : ("failed" as const),
77
+ }));
78
+ }
79
+
80
+ function deduplicateEvidenceDocuments(
81
+ documents: readonly DiagnosticEvidenceDocument[],
82
+ ): DiagnosticEvidenceDocument[] {
83
+ const byFile = new Map<string, DiagnosticEvidenceDocument>();
84
+ for (const document of documents) {
85
+ const existing = byFile.get(document.file);
86
+ if (!existing || evidenceStatusRank(document.status) > evidenceStatusRank(existing.status)) {
87
+ byFile.set(document.file, document);
88
+ }
89
+ }
90
+ return Array.from(byFile.values()).sort((a, b) => a.file.localeCompare(b.file));
91
+ }
92
+
93
+ function evidenceStatusRank(status: DiagnosticEvidenceDocument["status"]): number {
94
+ switch (status) {
95
+ case "confirmed":
96
+ return 1;
97
+ case "unconfirmed":
98
+ return 2;
99
+ case "failed":
100
+ return 3;
101
+ case "removed":
102
+ return 4;
103
+ }
34
104
  }
@@ -1,5 +1,11 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import {
3
+ type CodeQueryResult,
4
+ type CodeRequestControl,
5
+ mapCodeQueryResult,
6
+ } from "@mrclrchtr/supi-code-runtime/api";
2
7
  import type { LspClient } from "../client/client.ts";
8
+ import type { DiagnosticEntry } from "../client/client-document-state.ts";
3
9
  import type { Diagnostic } from "../config/types.ts";
4
10
  import { relativeFilePathFromUri } from "../diagnostics/diagnostic-summary.ts";
5
11
  import { shouldIgnoreLspPath } from "../summary.ts";
@@ -10,23 +16,27 @@ export async function syncClientFileAndGetDiagnostics(
10
16
  client: Pick<LspClient, "syncAndWaitForDiagnostics">,
11
17
  filePath: string,
12
18
  maxSeverity: number,
13
- ): Promise<Diagnostic[]> {
14
- return filterDiagnosticsBySeverity(
15
- await client.syncAndWaitForDiagnostics(filePath, readFileSync(filePath, "utf-8")),
16
- maxSeverity,
19
+ control?: CodeRequestControl,
20
+ ): Promise<CodeQueryResult<Diagnostic[]>> {
21
+ const content = readFileSync(filePath, "utf-8");
22
+ const result = control
23
+ ? await client.syncAndWaitForDiagnostics(filePath, content, control)
24
+ : await client.syncAndWaitForDiagnostics(filePath, content);
25
+ return mapCodeQueryResult(result, (diagnostics) =>
26
+ filterDiagnosticsBySeverity(diagnostics, maxSeverity),
17
27
  );
18
28
  }
19
29
 
20
30
  export function collectOutstandingDiagnosticsDetailed(
21
- clients: Iterable<Pick<LspClient, "getAllDiagnostics">>,
31
+ clientEntries: Iterable<ReadonlyArray<DiagnosticEntry>>,
22
32
  cwd: string,
23
33
  excludePatterns: string[],
24
34
  maxSeverity: number,
25
35
  ): Array<{ file: string; diagnostics: Diagnostic[] }> {
26
36
  const fileDiags = new Map<string, Diagnostic[]>();
27
37
 
28
- for (const client of clients) {
29
- for (const entry of client.getAllDiagnostics()) {
38
+ for (const entries of clientEntries) {
39
+ for (const entry of entries) {
30
40
  const file = relativeFilePathFromUri(entry.uri, cwd);
31
41
  if (shouldIgnoreLspPath(file, cwd)) continue;
32
42
  if (isExcludedByPattern(file, excludePatterns)) continue;