@mrclrchtr/supi-code-intelligence 1.16.0 → 2.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 (348) hide show
  1. package/README.md +373 -63
  2. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  9. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  10. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  11. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  12. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  13. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  14. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  15. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
  16. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
  17. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
  18. package/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
  19. package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
  20. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
  21. package/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
  22. package/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  23. package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  24. package/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  25. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  26. package/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  27. package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  28. package/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  29. package/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  30. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  31. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  32. package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  33. package/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  34. package/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  35. package/node_modules/@mrclrchtr/supi-lsp/README.md +44 -56
  36. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  37. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  38. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  40. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  42. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  44. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  45. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  46. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  47. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  49. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
  50. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
  51. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
  52. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
  53. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
  54. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
  55. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
  56. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  57. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  58. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  59. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  60. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  61. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  62. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  63. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  64. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  65. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  66. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  67. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  68. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  69. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.diagnostic.d.ts +21 -2
  70. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.diagnostic.js +1 -0
  71. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.inlineValue.d.ts +2 -1
  72. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/package.json +1 -1
  73. package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -31
  74. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +27 -0
  75. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-refresh.ts +5 -0
  76. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +246 -1
  77. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +4 -0
  78. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +3 -0
  79. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +4 -2
  80. package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +102 -0
  81. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +59 -0
  82. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +4 -0
  83. package/node_modules/@mrclrchtr/supi-lsp/src/index.ts +11 -0
  84. package/node_modules/@mrclrchtr/supi-lsp/src/manager/capability-index.ts +1 -1
  85. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +2 -1
  86. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +115 -26
  87. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +177 -134
  88. package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +214 -0
  89. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +320 -0
  90. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +26 -5
  91. package/node_modules/@mrclrchtr/supi-lsp/src/session/service-registry.ts +81 -4
  92. package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +2 -0
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +18 -37
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  96. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  97. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  98. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  99. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  100. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  101. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  102. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  103. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  104. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  105. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  106. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  107. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
  108. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
  109. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
  110. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
  111. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
  112. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
  113. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
  114. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  115. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  116. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  117. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  118. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  119. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  120. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  121. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  122. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  123. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  124. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  125. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  126. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  127. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/LICENSE +21 -0
  128. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +265 -0
  129. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
  130. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
  131. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
  132. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
  133. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  134. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
  135. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +100 -0
  136. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
  137. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
  138. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
  139. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
  140. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
  141. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
  142. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
  143. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
  144. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  145. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
  146. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -12
  147. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +29 -0
  148. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +28 -0
  149. package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -0
  150. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +14 -2
  151. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +157 -0
  152. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +8 -1
  153. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +86 -0
  154. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +35 -16
  155. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/structure.ts +1 -0
  156. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +10 -0
  157. package/package.json +14 -9
  158. package/src/{model.ts → analysis/architecture/discovery.ts} +15 -72
  159. package/src/analysis/architecture/model.ts +76 -0
  160. package/src/analysis/brief/directory.ts +324 -0
  161. package/src/analysis/brief/enrich.ts +157 -0
  162. package/src/analysis/brief/file.ts +84 -0
  163. package/src/analysis/brief/inventory.ts +177 -0
  164. package/src/analysis/brief/main.ts +176 -0
  165. package/src/analysis/brief/models.ts +75 -0
  166. package/src/{brief.ts → analysis/brief/public-brief.ts} +21 -26
  167. package/src/analysis/brief/summarize.ts +208 -0
  168. package/src/analysis/brief/types.ts +27 -0
  169. package/src/{use-case/generate-brief.ts → analysis/brief/use-case.ts} +67 -144
  170. package/src/analysis/coverage/coverage-warnings.ts +224 -0
  171. package/src/analysis/evidence.ts +91 -0
  172. package/src/{semantic-action-helpers.ts → analysis/helpers.ts} +4 -4
  173. package/src/analysis/provider.ts +186 -0
  174. package/src/analysis/read-next.ts +126 -0
  175. package/src/analysis/readiness.ts +73 -0
  176. package/src/{refactor → analysis/refactor}/safety.ts +1 -1
  177. package/src/{use-case/support/semantic-references.ts → analysis/references/semantic-refs.ts} +58 -26
  178. package/src/analysis/search/pattern.ts +345 -0
  179. package/src/analysis/search/ripgrep.ts +473 -0
  180. package/src/{git-context.ts → analysis/signals/git.ts} +20 -7
  181. package/src/{prioritization-signals.ts → analysis/signals/project.ts} +33 -19
  182. package/src/analysis/target/anchored.ts +349 -0
  183. package/src/analysis/target/bridge.ts +152 -0
  184. package/src/analysis/target/file-resolution.ts +125 -0
  185. package/src/{targeting/resolve-file.ts → analysis/target/file.ts} +40 -20
  186. package/src/{planner → analysis/target}/planner.ts +30 -20
  187. package/src/{targeting → analysis/target}/query.ts +17 -3
  188. package/src/analysis/target/resolve-common.ts +115 -0
  189. package/src/analysis/target/service.ts +229 -0
  190. package/src/analysis/target/symbol.ts +196 -0
  191. package/src/{targeting → analysis/target}/types.ts +17 -1
  192. package/src/analysis/tests/likely-tests.ts +174 -0
  193. package/src/analysis/tests/test-discovery.ts +540 -0
  194. package/src/analysis/tests/vitest.ts +87 -0
  195. package/src/api.ts +34 -41
  196. package/src/app/app.ts +125 -0
  197. package/src/app/manager.ts +65 -0
  198. package/src/extension.ts +132 -1
  199. package/src/index.ts +1 -57
  200. package/src/session/refactor-plans.ts +82 -0
  201. package/src/session/session.ts +300 -0
  202. package/src/session/target-store.ts +293 -0
  203. package/src/substrate/lsp/diagnostic-injection.ts +218 -0
  204. package/src/substrate/lsp/lifecycle.ts +66 -0
  205. package/src/substrate/lsp/overrides.ts +132 -0
  206. package/src/substrate/lsp/recovery.ts +55 -0
  207. package/src/substrate/lsp/settings.ts +286 -0
  208. package/src/substrate/lsp/state.ts +46 -0
  209. package/src/substrate/lsp/utils.ts +17 -0
  210. package/src/substrate/tree-sitter/lifecycle.ts +52 -0
  211. package/src/tool/find/execute.ts +118 -0
  212. package/src/{presentation/markdown/pattern.ts → tool/find/markdown.ts} +102 -53
  213. package/src/{use-case/generate-pattern.ts → tool/find/orchestrate.ts} +67 -33
  214. package/src/tool/find/semantic-search.ts +114 -0
  215. package/src/tool/find/tui.ts +39 -0
  216. package/src/tool/graph/callees.ts +87 -0
  217. package/src/tool/graph/calls-md.ts +54 -0
  218. package/src/tool/graph/collect-relation.ts +371 -0
  219. package/src/tool/graph/execute.ts +95 -0
  220. package/src/tool/graph/implementations-md.ts +79 -0
  221. package/src/tool/graph/implementations.ts +76 -0
  222. package/src/tool/graph/markdown-base.ts +243 -0
  223. package/src/tool/graph/orchestrate.ts +285 -0
  224. package/src/tool/graph/references-md.ts +34 -0
  225. package/src/tool/graph/references.ts +81 -0
  226. package/src/tool/graph/tui.ts +120 -0
  227. package/src/tool/graph/types.ts +97 -0
  228. package/src/tool/guidance.ts +96 -49
  229. package/src/tool/health/execute.ts +76 -0
  230. package/src/tool/health/markdown.ts +280 -0
  231. package/src/tool/health/orchestrate.ts +159 -0
  232. package/src/tool/health/sections/diagnostics.ts +182 -0
  233. package/src/tool/health/sections/recovery.ts +89 -0
  234. package/src/tool/health/sections/signals.ts +89 -0
  235. package/src/tool/health/tui.ts +137 -0
  236. package/src/tool/impact/analysis.ts +247 -0
  237. package/src/tool/impact/change-set-impact.ts +151 -0
  238. package/src/tool/impact/execute.ts +98 -0
  239. package/src/tool/impact/file-level-impact.ts +81 -0
  240. package/src/tool/impact/markdown.ts +283 -0
  241. package/src/tool/impact/orchestrate.ts +97 -0
  242. package/src/tool/impact/result.ts +21 -0
  243. package/src/tool/impact/single-impact.ts +89 -0
  244. package/src/tool/impact/tui.ts +39 -0
  245. package/src/tool/impact/types.ts +34 -0
  246. package/src/tool/infra/cross-field.ts +335 -0
  247. package/src/tool/infra/error-results.ts +199 -0
  248. package/src/tool/infra/pipeline.ts +311 -0
  249. package/src/tool/infra/progress.ts +21 -0
  250. package/src/tool/infra/readiness-message.ts +5 -0
  251. package/src/tool/infra/truncate.ts +49 -0
  252. package/src/tool/inspect/execute.ts +76 -0
  253. package/src/tool/inspect/markdown.ts +169 -0
  254. package/src/tool/inspect/orchestrate.ts +156 -0
  255. package/src/tool/inspect/tui.ts +36 -0
  256. package/src/tool/orientation/deps.ts +63 -0
  257. package/src/tool/orientation/execute.ts +50 -0
  258. package/src/tool/orientation/markdown.ts +35 -0
  259. package/src/tool/orientation/mode.ts +88 -0
  260. package/src/tool/orientation/orchestrate.ts +478 -0
  261. package/src/tool/orientation/precise-target.ts +257 -0
  262. package/src/tool/orientation/tui.ts +131 -0
  263. package/src/{refactor/apply-workspace-edit.ts → tool/refactor-apply/apply.ts} +39 -16
  264. package/src/tool/refactor-apply/execute.ts +20 -0
  265. package/src/tool/refactor-apply/markdown.ts +118 -0
  266. package/src/tool/refactor-apply/orchestrate.ts +89 -0
  267. package/src/tool/refactor-apply/tui.ts +51 -0
  268. package/src/tool/refactor-plan/execute.ts +73 -0
  269. package/src/tool/refactor-plan/markdown.ts +118 -0
  270. package/src/tool/refactor-plan/orchestrate.ts +363 -0
  271. package/src/tool/refactor-plan/tui.ts +42 -0
  272. package/src/tool/register.ts +123 -0
  273. package/src/tool/resolve/execute.ts +82 -0
  274. package/src/tool/resolve/markdown.ts +150 -0
  275. package/src/tool/resolve/orchestrate.ts +134 -0
  276. package/src/tool/resolve/tui.ts +40 -0
  277. package/src/tool/schemas.ts +337 -0
  278. package/src/tool/specs.ts +257 -0
  279. package/src/types/details.ts +188 -0
  280. package/src/types/execution.ts +60 -0
  281. package/src/types/index.ts +28 -0
  282. package/src/{intent/types.ts → types/tool-names.ts} +15 -14
  283. package/src/ui/footer.ts +52 -0
  284. package/src/ui/markdown/brief.ts +218 -0
  285. package/src/ui/markdown/gather.ts +177 -0
  286. package/src/{use-case/build-overview.ts → ui/markdown/overview-data.ts} +2 -2
  287. package/src/{presentation → ui}/markdown/overview.ts +2 -2
  288. package/src/ui/markdown/types.ts +80 -0
  289. package/{node_modules/@mrclrchtr/supi-lsp/src/ui/renderer.ts → src/ui/message-renderer.ts} +11 -10
  290. package/src/ui/status-command.ts +260 -0
  291. package/src/ui/status-overlay.ts +500 -0
  292. package/src/ui/tui/common.ts +220 -0
  293. package/src/ui/tui/index.ts +37 -0
  294. package/node_modules/@mrclrchtr/supi-lsp/src/extension.ts +0 -1
  295. package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +0 -373
  296. package/node_modules/@mrclrchtr/supi-lsp/src/handlers/diagnostic-injection.ts +0 -182
  297. package/node_modules/@mrclrchtr/supi-lsp/src/handlers/session-lifecycle.ts +0 -149
  298. package/node_modules/@mrclrchtr/supi-lsp/src/handlers/status-command.ts +0 -38
  299. package/node_modules/@mrclrchtr/supi-lsp/src/handlers/workspace-recovery.ts +0 -92
  300. package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +0 -37
  301. package/node_modules/@mrclrchtr/supi-lsp/src/session/lsp-state.ts +0 -93
  302. package/node_modules/@mrclrchtr/supi-lsp/src/session/settings-registration.ts +0 -296
  303. package/node_modules/@mrclrchtr/supi-lsp/src/session/tree-persist.ts +0 -75
  304. package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +0 -69
  305. package/node_modules/@mrclrchtr/supi-lsp/src/tool/names.ts +0 -27
  306. package/node_modules/@mrclrchtr/supi-lsp/src/tool/overrides.ts +0 -204
  307. package/node_modules/@mrclrchtr/supi-lsp/src/tool/register-tools.ts +0 -69
  308. package/node_modules/@mrclrchtr/supi-lsp/src/tool/service-actions.ts +0 -399
  309. package/node_modules/@mrclrchtr/supi-lsp/src/tool/tool-specs.ts +0 -248
  310. package/node_modules/@mrclrchtr/supi-lsp/src/ui/ui.ts +0 -360
  311. package/node_modules/@mrclrchtr/supi-lsp/src/workspace-change.ts +0 -40
  312. package/node_modules/@mrclrchtr/supi-tree-sitter/src/extension.ts +0 -1
  313. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/formatting.ts +0 -104
  314. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/guidance.ts +0 -35
  315. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/handlers.ts +0 -196
  316. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/register-tools.ts +0 -107
  317. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/tool-specs.ts +0 -117
  318. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tree-sitter.ts +0 -47
  319. package/src/brief-focused.ts +0 -563
  320. package/src/code-intelligence.ts +0 -70
  321. package/src/pattern-structured.ts +0 -194
  322. package/src/presentation/markdown/affected.ts +0 -170
  323. package/src/presentation/markdown/brief.ts +0 -174
  324. package/src/presentation/markdown/map.ts +0 -54
  325. package/src/presentation/markdown/refactor.ts +0 -27
  326. package/src/presentation/markdown/relations.ts +0 -121
  327. package/src/resolve-target.ts +0 -130
  328. package/src/search-helpers.ts +0 -303
  329. package/src/substrates/types.ts +0 -30
  330. package/src/target-resolution.ts +0 -122
  331. package/src/targeting/resolve-anchored.ts +0 -80
  332. package/src/targeting/resolve-symbol.ts +0 -110
  333. package/src/tool/execute-affected.ts +0 -71
  334. package/src/tool/execute-brief.ts +0 -59
  335. package/src/tool/execute-map.ts +0 -35
  336. package/src/tool/execute-pattern.ts +0 -29
  337. package/src/tool/execute-refactor.ts +0 -101
  338. package/src/tool/execute-relations.ts +0 -79
  339. package/src/tool/register-tools.ts +0 -32
  340. package/src/tool/tool-specs.ts +0 -190
  341. package/src/tool/validation.ts +0 -43
  342. package/src/types.ts +0 -169
  343. package/src/use-case/generate-affected.ts +0 -297
  344. package/src/use-case/generate-map.ts +0 -176
  345. package/src/use-case/generate-relations.ts +0 -438
  346. package/src/use-case/types.ts +0 -43
  347. package/src/workspace/request-context.ts +0 -139
  348. /package/src/{query-params.ts → tool/params.ts} +0 -0
@@ -0,0 +1,540 @@
1
+ // biome-ignore-all lint/style/noExcessiveLinesPerFile: shared test discovery and presentation helpers stay together to preserve one contract
2
+ /**
3
+ * Shared test-file discovery helpers.
4
+ *
5
+ * Used by `code_graph` and `code_impact` to find companion
6
+ * test files and extract recognizable test labels.
7
+ *
8
+ * The discovery uses two strategies:
9
+ * 1. **Semantic reference/import evidence** — files that import the source and match test patterns.
10
+ * 2. **Deterministic path conventions** — package-layout mirrors, same-directory companions.
11
+ *
12
+ * Discovery provenance describes only how test files were found. Test-label
13
+ * extraction is tracked separately so callers do not overload provenance with
14
+ * outline/provider availability claims.
15
+ */
16
+
17
+ import { existsSync, readFileSync } from "node:fs";
18
+ import * as path from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ import type {
21
+ CodePosition,
22
+ SemanticProvider,
23
+ StructuralProvider,
24
+ } from "@mrclrchtr/supi-code-runtime/api";
25
+
26
+ // ── Public interface ─────────────────────────────────────────────────
27
+
28
+ export interface DiscoverTestFilesOptions {
29
+ /** Semantic provider references method. When absent, no reference-based discovery. */
30
+ references?: SemanticProvider["references"];
31
+ /** Structural provider outline method. When present, test labels are extracted. */
32
+ outline?: StructuralProvider["outline"];
33
+ /** Current working directory used for package-root detection and relative display. */
34
+ cwd?: string;
35
+ /** Maximum number of test files to return (default: 8). */
36
+ cap?: number;
37
+ /** LSP position to look up references at (default: file start). */
38
+ position?: CodePosition;
39
+ }
40
+
41
+ /** Label extraction status for one discovered companion test file. */
42
+ export type TestLabelExtractionStatus = "recognized" | "none-recognized" | "unavailable";
43
+
44
+ export interface DiscoveredTestFile {
45
+ /** Absolute path to the discovered test file. */
46
+ absPath: string;
47
+ /** How this file was discovered. */
48
+ provenance: "semantic reference" | "companion file" | "package layout";
49
+ /** Recognized test labels extracted from outline when available. */
50
+ testNames: string[];
51
+ /** Extraction status separate from file-discovery provenance. */
52
+ labelStatus: TestLabelExtractionStatus;
53
+ }
54
+
55
+ /** Compact per-file metadata shared in tool details. */
56
+ export interface TestSurfaceFile {
57
+ /** Workspace-relative test file path when cwd is known, else absolute path. */
58
+ file: string;
59
+ /** Ranked extracted labels shown to users/consumers. */
60
+ labels: string[];
61
+ /** Whether labels were recognized, absent, or unavailable. */
62
+ labelStatus: TestLabelExtractionStatus;
63
+ }
64
+
65
+ /** Small shared tests metadata shape for tool details. */
66
+ export interface TestSurfaceDetails {
67
+ /** Whether companion tests were found, empty, or unavailable. */
68
+ status: TestDiscoveryKind;
69
+ /** Discovery provenance only; omitted when unavailable. */
70
+ provenance?: TestDiscoveryProvenance;
71
+ /** Per-file extracted labels and status. */
72
+ files: TestSurfaceFile[];
73
+ }
74
+
75
+ /** Evidence source for test discovery. */
76
+ export type TestDiscoveryProvenance = "semantic+conventions" | "conventions-only";
77
+
78
+ export type TestDiscoveryKind = "found" | "empty" | "unavailable";
79
+
80
+ export type TestDiscoveryReason = "no-semantic-or-structural-provider" | "no-companion-test-files";
81
+
82
+ /** Result of a test discovery call, with files and overall provenance. */
83
+ export interface DiscoverTestFilesResult {
84
+ /** Normalized outcome for callers: found, empty, or unavailable. */
85
+ kind: TestDiscoveryKind;
86
+ /** Optional normalized reason for empty or unavailable outcomes. */
87
+ reason?: TestDiscoveryReason;
88
+ /** Discovered test files, deduplicated, sorted, and capped. */
89
+ files: DiscoveredTestFile[];
90
+ /**
91
+ * Discovery provenance.
92
+ * - `semantic+conventions`: semantic reference evidence contributed.
93
+ * - `conventions-only`: only deterministic path/layout conventions contributed.
94
+ */
95
+ provenance: TestDiscoveryProvenance;
96
+ /** Whether a semantic references provider was available (non-null). */
97
+ semanticsAvailable: boolean;
98
+ /** Whether a structural outline provider was available (non-null). */
99
+ structuralAvailable: boolean;
100
+ }
101
+
102
+ interface ExtractedTestLabels {
103
+ names: string[];
104
+ status: TestLabelExtractionStatus;
105
+ }
106
+
107
+ /** Read label data for one known test file without assigning discovery provenance. */
108
+ export async function describeTestFile(
109
+ testFile: string,
110
+ options: { outline?: StructuralProvider["outline"]; cwd: string },
111
+ ): Promise<Pick<DiscoveredTestFile, "absPath" | "testNames" | "labelStatus">> {
112
+ const extracted = await extractTestLabels(testFile, options.outline, options.cwd);
113
+ return {
114
+ absPath: testFile,
115
+ testNames: extracted.names,
116
+ labelStatus: extracted.status,
117
+ };
118
+ }
119
+
120
+ /** Build a small shared tests metadata shape for structured tool details. */
121
+ export function buildTestSurfaceDetails(
122
+ input: {
123
+ status: TestDiscoveryKind;
124
+ provenance?: TestDiscoveryProvenance;
125
+ files: Array<Pick<DiscoveredTestFile, "absPath" | "testNames" | "labelStatus">>;
126
+ },
127
+ cwd: string,
128
+ labelLimit = 8,
129
+ ): TestSurfaceDetails {
130
+ return {
131
+ status: input.status,
132
+ provenance: input.status === "unavailable" ? undefined : input.provenance,
133
+ files: input.files.map((file) => ({
134
+ file: path.relative(cwd, file.absPath) || file.absPath,
135
+ labels: rankTestLabels(file.testNames, labelLimit),
136
+ labelStatus: file.labelStatus,
137
+ })),
138
+ };
139
+ }
140
+
141
+ /** Render ranked labels for markdown output, or an honest placeholder when none were recognized. */
142
+ export function renderRankedTestLabelsForMarkdown(names: string[], limit: number): string[] {
143
+ const ranked = rankTestLabels(names, limit);
144
+ if (ranked.length === 0) return [" _(no recognized test blocks)_"];
145
+ return ranked.map((name) => ` - \`${name}\``);
146
+ }
147
+
148
+ /**
149
+ * Discover companion test files for a source file using both semantic evidence
150
+ * and deterministic path conventions.
151
+ *
152
+ * Returns discovered test files with metadata, deduplicated, sorted, and capped.
153
+ */
154
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: reference-based + convention-based discovery orchestration stays together
155
+ export async function discoverTestFilesForSource(
156
+ sourceAbs: string,
157
+ options: DiscoverTestFilesOptions,
158
+ ): Promise<DiscoverTestFilesResult> {
159
+ sourceAbs = path.resolve(sourceAbs);
160
+
161
+ const cap = options.cap ?? 8;
162
+ const seen = new Set<string>();
163
+ const results: DiscoveredTestFile[] = [];
164
+ const semanticsAvailable = options.references != null;
165
+ const structuralAvailable = options.outline != null;
166
+
167
+ let semanticContributed = false;
168
+ if (options.references) {
169
+ const refs = await options.references(sourceAbs, options.position ?? { line: 0, character: 0 });
170
+ if (refs && refs.length > 0) {
171
+ const refFiles = [
172
+ ...new Set(
173
+ refs
174
+ .filter((ref) => isTestFilePath(ref.uri))
175
+ .map((ref) => (ref.uri.startsWith("file://") ? fileURLToPath(ref.uri) : ref.uri)),
176
+ ),
177
+ ];
178
+
179
+ if (refFiles.length > 0) {
180
+ semanticContributed = true;
181
+ }
182
+
183
+ for (const absPath of refFiles) {
184
+ if (seen.has(absPath)) continue;
185
+ seen.add(absPath);
186
+ const described = await describeTestFile(absPath, {
187
+ outline: options.outline,
188
+ cwd: options.cwd ?? path.dirname(sourceAbs),
189
+ });
190
+ results.push({
191
+ ...described,
192
+ provenance: "semantic reference",
193
+ });
194
+ }
195
+ }
196
+ }
197
+
198
+ const conventionFiles = findConventionTestFiles(sourceAbs, options.cwd);
199
+ for (const absPath of conventionFiles) {
200
+ if (seen.has(absPath)) continue;
201
+ seen.add(absPath);
202
+ const described = await describeTestFile(absPath, {
203
+ outline: options.outline,
204
+ cwd: options.cwd ?? path.dirname(sourceAbs),
205
+ });
206
+ results.push({
207
+ ...described,
208
+ provenance: "companion file",
209
+ });
210
+ }
211
+
212
+ results.sort((a, b) => a.absPath.localeCompare(b.absPath));
213
+ const files = results.slice(0, cap);
214
+ const provenance: TestDiscoveryProvenance = semanticContributed
215
+ ? "semantic+conventions"
216
+ : "conventions-only";
217
+
218
+ if (files.length === 0) {
219
+ if (!semanticsAvailable && !structuralAvailable) {
220
+ return {
221
+ kind: "unavailable",
222
+ reason: "no-semantic-or-structural-provider",
223
+ files,
224
+ provenance,
225
+ semanticsAvailable,
226
+ structuralAvailable,
227
+ };
228
+ }
229
+
230
+ return {
231
+ kind: "empty",
232
+ reason: "no-companion-test-files",
233
+ files,
234
+ provenance,
235
+ semanticsAvailable,
236
+ structuralAvailable,
237
+ };
238
+ }
239
+
240
+ return {
241
+ kind: "found",
242
+ files,
243
+ provenance,
244
+ semanticsAvailable,
245
+ structuralAvailable,
246
+ };
247
+ }
248
+
249
+ // ── Internal helpers ─────────────────────────────────────────────────
250
+
251
+ /**
252
+ * Apply deterministic path conventions to discover test files for a source.
253
+ *
254
+ * Checks:
255
+ * 1. Same-directory: `./basename.test.ext`, `./basename.spec.ext`
256
+ * 2. Same-directory test dirs: `./__tests__/`, `./tests/`, `./spec/`, `./__spec__/`
257
+ * each with `basename.test.ext` and `basename.spec.ext`
258
+ * 3. Package-level mirrors: `<pkg>/<test-layout>/<unit|integration>/<src-relative>`
259
+ * using both `.test.ext` and `.spec.ext` variants.
260
+ *
261
+ * Does NOT match files whose bare stem contains `test` or `spec` without boundary boundaries
262
+ * (e.g. `contest.ts`, `testing.ts`, `tool-specs.ts` are excluded).
263
+ */
264
+ const TEST_LAYOUT_PREFIXES = [
265
+ "__tests__/unit",
266
+ "__tests__/integration",
267
+ "tests/unit",
268
+ "tests/integration",
269
+ "spec/unit",
270
+ "spec/integration",
271
+ "__spec__/unit",
272
+ "__spec__/integration",
273
+ ] as const;
274
+ const TEST_FILE_SUFFIXES = [".test", ".spec"] as const;
275
+
276
+ function findConventionTestFiles(sourceAbs: string, cwd?: string): string[] {
277
+ const results: string[] = [];
278
+ const seen = new Set<string>();
279
+
280
+ const dir = path.dirname(sourceAbs);
281
+ const ext = path.extname(sourceAbs);
282
+ const basename = path.basename(sourceAbs, ext);
283
+
284
+ // Guard: skip false-positive source stems that look like tests but aren't.
285
+ if (/^contest$/i.test(basename) || /^testing$/i.test(basename) || /tool-specs$/i.test(basename)) {
286
+ // Source itself is not a test; still permit companion discovery.
287
+ }
288
+
289
+ for (const suffix of TEST_FILE_SUFFIXES) {
290
+ addExistingTestCandidate(results, seen, path.join(dir, `${basename}${suffix}${ext}`));
291
+ }
292
+
293
+ for (const suffix of TEST_FILE_SUFFIXES) {
294
+ const companion = `${basename}${suffix}${ext}`;
295
+ addExistingTestCandidate(results, seen, path.join(dir, "__tests__", companion));
296
+ addExistingTestCandidate(results, seen, path.join(dir, "tests", companion));
297
+ addExistingTestCandidate(results, seen, path.join(dir, "spec", companion));
298
+ addExistingTestCandidate(results, seen, path.join(dir, "__spec__", companion));
299
+ }
300
+
301
+ if (cwd) {
302
+ const pkgRoot = findNearestPackageRoot(dir, cwd);
303
+ if (pkgRoot) {
304
+ addPackageMirrorCandidates(results, seen, { pkgRoot, sourceAbs, ext });
305
+ addBoundedToolCandidates(results, seen, { pkgRoot, dir, basename, ext });
306
+ }
307
+ }
308
+
309
+ return results;
310
+ }
311
+
312
+ function addExistingTestCandidate(results: string[], seen: Set<string>, candidate: string): void {
313
+ if (seen.has(candidate) || !existsSync(candidate)) return;
314
+ seen.add(candidate);
315
+ results.push(candidate);
316
+ }
317
+
318
+ function addPackageMirrorCandidates(
319
+ results: string[],
320
+ seen: Set<string>,
321
+ options: { pkgRoot: string; sourceAbs: string; ext: string },
322
+ ): void {
323
+ const { pkgRoot, sourceAbs, ext } = options;
324
+ const relFromPkg = path.relative(pkgRoot, sourceAbs);
325
+
326
+ for (const testPrefix of TEST_LAYOUT_PREFIXES) {
327
+ let mirroredRel = relFromPkg;
328
+ for (const srcPrefix of ["src/", "lib/", "source/"]) {
329
+ if (mirroredRel.startsWith(srcPrefix)) {
330
+ mirroredRel = mirroredRel.slice(srcPrefix.length);
331
+ break;
332
+ }
333
+ }
334
+
335
+ for (const suffix of TEST_FILE_SUFFIXES) {
336
+ const mirroredName = `${path.basename(mirroredRel, ext)}${suffix}${ext}`;
337
+ const mirroredDir = path.dirname(mirroredRel);
338
+ addExistingTestCandidate(
339
+ results,
340
+ seen,
341
+ path.join(pkgRoot, testPrefix, mirroredDir, mirroredName),
342
+ );
343
+ }
344
+ }
345
+ }
346
+
347
+ function addBoundedToolCandidates(
348
+ results: string[],
349
+ seen: Set<string>,
350
+ options: { pkgRoot: string; dir: string; basename: string; ext: string },
351
+ ): void {
352
+ const { pkgRoot, dir, basename, ext } = options;
353
+ const relDirFromPkg = path.relative(pkgRoot, dir).replaceAll("\\", "/");
354
+
355
+ // Match flat tool layouts: src/tool/execute-<name>.ts or legacy src/tools/execute-<name>.ts.
356
+ const toolSrcMatch = basename.match(/^execute-(.+)$/);
357
+ // Match feature layouts: src/tool/<name>/execute.ts or legacy src/tools/<name>/execute.ts.
358
+ const featureDirMatch = relDirFromPkg.match(/^src\/tools?\/([^/]+)$/);
359
+ const featureSrcMatch = featureDirMatch && basename === "execute";
360
+
361
+ let toolName: string | null = null;
362
+ if (toolSrcMatch && (relDirFromPkg === "src/tool" || relDirFromPkg === "src/tools")) {
363
+ toolName = toolSrcMatch[1];
364
+ } else if (featureSrcMatch && featureDirMatch) {
365
+ toolName = featureDirMatch[1];
366
+ }
367
+ if (!toolName || toolName.length === 0) return;
368
+
369
+ const boundedStems = [`code-${toolName}-tool`, `${toolName}-tool`, `execute-${toolName}`];
370
+ for (const testPrefix of TEST_LAYOUT_PREFIXES) {
371
+ for (const stem of boundedStems) {
372
+ for (const suffix of TEST_FILE_SUFFIXES) {
373
+ addExistingTestCandidate(
374
+ results,
375
+ seen,
376
+ path.join(pkgRoot, testPrefix, `${stem}${suffix}${ext}`),
377
+ );
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Walk up from `startDir` until we find a directory with a `package.json`,
385
+ * then return that directory path. Returns null if nothing found up to the
386
+ * filesystem root or `cwd` if cwd itself has a package.json.
387
+ */
388
+ function findNearestPackageRoot(startDir: string, cwd: string): string | null {
389
+ if (existsSync(path.join(cwd, "package.json"))) {
390
+ let current = startDir;
391
+ while (current.startsWith(cwd)) {
392
+ if (existsSync(path.join(current, "package.json"))) {
393
+ return current;
394
+ }
395
+ const parent = path.dirname(current);
396
+ if (parent === current) break;
397
+ current = parent;
398
+ }
399
+ }
400
+
401
+ let current = startDir;
402
+ for (;;) {
403
+ if (existsSync(path.join(current, "package.json"))) {
404
+ return current;
405
+ }
406
+ const parent = path.dirname(current);
407
+ if (parent === current) break;
408
+ current = parent;
409
+ }
410
+
411
+ return null;
412
+ }
413
+
414
+ async function extractTestLabels(
415
+ testFile: string,
416
+ outline: StructuralProvider["outline"] | undefined,
417
+ cwd: string,
418
+ ): Promise<ExtractedTestLabels> {
419
+ if (!outline) {
420
+ return extractFallbackTestLabels(testFile);
421
+ }
422
+
423
+ try {
424
+ const relPath = path.relative(cwd, testFile);
425
+ const outlineResult = await outline(relPath);
426
+ if (outlineResult.kind === "success") {
427
+ const entries = outlineResult.data.map((item) => item.name);
428
+ const testLike = entries.filter((name) => isTestLikeName(name)).slice(0, 8);
429
+ if (testLike.length > 0) {
430
+ return {
431
+ names: testLike,
432
+ status: "recognized",
433
+ };
434
+ }
435
+
436
+ return extractFallbackTestLabels(testFile, "none-recognized");
437
+ }
438
+ } catch {
439
+ // Continue with the conservative fallback parser.
440
+ }
441
+
442
+ return extractFallbackTestLabels(testFile);
443
+ }
444
+
445
+ function extractFallbackTestLabels(
446
+ testFile: string,
447
+ emptyStatus: TestLabelExtractionStatus = "unavailable",
448
+ ): ExtractedTestLabels {
449
+ try {
450
+ const source = readFileSync(testFile, "utf-8");
451
+ const matches = collectObviousTestCallLabels(source);
452
+ if (matches.length > 0) {
453
+ return {
454
+ names: matches,
455
+ status: "recognized",
456
+ };
457
+ }
458
+ } catch {
459
+ // Fall through to the explicit empty/unavailable result.
460
+ }
461
+
462
+ return { names: [], status: emptyStatus };
463
+ }
464
+
465
+ function collectObviousTestCallLabels(source: string): string[] {
466
+ const labels: string[] = [];
467
+ const seen = new Set<string>();
468
+ const regex =
469
+ /(^|[^\w$.])((?:describe|it|test|spec)(?:\.(?:only|skip|todo|concurrent|each))?)\(\s*(['"`])((?:\\.|(?!\3)[^\\])*)\3/gs;
470
+
471
+ for (const match of source.matchAll(regex)) {
472
+ const callee = match[2];
473
+ const quote = match[3];
474
+ const label = match[4];
475
+ if (!callee || !quote || !label) continue;
476
+ const rendered = `${callee}(${quote}${label}${quote})`;
477
+ if (seen.has(rendered)) continue;
478
+ seen.add(rendered);
479
+ labels.push(rendered);
480
+ if (labels.length >= 8) break;
481
+ }
482
+
483
+ return labels;
484
+ }
485
+
486
+ function rankTestLabels(names: string[], limit: number): string[] {
487
+ const unique = [...new Set(names)];
488
+ if (unique.length === 0) return [];
489
+
490
+ const runnable = unique.filter((name) => isRunnableTestLabel(name));
491
+ const describe = unique.filter((name) => isDescribeTestLabel(name));
492
+ const other = unique.filter((name) => !isRunnableTestLabel(name) && !isDescribeTestLabel(name));
493
+
494
+ if (runnable.length === 0) {
495
+ return [...describe, ...other].slice(0, limit);
496
+ }
497
+
498
+ const describeCap = Math.min(2, describe.length);
499
+ const runnableCap = Math.min(runnable.length, Math.max(1, limit - describeCap));
500
+ const remainingAfterRunnable = Math.max(0, limit - runnableCap);
501
+ const describeShown = describe.slice(0, Math.min(describeCap, remainingAfterRunnable));
502
+ const remainingAfterDescribe = Math.max(0, limit - runnableCap - describeShown.length);
503
+ const otherShown = other.slice(0, remainingAfterDescribe);
504
+
505
+ return [...runnable.slice(0, runnableCap), ...describeShown, ...otherShown];
506
+ }
507
+
508
+ function isRunnableTestLabel(name: string): boolean {
509
+ return /^(it|test|spec)\b/i.test(name);
510
+ }
511
+
512
+ function isDescribeTestLabel(name: string): boolean {
513
+ return /^describe\b/i.test(name);
514
+ }
515
+
516
+ // ── Existing language-agnostic detection helpers ─────────────────────
517
+
518
+ /** Test-support directories that should not be treated as runnable tests. */
519
+ const TEST_SUPPORT_SEGMENTS = ["/__tests__/helpers/", "/__tests__/fixtures/"];
520
+
521
+ /** Check if a file path points at test support code rather than a runnable test. */
522
+ export function isTestSupportPath(filePath: string): boolean {
523
+ const normalized = filePath.replaceAll("\\", "/");
524
+ return TEST_SUPPORT_SEGMENTS.some((segment) => normalized.includes(segment));
525
+ }
526
+
527
+ /** Check if an outline name looks like a test declaration. */
528
+ export function isTestLikeName(name: string): boolean {
529
+ return /^(test|it|describe|spec)\b/i.test(name) || /^(test|it|describe|spec)$/i.test(name);
530
+ }
531
+
532
+ /** Check if a relative file path looks like a test file location. */
533
+ export function isTestFilePath(relPath: string): boolean {
534
+ if (isTestSupportPath(relPath)) {
535
+ return false;
536
+ }
537
+
538
+ const normalized = relPath.replaceAll("\\", "/");
539
+ return /\.test\.|\.spec\.|\/__tests__\//.test(normalized);
540
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Vitest workspace detection helpers.
3
+ *
4
+ * Extracted from orchestrate.ts to keep impact orchestration focused.
5
+ */
6
+
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import * as path from "node:path";
9
+
10
+ export const VITEST_RUNNABLE_EXTENSIONS = new Set([
11
+ ".js",
12
+ ".jsx",
13
+ ".ts",
14
+ ".tsx",
15
+ ".mjs",
16
+ ".cjs",
17
+ ".mts",
18
+ ".cts",
19
+ ]);
20
+
21
+ const VITEST_CONFIG_FILES = [
22
+ "vitest.config.ts",
23
+ "vitest.config.mts",
24
+ "vitest.config.cts",
25
+ "vitest.config.js",
26
+ "vitest.config.mjs",
27
+ "vitest.config.cjs",
28
+ "vitest.workspace.ts",
29
+ "vitest.workspace.mts",
30
+ "vitest.workspace.cts",
31
+ "vitest.workspace.js",
32
+ "vitest.workspace.mjs",
33
+ "vitest.workspace.cjs",
34
+ ];
35
+
36
+ export function detectVitestWorkspace(cwd: string): boolean {
37
+ let current = path.resolve(cwd);
38
+
39
+ for (;;) {
40
+ if (packageJsonUsesVitest(current) || hasVitestConfig(current)) {
41
+ return true;
42
+ }
43
+ if (existsSync(path.join(current, ".git"))) {
44
+ return false;
45
+ }
46
+
47
+ const parent = path.dirname(current);
48
+ if (parent === current) {
49
+ return false;
50
+ }
51
+ current = parent;
52
+ }
53
+ }
54
+
55
+ function packageJsonUsesVitest(dir: string): boolean {
56
+ const packageJsonPath = path.join(dir, "package.json");
57
+ if (!existsSync(packageJsonPath)) {
58
+ return false;
59
+ }
60
+
61
+ try {
62
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
63
+ scripts?: Record<string, string>;
64
+ dependencies?: Record<string, string>;
65
+ devDependencies?: Record<string, string>;
66
+ peerDependencies?: Record<string, string>;
67
+ optionalDependencies?: Record<string, string>;
68
+ };
69
+
70
+ if (Object.values(parsed.scripts ?? {}).some((script) => script.includes("vitest"))) {
71
+ return true;
72
+ }
73
+
74
+ return [
75
+ parsed.dependencies,
76
+ parsed.devDependencies,
77
+ parsed.peerDependencies,
78
+ parsed.optionalDependencies,
79
+ ].some((deps) => Boolean(deps?.vitest));
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ function hasVitestConfig(dir: string): boolean {
86
+ return VITEST_CONFIG_FILES.some((file) => existsSync(path.join(dir, file)));
87
+ }