@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
@@ -6,70 +6,326 @@
6
6
  * semantic tool queries.
7
7
  */
8
8
 
9
+ import { statSync } from "node:fs";
9
10
  import * as nodePath from "node:path";
10
- import type { WorkspaceLspRuntime } from "@mrclrchtr/supi-lsp/api";
11
11
  import {
12
- clearTsconfigCache,
12
+ type CodeRequestControl,
13
+ isCodeRequestInterruption,
14
+ throwIfCodeRequestInterrupted,
15
+ } from "@mrclrchtr/supi-code-runtime/api";
16
+ import { isWithinOrEqual } from "@mrclrchtr/supi-core/api";
17
+ import { uriToFile } from "@mrclrchtr/supi-core/path";
18
+ import type { DiagnosticEvidenceSummary } from "@mrclrchtr/supi-lsp/api";
19
+ import {
20
+ FileChangeType,
21
+ type FileEvent,
22
+ invalidateTsconfigCacheForConfig,
23
+ invalidateTsconfigCacheForConfigDir,
13
24
  isLikelyStaleDiagnostic,
25
+ isProjectConfigFileName,
26
+ raceRequestControl,
14
27
  syncWorkspaceSentinelSnapshot,
28
+ type WorkspaceLspRuntime,
15
29
  } from "@mrclrchtr/supi-lsp/api";
30
+ import { mergeDiagnosticEvidence } from "../../diagnostics/evidence.ts";
31
+
32
+ /** Options shared by the workspace and file-scoped maintenance passes. */
33
+ export interface LspMaintenanceOptions {
34
+ control?: CodeRequestControl;
35
+ /**
36
+ * Workspace-relative scope prefix for source-file discovery. Discovery only
37
+ * tracks created files inside this prefix; null disables the scope bound.
38
+ */
39
+ scope?: string | null;
40
+ /**
41
+ * Track files created since the last refresh so the following refresh pull
42
+ * includes them. Only the workspace-runtime path sets this; the snapshot is
43
+ * still widened on file-scoped passes so later diffs stay correct.
44
+ */
45
+ trackSources?: boolean;
46
+ }
16
47
 
17
48
  /**
18
- * Run sentinel-sync, stale-module resync, prune, and diagnostic refresh
19
- * as a single maintenance pass. Call before explicit diagnostic queries
20
- * (code_health with refresh:true) or semantic tool readiness checks.
49
+ * Run sentinel-sync, diagnostic refresh, and stale-module resync as a single
50
+ * maintenance pass. Call before explicit diagnostic queries (code_health with
51
+ * refresh:true) or semantic tool readiness checks.
21
52
  */
22
53
  export async function refreshLspMaintenance(
23
54
  runtime: WorkspaceLspRuntime,
24
55
  cwd: string,
25
56
  sentinelSnapshot: Map<string, number>,
26
- ): Promise<Map<string, number>> {
27
- // Sentinel refresh: detect lockfile/tsconfig/d.ts changes from outside
28
- // write/edit/code_refactor_apply.
29
- const { snapshot, changes } = syncWorkspaceSentinelSnapshot(cwd, sentinelSnapshot);
30
-
31
- if (changes.length > 0) {
32
- clearTsconfigCache();
33
- runtime.noteWorkspaceChanges(changes);
57
+ options: LspMaintenanceOptions = {},
58
+ ): Promise<WorkspaceLspMaintenanceResult> {
59
+ const { snapshot } = await synchronizeSentinels(runtime, cwd, sentinelSnapshot, options);
60
+ let diagnosticEvidence = emptyEvidence();
61
+ let failureReason: string | undefined;
62
+
63
+ // Refresh before stale-module inspection. The refresh owns deleted-file
64
+ // classification; later snapshots must not erase its removed evidence.
65
+ try {
66
+ diagnosticEvidence = await runtime.refreshOpenDiagnostics(undefined, options.control);
67
+ } catch (error) {
68
+ if (isCodeRequestInterruption(error, options.control)) throw error;
69
+ failureReason = errorMessage(error);
70
+ diagnosticEvidence = readRuntimeEvidence(runtime);
34
71
  }
35
72
 
36
- // Stale-module resync: force-reopen files with "Cannot find module" errors
37
- await resyncStaleModuleFiles(runtime, cwd);
73
+ try {
74
+ const staleResult = await resyncStaleModuleFiles(runtime, cwd, options.control);
75
+ diagnosticEvidence = mergeDiagnosticEvidence(diagnosticEvidence, staleResult.evidence, cwd);
76
+ if (staleResult.completed) failureReason = undefined;
77
+ } catch (error) {
78
+ if (isCodeRequestInterruption(error, options.control)) throw error;
79
+ // Keep evidence from the first refresh when stale inspection fails.
80
+ }
81
+
82
+ return {
83
+ snapshot,
84
+ diagnosticEvidence,
85
+ ...(failureReason ? { failureReason } : {}),
86
+ };
87
+ }
88
+
89
+ /** Sentinel state and diagnostic evidence retained from one workspace pass. */
90
+ export interface WorkspaceLspMaintenanceResult {
91
+ /** The next sentinel snapshot for the session-owned maintenance state. */
92
+ snapshot: Map<string, number>;
93
+ /** Evidence from every diagnostic refresh performed by this pass. */
94
+ diagnosticEvidence: DiagnosticEvidenceSummary;
95
+ /** Failure from the first workspace refresh, when no fresh pass completed. */
96
+ failureReason?: string;
97
+ }
98
+
99
+ /** Sentinel state and stale-file facts from one file-scoped pass. */
100
+ export interface FileLspMaintenanceResult {
101
+ /** The next sentinel snapshot for the session-owned maintenance state. */
102
+ snapshot: Map<string, number>;
103
+ /** Number of stale files that matched the requested file. */
104
+ matchedStaleFileCount: number;
105
+ }
106
+
107
+ /** Refresh sentinel and stale-module state for one exact file without a workspace resync. */
108
+ export async function refreshFileLspMaintenance(options: {
109
+ runtime: WorkspaceLspRuntime;
110
+ cwd: string;
111
+ sentinelSnapshot: Map<string, number>;
112
+ filePath: string;
113
+ control?: CodeRequestControl;
114
+ }): Promise<FileLspMaintenanceResult> {
115
+ const { runtime, cwd, sentinelSnapshot, filePath, control } = options;
116
+ // A cancelled caller stops before any maintenance work starts.
117
+ throwIfCodeRequestInterrupted(control);
118
+ const { snapshot } = await synchronizeSentinels(runtime, cwd, sentinelSnapshot);
119
+ const target = nodePath.resolve(filePath);
120
+ const stale = confirmedOutstandingDiagnostics(runtime, cwd).some(
121
+ (entry) =>
122
+ nodePath.resolve(cwd, entry.file) === target &&
123
+ entry.diagnostics.some((diagnostic) => isLikelyStaleDiagnostic(diagnostic)),
124
+ );
38
125
 
39
- // Two-pass prune/refresh for diagnostics
40
126
  runtime.pruneMissingFiles();
41
- try {
42
- await runtime.refreshOpenDiagnostics();
43
- } catch {
44
- /* best-effort */
127
+ if (stale) {
128
+ runtime.closeFile(target);
129
+ // Race the caller's cancellation so a client start during re-open does
130
+ // not outlive the caller; the underlying runtime keeps its own lifecycle.
131
+ await raceRequestControl(runtime.trackFile(target), control);
45
132
  }
46
133
  runtime.pruneMissingFiles();
134
+ throwIfCodeRequestInterrupted(control);
135
+
136
+ return { snapshot, matchedStaleFileCount: stale ? 1 : 0 };
137
+ }
138
+
139
+ /**
140
+ * Sync the widened workspace snapshot, forward sentinel changes, and track
141
+ * source files newly created since the last pass.
142
+ *
143
+ * The snapshot is widened to every regular file on every pass so later diffs
144
+ * see genuine creations. Only sentinel-file events are forwarded as workspace
145
+ * changes (config semantics unchanged); created source files are tracked only
146
+ * on the workspace-runtime path and only once the snapshot was primed by an
147
+ * earlier pass — the first pass establishes the baseline and cannot tell a
148
+ * just-created file from a long-existing one.
149
+ */
150
+ async function synchronizeSentinels(
151
+ runtime: WorkspaceLspRuntime,
152
+ cwd: string,
153
+ sentinelSnapshot: Map<string, number>,
154
+ options: LspMaintenanceOptions = {},
155
+ ) {
156
+ const primed = sentinelSnapshot.size > 0;
157
+ const state = syncWorkspaceSentinelSnapshot(cwd, sentinelSnapshot, {
158
+ includeSourceFiles: true,
159
+ });
160
+
161
+ invalidateChangedProjectConfigs(state.changes);
162
+ if (state.changes.length > 0) runtime.noteWorkspaceChanges(state.changes);
163
+
164
+ if (options.trackSources && primed) {
165
+ await trackCreatedSourceFiles(runtime, state.sourceChanges, options.scope);
166
+ }
167
+
168
+ return state;
169
+ }
170
+
171
+ /** Invalidate cached tsconfig scope parses only for config files that changed. */
172
+ function invalidateChangedProjectConfigs(changes: readonly FileEvent[]): void {
173
+ for (const change of changes) {
174
+ const filePath = uriToFile(change.uri);
175
+ if (!isProjectConfigFileName(nodePath.basename(filePath))) continue;
176
+ // Change-targeted invalidation: only config files affect tsconfig scope
177
+ // decisions. A created config can also turn previously empty lookups into
178
+ // resolved ones below the new config's directory.
179
+ invalidateTsconfigCacheForConfig(filePath);
180
+ if (change.type === FileChangeType.Created) {
181
+ invalidateTsconfigCacheForConfigDir(nodePath.dirname(filePath));
182
+ }
183
+ }
184
+ }
47
185
 
48
- return snapshot;
186
+ /** Track source files newly created since the last pass, within the scope bound. */
187
+ async function trackCreatedSourceFiles(
188
+ runtime: WorkspaceLspRuntime,
189
+ sourceChanges: readonly FileEvent[],
190
+ scope: string | null | undefined,
191
+ ): Promise<void> {
192
+ for (const change of sourceChanges) {
193
+ if (change.type !== FileChangeType.Created) continue;
194
+ const filePath = uriToFile(change.uri);
195
+ if (scope && !isWithinOrEqual(scope, filePath)) continue;
196
+ // Best-effort: a file no client can serve stays untracked and is
197
+ // simply absent from evidence until a later explicit request.
198
+ await runtime.trackFile(filePath);
199
+ }
49
200
  }
50
201
 
51
- /** Re-open files with stale module-resolution errors. */
52
- async function resyncStaleModuleFiles(runtime: WorkspaceLspRuntime, cwd: string): Promise<void> {
202
+ /** Return only diagnostics whose document evidence is confirmed. */
203
+ function confirmedOutstandingDiagnostics(runtime: WorkspaceLspRuntime, cwd: string) {
53
204
  const outstanding = runtime.getOutstandingDiagnostics(1);
205
+ const confirmedFiles = new Set(
206
+ outstanding.evidence.documents
207
+ .filter((document) => document.status === "confirmed")
208
+ .map((document) => nodePath.resolve(cwd, document.file)),
209
+ );
210
+ return outstanding.entries.filter((entry) =>
211
+ confirmedFiles.has(nodePath.resolve(cwd, entry.file)),
212
+ );
213
+ }
214
+
215
+ /** Re-open files with confirmed stale module-resolution errors. */
216
+ async function resyncStaleModuleFiles(
217
+ runtime: WorkspaceLspRuntime,
218
+ cwd: string,
219
+ control?: CodeRequestControl,
220
+ ): Promise<{ evidence: DiagnosticEvidenceSummary; completed: boolean }> {
54
221
  const staleFiles: string[] = [];
55
222
 
56
- for (const entry of outstanding) {
223
+ for (const entry of confirmedOutstandingDiagnostics(runtime, cwd)) {
57
224
  if (entry.diagnostics.some((d) => isLikelyStaleDiagnostic(d))) {
58
225
  staleFiles.push(entry.file);
59
226
  }
60
227
  }
61
228
 
62
- if (staleFiles.length === 0) return;
229
+ if (staleFiles.length === 0) return { evidence: emptyEvidence(), completed: false };
63
230
 
231
+ const invalidated: Array<{
232
+ file: string;
233
+ status: "unconfirmed" | "failed" | "removed";
234
+ }> = staleFiles.map((file) => ({
235
+ file: nodePath.relative(cwd, nodePath.resolve(cwd, file)),
236
+ status: "unconfirmed",
237
+ }));
64
238
  for (const file of staleFiles) {
65
239
  const filePath = nodePath.resolve(cwd, file);
240
+ const relativeFile = nodePath.relative(cwd, filePath);
66
241
  runtime.closeFile(filePath);
67
- await runtime.trackFile(filePath);
242
+ try {
243
+ if (!(await runtime.trackFile(filePath))) {
244
+ markUnavailable(invalidated, relativeFile, filePath);
245
+ }
246
+ } catch (error) {
247
+ if (isCodeRequestInterruption(error, control)) throw error;
248
+ markUnavailable(invalidated, relativeFile, filePath);
249
+ }
68
250
  }
69
251
 
70
252
  try {
71
- await runtime.refreshOpenDiagnostics({ quietMs: 300, maxWaitMs: 2000 });
253
+ return {
254
+ evidence: mergeDiagnosticEvidence(
255
+ {
256
+ requested: invalidated.length,
257
+ confirmed: 0,
258
+ unconfirmed: invalidated.length,
259
+ failed: 0,
260
+ removed: 0,
261
+ documents: invalidated,
262
+ },
263
+ await runtime.refreshOpenDiagnostics({ quietMs: 300, maxWaitMs: 2000 }, control),
264
+ cwd,
265
+ ),
266
+ completed: true,
267
+ };
268
+ } catch (error) {
269
+ if (isCodeRequestInterruption(error, control)) throw error;
270
+ return {
271
+ evidence: {
272
+ requested: invalidated.length,
273
+ confirmed: 0,
274
+ unconfirmed: invalidated.filter((document) => document.status === "unconfirmed").length,
275
+ failed: invalidated.filter((document) => document.status === "failed").length,
276
+ removed: invalidated.filter((document) => document.status === "removed").length,
277
+ documents: invalidated,
278
+ },
279
+ completed: false,
280
+ };
281
+ }
282
+ }
283
+
284
+ function markUnavailable(
285
+ documents: Array<{ file: string; status: "unconfirmed" | "failed" | "removed" }>,
286
+ file: string,
287
+ filePath: string,
288
+ ): void {
289
+ const entry = documents.find((document) => document.file === file);
290
+ if (entry) entry.status = diagnosticFailureStatus(filePath);
291
+ }
292
+
293
+ function diagnosticFailureStatus(filePath: string): "failed" | "removed" {
294
+ try {
295
+ statSync(filePath);
296
+ return "failed";
297
+ } catch (error) {
298
+ if (
299
+ typeof error === "object" &&
300
+ error !== null &&
301
+ "code" in error &&
302
+ (error.code === "ENOENT" || error.code === "ENOTDIR")
303
+ ) {
304
+ return "removed";
305
+ }
306
+ return "failed";
307
+ }
308
+ }
309
+
310
+ function readRuntimeEvidence(runtime: WorkspaceLspRuntime): DiagnosticEvidenceSummary {
311
+ try {
312
+ return runtime.getWorkspaceDiagnosticSummary().evidence;
72
313
  } catch {
73
- /* best-effort */
314
+ return emptyEvidence();
74
315
  }
75
316
  }
317
+
318
+ function errorMessage(error: unknown): string {
319
+ return error instanceof Error && error.message ? error.message : "Diagnostic refresh failed.";
320
+ }
321
+
322
+ function emptyEvidence(): DiagnosticEvidenceSummary {
323
+ return {
324
+ requested: 0,
325
+ confirmed: 0,
326
+ unconfirmed: 0,
327
+ failed: 0,
328
+ removed: 0,
329
+ documents: [],
330
+ };
331
+ }
@@ -11,7 +11,11 @@ import type {
11
11
  ToolResultEvent,
12
12
  } from "@earendil-works/pi-coding-agent";
13
13
  import { fileToUri } from "@mrclrchtr/supi-core/path";
14
- import { clearTsconfigCache } from "@mrclrchtr/supi-lsp/api";
14
+ import {
15
+ invalidateTsconfigCacheForConfig,
16
+ invalidateTsconfigCacheForConfigDir,
17
+ isProjectConfigFileName,
18
+ } from "@mrclrchtr/supi-lsp/api";
15
19
  import type { LspAdapterState } from "./state.ts";
16
20
 
17
21
  /** Tool names whose successful results should trigger workspace change notifications. */
@@ -37,10 +41,12 @@ export function registerWorkspaceRecoveryHandler(pi: ExtensionAPI, state: LspAda
37
41
  const normalized = filePath.startsWith("@") ? filePath.slice(1) : filePath;
38
42
  const resolved = nodePath.resolve(cwd, normalized);
39
43
 
40
- // Invalidate tsconfig cache when config files change
41
- const ext = nodePath.extname(resolved).toLowerCase();
42
- if (ext === ".json" || ext === ".jsonc") {
43
- clearTsconfigCache();
44
+ // Invalidate the tsconfig scope cache only when a project config itself
45
+ // changed; other .json/.jsonc writes (package.json and friends) do not
46
+ // affect scope decisions and keep their cached parses.
47
+ if (isProjectConfigFileName(nodePath.basename(resolved))) {
48
+ invalidateTsconfigCacheForConfig(resolved);
49
+ invalidateTsconfigCacheForConfigDir(nodePath.dirname(resolved));
44
50
  }
45
51
 
46
52
  runtime.noteWorkspaceChanges([{ uri: fileToUri(resolved), type: 2 }]);
@@ -25,7 +25,7 @@ export interface LspAdapterState {
25
25
  lspActive: boolean;
26
26
  /** Snapshot of workspace sentinel files (package.json, tsconfig, lockfiles) for change detection. */
27
27
  sentinelSnapshot: Map<string, number>;
28
- /** Fired when server statuses or readiness change footer listens for re-renders. */
28
+ /** Fired for runtime lifecycle changes that require a footer render. */
29
29
  stateChanges: EventTarget;
30
30
  }
31
31
 
@@ -40,5 +40,5 @@ export function createLspAdapterState(): LspAdapterState {
40
40
  };
41
41
  }
42
42
 
43
- /** Event type dispatched on {@link LspAdapterState.stateChanges} when server statuses change. */
43
+ /** Event type dispatched on {@link LspAdapterState.stateChanges} for LSP lifecycle changes. */
44
44
  export const LSP_STATE_CHANGE_EVENT = "server-status-changed" as const;
@@ -5,9 +5,11 @@ import { LspRuntimeController, scanWorkspaceSentinels } from "@mrclrchtr/supi-ls
5
5
  import { TreeSitterRuntimeController } from "@mrclrchtr/supi-tree-sitter/api";
6
6
 
7
7
  const HOSTS = Symbol.for("supi-code-intelligence/workspace-provider-hosts");
8
+ const CLOSING_HOSTS = Symbol.for("supi-code-intelligence/closing-workspace-provider-hosts");
8
9
  const SHUTDOWN_GRACE_MS = 2_000;
9
10
 
10
11
  type HostRegistry = Map<string, WorkspaceProviderHost>;
12
+ type ClosingHostRegistry = Map<string, Promise<void>>;
11
13
 
12
14
  /** One acquired reference to a process-shared Workspace provider host. */
13
15
  export interface WorkspaceProviderHostLease {
@@ -23,6 +25,12 @@ function registry(): HostRegistry {
23
25
  return global[HOSTS];
24
26
  }
25
27
 
28
+ function closingRegistry(): ClosingHostRegistry {
29
+ const global = globalThis as typeof globalThis & { [CLOSING_HOSTS]?: ClosingHostRegistry };
30
+ global[CLOSING_HOSTS] ??= new Map();
31
+ return global[CLOSING_HOSTS];
32
+ }
33
+
26
34
  function canonicalWorkspace(cwd: string): string {
27
35
  try {
28
36
  return realpathSync(cwd);
@@ -76,8 +84,16 @@ class WorkspaceProviderHost {
76
84
  released = true;
77
85
  this.#leases--;
78
86
  if (this.#leases === 0) {
79
- registry().delete(this.cwd);
80
- await this.#shutdown();
87
+ await Promise.resolve();
88
+ if (this.#leases !== 0) return;
89
+ const shutdown = this.#shutdown();
90
+ closingRegistry().set(this.cwd, shutdown);
91
+ try {
92
+ await shutdown;
93
+ } finally {
94
+ if (closingRegistry().get(this.cwd) === shutdown) closingRegistry().delete(this.cwd);
95
+ registry().delete(this.cwd);
96
+ }
81
97
  }
82
98
  },
83
99
  };
@@ -101,7 +117,7 @@ class WorkspaceProviderHost {
101
117
  this.#closed = true;
102
118
  await Promise.all([
103
119
  ...(this.#lsp ? [settleWithin(this.#lsp.shutdown())] : []),
104
- ...(this.#tree ? [settleWithin(this.#tree.shutdown())] : []),
120
+ ...(this.#tree ? [this.#tree.shutdown()] : []),
105
121
  ]);
106
122
  this.#lsp = null;
107
123
  this.#tree = null;
@@ -114,6 +130,11 @@ export async function acquireWorkspaceProviderHost(
114
130
  options: { projectTrusted: boolean },
115
131
  ): Promise<WorkspaceProviderHostLease> {
116
132
  const workspace = canonicalWorkspace(cwd);
133
+ for (;;) {
134
+ const closing = closingRegistry().get(workspace);
135
+ if (!closing) break;
136
+ await closing.catch(() => undefined);
137
+ }
117
138
  const hosts = registry();
118
139
  let host = hosts.get(workspace);
119
140
  if (!host) {
@@ -126,4 +147,5 @@ export async function acquireWorkspaceProviderHost(
126
147
  /** Clear process-shared hosts between isolated tests. */
127
148
  export function resetWorkspaceProviderHostsForTests(): void {
128
149
  registry().clear();
150
+ closingRegistry().clear();
129
151
  }
@@ -3,10 +3,8 @@
3
3
  import type { FindMode, FindWorkflowInput } from "../../session/find-types.ts";
4
4
  import type { CodeIntelResult, CodeIntelToolExecCtx } from "../../types/index.ts";
5
5
  import { toWorkflowControl } from "../infra/workflow-control.ts";
6
- import { searchErrorResult } from "../result/errors.ts";
7
- import { assembleFindWorkflowResult } from "../result/find.ts";
8
6
  import type { CodeFindAstKind } from "./ast-kinds.ts";
9
- import { renderFindResult } from "./render.ts";
7
+ import { finishFindResult } from "./result.ts";
10
8
 
11
9
  export interface CodeFindToolParams {
12
10
  query: string;
@@ -21,17 +19,5 @@ export async function executeFindTool(
21
19
  ctx: CodeIntelToolExecCtx,
22
20
  ): Promise<CodeIntelResult> {
23
21
  const outcome = await ctx.session.find(params as FindWorkflowInput, toWorkflowControl(ctx));
24
- if (outcome.kind === "unavailable") throw new Error(outcome.reason);
25
- if (outcome.kind === "invalid-input") {
26
- return searchErrorResult(`**Error:** ${outcome.message}`, {
27
- scope: Array.isArray(params?.scope) ? params.scope.join(", ") : null,
28
- nextQueries: ["Fix the search input and retry"],
29
- });
30
- }
31
-
32
- const assembly = assembleFindWorkflowResult(outcome);
33
- return {
34
- content: renderFindResult(assembly),
35
- details: { type: "search", data: assembly.details },
36
- };
22
+ return finishFindResult(outcome, params.scope);
37
23
  }
@@ -0,0 +1,8 @@
1
+ export const toolDescription =
2
+ 'Search source shape with mode:"ast" or LSP workspace symbols with mode:"semantic". Modes never silently fall back. Unscoped AST scans cover visible supported files, skipping hidden and generated/dependency directories.';
3
+
4
+ export const promptSnippet = "explicit structural or semantic code search";
5
+
6
+ export const promptGuidelines = [
7
+ "Use code_find for structural or semantic search evidence; use PI grep for literal/regex source search and code_graph references for symbol-identity relationships.",
8
+ ];
@@ -4,8 +4,8 @@ import {
4
4
  type EvidenceListMetadata,
5
5
  renderEvidenceListMetadataDisclosure,
6
6
  } from "../../analysis/evidence.ts";
7
- import type { FindResultAssembly } from "../result/find.ts";
8
7
  import { renderStructuredEmptyState, renderStructuredMatches } from "./markdown.ts";
8
+ import type { FindResultAssembly } from "./result.ts";
9
9
 
10
10
  export function renderFindResult(assembly: FindResultAssembly): string {
11
11
  const { outcome } = assembly;
@@ -38,9 +38,7 @@ function renderSemantic(assembly: FindResultAssembly, evidence: EvidenceListMeta
38
38
  const { outcome } = assembly;
39
39
  if (outcome.data.kind !== "semantic") return "";
40
40
  const symbols = outcome.data.symbols;
41
- if (symbols.length === 0) {
42
- return `**Semantic search** — \`${outcome.query}\`\n\nNo semantic results found in \`${outcome.scopeLabel}\`.`;
43
- }
41
+ if (symbols.length === 0) return renderEmptySemantic(assembly, evidence);
44
42
 
45
43
  const lines = [
46
44
  `**Semantic search** — \`${outcome.query}\` (${symbols.length} symbol${symbols.length === 1 ? "" : "s"} found)`,
@@ -55,3 +53,22 @@ function renderSemantic(assembly: FindResultAssembly, evidence: EvidenceListMeta
55
53
  if (disclosure) lines.push(disclosure);
56
54
  return lines.join("\n");
57
55
  }
56
+
57
+ function renderEmptySemantic(assembly: FindResultAssembly, evidence: EvidenceListMetadata): string {
58
+ const { outcome } = assembly;
59
+ const lines = [
60
+ `**Semantic search** — \`${outcome.query}\``,
61
+ "",
62
+ evidence.partialReason
63
+ ? `No LSP workspace-symbol results were collected in \`${outcome.scopeLabel}\`.`
64
+ : `No LSP workspace-symbol results found in \`${outcome.scopeLabel}\`.`,
65
+ ];
66
+ const disclosure = renderEvidenceListMetadataDisclosure(evidence);
67
+ if (disclosure) lines.push("", disclosure);
68
+ lines.push("", "Document-level semantic symbols can differ from the workspace index.");
69
+ const fileQuery = assembly.assembled.actions.find(
70
+ (action) => action.kind === "query" && action.instruction.startsWith("If you know the file"),
71
+ );
72
+ if (fileQuery?.kind === "query") lines.push(fileQuery.instruction);
73
+ return lines.join("\n");
74
+ }