mstro-app 0.5.0 → 0.5.1

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 (556) hide show
  1. package/LICENSE +129 -190
  2. package/PRIVACY.md +3 -3
  3. package/README.md +5 -1
  4. package/bin/commands/config.js +0 -1
  5. package/bin/mstro.js +0 -1
  6. package/bin/postinstall.js +0 -1
  7. package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
  8. package/dist/server/cli/headless/claude-invoker-process.js +0 -1
  9. package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
  10. package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -1
  11. package/dist/server/cli/headless/claude-invoker-stall.js +0 -1
  12. package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -1
  13. package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -1
  14. package/dist/server/cli/headless/claude-invoker-stream.js +0 -1
  15. package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -1
  16. package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -1
  17. package/dist/server/cli/headless/claude-invoker-tools.js +0 -1
  18. package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -1
  19. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  20. package/dist/server/cli/headless/claude-invoker.js +0 -1
  21. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  22. package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -1
  23. package/dist/server/cli/headless/haiku-assessments.js +0 -1
  24. package/dist/server/cli/headless/haiku-assessments.js.map +1 -1
  25. package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
  26. package/dist/server/cli/headless/headless-logger.js +0 -1
  27. package/dist/server/cli/headless/headless-logger.js.map +1 -1
  28. package/dist/server/cli/headless/index.d.ts.map +1 -1
  29. package/dist/server/cli/headless/index.js +0 -1
  30. package/dist/server/cli/headless/index.js.map +1 -1
  31. package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -1
  32. package/dist/server/cli/headless/native-timeout-detector.js +0 -1
  33. package/dist/server/cli/headless/native-timeout-detector.js.map +1 -1
  34. package/dist/server/cli/headless/output-utils.d.ts.map +1 -1
  35. package/dist/server/cli/headless/output-utils.js +0 -1
  36. package/dist/server/cli/headless/output-utils.js.map +1 -1
  37. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
  38. package/dist/server/cli/headless/prompt-utils.js +0 -1
  39. package/dist/server/cli/headless/prompt-utils.js.map +1 -1
  40. package/dist/server/cli/headless/resilient-runner.d.ts.map +1 -1
  41. package/dist/server/cli/headless/resilient-runner.js +0 -1
  42. package/dist/server/cli/headless/resilient-runner.js.map +1 -1
  43. package/dist/server/cli/headless/retry-strategies.d.ts.map +1 -1
  44. package/dist/server/cli/headless/retry-strategies.js +0 -1
  45. package/dist/server/cli/headless/retry-strategies.js.map +1 -1
  46. package/dist/server/cli/headless/runner.d.ts.map +1 -1
  47. package/dist/server/cli/headless/runner.js +0 -1
  48. package/dist/server/cli/headless/runner.js.map +1 -1
  49. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  50. package/dist/server/cli/headless/stall-assessor.js +0 -1
  51. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  52. package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
  53. package/dist/server/cli/headless/tool-watchdog.js +0 -1
  54. package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
  55. package/dist/server/cli/headless/types.d.ts.map +1 -1
  56. package/dist/server/cli/headless/types.js +0 -1
  57. package/dist/server/cli/headless/types.js.map +1 -1
  58. package/dist/server/cli/improvisation-attachments.d.ts.map +1 -1
  59. package/dist/server/cli/improvisation-attachments.js +0 -1
  60. package/dist/server/cli/improvisation-attachments.js.map +1 -1
  61. package/dist/server/cli/improvisation-history-store.d.ts.map +1 -1
  62. package/dist/server/cli/improvisation-history-store.js +0 -1
  63. package/dist/server/cli/improvisation-history-store.js.map +1 -1
  64. package/dist/server/cli/improvisation-movements.d.ts.map +1 -1
  65. package/dist/server/cli/improvisation-movements.js +0 -1
  66. package/dist/server/cli/improvisation-movements.js.map +1 -1
  67. package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -1
  68. package/dist/server/cli/improvisation-output-queue.js +0 -1
  69. package/dist/server/cli/improvisation-output-queue.js.map +1 -1
  70. package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
  71. package/dist/server/cli/improvisation-retry.js +0 -1
  72. package/dist/server/cli/improvisation-retry.js.map +1 -1
  73. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  74. package/dist/server/cli/improvisation-session-manager.js +0 -1
  75. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  76. package/dist/server/cli/improvisation-types.d.ts.map +1 -1
  77. package/dist/server/cli/improvisation-types.js +0 -1
  78. package/dist/server/cli/improvisation-types.js.map +1 -1
  79. package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -1
  80. package/dist/server/cli/retry/retry-best-result.js +0 -1
  81. package/dist/server/cli/retry/retry-best-result.js.map +1 -1
  82. package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -1
  83. package/dist/server/cli/retry/retry-context-loss.js +0 -1
  84. package/dist/server/cli/retry/retry-context-loss.js.map +1 -1
  85. package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -1
  86. package/dist/server/cli/retry/retry-premature-completion.js +1 -2
  87. package/dist/server/cli/retry/retry-premature-completion.js.map +1 -1
  88. package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -1
  89. package/dist/server/cli/retry/retry-recovery-strategies.js +0 -1
  90. package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -1
  91. package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -1
  92. package/dist/server/cli/retry/retry-resume-strategy.js +0 -1
  93. package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -1
  94. package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -1
  95. package/dist/server/cli/retry/retry-runner-factory.js +0 -1
  96. package/dist/server/cli/retry/retry-runner-factory.js.map +1 -1
  97. package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -1
  98. package/dist/server/cli/retry/retry-tool-results.js +0 -1
  99. package/dist/server/cli/retry/retry-tool-results.js.map +1 -1
  100. package/dist/server/cli/retry/retry-types.d.ts.map +1 -1
  101. package/dist/server/cli/retry/retry-types.js +0 -1
  102. package/dist/server/cli/retry/retry-types.js.map +1 -1
  103. package/dist/server/index.js +0 -1
  104. package/dist/server/index.js.map +1 -1
  105. package/dist/server/mcp/bouncer-cli.js +0 -1
  106. package/dist/server/mcp/bouncer-cli.js.map +1 -1
  107. package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
  108. package/dist/server/mcp/bouncer-haiku.js +0 -1
  109. package/dist/server/mcp/bouncer-haiku.js.map +1 -1
  110. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  111. package/dist/server/mcp/bouncer-integration.js +0 -1
  112. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  113. package/dist/server/mcp/security-analysis.d.ts.map +1 -1
  114. package/dist/server/mcp/security-analysis.js +0 -1
  115. package/dist/server/mcp/security-analysis.js.map +1 -1
  116. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  117. package/dist/server/mcp/security-audit.js +0 -1
  118. package/dist/server/mcp/security-audit.js.map +1 -1
  119. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  120. package/dist/server/mcp/security-patterns.js +0 -1
  121. package/dist/server/mcp/security-patterns.js.map +1 -1
  122. package/dist/server/mcp/server.js +0 -1
  123. package/dist/server/mcp/server.js.map +1 -1
  124. package/dist/server/routes/files.d.ts.map +1 -1
  125. package/dist/server/routes/files.js +0 -1
  126. package/dist/server/routes/files.js.map +1 -1
  127. package/dist/server/routes/improvise.d.ts.map +1 -1
  128. package/dist/server/routes/improvise.js +0 -1
  129. package/dist/server/routes/improvise.js.map +1 -1
  130. package/dist/server/routes/index.d.ts.map +1 -1
  131. package/dist/server/routes/index.js +0 -1
  132. package/dist/server/routes/index.js.map +1 -1
  133. package/dist/server/routes/instances.d.ts.map +1 -1
  134. package/dist/server/routes/instances.js +0 -1
  135. package/dist/server/routes/instances.js.map +1 -1
  136. package/dist/server/routes/notifications.d.ts.map +1 -1
  137. package/dist/server/routes/notifications.js +0 -1
  138. package/dist/server/routes/notifications.js.map +1 -1
  139. package/dist/server/server-setup.d.ts.map +1 -1
  140. package/dist/server/server-setup.js +0 -1
  141. package/dist/server/server-setup.js.map +1 -1
  142. package/dist/server/services/analytics.d.ts.map +1 -1
  143. package/dist/server/services/analytics.js +0 -1
  144. package/dist/server/services/analytics.js.map +1 -1
  145. package/dist/server/services/auth.d.ts.map +1 -1
  146. package/dist/server/services/auth.js +0 -1
  147. package/dist/server/services/auth.js.map +1 -1
  148. package/dist/server/services/client-id.d.ts.map +1 -1
  149. package/dist/server/services/client-id.js +0 -1
  150. package/dist/server/services/client-id.js.map +1 -1
  151. package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
  152. package/dist/server/services/file-explorer-ops.js +0 -1
  153. package/dist/server/services/file-explorer-ops.js.map +1 -1
  154. package/dist/server/services/files.d.ts.map +1 -1
  155. package/dist/server/services/files.js +0 -1
  156. package/dist/server/services/files.js.map +1 -1
  157. package/dist/server/services/instances.d.ts.map +1 -1
  158. package/dist/server/services/instances.js +0 -1
  159. package/dist/server/services/instances.js.map +1 -1
  160. package/dist/server/services/pathUtils.d.ts.map +1 -1
  161. package/dist/server/services/pathUtils.js +0 -1
  162. package/dist/server/services/pathUtils.js.map +1 -1
  163. package/dist/server/services/plan/agent-loader.d.ts.map +1 -1
  164. package/dist/server/services/plan/agent-loader.js +0 -1
  165. package/dist/server/services/plan/agent-loader.js.map +1 -1
  166. package/dist/server/services/plan/board-config.d.ts.map +1 -1
  167. package/dist/server/services/plan/board-config.js +0 -1
  168. package/dist/server/services/plan/board-config.js.map +1 -1
  169. package/dist/server/services/plan/composer.d.ts.map +1 -1
  170. package/dist/server/services/plan/composer.js +0 -1
  171. package/dist/server/services/plan/composer.js.map +1 -1
  172. package/dist/server/services/plan/config-installer.d.ts.map +1 -1
  173. package/dist/server/services/plan/config-installer.js +0 -1
  174. package/dist/server/services/plan/config-installer.js.map +1 -1
  175. package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
  176. package/dist/server/services/plan/dependency-resolver.js +0 -1
  177. package/dist/server/services/plan/dependency-resolver.js.map +1 -1
  178. package/dist/server/services/plan/executor.d.ts.map +1 -1
  179. package/dist/server/services/plan/executor.js +45 -3
  180. package/dist/server/services/plan/executor.js.map +1 -1
  181. package/dist/server/services/plan/front-matter.d.ts.map +1 -1
  182. package/dist/server/services/plan/front-matter.js +0 -1
  183. package/dist/server/services/plan/front-matter.js.map +1 -1
  184. package/dist/server/services/plan/issue-classification.d.ts.map +1 -1
  185. package/dist/server/services/plan/issue-classification.js +0 -1
  186. package/dist/server/services/plan/issue-classification.js.map +1 -1
  187. package/dist/server/services/plan/issue-loader.d.ts.map +1 -1
  188. package/dist/server/services/plan/issue-loader.js +0 -1
  189. package/dist/server/services/plan/issue-loader.js.map +1 -1
  190. package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
  191. package/dist/server/services/plan/issue-prompt-builder.js +0 -1
  192. package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
  193. package/dist/server/services/plan/issue-retry.d.ts +3 -1
  194. package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
  195. package/dist/server/services/plan/issue-retry.js +2 -1
  196. package/dist/server/services/plan/issue-retry.js.map +1 -1
  197. package/dist/server/services/plan/issue-writer.d.ts.map +1 -1
  198. package/dist/server/services/plan/issue-writer.js +0 -1
  199. package/dist/server/services/plan/issue-writer.js.map +1 -1
  200. package/dist/server/services/plan/output-manager.d.ts.map +1 -1
  201. package/dist/server/services/plan/output-manager.js +0 -1
  202. package/dist/server/services/plan/output-manager.js.map +1 -1
  203. package/dist/server/services/plan/parser-core.d.ts.map +1 -1
  204. package/dist/server/services/plan/parser-core.js +0 -1
  205. package/dist/server/services/plan/parser-core.js.map +1 -1
  206. package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
  207. package/dist/server/services/plan/parser-migration.js +0 -1
  208. package/dist/server/services/plan/parser-migration.js.map +1 -1
  209. package/dist/server/services/plan/parser.d.ts.map +1 -1
  210. package/dist/server/services/plan/parser.js +0 -1
  211. package/dist/server/services/plan/parser.js.map +1 -1
  212. package/dist/server/services/plan/progress-log.d.ts.map +1 -1
  213. package/dist/server/services/plan/progress-log.js +0 -1
  214. package/dist/server/services/plan/progress-log.js.map +1 -1
  215. package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
  216. package/dist/server/services/plan/prompt-builder.js +0 -1
  217. package/dist/server/services/plan/prompt-builder.js.map +1 -1
  218. package/dist/server/services/plan/readiness-planner.d.ts.map +1 -1
  219. package/dist/server/services/plan/readiness-planner.js +0 -1
  220. package/dist/server/services/plan/readiness-planner.js.map +1 -1
  221. package/dist/server/services/plan/review-gate.d.ts.map +1 -1
  222. package/dist/server/services/plan/review-gate.js +0 -1
  223. package/dist/server/services/plan/review-gate.js.map +1 -1
  224. package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
  225. package/dist/server/services/plan/state-reconciler.js +0 -1
  226. package/dist/server/services/plan/state-reconciler.js.map +1 -1
  227. package/dist/server/services/plan/types.d.ts.map +1 -1
  228. package/dist/server/services/plan/types.js +0 -1
  229. package/dist/server/services/plan/types.js.map +1 -1
  230. package/dist/server/services/plan/watcher.d.ts.map +1 -1
  231. package/dist/server/services/plan/watcher.js +0 -1
  232. package/dist/server/services/plan/watcher.js.map +1 -1
  233. package/dist/server/services/platform-credentials.d.ts.map +1 -1
  234. package/dist/server/services/platform-credentials.js +0 -1
  235. package/dist/server/services/platform-credentials.js.map +1 -1
  236. package/dist/server/services/platform-token-lifecycle.d.ts +70 -0
  237. package/dist/server/services/platform-token-lifecycle.d.ts.map +1 -0
  238. package/dist/server/services/platform-token-lifecycle.js +156 -0
  239. package/dist/server/services/platform-token-lifecycle.js.map +1 -0
  240. package/dist/server/services/platform.d.ts +21 -56
  241. package/dist/server/services/platform.d.ts.map +1 -1
  242. package/dist/server/services/platform.js +98 -142
  243. package/dist/server/services/platform.js.map +1 -1
  244. package/dist/server/services/sentry.d.ts.map +1 -1
  245. package/dist/server/services/sentry.js +0 -1
  246. package/dist/server/services/sentry.js.map +1 -1
  247. package/dist/server/services/settings.d.ts.map +1 -1
  248. package/dist/server/services/settings.js +0 -1
  249. package/dist/server/services/settings.js.map +1 -1
  250. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  251. package/dist/server/services/terminal/pty-manager.js +0 -1
  252. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  253. package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
  254. package/dist/server/services/terminal/pty-utils.js +0 -1
  255. package/dist/server/services/terminal/pty-utils.js.map +1 -1
  256. package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
  257. package/dist/server/services/websocket/autocomplete.js +0 -1
  258. package/dist/server/services/websocket/autocomplete.js.map +1 -1
  259. package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -1
  260. package/dist/server/services/websocket/file-definition-handlers.js +0 -1
  261. package/dist/server/services/websocket/file-definition-handlers.js.map +1 -1
  262. package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -1
  263. package/dist/server/services/websocket/file-download-handler.js +0 -1
  264. package/dist/server/services/websocket/file-download-handler.js.map +1 -1
  265. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
  266. package/dist/server/services/websocket/file-explorer-handlers.js +0 -1
  267. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
  268. package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -1
  269. package/dist/server/services/websocket/file-search-handlers.js +0 -1
  270. package/dist/server/services/websocket/file-search-handlers.js.map +1 -1
  271. package/dist/server/services/websocket/file-upload-handler.d.ts +2 -3
  272. package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -1
  273. package/dist/server/services/websocket/file-upload-handler.js +4 -7
  274. package/dist/server/services/websocket/file-upload-handler.js.map +1 -1
  275. package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
  276. package/dist/server/services/websocket/file-utils.js +0 -1
  277. package/dist/server/services/websocket/file-utils.js.map +1 -1
  278. package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
  279. package/dist/server/services/websocket/git-branch-handlers.js +0 -1
  280. package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
  281. package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -1
  282. package/dist/server/services/websocket/git-diff-handlers.js +0 -1
  283. package/dist/server/services/websocket/git-diff-handlers.js.map +1 -1
  284. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
  285. package/dist/server/services/websocket/git-handlers.js +58 -6
  286. package/dist/server/services/websocket/git-handlers.js.map +1 -1
  287. package/dist/server/services/websocket/git-head-watcher.d.ts.map +1 -1
  288. package/dist/server/services/websocket/git-head-watcher.js +0 -1
  289. package/dist/server/services/websocket/git-head-watcher.js.map +1 -1
  290. package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -1
  291. package/dist/server/services/websocket/git-log-handlers.js +0 -1
  292. package/dist/server/services/websocket/git-log-handlers.js.map +1 -1
  293. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
  294. package/dist/server/services/websocket/git-pr-handlers.js +0 -1
  295. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
  296. package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -1
  297. package/dist/server/services/websocket/git-tag-handlers.js +0 -1
  298. package/dist/server/services/websocket/git-tag-handlers.js.map +1 -1
  299. package/dist/server/services/websocket/git-utils.d.ts +18 -3
  300. package/dist/server/services/websocket/git-utils.d.ts.map +1 -1
  301. package/dist/server/services/websocket/git-utils.js +58 -8
  302. package/dist/server/services/websocket/git-utils.js.map +1 -1
  303. package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
  304. package/dist/server/services/websocket/git-worktree-handlers.js +230 -14
  305. package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
  306. package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
  307. package/dist/server/services/websocket/handler-context.js +0 -1
  308. package/dist/server/services/websocket/handler-context.js.map +1 -1
  309. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  310. package/dist/server/services/websocket/handler.js +3 -4
  311. package/dist/server/services/websocket/handler.js.map +1 -1
  312. package/dist/server/services/websocket/index.d.ts.map +1 -1
  313. package/dist/server/services/websocket/index.js +0 -1
  314. package/dist/server/services/websocket/index.js.map +1 -1
  315. package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -1
  316. package/dist/server/services/websocket/msg-id-tracker.js +0 -1
  317. package/dist/server/services/websocket/msg-id-tracker.js.map +1 -1
  318. package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
  319. package/dist/server/services/websocket/plan-board-handlers.js +0 -1
  320. package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
  321. package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
  322. package/dist/server/services/websocket/plan-execution-handlers.js +6 -2
  323. package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
  324. package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
  325. package/dist/server/services/websocket/plan-handlers.js +0 -1
  326. package/dist/server/services/websocket/plan-handlers.js.map +1 -1
  327. package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
  328. package/dist/server/services/websocket/plan-helpers.js +0 -1
  329. package/dist/server/services/websocket/plan-helpers.js.map +1 -1
  330. package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
  331. package/dist/server/services/websocket/plan-issue-handlers.js +0 -1
  332. package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
  333. package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
  334. package/dist/server/services/websocket/plan-sprint-handlers.js +0 -1
  335. package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
  336. package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
  337. package/dist/server/services/websocket/quality-complexity.js +0 -1
  338. package/dist/server/services/websocket/quality-complexity.js.map +1 -1
  339. package/dist/server/services/websocket/quality-grading.d.ts +46 -0
  340. package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
  341. package/dist/server/services/websocket/quality-grading.js +482 -0
  342. package/dist/server/services/websocket/quality-grading.js.map +1 -0
  343. package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
  344. package/dist/server/services/websocket/quality-handlers.js +0 -1
  345. package/dist/server/services/websocket/quality-handlers.js.map +1 -1
  346. package/dist/server/services/websocket/quality-linting.d.ts.map +1 -1
  347. package/dist/server/services/websocket/quality-linting.js +0 -1
  348. package/dist/server/services/websocket/quality-linting.js.map +1 -1
  349. package/dist/server/services/websocket/quality-persistence.d.ts +14 -0
  350. package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
  351. package/dist/server/services/websocket/quality-persistence.js +28 -12
  352. package/dist/server/services/websocket/quality-persistence.js.map +1 -1
  353. package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
  354. package/dist/server/services/websocket/quality-review-agent.js +0 -1
  355. package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
  356. package/dist/server/services/websocket/quality-service.d.ts +3 -1
  357. package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
  358. package/dist/server/services/websocket/quality-service.js +53 -58
  359. package/dist/server/services/websocket/quality-service.js.map +1 -1
  360. package/dist/server/services/websocket/quality-tools.d.ts +1 -1
  361. package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
  362. package/dist/server/services/websocket/quality-tools.js +6 -3
  363. package/dist/server/services/websocket/quality-tools.js.map +1 -1
  364. package/dist/server/services/websocket/quality-types.d.ts +18 -2
  365. package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
  366. package/dist/server/services/websocket/quality-types.js +0 -1
  367. package/dist/server/services/websocket/quality-types.js.map +1 -1
  368. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
  369. package/dist/server/services/websocket/session-handlers.js +0 -1
  370. package/dist/server/services/websocket/session-handlers.js.map +1 -1
  371. package/dist/server/services/websocket/session-history.d.ts.map +1 -1
  372. package/dist/server/services/websocket/session-history.js +0 -1
  373. package/dist/server/services/websocket/session-history.js.map +1 -1
  374. package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
  375. package/dist/server/services/websocket/session-initialization.js +0 -1
  376. package/dist/server/services/websocket/session-initialization.js.map +1 -1
  377. package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
  378. package/dist/server/services/websocket/session-registry.js +0 -1
  379. package/dist/server/services/websocket/session-registry.js.map +1 -1
  380. package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
  381. package/dist/server/services/websocket/settings-handlers.js +0 -1
  382. package/dist/server/services/websocket/settings-handlers.js.map +1 -1
  383. package/dist/server/services/websocket/skill-handlers.d.ts.map +1 -1
  384. package/dist/server/services/websocket/skill-handlers.js +0 -1
  385. package/dist/server/services/websocket/skill-handlers.js.map +1 -1
  386. package/dist/server/services/websocket/skill-watcher.d.ts.map +1 -1
  387. package/dist/server/services/websocket/skill-watcher.js +0 -1
  388. package/dist/server/services/websocket/skill-watcher.js.map +1 -1
  389. package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -1
  390. package/dist/server/services/websocket/tab-broadcast.js +0 -1
  391. package/dist/server/services/websocket/tab-broadcast.js.map +1 -1
  392. package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -1
  393. package/dist/server/services/websocket/tab-event-buffer.js +0 -1
  394. package/dist/server/services/websocket/tab-event-buffer.js.map +1 -1
  395. package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -1
  396. package/dist/server/services/websocket/tab-event-replay.js +0 -1
  397. package/dist/server/services/websocket/tab-event-replay.js.map +1 -1
  398. package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
  399. package/dist/server/services/websocket/tab-handlers.js +0 -1
  400. package/dist/server/services/websocket/tab-handlers.js.map +1 -1
  401. package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
  402. package/dist/server/services/websocket/terminal-handlers.js +39 -4
  403. package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
  404. package/dist/server/services/websocket/types.d.ts +2 -2
  405. package/dist/server/services/websocket/types.d.ts.map +1 -1
  406. package/dist/server/services/websocket/types.js +2 -3
  407. package/dist/server/services/websocket/types.js.map +1 -1
  408. package/dist/server/utils/agent-manager.d.ts.map +1 -1
  409. package/dist/server/utils/agent-manager.js +0 -1
  410. package/dist/server/utils/agent-manager.js.map +1 -1
  411. package/dist/server/utils/paths.d.ts.map +1 -1
  412. package/dist/server/utils/paths.js +0 -1
  413. package/dist/server/utils/paths.js.map +1 -1
  414. package/dist/server/utils/port-manager.d.ts.map +1 -1
  415. package/dist/server/utils/port-manager.js +0 -1
  416. package/dist/server/utils/port-manager.js.map +1 -1
  417. package/dist/server/utils/port.d.ts.map +1 -1
  418. package/dist/server/utils/port.js +0 -1
  419. package/dist/server/utils/port.js.map +1 -1
  420. package/package.json +2 -2
  421. package/server/cli/headless/claude-invoker-process.ts +0 -1
  422. package/server/cli/headless/claude-invoker-stall.ts +0 -1
  423. package/server/cli/headless/claude-invoker-stream.ts +0 -1
  424. package/server/cli/headless/claude-invoker-tools.ts +0 -1
  425. package/server/cli/headless/claude-invoker.ts +0 -1
  426. package/server/cli/headless/haiku-assessments.ts +0 -1
  427. package/server/cli/headless/headless-logger.ts +0 -1
  428. package/server/cli/headless/index.ts +0 -1
  429. package/server/cli/headless/native-timeout-detector.ts +0 -1
  430. package/server/cli/headless/output-utils.ts +0 -1
  431. package/server/cli/headless/prompt-utils.ts +0 -1
  432. package/server/cli/headless/resilient-runner.ts +0 -1
  433. package/server/cli/headless/retry-strategies.ts +0 -1
  434. package/server/cli/headless/runner.ts +0 -1
  435. package/server/cli/headless/stall-assessor.ts +0 -1
  436. package/server/cli/headless/tool-watchdog.ts +0 -1
  437. package/server/cli/headless/types.ts +0 -1
  438. package/server/cli/improvisation-attachments.ts +0 -1
  439. package/server/cli/improvisation-history-store.ts +0 -1
  440. package/server/cli/improvisation-movements.ts +0 -1
  441. package/server/cli/improvisation-output-queue.ts +0 -1
  442. package/server/cli/improvisation-retry.ts +0 -1
  443. package/server/cli/improvisation-session-manager.ts +0 -1
  444. package/server/cli/improvisation-types.ts +0 -1
  445. package/server/cli/retry/retry-best-result.ts +0 -1
  446. package/server/cli/retry/retry-context-loss.ts +0 -1
  447. package/server/cli/retry/retry-premature-completion.ts +1 -2
  448. package/server/cli/retry/retry-recovery-strategies.ts +0 -1
  449. package/server/cli/retry/retry-resume-strategy.ts +0 -1
  450. package/server/cli/retry/retry-runner-factory.ts +0 -1
  451. package/server/cli/retry/retry-tool-results.ts +0 -1
  452. package/server/cli/retry/retry-types.ts +0 -1
  453. package/server/index.ts +0 -1
  454. package/server/mcp/bouncer-cli.ts +0 -1
  455. package/server/mcp/bouncer-haiku.ts +0 -1
  456. package/server/mcp/bouncer-integration.ts +0 -1
  457. package/server/mcp/security-analysis.ts +0 -1
  458. package/server/mcp/security-audit.ts +0 -1
  459. package/server/mcp/security-patterns.ts +0 -1
  460. package/server/mcp/server.ts +0 -1
  461. package/server/routes/files.ts +0 -1
  462. package/server/routes/improvise.ts +0 -1
  463. package/server/routes/index.ts +0 -1
  464. package/server/routes/instances.ts +0 -1
  465. package/server/routes/notifications.ts +0 -1
  466. package/server/server-setup.ts +0 -1
  467. package/server/services/analytics.ts +0 -1
  468. package/server/services/auth.ts +0 -1
  469. package/server/services/client-id.ts +0 -1
  470. package/server/services/file-explorer-ops.ts +0 -1
  471. package/server/services/files.ts +0 -1
  472. package/server/services/instances.ts +0 -1
  473. package/server/services/pathUtils.ts +0 -1
  474. package/server/services/plan/agent-loader.ts +0 -1
  475. package/server/services/plan/agents/code-review.md +13 -11
  476. package/server/services/plan/board-config.ts +0 -1
  477. package/server/services/plan/composer.ts +0 -1
  478. package/server/services/plan/config-installer.ts +0 -1
  479. package/server/services/plan/dependency-resolver.ts +0 -1
  480. package/server/services/plan/executor.ts +45 -3
  481. package/server/services/plan/front-matter.ts +0 -1
  482. package/server/services/plan/issue-classification.ts +0 -1
  483. package/server/services/plan/issue-loader.ts +0 -1
  484. package/server/services/plan/issue-prompt-builder.ts +0 -1
  485. package/server/services/plan/issue-retry.ts +5 -2
  486. package/server/services/plan/issue-writer.ts +0 -1
  487. package/server/services/plan/output-manager.ts +0 -1
  488. package/server/services/plan/parser-core.ts +0 -1
  489. package/server/services/plan/parser-migration.ts +0 -1
  490. package/server/services/plan/parser.ts +0 -1
  491. package/server/services/plan/progress-log.ts +0 -1
  492. package/server/services/plan/prompt-builder.ts +0 -1
  493. package/server/services/plan/readiness-planner.ts +0 -1
  494. package/server/services/plan/review-gate.ts +0 -1
  495. package/server/services/plan/state-reconciler.ts +0 -1
  496. package/server/services/plan/types.ts +0 -1
  497. package/server/services/plan/watcher.ts +0 -1
  498. package/server/services/platform-credentials.ts +0 -1
  499. package/server/services/platform-token-lifecycle.ts +171 -0
  500. package/server/services/platform.ts +106 -148
  501. package/server/services/sentry.ts +0 -1
  502. package/server/services/settings.ts +0 -1
  503. package/server/services/terminal/pty-manager.ts +0 -1
  504. package/server/services/terminal/pty-utils.ts +0 -1
  505. package/server/services/websocket/autocomplete.ts +0 -1
  506. package/server/services/websocket/file-definition-handlers.ts +0 -1
  507. package/server/services/websocket/file-download-handler.ts +0 -1
  508. package/server/services/websocket/file-explorer-handlers.ts +0 -1
  509. package/server/services/websocket/file-search-handlers.ts +0 -1
  510. package/server/services/websocket/file-upload-handler.ts +6 -5
  511. package/server/services/websocket/file-utils.ts +0 -1
  512. package/server/services/websocket/git-branch-handlers.ts +0 -1
  513. package/server/services/websocket/git-diff-handlers.ts +0 -1
  514. package/server/services/websocket/git-handlers.ts +66 -10
  515. package/server/services/websocket/git-head-watcher.ts +0 -1
  516. package/server/services/websocket/git-log-handlers.ts +0 -1
  517. package/server/services/websocket/git-pr-handlers.ts +0 -1
  518. package/server/services/websocket/git-tag-handlers.ts +0 -1
  519. package/server/services/websocket/git-utils.ts +69 -9
  520. package/server/services/websocket/git-worktree-handlers.ts +260 -17
  521. package/server/services/websocket/handler-context.ts +0 -1
  522. package/server/services/websocket/handler.ts +3 -4
  523. package/server/services/websocket/index.ts +0 -1
  524. package/server/services/websocket/msg-id-tracker.ts +0 -1
  525. package/server/services/websocket/plan-board-handlers.ts +0 -1
  526. package/server/services/websocket/plan-execution-handlers.ts +6 -2
  527. package/server/services/websocket/plan-handlers.ts +0 -1
  528. package/server/services/websocket/plan-helpers.ts +0 -1
  529. package/server/services/websocket/plan-issue-handlers.ts +0 -1
  530. package/server/services/websocket/plan-sprint-handlers.ts +0 -1
  531. package/server/services/websocket/quality-complexity.ts +0 -1
  532. package/server/services/websocket/quality-grading.ts +611 -0
  533. package/server/services/websocket/quality-handlers.ts +0 -1
  534. package/server/services/websocket/quality-linting.ts +0 -1
  535. package/server/services/websocket/quality-persistence.ts +30 -8
  536. package/server/services/websocket/quality-review-agent.ts +0 -1
  537. package/server/services/websocket/quality-service.ts +54 -55
  538. package/server/services/websocket/quality-tools.ts +11 -3
  539. package/server/services/websocket/quality-types.ts +21 -3
  540. package/server/services/websocket/session-handlers.ts +0 -1
  541. package/server/services/websocket/session-history.ts +0 -1
  542. package/server/services/websocket/session-initialization.ts +0 -1
  543. package/server/services/websocket/session-registry.ts +0 -1
  544. package/server/services/websocket/settings-handlers.ts +0 -1
  545. package/server/services/websocket/skill-handlers.ts +0 -1
  546. package/server/services/websocket/skill-watcher.ts +0 -1
  547. package/server/services/websocket/tab-broadcast.ts +0 -1
  548. package/server/services/websocket/tab-event-buffer.ts +0 -1
  549. package/server/services/websocket/tab-event-replay.ts +0 -1
  550. package/server/services/websocket/tab-handlers.ts +0 -1
  551. package/server/services/websocket/terminal-handlers.ts +39 -3
  552. package/server/services/websocket/types.ts +2 -3
  553. package/server/utils/agent-manager.ts +0 -1
  554. package/server/utils/paths.ts +0 -1
  555. package/server/utils/port-manager.ts +0 -1
  556. package/server/utils/port.ts +0 -1
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * WebSocket Improvise Module
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Idempotency tracker for client-sent message IDs.
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
5
4
  import { join } from 'node:path';
@@ -1,11 +1,11 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { handlePlanPrompt } from '../plan/composer.js';
5
4
  import type { PlanExecutor } from '../plan/executor.js';
6
5
  import { parsePlanDirectory } from '../plan/parser.js';
7
6
  import type { HandlerContext } from './handler-context.js';
8
7
  import { denyIfViewOnly, executorCache, getExecutor } from './plan-helpers.js';
8
+ import { mergePreUploadedAttachments } from './session-handlers.js';
9
9
  import type { WebSocketMessage, WSContext } from './types.js';
10
10
 
11
11
  // ============================================================================
@@ -25,7 +25,11 @@ export function handlePrompt(
25
25
  return;
26
26
  }
27
27
  const executionDir = boardId ? ctx.gitDirectories.get(boardId) : undefined;
28
- handlePlanPrompt(ctx, ws, prompt, workingDir, boardId, executionDir).catch(error => {
28
+ // Pull in any chunked uploads stashed under this board's tabId by the
29
+ // composer paperclip / drag-drop path, so they flow into the plan prompt
30
+ // exactly like chat-tab attachments do.
31
+ const attachments = boardId ? mergePreUploadedAttachments(ctx, boardId, undefined) : undefined;
32
+ handlePlanPrompt(ctx, ws, prompt, workingDir, boardId, executionDir, attachments).catch(error => {
29
33
  ctx.send(ws, {
30
34
  type: 'planError',
31
35
  data: { error: error instanceof Error ? error.message : String(error) },
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  /**
5
4
  * Plan Handlers — WebSocket message router for Plan view
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { mkdirSync, writeFileSync } from 'node:fs';
5
4
  import { join, resolve } from 'node:path';
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
5
4
  import { basename, join } from 'node:path';
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
4
  import { join } from 'node:path';
@@ -1,5 +1,4 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
- // Licensed under the MIT License. See LICENSE file for details.
3
2
 
4
3
  import { extname, relative } from 'node:path';
5
4
  import { chunkFileList, filesByExt, runCommand, type SourceFile } from './quality-tools.js';
@@ -0,0 +1,611 @@
1
+ // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
+
3
+ // ============================================================================
4
+ // Multi-Dimensional Quality Grading
5
+ // ============================================================================
6
+ //
7
+ // Pure logic for the three-dimension grading model:
8
+ // - Security — severity-threshold (worst severity issue determines grade)
9
+ // - Reliability — severity-threshold, slightly more lenient than Security
10
+ // - Maintainability — density-based (issues / KLOC) with a severity escape hatch
11
+ //
12
+ // Rationale (why these specific bands):
13
+ // The previous single-score exponential-decay model conflated security holes
14
+ // with prettier complaints. Industry tools (SonarQube, Code Climate, CodeScene)
15
+ // all separate severity-driven dimensions from aggregate metrics so that a
16
+ // critical issue can never be "averaged away" by a clean lint score.
17
+ //
18
+ // All functions in this module are pure: same inputs -> same outputs, no I/O.
19
+ // ============================================================================
20
+
21
+ // ============================================================================
22
+ // Types
23
+ // ============================================================================
24
+
25
+ export type DimensionName = 'security' | 'reliability' | 'maintainability';
26
+ export type Grade = 'A' | 'B' | 'C' | 'D' | 'F' | 'N/A';
27
+ type Severity = 'critical' | 'high' | 'medium' | 'low';
28
+
29
+ export interface DimensionScore {
30
+ name: DimensionName;
31
+ score: number;
32
+ grade: Grade;
33
+ rationale: string;
34
+ available: boolean;
35
+ findingCount: number;
36
+ worstSeverity: Severity | null;
37
+ }
38
+
39
+ export interface QualityGate {
40
+ passed: boolean;
41
+ failingConditions: string[];
42
+ }
43
+
44
+ export interface QualityRating {
45
+ overall: { score: number; grade: Grade };
46
+ dimensions: DimensionScore[];
47
+ qualityGate: QualityGate;
48
+ gradeRationale: string;
49
+ }
50
+
51
+ // ============================================================================
52
+ // Category -> Dimension Mapping
53
+ // ============================================================================
54
+
55
+ const SECURITY_CATEGORIES = new Set<string>(['security']);
56
+ const RELIABILITY_CATEGORIES = new Set<string>(['bugs', 'logic', 'performance', 'complexity']);
57
+ const MAINTAINABILITY_CATEGORIES = new Set<string>([
58
+ 'lint',
59
+ 'linting',
60
+ 'format',
61
+ 'file-length',
62
+ 'function-length',
63
+ 'architecture',
64
+ 'oop',
65
+ 'maintainability',
66
+ ]);
67
+
68
+ /**
69
+ * Map a finding category to one of the three quality dimensions.
70
+ * Unknown categories default to maintainability (the catch-all bucket) so
71
+ * that surprise categories never silently disappear from the grade.
72
+ */
73
+ export function categoryToDimension(category: string): DimensionName {
74
+ if (SECURITY_CATEGORIES.has(category)) return 'security';
75
+ if (RELIABILITY_CATEGORIES.has(category)) return 'reliability';
76
+ if (MAINTAINABILITY_CATEGORIES.has(category)) return 'maintainability';
77
+ return 'maintainability';
78
+ }
79
+
80
+ // ============================================================================
81
+ // Legacy Fallback
82
+ // ============================================================================
83
+
84
+ /**
85
+ * Score-to-grade conversion used by legacy callers that still operate on a
86
+ * single 0-100 number. The new multi-dimensional path computes grades
87
+ * directly from finding shape; this remains for backward compatibility.
88
+ */
89
+ export function gradeFromScore(score: number): Grade {
90
+ if (score >= 90) return 'A';
91
+ if (score >= 80) return 'B';
92
+ if (score >= 70) return 'C';
93
+ if (score >= 60) return 'D';
94
+ return 'F';
95
+ }
96
+
97
+ // ============================================================================
98
+ // Score Bands
99
+ // ============================================================================
100
+
101
+ const BAND_TOP: Record<Exclude<Grade, 'N/A'>, number> = {
102
+ A: 100,
103
+ B: 89,
104
+ C: 79,
105
+ D: 69,
106
+ F: 59,
107
+ };
108
+
109
+ const BAND_BOTTOM: Record<Exclude<Grade, 'N/A'>, number> = {
110
+ A: 90,
111
+ B: 80,
112
+ C: 70,
113
+ D: 60,
114
+ F: 0,
115
+ };
116
+
117
+ /**
118
+ * Linearly interpolate a score within a grade's band.
119
+ *
120
+ * `position` is in [0, 1]: 0 means "as bad as this grade gets" (band bottom),
121
+ * 1 means "as good as this grade gets" (band top, just below the next grade).
122
+ */
123
+ function scoreInBand(grade: Exclude<Grade, 'N/A'>, position: number): number {
124
+ const clamped = Math.max(0, Math.min(1, position));
125
+ const bottom = BAND_BOTTOM[grade];
126
+ const top = BAND_TOP[grade];
127
+ return Math.round(bottom + (top - bottom) * clamped);
128
+ }
129
+
130
+ // ============================================================================
131
+ // Severity Helpers
132
+ // ============================================================================
133
+
134
+ function isSeverity(s: string): s is Severity {
135
+ return s === 'critical' || s === 'high' || s === 'medium' || s === 'low';
136
+ }
137
+
138
+ interface SeverityCounts {
139
+ critical: number;
140
+ high: number;
141
+ medium: number;
142
+ low: number;
143
+ total: number;
144
+ }
145
+
146
+ function countSeverities(findings: Array<{ severity: string }>): SeverityCounts {
147
+ const counts: SeverityCounts = { critical: 0, high: 0, medium: 0, low: 0, total: 0 };
148
+ for (const f of findings) {
149
+ if (!isSeverity(f.severity)) continue;
150
+ counts[f.severity]++;
151
+ counts.total++;
152
+ }
153
+ return counts;
154
+ }
155
+
156
+ function worstSeverity(counts: SeverityCounts): Severity | null {
157
+ if (counts.critical > 0) return 'critical';
158
+ if (counts.high > 0) return 'high';
159
+ if (counts.medium > 0) return 'medium';
160
+ if (counts.low > 0) return 'low';
161
+ return null;
162
+ }
163
+
164
+ // ============================================================================
165
+ // Security Dimension
166
+ // ============================================================================
167
+
168
+ /**
169
+ * Security grading — strictest of the three dimensions. Any medium-or-worse
170
+ * security finding immediately drops the grade below B because security
171
+ * issues can't be amortized over codebase size.
172
+ *
173
+ * Within-band score: more findings at the threshold severity -> lower score.
174
+ * The interpolation favors "fewer issues is meaningfully better" so 1 medium
175
+ * scores higher than 5 mediums even though both are grade C.
176
+ */
177
+ function gradeSecurity(findings: Array<{ severity: string }>): DimensionScore {
178
+ const counts = countSeverities(findings);
179
+ const worst = worstSeverity(counts);
180
+
181
+ if (counts.total === 0) {
182
+ return {
183
+ name: 'security',
184
+ score: 100,
185
+ grade: 'A',
186
+ rationale: '0 security findings',
187
+ available: true,
188
+ findingCount: 0,
189
+ worstSeverity: null,
190
+ };
191
+ }
192
+
193
+ let grade: Exclude<Grade, 'N/A'>;
194
+ let position: number;
195
+ let rationale: string;
196
+
197
+ if (counts.critical > 0) {
198
+ grade = 'F';
199
+ // F band: fewer criticals -> higher within-band, but still F.
200
+ position = 1 / (1 + counts.critical);
201
+ rationale = `${counts.critical} critical-severity security ${pluralize('issue', counts.critical)}`;
202
+ } else if (counts.high > 0) {
203
+ grade = 'D';
204
+ position = 1 / (1 + counts.high);
205
+ rationale = `${counts.high} high-severity security ${pluralize('issue', counts.high)}`;
206
+ } else if (counts.medium > 0) {
207
+ grade = 'C';
208
+ position = 1 / (1 + counts.medium);
209
+ rationale = `${counts.medium} medium-severity security ${pluralize('issue', counts.medium)}`;
210
+ } else {
211
+ // Only low-severity findings.
212
+ grade = 'B';
213
+ // 1 low -> top of B (89); more lows -> down toward 80.
214
+ position = 1 / Math.max(1, counts.low);
215
+ rationale = `${counts.low} low-severity security ${pluralize('issue', counts.low)}`;
216
+ }
217
+
218
+ return {
219
+ name: 'security',
220
+ score: scoreInBand(grade, position),
221
+ grade,
222
+ rationale,
223
+ available: true,
224
+ findingCount: counts.total,
225
+ worstSeverity: worst,
226
+ };
227
+ }
228
+
229
+ // ============================================================================
230
+ // Reliability Dimension
231
+ // ============================================================================
232
+
233
+ interface BandResult {
234
+ grade: Exclude<Grade, 'N/A'>;
235
+ position: number;
236
+ rationale: string;
237
+ }
238
+
239
+ function reliabilityBandClean(counts: SeverityCounts): BandResult {
240
+ const position = counts.total === 0 ? 1 : 0.5;
241
+ const rationale = counts.total === 0 ? '0 reliability findings' : '1 low-severity reliability issue';
242
+ return { grade: 'A', position, rationale };
243
+ }
244
+
245
+ function reliabilityBandSevere(counts: SeverityCounts): BandResult | null {
246
+ if (counts.critical > 0) {
247
+ return {
248
+ grade: 'F',
249
+ position: 1 / (1 + counts.critical),
250
+ rationale: `${counts.critical} critical-severity ${pluralize('bug', counts.critical)}`,
251
+ };
252
+ }
253
+ if (counts.high >= 2) {
254
+ return {
255
+ grade: 'D',
256
+ position: 1 / (1 + (counts.high - 1)),
257
+ rationale: `${counts.high} high-severity ${pluralize('bug', counts.high)}`,
258
+ };
259
+ }
260
+ return null;
261
+ }
262
+
263
+ function reliabilityBandMid(counts: SeverityCounts): BandResult {
264
+ if (counts.high >= 1) {
265
+ return {
266
+ grade: 'C',
267
+ position: 1 / (1 + counts.high),
268
+ rationale: `${counts.high} high-severity ${pluralize('bug', counts.high)}`,
269
+ };
270
+ }
271
+ if (counts.medium >= 3) {
272
+ return {
273
+ grade: 'C',
274
+ position: 1 / Math.max(1, counts.medium - 2),
275
+ rationale: `${counts.medium} medium-severity reliability ${pluralize('issue', counts.medium)}`,
276
+ };
277
+ }
278
+ if (counts.medium >= 1) {
279
+ return {
280
+ grade: 'B',
281
+ position: 1 / Math.max(1, counts.medium),
282
+ rationale: `${counts.medium} medium-severity reliability ${pluralize('issue', counts.medium)}`,
283
+ };
284
+ }
285
+ // Only low-severity findings, > 1 of them.
286
+ return {
287
+ grade: 'B',
288
+ position: 1 / Math.max(1, counts.low - 1),
289
+ rationale: `${counts.low} low-severity reliability ${pluralize('issue', counts.low)}`,
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Reliability grading — slightly more lenient than Security because not every
295
+ * complexity warning is a runtime defect. A single low-severity logic issue
296
+ * still earns an A; medium issues escalate gradually.
297
+ */
298
+ function gradeReliability(findings: Array<{ severity: string }>): DimensionScore {
299
+ const counts = countSeverities(findings);
300
+ const worst = worstSeverity(counts);
301
+ const isClean = counts.total === 0 || (counts.low <= 1 && counts.medium === 0 && counts.high === 0 && counts.critical === 0);
302
+ const band = isClean
303
+ ? reliabilityBandClean(counts)
304
+ : reliabilityBandSevere(counts) ?? reliabilityBandMid(counts);
305
+
306
+ return {
307
+ name: 'reliability',
308
+ score: scoreInBand(band.grade, band.position),
309
+ grade: band.grade,
310
+ rationale: band.rationale,
311
+ available: true,
312
+ findingCount: counts.total,
313
+ worstSeverity: worst,
314
+ };
315
+ }
316
+
317
+ // ============================================================================
318
+ // Maintainability Dimension
319
+ // ============================================================================
320
+
321
+ interface MaintainabilityBand {
322
+ grade: Exclude<Grade, 'N/A'>;
323
+ position: number;
324
+ label: string;
325
+ }
326
+
327
+ function maintainabilityByCount(n: number): MaintainabilityBand {
328
+ const label = `${n} maintainability ${pluralize('issue', n)}`;
329
+ if (n <= 5) return { grade: 'A', position: 1 - n / 5, label };
330
+ if (n <= 15) return { grade: 'B', position: 1 - (n - 5) / 10, label };
331
+ if (n <= 30) return { grade: 'C', position: 1 - (n - 15) / 15, label };
332
+ if (n <= 60) return { grade: 'D', position: 1 - (n - 30) / 30, label };
333
+ return { grade: 'F', position: 1 / (1 + (n - 60) / 30), label };
334
+ }
335
+
336
+ function maintainabilityByDensity(n: number, kloc: number): MaintainabilityBand {
337
+ const density = n / kloc;
338
+ const label = `${roundOne(density)} ${pluralize('issue', n)} / KLOC`;
339
+ if (density < 5) return { grade: 'A', position: 1 - density / 5, label };
340
+ if (density < 10) return { grade: 'B', position: 1 - (density - 5) / 5, label };
341
+ if (density < 25) return { grade: 'C', position: 1 - (density - 10) / 15, label };
342
+ if (density < 50) return { grade: 'D', position: 1 - (density - 25) / 25, label };
343
+ return { grade: 'F', position: 1 / (1 + (density - 50) / 25), label };
344
+ }
345
+
346
+ interface SeverityEscape {
347
+ grade: Exclude<Grade, 'N/A'>;
348
+ note: string;
349
+ }
350
+
351
+ function maintainabilityEscape(counts: SeverityCounts): SeverityEscape | null {
352
+ if (counts.critical > 0) {
353
+ return { grade: 'D', note: `${counts.critical} critical-severity ${pluralize('issue', counts.critical)}` };
354
+ }
355
+ if (counts.high > 0) {
356
+ return { grade: 'C', note: `${counts.high} high-severity ${pluralize('issue', counts.high)}` };
357
+ }
358
+ return null;
359
+ }
360
+
361
+ /**
362
+ * Maintainability uses a density-based grade (issues per KLOC) once the
363
+ * codebase is at least 5 KLOC. For smaller codebases, density is too noisy
364
+ * (one extra lint issue moves density by 1.0+), so we fall back to absolute
365
+ * counts — preventing tiny projects from being unfairly penalized.
366
+ *
367
+ * Severity escape hatch: a single high-severity maintainability finding
368
+ * (e.g., a 1500-line file) caps the grade at C; a critical caps at D.
369
+ * "Worst wins" — we take min of density-grade and severity-cap.
370
+ */
371
+ function gradeMaintainability(findings: Array<{ severity: string }>, totalLines: number): DimensionScore {
372
+ const counts = countSeverities(findings);
373
+ const kloc = Math.max(totalLines / 1000, 1.0);
374
+
375
+ if (counts.total === 0) {
376
+ return {
377
+ name: 'maintainability',
378
+ score: 100,
379
+ grade: 'A',
380
+ rationale: '0 maintainability findings',
381
+ available: true,
382
+ findingCount: 0,
383
+ worstSeverity: null,
384
+ };
385
+ }
386
+
387
+ const band = kloc < 5 ? maintainabilityByCount(counts.total) : maintainabilityByDensity(counts.total, kloc);
388
+ const severityCap = maintainabilityEscape(counts);
389
+ const useCap = severityCap && gradeIsWorse(severityCap.grade, band.grade);
390
+ const finalGrade = useCap ? severityCap.grade : band.grade;
391
+ const finalPosition = useCap ? 0.5 : band.position;
392
+ const rationale = useCap ? `${band.label}, ${severityCap.note}` : band.label;
393
+
394
+ return {
395
+ name: 'maintainability',
396
+ score: scoreInBand(finalGrade, finalPosition),
397
+ grade: finalGrade,
398
+ rationale,
399
+ available: true,
400
+ findingCount: counts.total,
401
+ worstSeverity: worstSeverity(counts),
402
+ };
403
+ }
404
+
405
+ // ============================================================================
406
+ // Grade Comparison Helpers
407
+ // ============================================================================
408
+
409
+ const GRADE_RANK: Record<Exclude<Grade, 'N/A'>, number> = {
410
+ F: 1,
411
+ D: 2,
412
+ C: 3,
413
+ B: 4,
414
+ A: 5,
415
+ };
416
+
417
+ function gradeIsWorse(a: Exclude<Grade, 'N/A'>, b: Exclude<Grade, 'N/A'>): boolean {
418
+ return GRADE_RANK[a] < GRADE_RANK[b];
419
+ }
420
+
421
+ function worstOf(grades: Array<Exclude<Grade, 'N/A'>>): Exclude<Grade, 'N/A'> {
422
+ let worst: Exclude<Grade, 'N/A'> = 'A';
423
+ for (const g of grades) {
424
+ if (gradeIsWorse(g, worst)) worst = g;
425
+ }
426
+ return worst;
427
+ }
428
+
429
+ // ============================================================================
430
+ // Misc Helpers
431
+ // ============================================================================
432
+
433
+ function pluralize(word: string, n: number): string {
434
+ return n === 1 ? word : `${word}s`;
435
+ }
436
+
437
+ function roundOne(n: number): number {
438
+ return Math.round(n * 10) / 10;
439
+ }
440
+
441
+ function dimensionDisplayName(name: DimensionName): string {
442
+ return name.charAt(0).toUpperCase() + name.slice(1);
443
+ }
444
+
445
+ function naDimension(name: DimensionName): DimensionScore {
446
+ return {
447
+ name,
448
+ score: 0,
449
+ grade: 'N/A',
450
+ rationale: 'No tools available to evaluate',
451
+ available: false,
452
+ findingCount: 0,
453
+ worstSeverity: null,
454
+ };
455
+ }
456
+
457
+ // ============================================================================
458
+ // Top-Level Entry Point
459
+ // ============================================================================
460
+
461
+ /**
462
+ * Compute the full multi-dimensional quality rating from the merged finding
463
+ * set. Callers can override availability in two ways:
464
+ * - `availableDimensions`: hard whitelist — only listed dims are graded.
465
+ * - `forceNA`: forces specific dims to N/A even if they would otherwise
466
+ * auto-detect as available. Use this when the underlying tools didn't
467
+ * run (e.g., no linter installed -> Maintainability has limited coverage).
468
+ *
469
+ * Default availability rules:
470
+ * - maintainability is always available (lint/format/length checks always run)
471
+ * - security/reliability are available iff at least one finding maps there
472
+ *
473
+ * Overall score uses min(avg, worst) so a single bad dimension caps the
474
+ * total — you cannot earn a great overall score by averaging away a hole.
475
+ */
476
+ function bucketByDimension(
477
+ findings: Array<{ severity: string; category: string }>,
478
+ ): {
479
+ security: Array<{ severity: string; category: string }>;
480
+ reliability: Array<{ severity: string; category: string }>;
481
+ maintainability: Array<{ severity: string; category: string }>;
482
+ } {
483
+ const security: Array<{ severity: string; category: string }> = [];
484
+ const reliability: Array<{ severity: string; category: string }> = [];
485
+ const maintainability: Array<{ severity: string; category: string }> = [];
486
+ for (const f of findings) {
487
+ const dim = categoryToDimension(f.category);
488
+ if (dim === 'security') security.push(f);
489
+ else if (dim === 'reliability') reliability.push(f);
490
+ else maintainability.push(f);
491
+ }
492
+ return { security, reliability, maintainability };
493
+ }
494
+
495
+ function isDimensionAvailable(
496
+ dim: DimensionName,
497
+ hasFindings: boolean,
498
+ options?: { availableDimensions?: Set<DimensionName>; forceNA?: Set<DimensionName> },
499
+ ): boolean {
500
+ if (options?.forceNA?.has(dim)) return false;
501
+ const explicit = options?.availableDimensions;
502
+ if (explicit) return explicit.has(dim);
503
+ // Auto-detect: maintainability always on, security/reliability iff findings exist.
504
+ return dim === 'maintainability' ? true : hasFindings;
505
+ }
506
+
507
+ function computeOverall(availableDims: DimensionScore[]): { grade: Grade; score: number } {
508
+ if (availableDims.length === 0) {
509
+ return { grade: 'N/A', score: 0 };
510
+ }
511
+ const grades = availableDims.map((d) => d.grade as Exclude<Grade, 'N/A'>);
512
+ const scores = availableDims.map((d) => d.score);
513
+ const avg = scores.reduce((s, n) => s + n, 0) / scores.length;
514
+ return {
515
+ grade: worstOf(grades),
516
+ score: Math.round(Math.min(avg, Math.min(...scores))),
517
+ };
518
+ }
519
+
520
+ export function computeQualityRating(
521
+ allFindings: Array<{ severity: string; category: string }>,
522
+ totalLines: number,
523
+ options?: { availableDimensions?: Set<DimensionName>; forceNA?: Set<DimensionName> },
524
+ ): QualityRating {
525
+ const buckets = bucketByDimension(allFindings);
526
+
527
+ const security = isDimensionAvailable('security', buckets.security.length > 0, options)
528
+ ? gradeSecurity(buckets.security)
529
+ : naDimension('security');
530
+ const reliability = isDimensionAvailable('reliability', buckets.reliability.length > 0, options)
531
+ ? gradeReliability(buckets.reliability)
532
+ : naDimension('reliability');
533
+ const maintainability = isDimensionAvailable('maintainability', true, options)
534
+ ? gradeMaintainability(buckets.maintainability, totalLines)
535
+ : naDimension('maintainability');
536
+
537
+ const dimensions: DimensionScore[] = [security, reliability, maintainability];
538
+ const availableDims = dimensions.filter((d) => d.available);
539
+ const overall = computeOverall(availableDims);
540
+
541
+ // Quality gate.
542
+ const qualityGate = computeQualityGate(security, reliability);
543
+
544
+ // Grade rationale.
545
+ const gradeRationale = computeGradeRationale(availableDims, overall.grade, allFindings.length);
546
+
547
+ return {
548
+ overall,
549
+ dimensions,
550
+ qualityGate,
551
+ gradeRationale,
552
+ };
553
+ }
554
+
555
+ // ============================================================================
556
+ // Quality Gate
557
+ // ============================================================================
558
+
559
+ /**
560
+ * The Quality Gate is a coarse PASS/FAIL signal layered on top of the grades.
561
+ * It only fires for the most user-actionable thresholds — any medium+ security
562
+ * finding, or any critical bug. N/A dimensions never trigger a fail (we don't
563
+ * fail on missing data).
564
+ */
565
+ function computeQualityGate(security: DimensionScore, reliability: DimensionScore): QualityGate {
566
+ const failingConditions: string[] = [];
567
+
568
+ if (security.available && (security.grade === 'C' || security.grade === 'D' || security.grade === 'F')) {
569
+ failingConditions.push(`Security grade ${security.grade} — ${security.rationale}`);
570
+ }
571
+ if (reliability.available && reliability.grade === 'F') {
572
+ failingConditions.push(`Reliability grade F — ${reliability.rationale}`);
573
+ }
574
+
575
+ return {
576
+ passed: failingConditions.length === 0,
577
+ failingConditions,
578
+ };
579
+ }
580
+
581
+ // ============================================================================
582
+ // Grade Rationale
583
+ // ============================================================================
584
+
585
+ function computeGradeRationale(
586
+ availableDims: DimensionScore[],
587
+ overallGrade: Grade,
588
+ totalFindingCount: number,
589
+ ): string {
590
+ if (totalFindingCount === 0) {
591
+ return 'Clean — no findings detected';
592
+ }
593
+ if (availableDims.length === 0 || overallGrade === 'N/A') {
594
+ return 'No dimensions available to grade';
595
+ }
596
+
597
+ // All available dimensions equal -> "consistent quality".
598
+ const firstGrade = availableDims[0].grade;
599
+ const allEqual = availableDims.every((d) => d.grade === firstGrade);
600
+ if (allEqual) {
601
+ return `All dimensions ${firstGrade} — consistent quality`;
602
+ }
603
+
604
+ // Find the dimension that pinned the overall grade (worst available).
605
+ const worstDim =
606
+ availableDims.find((d) => d.grade === overallGrade) ??
607
+ // Fallback shouldn't fire since overallGrade was derived from availableDims.
608
+ availableDims[0];
609
+
610
+ return `Capped at ${overallGrade} by ${dimensionDisplayName(worstDim.name)} (${worstDim.rationale})`;
611
+ }