mstro-app 0.4.3 → 0.4.4

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 (306) hide show
  1. package/dist/server/cli/headless/claude-invoker-process.d.ts +11 -0
  2. package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -0
  3. package/dist/server/cli/headless/claude-invoker-process.js +140 -0
  4. package/dist/server/cli/headless/claude-invoker-process.js.map +1 -0
  5. package/dist/server/cli/headless/claude-invoker-stall.d.ts +40 -0
  6. package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -0
  7. package/dist/server/cli/headless/claude-invoker-stall.js +98 -0
  8. package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -0
  9. package/dist/server/cli/headless/claude-invoker-stream.d.ts +44 -0
  10. package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -0
  11. package/dist/server/cli/headless/claude-invoker-stream.js +276 -0
  12. package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -0
  13. package/dist/server/cli/headless/claude-invoker-tools.d.ts +21 -0
  14. package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -0
  15. package/dist/server/cli/headless/claude-invoker-tools.js +137 -0
  16. package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -0
  17. package/dist/server/cli/headless/claude-invoker.d.ts +6 -4
  18. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  19. package/dist/server/cli/headless/claude-invoker.js +10 -807
  20. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  21. package/dist/server/cli/headless/haiku-assessments.d.ts +62 -0
  22. package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -0
  23. package/dist/server/cli/headless/haiku-assessments.js +281 -0
  24. package/dist/server/cli/headless/haiku-assessments.js.map +1 -0
  25. package/dist/server/cli/headless/headless-logger.d.ts +3 -2
  26. package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
  27. package/dist/server/cli/headless/headless-logger.js +28 -5
  28. package/dist/server/cli/headless/headless-logger.js.map +1 -1
  29. package/dist/server/cli/headless/native-timeout-detector.d.ts +44 -0
  30. package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -0
  31. package/dist/server/cli/headless/native-timeout-detector.js +99 -0
  32. package/dist/server/cli/headless/native-timeout-detector.js.map +1 -0
  33. package/dist/server/cli/headless/stall-assessor.d.ts +2 -110
  34. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  35. package/dist/server/cli/headless/stall-assessor.js +65 -457
  36. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  37. package/dist/server/cli/improvisation-attachments.d.ts +21 -0
  38. package/dist/server/cli/improvisation-attachments.d.ts.map +1 -0
  39. package/dist/server/cli/improvisation-attachments.js +116 -0
  40. package/dist/server/cli/improvisation-attachments.js.map +1 -0
  41. package/dist/server/cli/improvisation-retry.d.ts +52 -0
  42. package/dist/server/cli/improvisation-retry.d.ts.map +1 -0
  43. package/dist/server/cli/improvisation-retry.js +434 -0
  44. package/dist/server/cli/improvisation-retry.js.map +1 -0
  45. package/dist/server/cli/improvisation-session-manager.d.ts +10 -266
  46. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  47. package/dist/server/cli/improvisation-session-manager.js +117 -1079
  48. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  49. package/dist/server/cli/improvisation-types.d.ts +86 -0
  50. package/dist/server/cli/improvisation-types.d.ts.map +1 -0
  51. package/dist/server/cli/improvisation-types.js +10 -0
  52. package/dist/server/cli/improvisation-types.js.map +1 -0
  53. package/dist/server/cli/prompt-builders.d.ts +68 -0
  54. package/dist/server/cli/prompt-builders.d.ts.map +1 -0
  55. package/dist/server/cli/prompt-builders.js +312 -0
  56. package/dist/server/cli/prompt-builders.js.map +1 -0
  57. package/dist/server/index.js +33 -212
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/mcp/bouncer-haiku.d.ts +10 -0
  60. package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -0
  61. package/dist/server/mcp/bouncer-haiku.js +152 -0
  62. package/dist/server/mcp/bouncer-haiku.js.map +1 -0
  63. package/dist/server/mcp/bouncer-integration.d.ts +3 -4
  64. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  65. package/dist/server/mcp/bouncer-integration.js +50 -196
  66. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  67. package/dist/server/mcp/security-analysis.d.ts +38 -0
  68. package/dist/server/mcp/security-analysis.d.ts.map +1 -0
  69. package/dist/server/mcp/security-analysis.js +183 -0
  70. package/dist/server/mcp/security-analysis.js.map +1 -0
  71. package/dist/server/mcp/security-audit.d.ts +1 -1
  72. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  73. package/dist/server/mcp/security-patterns.d.ts +1 -25
  74. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  75. package/dist/server/mcp/security-patterns.js +55 -260
  76. package/dist/server/mcp/security-patterns.js.map +1 -1
  77. package/dist/server/server-setup.d.ts +22 -0
  78. package/dist/server/server-setup.d.ts.map +1 -0
  79. package/dist/server/server-setup.js +101 -0
  80. package/dist/server/server-setup.js.map +1 -0
  81. package/dist/server/services/file-explorer-ops.d.ts +24 -0
  82. package/dist/server/services/file-explorer-ops.d.ts.map +1 -0
  83. package/dist/server/services/file-explorer-ops.js +211 -0
  84. package/dist/server/services/file-explorer-ops.js.map +1 -0
  85. package/dist/server/services/files.d.ts +2 -85
  86. package/dist/server/services/files.d.ts.map +1 -1
  87. package/dist/server/services/files.js +7 -427
  88. package/dist/server/services/files.js.map +1 -1
  89. package/dist/server/services/plan/composer.d.ts.map +1 -1
  90. package/dist/server/services/plan/composer.js +2 -1
  91. package/dist/server/services/plan/composer.js.map +1 -1
  92. package/dist/server/services/plan/executor.d.ts.map +1 -1
  93. package/dist/server/services/plan/executor.js +3 -1
  94. package/dist/server/services/plan/executor.js.map +1 -1
  95. package/dist/server/services/plan/parser-core.d.ts +20 -0
  96. package/dist/server/services/plan/parser-core.d.ts.map +1 -0
  97. package/dist/server/services/plan/parser-core.js +350 -0
  98. package/dist/server/services/plan/parser-core.js.map +1 -0
  99. package/dist/server/services/plan/parser-migration.d.ts +5 -0
  100. package/dist/server/services/plan/parser-migration.d.ts.map +1 -0
  101. package/dist/server/services/plan/parser-migration.js +124 -0
  102. package/dist/server/services/plan/parser-migration.js.map +1 -0
  103. package/dist/server/services/plan/parser.d.ts +0 -8
  104. package/dist/server/services/plan/parser.d.ts.map +1 -1
  105. package/dist/server/services/plan/parser.js +50 -569
  106. package/dist/server/services/plan/parser.js.map +1 -1
  107. package/dist/server/services/plan/review-gate.d.ts +2 -0
  108. package/dist/server/services/plan/review-gate.d.ts.map +1 -1
  109. package/dist/server/services/plan/review-gate.js +2 -2
  110. package/dist/server/services/plan/review-gate.js.map +1 -1
  111. package/dist/server/services/plan/types.d.ts +2 -0
  112. package/dist/server/services/plan/types.d.ts.map +1 -1
  113. package/dist/server/services/platform-credentials.d.ts +24 -0
  114. package/dist/server/services/platform-credentials.d.ts.map +1 -0
  115. package/dist/server/services/platform-credentials.js +68 -0
  116. package/dist/server/services/platform-credentials.js.map +1 -0
  117. package/dist/server/services/platform.d.ts +1 -31
  118. package/dist/server/services/platform.d.ts.map +1 -1
  119. package/dist/server/services/platform.js +10 -119
  120. package/dist/server/services/platform.js.map +1 -1
  121. package/dist/server/services/terminal/pty-manager.d.ts +7 -97
  122. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  123. package/dist/server/services/terminal/pty-manager.js +53 -266
  124. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  125. package/dist/server/services/terminal/pty-utils.d.ts +57 -0
  126. package/dist/server/services/terminal/pty-utils.d.ts.map +1 -0
  127. package/dist/server/services/terminal/pty-utils.js +141 -0
  128. package/dist/server/services/terminal/pty-utils.js.map +1 -0
  129. package/dist/server/services/websocket/file-definition-handlers.d.ts +4 -0
  130. package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -0
  131. package/dist/server/services/websocket/file-definition-handlers.js +153 -0
  132. package/dist/server/services/websocket/file-definition-handlers.js.map +1 -0
  133. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
  134. package/dist/server/services/websocket/file-explorer-handlers.js +52 -391
  135. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
  136. package/dist/server/services/websocket/file-search-handlers.d.ts +5 -0
  137. package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -0
  138. package/dist/server/services/websocket/file-search-handlers.js +238 -0
  139. package/dist/server/services/websocket/file-search-handlers.js.map +1 -0
  140. package/dist/server/services/websocket/file-utils.js +3 -3
  141. package/dist/server/services/websocket/file-utils.js.map +1 -1
  142. package/dist/server/services/websocket/git-branch-handlers.d.ts +7 -0
  143. package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -0
  144. package/dist/server/services/websocket/git-branch-handlers.js +110 -0
  145. package/dist/server/services/websocket/git-branch-handlers.js.map +1 -0
  146. package/dist/server/services/websocket/git-diff-handlers.d.ts +6 -0
  147. package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -0
  148. package/dist/server/services/websocket/git-diff-handlers.js +123 -0
  149. package/dist/server/services/websocket/git-diff-handlers.js.map +1 -0
  150. package/dist/server/services/websocket/git-handlers.d.ts +2 -31
  151. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
  152. package/dist/server/services/websocket/git-handlers.js +35 -541
  153. package/dist/server/services/websocket/git-handlers.js.map +1 -1
  154. package/dist/server/services/websocket/git-log-handlers.d.ts +6 -0
  155. package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -0
  156. package/dist/server/services/websocket/git-log-handlers.js +128 -0
  157. package/dist/server/services/websocket/git-log-handlers.js.map +1 -0
  158. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
  159. package/dist/server/services/websocket/git-pr-handlers.js +13 -53
  160. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
  161. package/dist/server/services/websocket/git-tag-handlers.d.ts +6 -0
  162. package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -0
  163. package/dist/server/services/websocket/git-tag-handlers.js +76 -0
  164. package/dist/server/services/websocket/git-tag-handlers.js.map +1 -0
  165. package/dist/server/services/websocket/git-utils.d.ts +43 -0
  166. package/dist/server/services/websocket/git-utils.d.ts.map +1 -0
  167. package/dist/server/services/websocket/git-utils.js +201 -0
  168. package/dist/server/services/websocket/git-utils.js.map +1 -0
  169. package/dist/server/services/websocket/handler.d.ts +2 -0
  170. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  171. package/dist/server/services/websocket/handler.js +37 -126
  172. package/dist/server/services/websocket/handler.js.map +1 -1
  173. package/dist/server/services/websocket/plan-board-handlers.d.ts +11 -0
  174. package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -0
  175. package/dist/server/services/websocket/plan-board-handlers.js +218 -0
  176. package/dist/server/services/websocket/plan-board-handlers.js.map +1 -0
  177. package/dist/server/services/websocket/plan-execution-handlers.d.ts +9 -0
  178. package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -0
  179. package/dist/server/services/websocket/plan-execution-handlers.js +142 -0
  180. package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -0
  181. package/dist/server/services/websocket/plan-handlers.d.ts +7 -2
  182. package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
  183. package/dist/server/services/websocket/plan-handlers.js +6 -925
  184. package/dist/server/services/websocket/plan-handlers.js.map +1 -1
  185. package/dist/server/services/websocket/plan-helpers.d.ts +19 -0
  186. package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -0
  187. package/dist/server/services/websocket/plan-helpers.js +199 -0
  188. package/dist/server/services/websocket/plan-helpers.js.map +1 -0
  189. package/dist/server/services/websocket/plan-issue-handlers.d.ts +12 -0
  190. package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -0
  191. package/dist/server/services/websocket/plan-issue-handlers.js +162 -0
  192. package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -0
  193. package/dist/server/services/websocket/plan-sprint-handlers.d.ts +7 -0
  194. package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -0
  195. package/dist/server/services/websocket/plan-sprint-handlers.js +206 -0
  196. package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -0
  197. package/dist/server/services/websocket/quality-complexity.d.ts +14 -0
  198. package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -0
  199. package/dist/server/services/websocket/quality-complexity.js +262 -0
  200. package/dist/server/services/websocket/quality-complexity.js.map +1 -0
  201. package/dist/server/services/websocket/quality-fix-agent.d.ts +16 -0
  202. package/dist/server/services/websocket/quality-fix-agent.d.ts.map +1 -0
  203. package/dist/server/services/websocket/quality-fix-agent.js +140 -0
  204. package/dist/server/services/websocket/quality-fix-agent.js.map +1 -0
  205. package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
  206. package/dist/server/services/websocket/quality-handlers.js +34 -346
  207. package/dist/server/services/websocket/quality-handlers.js.map +1 -1
  208. package/dist/server/services/websocket/quality-linting.d.ts +9 -0
  209. package/dist/server/services/websocket/quality-linting.d.ts.map +1 -0
  210. package/dist/server/services/websocket/quality-linting.js +178 -0
  211. package/dist/server/services/websocket/quality-linting.js.map +1 -0
  212. package/dist/server/services/websocket/quality-review-agent.d.ts +19 -0
  213. package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -0
  214. package/dist/server/services/websocket/quality-review-agent.js +206 -0
  215. package/dist/server/services/websocket/quality-review-agent.js.map +1 -0
  216. package/dist/server/services/websocket/quality-service.d.ts +3 -51
  217. package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
  218. package/dist/server/services/websocket/quality-service.js +9 -651
  219. package/dist/server/services/websocket/quality-service.js.map +1 -1
  220. package/dist/server/services/websocket/quality-tools.d.ts +23 -0
  221. package/dist/server/services/websocket/quality-tools.d.ts.map +1 -0
  222. package/dist/server/services/websocket/quality-tools.js +208 -0
  223. package/dist/server/services/websocket/quality-tools.js.map +1 -0
  224. package/dist/server/services/websocket/quality-types.d.ts +59 -0
  225. package/dist/server/services/websocket/quality-types.d.ts.map +1 -0
  226. package/dist/server/services/websocket/quality-types.js +101 -0
  227. package/dist/server/services/websocket/quality-types.js.map +1 -0
  228. package/dist/server/services/websocket/session-handlers.d.ts +3 -4
  229. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
  230. package/dist/server/services/websocket/session-handlers.js +3 -378
  231. package/dist/server/services/websocket/session-handlers.js.map +1 -1
  232. package/dist/server/services/websocket/session-history.d.ts +4 -0
  233. package/dist/server/services/websocket/session-history.d.ts.map +1 -0
  234. package/dist/server/services/websocket/session-history.js +208 -0
  235. package/dist/server/services/websocket/session-history.js.map +1 -0
  236. package/dist/server/services/websocket/session-initialization.d.ts +5 -0
  237. package/dist/server/services/websocket/session-initialization.d.ts.map +1 -0
  238. package/dist/server/services/websocket/session-initialization.js +163 -0
  239. package/dist/server/services/websocket/session-initialization.js.map +1 -0
  240. package/dist/server/services/websocket/types.d.ts +12 -2
  241. package/dist/server/services/websocket/types.d.ts.map +1 -1
  242. package/package.json +1 -1
  243. package/server/cli/headless/claude-invoker-process.ts +204 -0
  244. package/server/cli/headless/claude-invoker-stall.ts +164 -0
  245. package/server/cli/headless/claude-invoker-stream.ts +353 -0
  246. package/server/cli/headless/claude-invoker-tools.ts +187 -0
  247. package/server/cli/headless/claude-invoker.ts +15 -1096
  248. package/server/cli/headless/haiku-assessments.ts +365 -0
  249. package/server/cli/headless/headless-logger.ts +26 -5
  250. package/server/cli/headless/native-timeout-detector.ts +117 -0
  251. package/server/cli/headless/stall-assessor.ts +65 -618
  252. package/server/cli/improvisation-attachments.ts +148 -0
  253. package/server/cli/improvisation-retry.ts +602 -0
  254. package/server/cli/improvisation-session-manager.ts +140 -1349
  255. package/server/cli/improvisation-types.ts +98 -0
  256. package/server/cli/prompt-builders.ts +370 -0
  257. package/server/index.ts +35 -246
  258. package/server/mcp/bouncer-haiku.ts +182 -0
  259. package/server/mcp/bouncer-integration.ts +87 -248
  260. package/server/mcp/security-analysis.ts +217 -0
  261. package/server/mcp/security-audit.ts +1 -1
  262. package/server/mcp/security-patterns.ts +60 -283
  263. package/server/server-setup.ts +114 -0
  264. package/server/services/file-explorer-ops.ts +293 -0
  265. package/server/services/files.ts +20 -532
  266. package/server/services/plan/composer.ts +2 -1
  267. package/server/services/plan/executor.ts +3 -1
  268. package/server/services/plan/parser-core.ts +406 -0
  269. package/server/services/plan/parser-migration.ts +128 -0
  270. package/server/services/plan/parser.ts +52 -620
  271. package/server/services/plan/review-gate.ts +4 -2
  272. package/server/services/plan/types.ts +2 -0
  273. package/server/services/platform-credentials.ts +83 -0
  274. package/server/services/platform.ts +15 -141
  275. package/server/services/terminal/pty-manager.ts +66 -313
  276. package/server/services/terminal/pty-utils.ts +176 -0
  277. package/server/services/websocket/file-definition-handlers.ts +165 -0
  278. package/server/services/websocket/file-explorer-handlers.ts +37 -452
  279. package/server/services/websocket/file-search-handlers.ts +291 -0
  280. package/server/services/websocket/file-utils.ts +3 -3
  281. package/server/services/websocket/git-branch-handlers.ts +130 -0
  282. package/server/services/websocket/git-diff-handlers.ts +140 -0
  283. package/server/services/websocket/git-handlers.ts +40 -625
  284. package/server/services/websocket/git-log-handlers.ts +149 -0
  285. package/server/services/websocket/git-pr-handlers.ts +17 -62
  286. package/server/services/websocket/git-tag-handlers.ts +91 -0
  287. package/server/services/websocket/git-utils.ts +230 -0
  288. package/server/services/websocket/handler.ts +39 -126
  289. package/server/services/websocket/plan-board-handlers.ts +277 -0
  290. package/server/services/websocket/plan-execution-handlers.ts +184 -0
  291. package/server/services/websocket/plan-handlers.ts +8 -1114
  292. package/server/services/websocket/plan-helpers.ts +215 -0
  293. package/server/services/websocket/plan-issue-handlers.ts +204 -0
  294. package/server/services/websocket/plan-sprint-handlers.ts +252 -0
  295. package/server/services/websocket/quality-complexity.ts +294 -0
  296. package/server/services/websocket/quality-fix-agent.ts +181 -0
  297. package/server/services/websocket/quality-handlers.ts +36 -404
  298. package/server/services/websocket/quality-linting.ts +187 -0
  299. package/server/services/websocket/quality-review-agent.ts +246 -0
  300. package/server/services/websocket/quality-service.ts +11 -762
  301. package/server/services/websocket/quality-tools.ts +209 -0
  302. package/server/services/websocket/quality-types.ts +169 -0
  303. package/server/services/websocket/session-handlers.ts +5 -437
  304. package/server/services/websocket/session-history.ts +222 -0
  305. package/server/services/websocket/session-initialization.ts +209 -0
  306. package/server/services/websocket/types.ts +17 -0
@@ -0,0 +1,209 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+
4
+ import { spawn } from 'node:child_process';
5
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
6
+ import { extname, join, relative } from 'node:path';
7
+ import { ECOSYSTEM_TOOLS, type Ecosystem, IGNORE_DIRS, type QualityTool, SOURCE_EXTENSIONS } from './quality-types.js';
8
+
9
+ // ============================================================================
10
+ // Ecosystem Detection
11
+ // ============================================================================
12
+
13
+ export function detectEcosystem(dirPath: string): Ecosystem[] {
14
+ const ecosystems: Ecosystem[] = [];
15
+ try {
16
+ const files = readdirSync(dirPath);
17
+ if (files.includes('package.json')) ecosystems.push('node');
18
+ if (files.includes('pyproject.toml') || files.includes('setup.py') || files.includes('requirements.txt')) ecosystems.push('python');
19
+ if (files.includes('Cargo.toml')) ecosystems.push('rust');
20
+ if (files.includes('go.mod')) ecosystems.push('go');
21
+ if (files.includes('Package.swift') || files.some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace'))) ecosystems.push('swift');
22
+ if (files.includes('build.gradle') || files.includes('build.gradle.kts')) ecosystems.push('kotlin');
23
+ } catch {
24
+ // Directory not readable
25
+ }
26
+ if (ecosystems.length === 0) ecosystems.push('unknown');
27
+ return ecosystems;
28
+ }
29
+
30
+ /** Detect the Node.js package manager from lockfiles */
31
+ function detectNodePackageManager(dirPath: string): 'npm' | 'yarn' | 'pnpm' | 'bun' {
32
+ try {
33
+ const files = readdirSync(dirPath);
34
+ if (files.includes('bun.lockb') || files.includes('bun.lock')) return 'bun';
35
+ if (files.includes('pnpm-lock.yaml')) return 'pnpm';
36
+ if (files.includes('yarn.lock')) return 'yarn';
37
+ } catch {
38
+ // Directory not readable
39
+ }
40
+ return 'npm';
41
+ }
42
+
43
+ /** Build the install command for a Node.js dev dependency */
44
+ function nodeInstallCmd(pm: 'npm' | 'yarn' | 'pnpm' | 'bun', pkg: string): string {
45
+ switch (pm) {
46
+ case 'yarn': return `yarn add -D ${pkg}`;
47
+ case 'pnpm': return `pnpm add -D ${pkg}`;
48
+ case 'bun': return `bun add -d ${pkg}`;
49
+ default: return `npm install -D ${pkg}`;
50
+ }
51
+ }
52
+
53
+ // ============================================================================
54
+ // Tool Detection & Installation
55
+ // ============================================================================
56
+
57
+ async function checkToolInstalled(check: string[], cwd: string): Promise<boolean> {
58
+ return new Promise((resolve) => {
59
+ const proc = spawn(check[0], check.slice(1), {
60
+ cwd,
61
+ stdio: ['ignore', 'pipe', 'pipe'],
62
+ timeout: 10000,
63
+ });
64
+ proc.on('close', (code) => resolve(code === 0));
65
+ proc.on('error', () => resolve(false));
66
+ });
67
+ }
68
+
69
+ export async function detectTools(dirPath: string): Promise<{ tools: QualityTool[]; ecosystem: string[] }> {
70
+ const ecosystems = detectEcosystem(dirPath);
71
+ const tools: QualityTool[] = [];
72
+ const nodePm = ecosystems.includes('node') ? detectNodePackageManager(dirPath) : 'npm';
73
+
74
+ for (const eco of ecosystems) {
75
+ const specs = ECOSYSTEM_TOOLS[eco] || [];
76
+ for (const spec of specs) {
77
+ const installed = await checkToolInstalled(spec.check, dirPath);
78
+ const installCommand = eco === 'node'
79
+ ? nodeInstallCmd(nodePm, spec.installCmd.replace(/^npm install -D /, ''))
80
+ : spec.installCmd;
81
+ tools.push({
82
+ name: spec.name,
83
+ installed,
84
+ installCommand,
85
+ category: spec.category,
86
+ });
87
+ }
88
+ }
89
+
90
+ return { tools, ecosystem: ecosystems };
91
+ }
92
+
93
+ export async function installTools(
94
+ dirPath: string,
95
+ toolNames?: string[],
96
+ ): Promise<{ tools: QualityTool[]; ecosystem: string[] }> {
97
+ const { tools } = await detectTools(dirPath);
98
+ const toInstall = tools.filter((t) => !t.installed && (!toolNames || toolNames.includes(t.name)));
99
+
100
+ const failures: string[] = [];
101
+ for (const tool of toInstall) {
102
+ if (tool.installCommand.startsWith('(')) continue;
103
+ const commands = tool.installCommand.split(' || ');
104
+ let installed = false;
105
+ for (const cmd of commands) {
106
+ const parts = cmd.trim().split(' ');
107
+ const result = await runCommand(parts[0], parts.slice(1), dirPath);
108
+ if (result.exitCode === 0) { installed = true; break; }
109
+ }
110
+ if (!installed) {
111
+ failures.push(`${tool.name}: all install methods failed`);
112
+ }
113
+ }
114
+
115
+ const detected = await detectTools(dirPath);
116
+ const requestedNames = new Set(toolNames ?? toInstall.map((t) => t.name));
117
+ const stillMissing = detected.tools.filter((t) => !t.installed && requestedNames.has(t.name)).map((t) => t.name);
118
+
119
+ if (stillMissing.length > 0) {
120
+ const detail = failures.length > 0 ? ` ${failures.join('; ')}` : '';
121
+ throw new Error(`Failed to install: ${stillMissing.join(', ')}.${detail}`);
122
+ }
123
+
124
+ return detected;
125
+ }
126
+
127
+ // ============================================================================
128
+ // File Scanning
129
+ // ============================================================================
130
+
131
+ export interface SourceFile {
132
+ path: string;
133
+ relativePath: string;
134
+ lines: number;
135
+ content: string;
136
+ }
137
+
138
+ function tryStatSync(path: string): ReturnType<typeof statSync> | null {
139
+ try { return statSync(path); } catch { return null; }
140
+ }
141
+
142
+ function tryReadFile(path: string): string | null {
143
+ try { return readFileSync(path, 'utf-8'); } catch { return null; }
144
+ }
145
+
146
+ function tryReaddirSync(dir: string): string[] | null {
147
+ try { return readdirSync(dir); } catch { return null; }
148
+ }
149
+
150
+ function tryReadSourceFile(fullPath: string, rootPath: string): SourceFile | null {
151
+ const content = tryReadFile(fullPath);
152
+ if (!content) return null;
153
+ return {
154
+ path: fullPath,
155
+ relativePath: relative(rootPath, fullPath),
156
+ lines: content.split('\n').length,
157
+ content,
158
+ };
159
+ }
160
+
161
+ function processEntry(entry: string, dir: string, rootPath: string, stack: string[], files: SourceFile[]): void {
162
+ if (IGNORE_DIRS.has(entry)) return;
163
+ const fullPath = join(dir, entry);
164
+ const stat = tryStatSync(fullPath);
165
+ if (!stat) return;
166
+
167
+ if (stat.isDirectory()) { stack.push(fullPath); return; }
168
+ if (!stat.isFile() || !SOURCE_EXTENSIONS.has(extname(entry).toLowerCase())) return;
169
+
170
+ const sourceFile = tryReadSourceFile(fullPath, rootPath);
171
+ if (sourceFile) files.push(sourceFile);
172
+ }
173
+
174
+ export function collectSourceFiles(dirPath: string, rootPath: string): SourceFile[] {
175
+ const files: SourceFile[] = [];
176
+ const stack = [dirPath];
177
+
178
+ while (stack.length > 0) {
179
+ const dir = stack.pop()!;
180
+ const entries = tryReaddirSync(dir);
181
+ if (!entries) continue;
182
+
183
+ for (const entry of entries) {
184
+ processEntry(entry, dir, rootPath, stack, files);
185
+ }
186
+ }
187
+
188
+ return files;
189
+ }
190
+
191
+ // ============================================================================
192
+ // Command Runner
193
+ // ============================================================================
194
+
195
+ export function runCommand(cmd: string, args: string[], cwd: string): Promise<{ stdout: string; stderr: string; exitCode: number }> {
196
+ return new Promise((resolve) => {
197
+ const proc = spawn(cmd, args, {
198
+ cwd,
199
+ stdio: ['ignore', 'pipe', 'pipe'],
200
+ timeout: 120000,
201
+ });
202
+ let stdout = '';
203
+ let stderr = '';
204
+ proc.stdout?.on('data', (d: Buffer) => { stdout += d.toString(); });
205
+ proc.stderr?.on('data', (d: Buffer) => { stderr += d.toString(); });
206
+ proc.on('close', (code) => resolve({ stdout, stderr, exitCode: code ?? 1 }));
207
+ proc.on('error', (err) => resolve({ stdout: '', stderr: err.message, exitCode: 1 }));
208
+ });
209
+ }
@@ -0,0 +1,169 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+
4
+ // ============================================================================
5
+ // Types
6
+ // ============================================================================
7
+
8
+ export interface QualityTool {
9
+ name: string;
10
+ installed: boolean;
11
+ installCommand: string;
12
+ category: 'linter' | 'formatter' | 'complexity' | 'general';
13
+ }
14
+
15
+ export interface CategoryScore {
16
+ name: string;
17
+ score: number;
18
+ weight: number;
19
+ effectiveWeight: number;
20
+ available: boolean;
21
+ issueCount?: number;
22
+ details?: Record<string, unknown>;
23
+ }
24
+
25
+ export interface QualityFinding {
26
+ severity: 'critical' | 'high' | 'medium' | 'low';
27
+ category: string;
28
+ file: string;
29
+ line: number | null;
30
+ title: string;
31
+ description: string;
32
+ suggestion?: string;
33
+ }
34
+
35
+ export interface QualityResults {
36
+ overall: number;
37
+ grade: string;
38
+ categories: CategoryScore[];
39
+ findings: QualityFinding[];
40
+ codeReview: QualityFinding[];
41
+ analyzedFiles: number;
42
+ totalLines: number;
43
+ timestamp: string;
44
+ ecosystem: string[];
45
+ }
46
+
47
+ export interface ScanProgress {
48
+ step: string;
49
+ current: number;
50
+ total: number;
51
+ }
52
+
53
+ export type Ecosystem = 'node' | 'python' | 'rust' | 'go' | 'swift' | 'kotlin' | 'unknown';
54
+
55
+ export interface ToolSpec {
56
+ name: string;
57
+ check: string[];
58
+ category: QualityTool['category'];
59
+ installCmd: string;
60
+ }
61
+
62
+ // ============================================================================
63
+ // Constants
64
+ // ============================================================================
65
+
66
+ export const ECOSYSTEM_TOOLS: Record<Ecosystem, ToolSpec[]> = {
67
+ node: [
68
+ { name: 'eslint', check: ['npx', 'eslint', '--version'], category: 'linter', installCmd: 'npm install -D eslint' },
69
+ { name: 'biome', check: ['npx', '@biomejs/biome', '--version'], category: 'linter', installCmd: 'npm install -D @biomejs/biome' },
70
+ { name: 'prettier', check: ['npx', 'prettier', '--version'], category: 'formatter', installCmd: 'npm install -D prettier' },
71
+ { name: 'typescript', check: ['npx', 'tsc', '--version'], category: 'general', installCmd: 'npm install -D typescript' },
72
+ ],
73
+ python: [
74
+ { name: 'ruff', check: ['ruff', '--version'], category: 'linter', installCmd: 'uv tool install ruff || pip install ruff' },
75
+ { name: 'black', check: ['black', '--version'], category: 'formatter', installCmd: 'uv tool install black || pip install black' },
76
+ { name: 'radon', check: ['radon', '--version'], category: 'complexity', installCmd: 'uv tool install radon || pip install radon' },
77
+ ],
78
+ rust: [
79
+ { name: 'clippy', check: ['cargo', 'clippy', '--version'], category: 'linter', installCmd: 'rustup component add clippy' },
80
+ { name: 'rustfmt', check: ['rustfmt', '--version'], category: 'formatter', installCmd: 'rustup component add rustfmt' },
81
+ ],
82
+ go: [
83
+ { name: 'golangci-lint', check: ['golangci-lint', '--version'], category: 'linter', installCmd: 'go install github.com/golangci-lint/golangci-lint/cmd/golangci-lint@latest' },
84
+ { name: 'gofmt', check: ['gofmt', '-h'], category: 'formatter', installCmd: '(built-in with Go)' },
85
+ ],
86
+ swift: [
87
+ { name: 'swiftlint', check: ['swiftlint', '--version'], category: 'linter', installCmd: 'brew install swiftlint' },
88
+ { name: 'swiftformat', check: ['swiftformat', '--version'], category: 'formatter', installCmd: 'brew install swiftformat' },
89
+ ],
90
+ kotlin: [
91
+ { name: 'ktlint', check: ['ktlint', '--version'], category: 'linter', installCmd: 'brew install ktlint' },
92
+ { name: 'ktfmt', check: ['ktfmt', '--version'], category: 'formatter', installCmd: 'brew install ktfmt' },
93
+ ],
94
+ unknown: [],
95
+ };
96
+
97
+ export const SOURCE_EXTENSIONS = new Set([
98
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
99
+ '.py', '.pyi',
100
+ '.rs',
101
+ '.go',
102
+ '.java', '.kt',
103
+ '.cs',
104
+ '.rb',
105
+ '.php',
106
+ '.swift',
107
+ '.c', '.cpp', '.h', '.hpp',
108
+ ]);
109
+
110
+ export const IGNORE_DIRS = new Set([
111
+ 'node_modules', '.git', 'dist', 'build', '.next', '__pycache__',
112
+ 'target', 'vendor', '.venv', 'venv', '.tox', 'coverage',
113
+ '.mstro', '.cache', '.turbo', '.output',
114
+ ]);
115
+
116
+ export const FILE_LENGTH_THRESHOLD = 300;
117
+ export const FUNCTION_LENGTH_THRESHOLD = 50;
118
+ export const TOTAL_STEPS = 7;
119
+
120
+ export function hasInstalledToolInCategory(
121
+ installedSet: Set<string>,
122
+ ecosystems: Ecosystem[],
123
+ category: QualityTool['category'],
124
+ ): boolean {
125
+ for (const eco of ecosystems) {
126
+ const specs = ECOSYSTEM_TOOLS[eco] || [];
127
+ for (const spec of specs) {
128
+ if (spec.category === category && installedSet.has(spec.name)) return true;
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+
134
+ // ============================================================================
135
+ // Shared Diagnostic Helpers
136
+ // ============================================================================
137
+
138
+ export function biomeSeverity(severity: string): QualityFinding['severity'] {
139
+ if (severity === 'error') return 'high';
140
+ if (severity === 'warning') return 'medium';
141
+ return 'low';
142
+ }
143
+
144
+ export function isBiomeComplexityDiagnostic(d: Record<string, unknown>): boolean {
145
+ return ((d.category as string) || '').includes('/complexity/');
146
+ }
147
+
148
+ export function isEslintComplexityRule(ruleId: string | null | undefined): boolean {
149
+ if (!ruleId) return false;
150
+ return ruleId === 'complexity' || ruleId.endsWith('-complexity') || ruleId.endsWith('/complexity');
151
+ }
152
+
153
+ export function biomeDiagToFinding(d: Record<string, unknown>, category: QualityFinding['category']): QualityFinding {
154
+ const sev = biomeSeverity(d.severity as string);
155
+ const location = d.location as Record<string, unknown> | undefined;
156
+ const span = (location?.span as Record<string, unknown>) ?? {};
157
+ const start = (span.start as Record<string, unknown>) ?? {};
158
+ const message = d.message as Record<string, unknown> | string | undefined;
159
+ const desc = (typeof message === 'object' ? (message?.text as string) : message) || '';
160
+ const ruleName = ((d.category as string) || '').split('/').pop() || 'Issue';
161
+ return {
162
+ severity: sev,
163
+ category,
164
+ file: (location?.path as string) || '',
165
+ line: (start.line as number) ?? null,
166
+ title: ruleName,
167
+ description: desc,
168
+ };
169
+ }