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
@@ -2,193 +2,21 @@
2
2
  // Licensed under the MIT License. See LICENSE file for details.
3
3
 
4
4
  /**
5
- * Plan Handlers — WebSocket message handlers for Plan view
5
+ * Plan Handlers — WebSocket message router for Plan view
6
6
  *
7
- * Routes plan* messages to the PPS parser and file operations.
7
+ * Routes plan* messages to domain-specific handler modules.
8
8
  * Follows the same pattern as quality-handlers.ts and git-handlers.ts.
9
9
  */
10
10
 
11
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
12
- import { join, resolve } from 'node:path';
13
- import { handlePlanPrompt } from '../plan/composer.js';
14
- import { PlanExecutor } from '../plan/executor.js';
15
- import { getNextId, parsePlanDirectory, parseSingleIssue, parseSingleMilestone, parseSingleSprint, planDirExists, resolvePmDir } from '../plan/parser.js';
16
- import { PlanWatcher } from '../plan/watcher.js';
17
11
  import type { HandlerContext } from './handler-context.js';
12
+ import { handleArchiveBoard, handleCreateBoard, handleGetBoard, handleGetBoardArtifacts, handleGetBoardState, handleReorderBoards, handleSetActiveBoard, handleUpdateBoard } from './plan-board-handlers.js';
13
+ import { handleExecute, handleExecuteEpic, handlePause, handlePrompt, handleResume, handleStop } from './plan-execution-handlers.js';
14
+ import { handleCreateIssue, handleDeleteIssue, handleGetIssue, handleGetMilestone, handleGetSprint, handleListIssues, handlePlanInit, handleScaffold, handleUpdateIssue } from './plan-issue-handlers.js';
15
+ import { handleActivateSprint, handleCompleteSprint, handleCreateSprint, handleGetSprintArtifacts } from './plan-sprint-handlers.js';
18
16
  import type { WebSocketMessage, WSContext } from './types.js';
19
17
 
20
- const watcherCache = new Map<string, PlanWatcher>();
21
- const executorCache = new Map<string, PlanExecutor>();
22
-
23
- // ============================================================================
24
- // Helpers
25
- // ============================================================================
26
-
27
- /** Validate that a user-supplied path resolves within the .pm/ (or legacy .plan/) directory. */
28
- function resolvePlanPath(workingDir: string, relativePath: string): string | null {
29
- const pmDir = resolvePmDir(workingDir);
30
- if (!pmDir) return null;
31
- const resolved = resolve(pmDir, relativePath);
32
- if (!resolved.startsWith(`${pmDir}/`) && resolved !== pmDir) return null;
33
- return resolved;
34
- }
35
-
36
- /** Guard for write operations — returns true if denied. */
37
- function denyIfViewOnly(ctx: HandlerContext, ws: WSContext, permission?: 'control' | 'view'): boolean {
38
- if (permission === 'view') {
39
- ctx.send(ws, { type: 'planError', data: { error: 'Permission denied' } });
40
- return true;
41
- }
42
- return false;
43
- }
44
-
45
- function formatYamlValue(value: unknown): string {
46
- if (value === null || value === undefined) return 'null';
47
- if (typeof value === 'boolean') return String(value);
48
- if (typeof value === 'number') return String(value);
49
- if (Array.isArray(value)) {
50
- if (value.length === 0) return '[]';
51
- return `[${value.map(v => typeof v === 'string' ? v : String(v)).join(', ')}]`;
52
- }
53
- return `"${String(value).replace(/"/g, '\\"')}"`;
54
- }
55
-
56
- function buildIssueMarkdown(
57
- id: string, title: string, type: string, priority: string,
58
- labels: string[], sprint: string | null, description: string,
59
- ): string {
60
- const labelsYaml = labels.length > 0 ? `[${labels.join(', ')}]` : '[]';
61
- const today = new Date().toISOString().split('T')[0];
62
- return `---
63
- id: ${id}
64
- title: "${title.replace(/"/g, '\\"')}"
65
- type: ${type}
66
- status: backlog
67
- priority: ${priority}
68
- estimate: null
69
- labels: ${labelsYaml}
70
- epic: null
71
- sprint: ${sprint || 'null'}
72
- milestone: null
73
- assigned: null
74
- created: "${today}"
75
- due: null
76
- blocked_by: []
77
- blocks: []
78
- relates_to: []
79
- ---
80
-
81
- # ${id}: ${title}
82
-
83
- ## Description
84
- ${description}
85
-
86
- ## Acceptance Criteria
87
-
88
- ## Technical Notes
89
-
90
- ## Files to Modify
91
-
92
- ## Activity
93
- `;
94
- }
95
-
96
- function buildProjectMarkdown(name: string): string {
97
- const today = new Date().toISOString().split('T')[0];
98
- const projectId = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
99
- return `---
100
- name: "${name}"
101
- id: ${projectId}
102
- created: "${today}"
103
- status: active
104
- estimation: fibonacci
105
- id_prefixes:
106
- epic: EP
107
- issue: IS
108
- bug: BG
109
- labels: []
110
- ---
111
-
112
- # ${name}
113
-
114
- ## Goals
115
-
116
- ## Teams
117
-
118
- ## Labels
119
-
120
- ## Workflows
121
- | Status | Category | Description |
122
- |---|---|---|
123
- | backlog | unstarted | Accepted, not yet scheduled |
124
- | todo | unstarted | Scheduled for current sprint |
125
- | in_progress | started | Actively being worked on |
126
- | in_review | started | PR open, awaiting review |
127
- | done | completed | Merged and verified |
128
- | cancelled | cancelled | Will not be done |
129
- `;
130
- }
131
-
132
- function buildStateMarkdown(name: string): string {
133
- return `---
134
- project: "${name}"
135
- current_sprint: null
136
- active_milestone: null
137
- paused: false
138
- last_session: null
139
- ---
140
-
141
- # Project State
142
-
143
- ## Current Focus
144
-
145
- ## Ready to Work
146
-
147
- ## In Progress
148
-
149
- ## Blocked
150
-
151
- ## Recently Completed
152
-
153
- ## Warnings
154
- `;
155
- }
156
-
157
- function getWatcher(workingDir: string, ctx: HandlerContext): PlanWatcher {
158
- let watcher = watcherCache.get(workingDir);
159
- if (!watcher) {
160
- watcher = new PlanWatcher(workingDir, ctx);
161
- watcherCache.set(workingDir, watcher);
162
- }
163
- return watcher;
164
- }
165
-
166
- function getExecutor(workingDir: string): PlanExecutor {
167
- let executor = executorCache.get(workingDir);
168
- if (!executor) {
169
- executor = new PlanExecutor(workingDir);
170
- executorCache.set(workingDir, executor);
171
- }
172
- return executor;
173
- }
174
-
175
- /** Cleanup watchers and executors for a working directory. */
176
- export function cleanupPlanResources(workingDir: string): void {
177
- const watcher = watcherCache.get(workingDir);
178
- if (watcher) {
179
- watcher.stop();
180
- watcherCache.delete(workingDir);
181
- }
182
- const executor = executorCache.get(workingDir);
183
- if (executor) {
184
- executor.stop();
185
- executorCache.delete(workingDir);
186
- }
187
- }
188
-
189
- // ============================================================================
190
- // Main dispatcher
191
- // ============================================================================
18
+ // Re-export for backward compatibility
19
+ export { cleanupPlanResources } from './plan-helpers.js';
192
20
 
193
21
  export function handlePlanMessage(
194
22
  ctx: HandlerContext,
@@ -210,11 +38,25 @@ export function handlePlanMessage(
210
38
  planDeleteIssue: () => handleDeleteIssue(ctx, ws, msg, workingDir, permission),
211
39
  planScaffold: () => handleScaffold(ctx, ws, msg, workingDir, permission),
212
40
  planPrompt: () => handlePrompt(ctx, ws, msg, workingDir, permission),
213
- planExecute: () => handleExecute(ctx, ws, workingDir, permission),
41
+ planExecute: () => handleExecute(ctx, ws, msg, workingDir, permission),
214
42
  planExecuteEpic: () => handleExecuteEpic(ctx, ws, msg, workingDir, permission),
215
43
  planPause: () => handlePause(ctx, ws, workingDir, permission),
216
44
  planStop: () => handleStop(ctx, ws, workingDir, permission),
217
45
  planResume: () => handleResume(ctx, ws, workingDir, permission),
46
+ // Board lifecycle
47
+ planCreateBoard: () => handleCreateBoard(ctx, ws, msg, workingDir, permission),
48
+ planUpdateBoard: () => handleUpdateBoard(ctx, ws, msg, workingDir, permission),
49
+ planArchiveBoard: () => handleArchiveBoard(ctx, ws, msg, workingDir, permission),
50
+ planGetBoard: () => handleGetBoard(ctx, ws, msg, workingDir),
51
+ planGetBoardState: () => handleGetBoardState(ctx, ws, msg, workingDir),
52
+ planReorderBoards: () => handleReorderBoards(ctx, ws, msg, workingDir, permission),
53
+ planSetActiveBoard: () => handleSetActiveBoard(ctx, ws, msg, workingDir, permission),
54
+ planGetBoardArtifacts: () => handleGetBoardArtifacts(ctx, ws, msg, workingDir),
55
+ // Sprint lifecycle (legacy)
56
+ planCreateSprint: () => handleCreateSprint(ctx, ws, msg, workingDir, permission),
57
+ planActivateSprint: () => handleActivateSprint(ctx, ws, msg, workingDir, permission),
58
+ planCompleteSprint: () => handleCompleteSprint(ctx, ws, msg, workingDir, permission),
59
+ planGetSprintArtifacts: () => handleGetSprintArtifacts(ctx, ws, msg, workingDir),
218
60
  };
219
61
 
220
62
  const handler = handlers[msg.type];
@@ -227,366 +69,3 @@ export function handlePlanMessage(
227
69
  ctx.send(ws, { type: 'planError', data: { error: errMsg } });
228
70
  }
229
71
  }
230
-
231
- // ============================================================================
232
- // Read-only handlers
233
- // ============================================================================
234
-
235
- function handlePlanInit(ctx: HandlerContext, ws: WSContext, workingDir: string): void {
236
- if (!planDirExists(workingDir)) {
237
- ctx.send(ws, { type: 'planNotFound', data: {} });
238
- return;
239
- }
240
-
241
- const fullState = parsePlanDirectory(workingDir);
242
- if (!fullState) {
243
- ctx.send(ws, { type: 'planNotFound', data: {} });
244
- return;
245
- }
246
-
247
- ctx.send(ws, { type: 'planState', data: fullState });
248
-
249
- const watcher = getWatcher(workingDir, ctx);
250
- watcher.start();
251
- }
252
-
253
- function handleListIssues(ctx: HandlerContext, ws: WSContext, workingDir: string): void {
254
- const fullState = parsePlanDirectory(workingDir);
255
- if (!fullState) {
256
- ctx.send(ws, { type: 'planNotFound', data: {} });
257
- return;
258
- }
259
- ctx.send(ws, { type: 'planIssueList', data: { issues: fullState.issues } });
260
- }
261
-
262
- function handleGetIssue(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, workingDir: string): void {
263
- const path = msg.data?.path;
264
- if (!path || !resolvePlanPath(workingDir, path)) {
265
- ctx.send(ws, { type: 'planError', data: { error: 'Invalid issue path' } });
266
- return;
267
- }
268
- const issue = parseSingleIssue(workingDir, path);
269
- if (!issue) {
270
- ctx.send(ws, { type: 'planError', data: { error: `Issue not found: ${path}` } });
271
- return;
272
- }
273
- ctx.send(ws, { type: 'planIssue', data: issue });
274
- }
275
-
276
- function handleGetSprint(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, workingDir: string): void {
277
- const path = msg.data?.path;
278
- if (!path || !resolvePlanPath(workingDir, path)) {
279
- ctx.send(ws, { type: 'planError', data: { error: 'Invalid sprint path' } });
280
- return;
281
- }
282
- const sprint = parseSingleSprint(workingDir, path);
283
- if (!sprint) {
284
- ctx.send(ws, { type: 'planError', data: { error: `Sprint not found: ${path}` } });
285
- return;
286
- }
287
- ctx.send(ws, { type: 'planSprint', data: sprint });
288
- }
289
-
290
- function handleGetMilestone(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, workingDir: string): void {
291
- const path = msg.data?.path;
292
- if (!path || !resolvePlanPath(workingDir, path)) {
293
- ctx.send(ws, { type: 'planError', data: { error: 'Invalid milestone path' } });
294
- return;
295
- }
296
- const milestone = parseSingleMilestone(workingDir, path);
297
- if (!milestone) {
298
- ctx.send(ws, { type: 'planError', data: { error: `Milestone not found: ${path}` } });
299
- return;
300
- }
301
- ctx.send(ws, { type: 'planMilestone', data: milestone });
302
- }
303
-
304
- // ============================================================================
305
- // Mutation handlers
306
- // ============================================================================
307
-
308
- function handleCreateIssue(
309
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
310
- workingDir: string, permission?: 'control' | 'view',
311
- ): void {
312
- if (denyIfViewOnly(ctx, ws, permission)) return;
313
-
314
- const { title, type = 'issue', priority = 'P2', labels = [], sprint, description = '' } = msg.data || {};
315
- if (!title) {
316
- ctx.send(ws, { type: 'planError', data: { error: 'Title required' } });
317
- return;
318
- }
319
-
320
- const pmDir = resolvePmDir(workingDir) ?? join(workingDir, '.pm');
321
- const backlogDir = join(pmDir, 'backlog');
322
- if (!existsSync(backlogDir)) {
323
- mkdirSync(backlogDir, { recursive: true });
324
- }
325
-
326
- const fullState = parsePlanDirectory(workingDir);
327
- const prefix = type === 'bug' ? 'BG' : type === 'epic' ? 'EP' : 'IS';
328
- const id = fullState ? getNextId(fullState.issues, prefix) : `${prefix}-001`;
329
-
330
- const content = buildIssueMarkdown(id, title, type, priority, labels, sprint, description);
331
- const fileName = `${id}.md`;
332
- writeFileSync(join(backlogDir, fileName), content, 'utf-8');
333
-
334
- const issue = parseSingleIssue(workingDir, `backlog/${fileName}`);
335
- ctx.broadcastToAll({ type: 'planIssueCreated', data: issue });
336
- }
337
-
338
- function handleUpdateIssue(
339
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
340
- workingDir: string, permission?: 'control' | 'view',
341
- ): void {
342
- if (denyIfViewOnly(ctx, ws, permission)) return;
343
-
344
- const { path, fields } = msg.data || {};
345
- if (!path || !fields) {
346
- ctx.send(ws, { type: 'planError', data: { error: 'Path and fields required' } });
347
- return;
348
- }
349
-
350
- const fullPath = resolvePlanPath(workingDir, path);
351
- if (!fullPath || !existsSync(fullPath)) {
352
- ctx.send(ws, { type: 'planError', data: { error: `File not found: ${path}` } });
353
- return;
354
- }
355
-
356
- const content = readFileSync(fullPath, 'utf-8');
357
- const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
358
- if (!match) {
359
- ctx.send(ws, { type: 'planError', data: { error: 'Invalid file format' } });
360
- return;
361
- }
362
-
363
- let yamlStr = match[1];
364
- const body = match[2];
365
-
366
- for (const [key, value] of Object.entries(fields as Record<string, unknown>)) {
367
- const yamlKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
368
- const yamlValue = formatYamlValue(value);
369
- const regex = new RegExp(`^${yamlKey}:.*$`, 'm');
370
- if (regex.test(yamlStr)) {
371
- yamlStr = yamlStr.replace(regex, `${yamlKey}: ${yamlValue}`);
372
- } else {
373
- yamlStr += `\n${yamlKey}: ${yamlValue}`;
374
- }
375
- }
376
-
377
- writeFileSync(fullPath, `---\n${yamlStr}\n---\n${body}`, 'utf-8');
378
-
379
- const issue = parseSingleIssue(workingDir, path);
380
- ctx.broadcastToAll({ type: 'planIssueUpdated', data: issue });
381
- }
382
-
383
- function handleDeleteIssue(
384
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
385
- workingDir: string, permission?: 'control' | 'view',
386
- ): void {
387
- if (denyIfViewOnly(ctx, ws, permission)) return;
388
-
389
- const path = msg.data?.path;
390
- if (!path) {
391
- ctx.send(ws, { type: 'planError', data: { error: 'Path required' } });
392
- return;
393
- }
394
-
395
- const fullPath = resolvePlanPath(workingDir, path);
396
- if (!fullPath || !existsSync(fullPath)) {
397
- ctx.send(ws, { type: 'planError', data: { error: `File not found: ${path}` } });
398
- return;
399
- }
400
-
401
- unlinkSync(fullPath);
402
- ctx.broadcastToAll({ type: 'planIssueDeleted', data: { path } });
403
- }
404
-
405
- function handleScaffold(
406
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
407
- workingDir: string, permission?: 'control' | 'view',
408
- ): void {
409
- if (denyIfViewOnly(ctx, ws, permission)) return;
410
-
411
- const name = msg.data?.name || 'My Project';
412
- const planDir = join(workingDir, '.pm');
413
-
414
- for (const dir of ['backlog', 'sprints', 'milestones', 'docs', 'docs/decisions']) {
415
- mkdirSync(join(planDir, dir), { recursive: true });
416
- }
417
-
418
- writeFileSync(join(planDir, 'project.md'), buildProjectMarkdown(name), 'utf-8');
419
- writeFileSync(join(planDir, 'STATE.md'), buildStateMarkdown(name), 'utf-8');
420
- writeFileSync(join(planDir, 'progress.md'), '# Progress Log\n', 'utf-8');
421
-
422
- const fullState = parsePlanDirectory(workingDir);
423
- ctx.broadcastToAll({ type: 'planScaffolded', data: fullState });
424
- }
425
-
426
- // ============================================================================
427
- // Composer + Execution handlers
428
- // ============================================================================
429
-
430
- function handlePrompt(
431
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
432
- workingDir: string, permission?: 'control' | 'view',
433
- ): void {
434
- if (denyIfViewOnly(ctx, ws, permission)) return;
435
-
436
- const prompt = msg.data?.prompt;
437
- if (!prompt) {
438
- ctx.send(ws, { type: 'planError', data: { error: 'Prompt required' } });
439
- return;
440
- }
441
- handlePlanPrompt(ctx, ws, prompt, workingDir).catch(error => {
442
- ctx.send(ws, {
443
- type: 'planError',
444
- data: { error: error instanceof Error ? error.message : String(error) },
445
- });
446
- });
447
- }
448
-
449
- function wireExecutorEvents(executor: PlanExecutor, ctx: HandlerContext, workingDir: string): void {
450
- executor.removeAllListeners();
451
-
452
- executor.on('statusChanged', (status: string) => {
453
- ctx.broadcastToAll({ type: 'planExecutionProgress', data: { status } });
454
- });
455
-
456
- executor.on('issueStarted', (issue: { id: string; title: string }) => {
457
- ctx.broadcastToAll({
458
- type: 'planExecutionProgress',
459
- data: { issueId: issue.id, status: 'executing', title: issue.title },
460
- });
461
- });
462
-
463
- executor.on('output', (data: { issueId: string; text: string }) => {
464
- ctx.broadcastToAll({ type: 'planExecutionOutput', data });
465
- });
466
-
467
- executor.on('issueCompleted', () => {
468
- ctx.broadcastToAll({ type: 'planExecutionMetrics', data: executor.getMetrics() });
469
- const fullState = parsePlanDirectory(workingDir);
470
- if (fullState) {
471
- ctx.broadcastToAll({ type: 'planStateUpdated', data: fullState });
472
- }
473
- });
474
-
475
- executor.on('issueError', (data: { issueId: string; error: string }) => {
476
- ctx.broadcastToAll({ type: 'planExecutionError', data });
477
- });
478
-
479
- executor.on('waveStarted', (data: { issueIds: string[] }) => {
480
- ctx.broadcastToAll({
481
- type: 'planExecutionProgress',
482
- data: { status: 'wave', issueIds: data.issueIds },
483
- });
484
- });
485
-
486
- executor.on('waveError', (data: { issueIds: string[]; error: string }) => {
487
- ctx.broadcastToAll({ type: 'planExecutionError', data });
488
- });
489
-
490
- executor.on('stateUpdated', () => {
491
- const fullState = parsePlanDirectory(workingDir);
492
- if (fullState) {
493
- ctx.broadcastToAll({ type: 'planStateUpdated', data: fullState });
494
- }
495
- });
496
-
497
- executor.on('complete', (reason: string) => {
498
- ctx.broadcastToAll({ type: 'planExecutionComplete', data: { reason, metrics: executor.getMetrics() } });
499
- });
500
-
501
- executor.on('error', (error: string) => {
502
- ctx.broadcastToAll({ type: 'planExecutionError', data: { error } });
503
- });
504
- }
505
-
506
- function handleExecute(
507
- ctx: HandlerContext, ws: WSContext,
508
- workingDir: string, permission?: 'control' | 'view',
509
- ): void {
510
- if (denyIfViewOnly(ctx, ws, permission)) return;
511
-
512
- const executor = getExecutor(workingDir);
513
-
514
- if (executor.getStatus() === 'executing' || executor.getStatus() === 'starting') {
515
- ctx.send(ws, { type: 'planError', data: { error: 'Execution already in progress' } });
516
- return;
517
- }
518
-
519
- wireExecutorEvents(executor, ctx, workingDir);
520
-
521
- ctx.send(ws, { type: 'planExecutionStarted', data: { status: 'executing' } });
522
- executor.start().catch(error => {
523
- ctx.send(ws, {
524
- type: 'planExecutionError',
525
- data: { error: error instanceof Error ? error.message : String(error) },
526
- });
527
- });
528
- }
529
-
530
- function handleExecuteEpic(
531
- ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage,
532
- workingDir: string, permission?: 'control' | 'view',
533
- ): void {
534
- if (denyIfViewOnly(ctx, ws, permission)) return;
535
-
536
- const epicPath = msg.data?.epicPath;
537
- if (!epicPath) {
538
- ctx.send(ws, { type: 'planError', data: { error: 'Epic path required' } });
539
- return;
540
- }
541
-
542
- const executor = getExecutor(workingDir);
543
-
544
- if (executor.getStatus() === 'executing' || executor.getStatus() === 'starting') {
545
- ctx.send(ws, { type: 'planError', data: { error: 'Execution already in progress' } });
546
- return;
547
- }
548
-
549
- wireExecutorEvents(executor, ctx, workingDir);
550
-
551
- ctx.send(ws, { type: 'planExecutionStarted', data: { status: 'executing', epicPath } });
552
- executor.startEpic(epicPath).catch(error => {
553
- ctx.send(ws, {
554
- type: 'planExecutionError',
555
- data: { error: error instanceof Error ? error.message : String(error) },
556
- });
557
- });
558
- }
559
-
560
- function handlePause(
561
- ctx: HandlerContext, ws: WSContext,
562
- workingDir: string, permission?: 'control' | 'view',
563
- ): void {
564
- if (denyIfViewOnly(ctx, ws, permission)) return;
565
- const executor = executorCache.get(workingDir);
566
- if (executor) executor.pause();
567
- }
568
-
569
- function handleStop(
570
- ctx: HandlerContext, ws: WSContext,
571
- workingDir: string, permission?: 'control' | 'view',
572
- ): void {
573
- if (denyIfViewOnly(ctx, ws, permission)) return;
574
- const executor = executorCache.get(workingDir);
575
- if (executor) executor.stop();
576
- }
577
-
578
- function handleResume(
579
- ctx: HandlerContext, ws: WSContext,
580
- workingDir: string, permission?: 'control' | 'view',
581
- ): void {
582
- if (denyIfViewOnly(ctx, ws, permission)) return;
583
- const executor = executorCache.get(workingDir);
584
- if (executor) {
585
- executor.resume().catch(error => {
586
- ctx.send(ws, {
587
- type: 'planExecutionError',
588
- data: { error: error instanceof Error ? error.message : String(error) },
589
- });
590
- });
591
- }
592
- }