mstro-app 0.4.2 → 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 (342) hide show
  1. package/bin/mstro.js +119 -40
  2. package/dist/server/cli/headless/claude-invoker-process.d.ts +11 -0
  3. package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -0
  4. package/dist/server/cli/headless/claude-invoker-process.js +140 -0
  5. package/dist/server/cli/headless/claude-invoker-process.js.map +1 -0
  6. package/dist/server/cli/headless/claude-invoker-stall.d.ts +40 -0
  7. package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -0
  8. package/dist/server/cli/headless/claude-invoker-stall.js +98 -0
  9. package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -0
  10. package/dist/server/cli/headless/claude-invoker-stream.d.ts +44 -0
  11. package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -0
  12. package/dist/server/cli/headless/claude-invoker-stream.js +276 -0
  13. package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -0
  14. package/dist/server/cli/headless/claude-invoker-tools.d.ts +21 -0
  15. package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -0
  16. package/dist/server/cli/headless/claude-invoker-tools.js +137 -0
  17. package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -0
  18. package/dist/server/cli/headless/claude-invoker.d.ts +6 -4
  19. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  20. package/dist/server/cli/headless/claude-invoker.js +10 -804
  21. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  22. package/dist/server/cli/headless/haiku-assessments.d.ts +62 -0
  23. package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -0
  24. package/dist/server/cli/headless/haiku-assessments.js +281 -0
  25. package/dist/server/cli/headless/haiku-assessments.js.map +1 -0
  26. package/dist/server/cli/headless/headless-logger.d.ts +3 -2
  27. package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
  28. package/dist/server/cli/headless/headless-logger.js +28 -5
  29. package/dist/server/cli/headless/headless-logger.js.map +1 -1
  30. package/dist/server/cli/headless/native-timeout-detector.d.ts +44 -0
  31. package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -0
  32. package/dist/server/cli/headless/native-timeout-detector.js +99 -0
  33. package/dist/server/cli/headless/native-timeout-detector.js.map +1 -0
  34. package/dist/server/cli/headless/stall-assessor.d.ts +2 -110
  35. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  36. package/dist/server/cli/headless/stall-assessor.js +65 -457
  37. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  38. package/dist/server/cli/headless/types.d.ts +4 -1
  39. package/dist/server/cli/headless/types.d.ts.map +1 -1
  40. package/dist/server/cli/improvisation-attachments.d.ts +21 -0
  41. package/dist/server/cli/improvisation-attachments.d.ts.map +1 -0
  42. package/dist/server/cli/improvisation-attachments.js +116 -0
  43. package/dist/server/cli/improvisation-attachments.js.map +1 -0
  44. package/dist/server/cli/improvisation-retry.d.ts +52 -0
  45. package/dist/server/cli/improvisation-retry.d.ts.map +1 -0
  46. package/dist/server/cli/improvisation-retry.js +434 -0
  47. package/dist/server/cli/improvisation-retry.js.map +1 -0
  48. package/dist/server/cli/improvisation-session-manager.d.ts +10 -266
  49. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  50. package/dist/server/cli/improvisation-session-manager.js +117 -1079
  51. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  52. package/dist/server/cli/improvisation-types.d.ts +86 -0
  53. package/dist/server/cli/improvisation-types.d.ts.map +1 -0
  54. package/dist/server/cli/improvisation-types.js +10 -0
  55. package/dist/server/cli/improvisation-types.js.map +1 -0
  56. package/dist/server/cli/prompt-builders.d.ts +68 -0
  57. package/dist/server/cli/prompt-builders.d.ts.map +1 -0
  58. package/dist/server/cli/prompt-builders.js +312 -0
  59. package/dist/server/cli/prompt-builders.js.map +1 -0
  60. package/dist/server/index.js +33 -212
  61. package/dist/server/index.js.map +1 -1
  62. package/dist/server/mcp/bouncer-haiku.d.ts +10 -0
  63. package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -0
  64. package/dist/server/mcp/bouncer-haiku.js +152 -0
  65. package/dist/server/mcp/bouncer-haiku.js.map +1 -0
  66. package/dist/server/mcp/bouncer-integration.d.ts +3 -4
  67. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  68. package/dist/server/mcp/bouncer-integration.js +50 -196
  69. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  70. package/dist/server/mcp/security-analysis.d.ts +38 -0
  71. package/dist/server/mcp/security-analysis.d.ts.map +1 -0
  72. package/dist/server/mcp/security-analysis.js +183 -0
  73. package/dist/server/mcp/security-analysis.js.map +1 -0
  74. package/dist/server/mcp/security-audit.d.ts +1 -1
  75. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  76. package/dist/server/mcp/security-patterns.d.ts +1 -25
  77. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  78. package/dist/server/mcp/security-patterns.js +55 -260
  79. package/dist/server/mcp/security-patterns.js.map +1 -1
  80. package/dist/server/server-setup.d.ts +22 -0
  81. package/dist/server/server-setup.d.ts.map +1 -0
  82. package/dist/server/server-setup.js +101 -0
  83. package/dist/server/server-setup.js.map +1 -0
  84. package/dist/server/services/file-explorer-ops.d.ts +24 -0
  85. package/dist/server/services/file-explorer-ops.d.ts.map +1 -0
  86. package/dist/server/services/file-explorer-ops.js +211 -0
  87. package/dist/server/services/file-explorer-ops.js.map +1 -0
  88. package/dist/server/services/files.d.ts +2 -85
  89. package/dist/server/services/files.d.ts.map +1 -1
  90. package/dist/server/services/files.js +7 -427
  91. package/dist/server/services/files.js.map +1 -1
  92. package/dist/server/services/plan/composer.d.ts +1 -1
  93. package/dist/server/services/plan/composer.d.ts.map +1 -1
  94. package/dist/server/services/plan/composer.js +118 -32
  95. package/dist/server/services/plan/composer.js.map +1 -1
  96. package/dist/server/services/plan/config-installer.d.ts +25 -0
  97. package/dist/server/services/plan/config-installer.d.ts.map +1 -0
  98. package/dist/server/services/plan/config-installer.js +182 -0
  99. package/dist/server/services/plan/config-installer.js.map +1 -0
  100. package/dist/server/services/plan/dependency-resolver.d.ts +1 -1
  101. package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
  102. package/dist/server/services/plan/dependency-resolver.js +4 -1
  103. package/dist/server/services/plan/dependency-resolver.js.map +1 -1
  104. package/dist/server/services/plan/executor.d.ts +38 -74
  105. package/dist/server/services/plan/executor.d.ts.map +1 -1
  106. package/dist/server/services/plan/executor.js +274 -460
  107. package/dist/server/services/plan/executor.js.map +1 -1
  108. package/dist/server/services/plan/front-matter.d.ts +18 -0
  109. package/dist/server/services/plan/front-matter.d.ts.map +1 -0
  110. package/dist/server/services/plan/front-matter.js +44 -0
  111. package/dist/server/services/plan/front-matter.js.map +1 -0
  112. package/dist/server/services/plan/output-manager.d.ts +22 -0
  113. package/dist/server/services/plan/output-manager.d.ts.map +1 -0
  114. package/dist/server/services/plan/output-manager.js +97 -0
  115. package/dist/server/services/plan/output-manager.js.map +1 -0
  116. package/dist/server/services/plan/parser-core.d.ts +20 -0
  117. package/dist/server/services/plan/parser-core.d.ts.map +1 -0
  118. package/dist/server/services/plan/parser-core.js +350 -0
  119. package/dist/server/services/plan/parser-core.js.map +1 -0
  120. package/dist/server/services/plan/parser-migration.d.ts +5 -0
  121. package/dist/server/services/plan/parser-migration.d.ts.map +1 -0
  122. package/dist/server/services/plan/parser-migration.js +124 -0
  123. package/dist/server/services/plan/parser-migration.js.map +1 -0
  124. package/dist/server/services/plan/parser.d.ts +11 -3
  125. package/dist/server/services/plan/parser.d.ts.map +1 -1
  126. package/dist/server/services/plan/parser.js +184 -369
  127. package/dist/server/services/plan/parser.js.map +1 -1
  128. package/dist/server/services/plan/prompt-builder.d.ts +17 -0
  129. package/dist/server/services/plan/prompt-builder.d.ts.map +1 -0
  130. package/dist/server/services/plan/prompt-builder.js +137 -0
  131. package/dist/server/services/plan/prompt-builder.js.map +1 -0
  132. package/dist/server/services/plan/review-gate.d.ts +28 -0
  133. package/dist/server/services/plan/review-gate.d.ts.map +1 -0
  134. package/dist/server/services/plan/review-gate.js +191 -0
  135. package/dist/server/services/plan/review-gate.js.map +1 -0
  136. package/dist/server/services/plan/state-reconciler.d.ts +1 -1
  137. package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
  138. package/dist/server/services/plan/state-reconciler.js +59 -7
  139. package/dist/server/services/plan/state-reconciler.js.map +1 -1
  140. package/dist/server/services/plan/types.d.ts +68 -0
  141. package/dist/server/services/plan/types.d.ts.map +1 -1
  142. package/dist/server/services/platform-credentials.d.ts +24 -0
  143. package/dist/server/services/platform-credentials.d.ts.map +1 -0
  144. package/dist/server/services/platform-credentials.js +68 -0
  145. package/dist/server/services/platform-credentials.js.map +1 -0
  146. package/dist/server/services/platform.d.ts +1 -31
  147. package/dist/server/services/platform.d.ts.map +1 -1
  148. package/dist/server/services/platform.js +11 -109
  149. package/dist/server/services/platform.js.map +1 -1
  150. package/dist/server/services/terminal/pty-manager.d.ts +7 -97
  151. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  152. package/dist/server/services/terminal/pty-manager.js +53 -266
  153. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  154. package/dist/server/services/terminal/pty-utils.d.ts +57 -0
  155. package/dist/server/services/terminal/pty-utils.d.ts.map +1 -0
  156. package/dist/server/services/terminal/pty-utils.js +141 -0
  157. package/dist/server/services/terminal/pty-utils.js.map +1 -0
  158. package/dist/server/services/websocket/file-definition-handlers.d.ts +4 -0
  159. package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -0
  160. package/dist/server/services/websocket/file-definition-handlers.js +153 -0
  161. package/dist/server/services/websocket/file-definition-handlers.js.map +1 -0
  162. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
  163. package/dist/server/services/websocket/file-explorer-handlers.js +52 -391
  164. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
  165. package/dist/server/services/websocket/file-search-handlers.d.ts +5 -0
  166. package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -0
  167. package/dist/server/services/websocket/file-search-handlers.js +238 -0
  168. package/dist/server/services/websocket/file-search-handlers.js.map +1 -0
  169. package/dist/server/services/websocket/file-utils.js +3 -3
  170. package/dist/server/services/websocket/file-utils.js.map +1 -1
  171. package/dist/server/services/websocket/git-branch-handlers.d.ts +7 -0
  172. package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -0
  173. package/dist/server/services/websocket/git-branch-handlers.js +110 -0
  174. package/dist/server/services/websocket/git-branch-handlers.js.map +1 -0
  175. package/dist/server/services/websocket/git-diff-handlers.d.ts +6 -0
  176. package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -0
  177. package/dist/server/services/websocket/git-diff-handlers.js +123 -0
  178. package/dist/server/services/websocket/git-diff-handlers.js.map +1 -0
  179. package/dist/server/services/websocket/git-handlers.d.ts +2 -31
  180. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
  181. package/dist/server/services/websocket/git-handlers.js +35 -541
  182. package/dist/server/services/websocket/git-handlers.js.map +1 -1
  183. package/dist/server/services/websocket/git-log-handlers.d.ts +6 -0
  184. package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -0
  185. package/dist/server/services/websocket/git-log-handlers.js +128 -0
  186. package/dist/server/services/websocket/git-log-handlers.js.map +1 -0
  187. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
  188. package/dist/server/services/websocket/git-pr-handlers.js +13 -53
  189. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
  190. package/dist/server/services/websocket/git-tag-handlers.d.ts +6 -0
  191. package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -0
  192. package/dist/server/services/websocket/git-tag-handlers.js +76 -0
  193. package/dist/server/services/websocket/git-tag-handlers.js.map +1 -0
  194. package/dist/server/services/websocket/git-utils.d.ts +43 -0
  195. package/dist/server/services/websocket/git-utils.d.ts.map +1 -0
  196. package/dist/server/services/websocket/git-utils.js +201 -0
  197. package/dist/server/services/websocket/git-utils.js.map +1 -0
  198. package/dist/server/services/websocket/handler.d.ts +2 -0
  199. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  200. package/dist/server/services/websocket/handler.js +37 -112
  201. package/dist/server/services/websocket/handler.js.map +1 -1
  202. package/dist/server/services/websocket/plan-board-handlers.d.ts +11 -0
  203. package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -0
  204. package/dist/server/services/websocket/plan-board-handlers.js +218 -0
  205. package/dist/server/services/websocket/plan-board-handlers.js.map +1 -0
  206. package/dist/server/services/websocket/plan-execution-handlers.d.ts +9 -0
  207. package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -0
  208. package/dist/server/services/websocket/plan-execution-handlers.js +142 -0
  209. package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -0
  210. package/dist/server/services/websocket/plan-handlers.d.ts +7 -2
  211. package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
  212. package/dist/server/services/websocket/plan-handlers.js +21 -462
  213. package/dist/server/services/websocket/plan-handlers.js.map +1 -1
  214. package/dist/server/services/websocket/plan-helpers.d.ts +19 -0
  215. package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -0
  216. package/dist/server/services/websocket/plan-helpers.js +199 -0
  217. package/dist/server/services/websocket/plan-helpers.js.map +1 -0
  218. package/dist/server/services/websocket/plan-issue-handlers.d.ts +12 -0
  219. package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -0
  220. package/dist/server/services/websocket/plan-issue-handlers.js +162 -0
  221. package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -0
  222. package/dist/server/services/websocket/plan-sprint-handlers.d.ts +7 -0
  223. package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -0
  224. package/dist/server/services/websocket/plan-sprint-handlers.js +206 -0
  225. package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -0
  226. package/dist/server/services/websocket/quality-complexity.d.ts +14 -0
  227. package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -0
  228. package/dist/server/services/websocket/quality-complexity.js +262 -0
  229. package/dist/server/services/websocket/quality-complexity.js.map +1 -0
  230. package/dist/server/services/websocket/quality-fix-agent.d.ts +16 -0
  231. package/dist/server/services/websocket/quality-fix-agent.d.ts.map +1 -0
  232. package/dist/server/services/websocket/quality-fix-agent.js +140 -0
  233. package/dist/server/services/websocket/quality-fix-agent.js.map +1 -0
  234. package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
  235. package/dist/server/services/websocket/quality-handlers.js +34 -346
  236. package/dist/server/services/websocket/quality-handlers.js.map +1 -1
  237. package/dist/server/services/websocket/quality-linting.d.ts +9 -0
  238. package/dist/server/services/websocket/quality-linting.d.ts.map +1 -0
  239. package/dist/server/services/websocket/quality-linting.js +178 -0
  240. package/dist/server/services/websocket/quality-linting.js.map +1 -0
  241. package/dist/server/services/websocket/quality-review-agent.d.ts +19 -0
  242. package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -0
  243. package/dist/server/services/websocket/quality-review-agent.js +206 -0
  244. package/dist/server/services/websocket/quality-review-agent.js.map +1 -0
  245. package/dist/server/services/websocket/quality-service.d.ts +3 -51
  246. package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
  247. package/dist/server/services/websocket/quality-service.js +9 -651
  248. package/dist/server/services/websocket/quality-service.js.map +1 -1
  249. package/dist/server/services/websocket/quality-tools.d.ts +23 -0
  250. package/dist/server/services/websocket/quality-tools.d.ts.map +1 -0
  251. package/dist/server/services/websocket/quality-tools.js +208 -0
  252. package/dist/server/services/websocket/quality-tools.js.map +1 -0
  253. package/dist/server/services/websocket/quality-types.d.ts +59 -0
  254. package/dist/server/services/websocket/quality-types.d.ts.map +1 -0
  255. package/dist/server/services/websocket/quality-types.js +101 -0
  256. package/dist/server/services/websocket/quality-types.js.map +1 -0
  257. package/dist/server/services/websocket/session-handlers.d.ts +3 -4
  258. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
  259. package/dist/server/services/websocket/session-handlers.js +3 -378
  260. package/dist/server/services/websocket/session-handlers.js.map +1 -1
  261. package/dist/server/services/websocket/session-history.d.ts +4 -0
  262. package/dist/server/services/websocket/session-history.d.ts.map +1 -0
  263. package/dist/server/services/websocket/session-history.js +208 -0
  264. package/dist/server/services/websocket/session-history.js.map +1 -0
  265. package/dist/server/services/websocket/session-initialization.d.ts +5 -0
  266. package/dist/server/services/websocket/session-initialization.d.ts.map +1 -0
  267. package/dist/server/services/websocket/session-initialization.js +163 -0
  268. package/dist/server/services/websocket/session-initialization.js.map +1 -0
  269. package/dist/server/services/websocket/types.d.ts +12 -2
  270. package/dist/server/services/websocket/types.d.ts.map +1 -1
  271. package/package.json +1 -2
  272. package/server/cli/headless/claude-invoker-process.ts +204 -0
  273. package/server/cli/headless/claude-invoker-stall.ts +164 -0
  274. package/server/cli/headless/claude-invoker-stream.ts +353 -0
  275. package/server/cli/headless/claude-invoker-tools.ts +187 -0
  276. package/server/cli/headless/claude-invoker.ts +15 -1092
  277. package/server/cli/headless/haiku-assessments.ts +365 -0
  278. package/server/cli/headless/headless-logger.ts +26 -5
  279. package/server/cli/headless/native-timeout-detector.ts +117 -0
  280. package/server/cli/headless/stall-assessor.ts +65 -618
  281. package/server/cli/headless/types.ts +4 -1
  282. package/server/cli/improvisation-attachments.ts +148 -0
  283. package/server/cli/improvisation-retry.ts +602 -0
  284. package/server/cli/improvisation-session-manager.ts +140 -1349
  285. package/server/cli/improvisation-types.ts +98 -0
  286. package/server/cli/prompt-builders.ts +370 -0
  287. package/server/index.ts +35 -246
  288. package/server/mcp/bouncer-haiku.ts +182 -0
  289. package/server/mcp/bouncer-integration.ts +87 -248
  290. package/server/mcp/security-analysis.ts +217 -0
  291. package/server/mcp/security-audit.ts +1 -1
  292. package/server/mcp/security-patterns.ts +60 -283
  293. package/server/server-setup.ts +114 -0
  294. package/server/services/file-explorer-ops.ts +293 -0
  295. package/server/services/files.ts +20 -532
  296. package/server/services/plan/composer.ts +140 -35
  297. package/server/services/plan/config-installer.ts +187 -0
  298. package/server/services/plan/dependency-resolver.ts +4 -1
  299. package/server/services/plan/executor.ts +281 -488
  300. package/server/services/plan/front-matter.ts +48 -0
  301. package/server/services/plan/output-manager.ts +113 -0
  302. package/server/services/plan/parser-core.ts +406 -0
  303. package/server/services/plan/parser-migration.ts +128 -0
  304. package/server/services/plan/parser.ts +188 -394
  305. package/server/services/plan/prompt-builder.ts +161 -0
  306. package/server/services/plan/review-gate.ts +212 -0
  307. package/server/services/plan/state-reconciler.ts +68 -7
  308. package/server/services/plan/types.ts +101 -1
  309. package/server/services/platform-credentials.ts +83 -0
  310. package/server/services/platform.ts +16 -131
  311. package/server/services/terminal/pty-manager.ts +66 -313
  312. package/server/services/terminal/pty-utils.ts +176 -0
  313. package/server/services/websocket/file-definition-handlers.ts +165 -0
  314. package/server/services/websocket/file-explorer-handlers.ts +37 -452
  315. package/server/services/websocket/file-search-handlers.ts +291 -0
  316. package/server/services/websocket/file-utils.ts +3 -3
  317. package/server/services/websocket/git-branch-handlers.ts +130 -0
  318. package/server/services/websocket/git-diff-handlers.ts +140 -0
  319. package/server/services/websocket/git-handlers.ts +40 -625
  320. package/server/services/websocket/git-log-handlers.ts +149 -0
  321. package/server/services/websocket/git-pr-handlers.ts +17 -62
  322. package/server/services/websocket/git-tag-handlers.ts +91 -0
  323. package/server/services/websocket/git-utils.ts +230 -0
  324. package/server/services/websocket/handler.ts +39 -112
  325. package/server/services/websocket/plan-board-handlers.ts +277 -0
  326. package/server/services/websocket/plan-execution-handlers.ts +184 -0
  327. package/server/services/websocket/plan-handlers.ts +23 -544
  328. package/server/services/websocket/plan-helpers.ts +215 -0
  329. package/server/services/websocket/plan-issue-handlers.ts +204 -0
  330. package/server/services/websocket/plan-sprint-handlers.ts +252 -0
  331. package/server/services/websocket/quality-complexity.ts +294 -0
  332. package/server/services/websocket/quality-fix-agent.ts +181 -0
  333. package/server/services/websocket/quality-handlers.ts +36 -404
  334. package/server/services/websocket/quality-linting.ts +187 -0
  335. package/server/services/websocket/quality-review-agent.ts +246 -0
  336. package/server/services/websocket/quality-service.ts +11 -762
  337. package/server/services/websocket/quality-tools.ts +209 -0
  338. package/server/services/websocket/quality-types.ts +169 -0
  339. package/server/services/websocket/session-handlers.ts +5 -437
  340. package/server/services/websocket/session-history.ts +222 -0
  341. package/server/services/websocket/session-initialization.ts +209 -0
  342. package/server/services/websocket/types.ts +46 -2
@@ -1,360 +1,41 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
2
  // Licensed under the MIT License. See LICENSE file for details.
3
3
  /**
4
- * PPS Parser — Parses .pm/ (or legacy .plan/) directory files into structured TypeScript objects.
4
+ * PPS Parser — Public API for reading .pm/ (or legacy .plan/) directories.
5
5
  *
6
- * Handles YAML front matter extraction and markdown body parsing.
6
+ * Entity parsing lives in parser-core.ts; migration in parser-migration.ts.
7
7
  */
8
8
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
- function stripQuotes(v) {
11
- if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
12
- return v.slice(1, -1);
13
- }
14
- return v;
15
- }
16
- function parseYamlValue(v) {
17
- if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
18
- return v.slice(1, -1);
19
- }
20
- if (v.startsWith('[') && v.endsWith(']')) {
21
- return v.slice(1, -1).split(',').map(s => stripQuotes(s.trim())).filter(Boolean);
22
- }
23
- if (v === 'true')
24
- return true;
25
- if (v === 'false')
26
- return false;
27
- if (v === 'null' || v === '~' || v === '')
28
- return null;
29
- if (/^-?\d+(\.\d+)?$/.test(v))
30
- return Number(v);
31
- return v;
32
- }
33
- /** Consume indented YAML list items starting after the current index. Returns [items, newIndex]. */
34
- function consumeIndentedList(lines, startIdx) {
35
- const items = [];
36
- let i = startIdx;
37
- while (i + 1 < lines.length && /^\s+-\s/.test(lines[i + 1])) {
38
- i++;
39
- const item = lines[i].trim().replace(/^-\s+/, '');
40
- items.push(stripQuotes(item));
41
- }
42
- return [items, i];
43
- }
44
- function parseFrontMatter(content) {
45
- const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
46
- if (!match) {
47
- return { frontMatter: {}, body: content };
48
- }
49
- const frontMatter = {};
50
- const lines = match[1].split('\n');
51
- for (let i = 0; i < lines.length; i++) {
52
- const trimmed = lines[i].trim();
53
- if (!trimmed || trimmed.startsWith('#'))
54
- continue;
55
- const colonIdx = trimmed.indexOf(':');
56
- if (colonIdx === -1)
57
- continue;
58
- const key = trimmed.slice(0, colonIdx).trim();
59
- const rawValue = trimmed.slice(colonIdx + 1).trim();
60
- if (!rawValue) {
61
- const [items, newIdx] = consumeIndentedList(lines, i);
62
- i = newIdx;
63
- frontMatter[key] = items.length > 0 ? items : null;
64
- }
65
- else {
66
- frontMatter[key] = parseYamlValue(rawValue);
67
- }
68
- }
69
- return { frontMatter, body: match[2] };
70
- }
71
- // ============================================================================
72
- // Section Extraction
73
- // ============================================================================
74
- function extractSections(body) {
75
- const sections = new Map();
76
- const lines = body.split('\n');
77
- let currentSection = '';
78
- let currentContent = [];
79
- for (const line of lines) {
80
- if (line.startsWith('## ')) {
81
- if (currentSection) {
82
- sections.set(currentSection, currentContent.join('\n').trim());
83
- }
84
- currentSection = line.slice(3).trim();
85
- currentContent = [];
86
- }
87
- else {
88
- currentContent.push(line);
89
- }
90
- }
91
- if (currentSection) {
92
- sections.set(currentSection, currentContent.join('\n').trim());
93
- }
94
- return sections;
95
- }
96
- function parseCheckboxes(content) {
97
- const items = [];
98
- for (const line of content.split('\n')) {
99
- const match = line.match(/^[-*]\s+\[([ xX])\]\s+(.+)$/);
100
- if (match) {
101
- items.push({ text: match[2].trim(), checked: match[1] !== ' ' });
102
- }
103
- }
104
- return items;
105
- }
106
- function parseListItems(content) {
107
- const items = [];
108
- for (const line of content.split('\n')) {
109
- const match = line.match(/^[-*]\s+(.+)$/);
110
- if (match)
111
- items.push(match[1].trim());
112
- }
113
- return items;
114
- }
115
- function parseIssueSummaries(content) {
116
- const summaries = [];
117
- for (const line of content.split('\n')) {
118
- // Match: 1. [IS-003](backlog/IS-003.md) — Title (P1)
119
- const match = line.match(/\d+\.\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*\((\w+)\))?\s*$/);
120
- if (match) {
121
- summaries.push({
122
- id: match[1],
123
- path: match[2],
124
- title: match[3].trim(),
125
- priority: match[4] || '',
126
- });
127
- continue;
128
- }
129
- // Match: - [IS-001](backlog/IS-001.md) — Title
130
- const match2 = line.match(/^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*[→→]\s*blocked by\s+\[([^\]]+)\])?\s*$/i);
131
- if (match2) {
132
- summaries.push({
133
- id: match2[1],
134
- path: match2[2],
135
- title: match2[3].trim(),
136
- priority: '',
137
- blockedBy: match2[4] || undefined,
138
- });
139
- }
140
- }
141
- return summaries;
142
- }
143
- function parseCompletedSummaries(content) {
144
- const summaries = [];
145
- for (const line of content.split('\n')) {
146
- const match = line.match(/^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*✓)?\s*$/);
147
- if (match) {
148
- summaries.push({
149
- id: match[1],
150
- path: match[2],
151
- title: match[3].trim(),
152
- priority: '',
153
- });
154
- }
155
- }
156
- return summaries;
157
- }
10
+ import { parseBoard, parseIssue, parseMilestone, parseProjectConfig, parseProjectState, parseSprint, parseWorkspace } from './parser-core.js';
11
+ import { isLegacyFormat, migrateToBoards } from './parser-migration.js';
158
12
  // ============================================================================
159
- // Entity Parsers
13
+ // Directory Resolution
160
14
  // ============================================================================
161
- function parseWorkflows(section) {
162
- if (!section)
163
- return [];
164
- const workflows = [];
165
- for (const line of section.split('\n')) {
166
- const match = line.match(/\|\s*(\w+)\s*\|\s*(\w+)\s*\|\s*(.+?)\s*\|/);
167
- if (match && match[1] !== 'Status') {
168
- workflows.push({
169
- status: match[1],
170
- category: match[2],
171
- description: match[3].trim(),
172
- });
173
- }
174
- }
175
- return workflows;
176
- }
177
- function parseTeams(section) {
178
- if (!section)
179
- return [];
180
- const teams = [];
181
- for (const line of section.split('\n')) {
182
- const match = line.match(/^[-*]\s+(\w+)(?:\s*[—–-]\s*(.+))?$/);
183
- if (match)
184
- teams.push({ name: match[1], description: match[2]?.trim() });
185
- }
186
- return teams;
187
- }
188
- function parseProjectConfig(content) {
189
- const { frontMatter, body } = parseFrontMatter(content);
190
- const sections = extractSections(body);
191
- const idPrefixes = {};
192
- const rawPrefixes = frontMatter.id_prefixes;
193
- if (rawPrefixes && typeof rawPrefixes === 'object') {
194
- Object.assign(idPrefixes, rawPrefixes);
195
- }
196
- return {
197
- name: String(frontMatter.name || ''),
198
- id: String(frontMatter.id || ''),
199
- created: String(frontMatter.created || ''),
200
- status: frontMatter.status || 'active',
201
- estimation: frontMatter.estimation || 'none',
202
- idPrefixes,
203
- workflows: parseWorkflows(sections.get('Workflows')),
204
- labels: (Array.isArray(frontMatter.labels) ? frontMatter.labels : []),
205
- teams: parseTeams(sections.get('Teams')),
206
- };
207
- }
208
- function parseProjectState(content) {
209
- const { frontMatter, body } = parseFrontMatter(content);
210
- const sections = extractSections(body);
211
- return {
212
- project: String(frontMatter.project || ''),
213
- currentSprint: frontMatter.current_sprint || null,
214
- activeMilestone: frontMatter.active_milestone || null,
215
- paused: frontMatter.paused === true,
216
- lastSession: frontMatter.last_session || null,
217
- readyToWork: parseIssueSummaries(sections.get('Ready to Work') || ''),
218
- inProgress: parseIssueSummaries(sections.get('In Progress') || ''),
219
- blocked: parseIssueSummaries(sections.get('Blocked') || ''),
220
- recentlyCompleted: parseCompletedSummaries(sections.get('Recently Completed') || ''),
221
- warnings: parseListItems(sections.get('Warnings') || ''),
222
- };
223
- }
224
- function toStringArray(val) {
225
- return Array.isArray(val) ? val.map(String) : [];
226
- }
227
- function optionalString(val) {
228
- if (val == null)
229
- return null;
230
- const s = String(val);
231
- return s === '' ? null : s;
15
+ export function isBoardCentricFormat(pmDir) {
16
+ return existsSync(join(pmDir, 'boards'));
232
17
  }
233
- function parseIssue(content, filePath) {
234
- const { frontMatter: fm, body } = parseFrontMatter(content);
235
- const sections = extractSections(body);
236
- return {
237
- id: String(fm.id || ''),
238
- title: String(fm.title || ''),
239
- type: fm.type || 'issue',
240
- status: String(fm.status || 'backlog'),
241
- priority: String(fm.priority || 'P2'),
242
- estimate: fm.estimate != null ? fm.estimate : null,
243
- labels: toStringArray(fm.labels),
244
- epic: optionalString(fm.epic),
245
- sprint: optionalString(fm.sprint),
246
- milestone: optionalString(fm.milestone),
247
- assigned: optionalString(fm.assigned),
248
- created: String(fm.created || ''),
249
- updated: optionalString(fm.updated),
250
- due: optionalString(fm.due),
251
- blockedBy: toStringArray(fm.blocked_by),
252
- blocks: toStringArray(fm.blocks),
253
- relatesTo: toStringArray(fm.relates_to),
254
- children: toStringArray(fm.children),
255
- progress: optionalString(fm.progress),
256
- description: sections.get('Description') || '',
257
- acceptanceCriteria: parseCheckboxes(sections.get('Acceptance Criteria') || ''),
258
- technicalNotes: sections.get('Technical Notes') || null,
259
- filesToModify: parseListItems(sections.get('Files to Modify') || ''),
260
- activity: parseListItems(sections.get('Activity') || ''),
261
- outputFile: optionalString(fm.output_file),
262
- body,
263
- path: filePath,
264
- };
265
- }
266
- function parseSprintIssues(section) {
267
- if (!section)
268
- return [];
269
- const issues = [];
270
- for (const line of section.split('\n')) {
271
- const match = line.match(/\|\s*\[([^\]]+)\]\(([^)]+)\)\s*\|\s*(.+?)\s*\|\s*(\S+)\s*\|\s*(\S+)\s*\|/);
272
- if (match) {
273
- issues.push({
274
- id: match[1],
275
- path: match[2],
276
- title: match[3].trim(),
277
- points: /^\d+$/.test(match[4]) ? Number(match[4]) : match[4],
278
- status: match[5],
279
- });
280
- }
281
- }
282
- return issues;
283
- }
284
- function optionalNumber(val) {
285
- return val != null ? Number(val) : null;
286
- }
287
- function parseSprint(content, filePath) {
288
- const { frontMatter: fm, body } = parseFrontMatter(content);
289
- const sections = extractSections(body);
290
- // Table-based parsing (markdown links in table rows)
291
- let issues = parseSprintIssues(sections.get('Issues'));
292
- // Fallback: front matter issues array (e.g., ["backlog/IS-001.md", ...])
293
- if (issues.length === 0 && Array.isArray(fm.issues)) {
294
- issues = fm.issues.map(path => {
295
- const id = path.replace(/^backlog\//, '').replace(/\.md$/, '');
296
- return { id, path, title: '', points: null, status: '' };
297
- });
298
- }
299
- return {
300
- id: String(fm.id || ''),
301
- title: String(fm.title || ''),
302
- status: fm.status || 'planned',
303
- start: String(fm.start || fm.start_date || ''),
304
- end: String(fm.end || fm.end_date || ''),
305
- goal: String(fm.goal || sections.get('Goal') || sections.get('Sprint Goal') || ''),
306
- capacity: optionalNumber(fm.capacity),
307
- committed: optionalNumber(fm.committed),
308
- completed: optionalNumber(fm.completed),
309
- issues,
310
- path: filePath,
311
- };
312
- }
313
- function parseMilestone(content, filePath) {
314
- const { frontMatter, body } = parseFrontMatter(content);
315
- const sections = extractSections(body);
316
- const epics = [];
317
- const epicSection = sections.get('Epics');
318
- if (epicSection) {
319
- for (const line of epicSection.split('\n')) {
320
- const match = line.match(/\|\s*\[([^\]]+)\]\(([^)]+)\)\s*\|\s*(.+?)\s*\|\s*(\S+)\s*\|/);
321
- if (match) {
322
- epics.push({
323
- id: match[1],
324
- path: match[2],
325
- title: match[3].trim(),
326
- progress: match[4],
327
- });
328
- }
329
- }
330
- }
331
- return {
332
- id: String(frontMatter.id || ''),
333
- title: String(frontMatter.title || ''),
334
- status: frontMatter.status || 'planned',
335
- targetDate: frontMatter.target_date || null,
336
- progress: frontMatter.progress || null,
337
- definition: sections.get('Definition of Done') || '',
338
- epics,
339
- path: filePath,
340
- };
341
- }
342
- // ============================================================================
343
- // Directory Parser
344
- // ============================================================================
345
- /** Resolve the PM directory — prefers .pm/, falls back to legacy .plan/ */
346
18
  export function resolvePmDir(workingDir) {
347
- const pmDir = join(workingDir, '.pm');
348
- if (existsSync(pmDir))
349
- return pmDir;
350
- const legacyDir = join(workingDir, '.plan');
351
- if (existsSync(legacyDir))
352
- return legacyDir;
19
+ const mstroPmDir = join(workingDir, '.mstro', 'pm');
20
+ if (existsSync(mstroPmDir))
21
+ return mstroPmDir;
22
+ const legacyPmDir = join(workingDir, '.pm');
23
+ if (existsSync(legacyPmDir))
24
+ return legacyPmDir;
25
+ const legacyPlanDir = join(workingDir, '.plan');
26
+ if (existsSync(legacyPlanDir))
27
+ return legacyPlanDir;
353
28
  return null;
354
29
  }
30
+ export function defaultPmDir(workingDir) {
31
+ return join(workingDir, '.mstro', 'pm');
32
+ }
355
33
  export function planDirExists(workingDir) {
356
34
  return resolvePmDir(workingDir) !== null;
357
35
  }
36
+ // ============================================================================
37
+ // File Utilities
38
+ // ============================================================================
358
39
  function readFileIfExists(path) {
359
40
  try {
360
41
  if (existsSync(path))
@@ -369,46 +50,117 @@ function readMdFilesInDir(dirPath) {
369
50
  try {
370
51
  return readdirSync(dirPath)
371
52
  .filter(f => f.endsWith('.md'))
372
- .map(name => {
373
- const content = readFileSync(join(dirPath, name), 'utf-8');
374
- return { name, content };
375
- });
53
+ .map(name => ({ name, content: readFileSync(join(dirPath, name), 'utf-8') }));
376
54
  }
377
55
  catch {
378
56
  return [];
379
57
  }
380
58
  }
59
+ function listDirFiles(dirPath, ext) {
60
+ if (!existsSync(dirPath))
61
+ return [];
62
+ try {
63
+ return readdirSync(dirPath).filter(f => f.endsWith(ext));
64
+ }
65
+ catch {
66
+ return [];
67
+ }
68
+ }
69
+ function readReviewResults(reviewsDir) {
70
+ const results = [];
71
+ for (const f of listDirFiles(reviewsDir, '.json')) {
72
+ const content = readFileIfExists(join(reviewsDir, f));
73
+ if (content)
74
+ results.push(JSON.parse(content));
75
+ }
76
+ return results;
77
+ }
78
+ // ============================================================================
79
+ // Defaults
80
+ // ============================================================================
81
+ const defaultProject = {
82
+ name: '', id: '', created: '', status: 'active', estimation: 'none',
83
+ idPrefixes: {}, workflows: [], labels: [], teams: [],
84
+ };
85
+ const defaultState = {
86
+ project: '', currentSprint: null, activeMilestone: null, paused: false,
87
+ lastSession: null, readyToWork: [], inProgress: [], blocked: [],
88
+ recentlyCompleted: [], warnings: [],
89
+ };
90
+ // ============================================================================
91
+ // Board & Plan Directory Parsing
92
+ // ============================================================================
93
+ export function parseBoardDirectory(pmDir, boardId) {
94
+ const boardDir = join(pmDir, 'boards', boardId);
95
+ if (!existsSync(boardDir))
96
+ return null;
97
+ const boardContent = readFileIfExists(join(boardDir, 'board.md'));
98
+ if (!boardContent)
99
+ return null;
100
+ const board = parseBoard(boardContent, `boards/${boardId}/board.md`);
101
+ const stateContent = readFileIfExists(join(boardDir, 'STATE.md'));
102
+ const state = stateContent ? parseProjectState(stateContent) : { ...defaultState };
103
+ const issueFiles = readMdFilesInDir(join(boardDir, 'backlog'));
104
+ const boardPrefix = `boards/${boardId}/`;
105
+ const issues = issueFiles.map(f => {
106
+ const issue = parseIssue(f.content, `${boardPrefix}backlog/${f.name}`);
107
+ issue.blockedBy = issue.blockedBy.map(bp => bp.startsWith('boards/') ? bp : `${boardPrefix}${bp}`);
108
+ issue.blocks = issue.blocks.map(bp => bp.startsWith('boards/') ? bp : `${boardPrefix}${bp}`);
109
+ if (issue.epic && !issue.epic.startsWith('boards/'))
110
+ issue.epic = `${boardPrefix}${issue.epic}`;
111
+ return issue;
112
+ });
113
+ return { board, state, issues };
114
+ }
115
+ function parseBoardCentricState(planDir) {
116
+ const workspaceContent = readFileIfExists(join(planDir, 'workspace.json'));
117
+ const workspace = workspaceContent ? parseWorkspace(workspaceContent) : { activeBoardId: null, boardOrder: [] };
118
+ const boards = [];
119
+ const boardsDir = join(planDir, 'boards');
120
+ if (existsSync(boardsDir)) {
121
+ for (const entry of readdirSync(boardsDir)) {
122
+ const boardMdPath = join(boardsDir, entry, 'board.md');
123
+ if (!existsSync(boardMdPath))
124
+ continue;
125
+ boards.push(parseBoard(readFileSync(boardMdPath, 'utf-8'), `boards/${entry}/board.md`));
126
+ }
127
+ }
128
+ const orderMap = new Map(workspace.boardOrder.map((id, i) => [id, i]));
129
+ boards.sort((a, b) => (orderMap.get(a.id) ?? 999) - (orderMap.get(b.id) ?? 999));
130
+ const activeBoard = workspace.activeBoardId ? parseBoardDirectory(planDir, workspace.activeBoardId) : null;
131
+ return { boards, workspace, activeBoard };
132
+ }
381
133
  export function parsePlanDirectory(workingDir) {
382
134
  const planDir = resolvePmDir(workingDir);
383
135
  if (!planDir)
384
136
  return null;
385
- // Parse project.md
137
+ if (isLegacyFormat(planDir))
138
+ migrateToBoards(planDir);
386
139
  const projectContent = readFileIfExists(join(planDir, 'project.md'));
387
- const project = projectContent
388
- ? parseProjectConfig(projectContent)
389
- : { name: '', id: '', created: '', status: 'active', estimation: 'none', idPrefixes: {}, workflows: [], labels: [], teams: [] };
390
- // Parse STATE.md
391
- const stateContent = readFileIfExists(join(planDir, 'STATE.md'));
392
- const state = stateContent
393
- ? parseProjectState(stateContent)
394
- : { project: '', currentSprint: null, activeMilestone: null, paused: false, lastSession: null, readyToWork: [], inProgress: [], blocked: [], recentlyCompleted: [], warnings: [] };
395
- // Parse backlog issues
396
- const issueFiles = readMdFilesInDir(join(planDir, 'backlog'));
397
- const issues = issueFiles.map(f => parseIssue(f.content, `backlog/${f.name}`));
398
- // Parse sprints
399
- const sprintFiles = readMdFilesInDir(join(planDir, 'sprints'));
400
- const sprints = sprintFiles.map(f => parseSprint(f.content, `sprints/${f.name}`));
401
- // Parse milestones
140
+ const project = projectContent ? parseProjectConfig(projectContent) : { ...defaultProject };
402
141
  const milestoneFiles = readMdFilesInDir(join(planDir, 'milestones'));
403
142
  const milestones = milestoneFiles.map(f => parseMilestone(f.content, `milestones/${f.name}`));
404
- return { project, state, issues, sprints, milestones };
143
+ if (!isBoardCentricFormat(planDir)) {
144
+ return {
145
+ project, state: { ...defaultState }, boards: [], workspace: { activeBoardId: null, boardOrder: [] },
146
+ activeBoard: null, issues: [], sprints: [], milestones,
147
+ };
148
+ }
149
+ const { boards, workspace, activeBoard } = parseBoardCentricState(planDir);
150
+ return {
151
+ project, state: activeBoard?.state ?? { ...defaultState },
152
+ boards, workspace, activeBoard, issues: activeBoard?.issues ?? [],
153
+ sprints: [], milestones,
154
+ };
405
155
  }
156
+ // ============================================================================
157
+ // Single Entity Parsers
158
+ // ============================================================================
406
159
  export function parseSingleIssue(workingDir, issuePath) {
407
160
  const pmDir = resolvePmDir(workingDir);
408
161
  if (!pmDir)
409
162
  return null;
410
- const fullPath = join(pmDir, issuePath);
411
- const content = readFileIfExists(fullPath);
163
+ const content = readFileIfExists(join(pmDir, issuePath));
412
164
  if (!content)
413
165
  return null;
414
166
  return parseIssue(content, issuePath);
@@ -417,8 +169,7 @@ export function parseSingleSprint(workingDir, sprintPath) {
417
169
  const pmDir = resolvePmDir(workingDir);
418
170
  if (!pmDir)
419
171
  return null;
420
- const fullPath = join(pmDir, sprintPath);
421
- const content = readFileIfExists(fullPath);
172
+ const content = readFileIfExists(join(pmDir, sprintPath));
422
173
  if (!content)
423
174
  return null;
424
175
  return parseSprint(content, sprintPath);
@@ -427,13 +178,14 @@ export function parseSingleMilestone(workingDir, milestonePath) {
427
178
  const pmDir = resolvePmDir(workingDir);
428
179
  if (!pmDir)
429
180
  return null;
430
- const fullPath = join(pmDir, milestonePath);
431
- const content = readFileIfExists(fullPath);
181
+ const content = readFileIfExists(join(pmDir, milestonePath));
432
182
  if (!content)
433
183
  return null;
434
184
  return parseMilestone(content, milestonePath);
435
185
  }
436
- /** Compute the next available ID for a given prefix (e.g., "IS" → "IS-004") */
186
+ // ============================================================================
187
+ // ID Generation
188
+ // ============================================================================
437
189
  export function getNextId(issues, prefix) {
438
190
  const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
439
191
  const pattern = new RegExp(`^${escaped}-(\\d+)$`);
@@ -448,4 +200,67 @@ export function getNextId(issues, prefix) {
448
200
  }
449
201
  return `${prefix}-${String(max + 1).padStart(3, '0')}`;
450
202
  }
203
+ export function getNextBoardId(boards) {
204
+ let max = 0;
205
+ for (const board of boards) {
206
+ const match = board.id.match(/^BOARD-(\d+)$/);
207
+ if (match) {
208
+ const num = Number.parseInt(match[1], 10);
209
+ if (num > max)
210
+ max = num;
211
+ }
212
+ }
213
+ return `BOARD-${String(max + 1).padStart(3, '0')}`;
214
+ }
215
+ export function getNextBoardNumber(boards) {
216
+ let max = 0;
217
+ for (const board of boards) {
218
+ const match = board.title.match(/^Board (\d+)$/);
219
+ if (match) {
220
+ const num = Number.parseInt(match[1], 10);
221
+ if (num > max)
222
+ max = num;
223
+ }
224
+ }
225
+ return max + 1;
226
+ }
227
+ export function parseBoardArtifacts(workingDir, boardId) {
228
+ const pmDir = resolvePmDir(workingDir);
229
+ if (!pmDir)
230
+ return null;
231
+ const boardDir = join(pmDir, 'boards', boardId);
232
+ if (!existsSync(boardDir))
233
+ return null;
234
+ const progressLog = readFileIfExists(join(boardDir, 'progress.md')) ?? '';
235
+ const outputFiles = listDirFiles(join(boardDir, 'out'), '.md');
236
+ const reviewResults = readReviewResults(join(boardDir, 'reviews'));
237
+ const executionLogs = listDirFiles(join(boardDir, 'logs'), '.log').sort();
238
+ return { boardId, progressLog, outputFiles, reviewResults, executionLogs };
239
+ }
240
+ /** @deprecated Use getNextBoardId — kept for migration compatibility */
241
+ export function getNextSprintId(sprints) {
242
+ let max = 0;
243
+ for (const sprint of sprints) {
244
+ const match = sprint.id.match(/^SPRINT-(\d+)$/);
245
+ if (match) {
246
+ const num = Number.parseInt(match[1], 10);
247
+ if (num > max)
248
+ max = num;
249
+ }
250
+ }
251
+ return `SPRINT-${String(max + 1).padStart(3, '0')}`;
252
+ }
253
+ /** @deprecated Use parseBoardArtifacts — kept for migration compatibility */
254
+ export function parseSprintArtifacts(workingDir, sprintId) {
255
+ const pmDir = resolvePmDir(workingDir);
256
+ if (!pmDir)
257
+ return null;
258
+ const sandboxDir = join(pmDir, 'sprints', sprintId);
259
+ if (!existsSync(sandboxDir))
260
+ return null;
261
+ const progressLog = readFileIfExists(join(sandboxDir, 'progress.md')) ?? '';
262
+ const outputFiles = listDirFiles(join(sandboxDir, 'out'), '.md');
263
+ const reviewResults = readReviewResults(join(sandboxDir, 'reviews'));
264
+ return { sprintId, progressLog, outputFiles, reviewResults };
265
+ }
451
266
  //# sourceMappingURL=parser.js.map