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,160 +1,14 @@
1
1
  // Copyright (c) 2025-present Mstro, Inc. All rights reserved.
2
2
  // Licensed under the MIT License. See LICENSE file for details.
3
- import { spawn } from 'node:child_process';
4
- import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
5
- import { join } from 'node:path';
3
+ import { handleGitCheckout, handleGitCreateBranch, handleGitDeleteBranch, handleGitListBranches } from './git-branch-handlers.js';
4
+ import { handleGitCommitDiff, handleGitDiff, handleGitShowCommit } from './git-diff-handlers.js';
5
+ import { handleGitDiscoverRepos, handleGitLog, handleGitSetDirectory } from './git-log-handlers.js';
6
6
  import { handleGitPRMessage } from './git-pr-handlers.js';
7
+ import { handleGitCreateTag, handleGitListTags, handleGitPushTag } from './git-tag-handlers.js';
8
+ import { executeGitCommand, parseGitStatus, sendGitError, spawnHaikuWithPrompt, stripCoauthorLines, truncateDiff } from './git-utils.js';
7
9
  import { handleGitWorktreeMessage } from './git-worktree-handlers.js';
8
- /** Detect git provider from remote URL */
9
- export function detectGitProvider(remoteUrl) {
10
- if (remoteUrl.includes('github.com'))
11
- return 'github';
12
- if (remoteUrl.includes('gitlab.com') || remoteUrl.includes('gitlab'))
13
- return 'gitlab';
14
- return 'unknown';
15
- }
16
- /** Execute a git command and return stdout */
17
- export function executeGitCommand(args, workingDir) {
18
- return new Promise((resolve) => {
19
- const git = spawn('git', args, {
20
- cwd: workingDir,
21
- stdio: ['ignore', 'pipe', 'pipe']
22
- });
23
- let stdout = '';
24
- let stderr = '';
25
- git.stdout?.on('data', (data) => {
26
- stdout += data.toString();
27
- });
28
- git.stderr?.on('data', (data) => {
29
- stderr += data.toString();
30
- });
31
- git.on('close', (code) => {
32
- resolve({ stdout, stderr, exitCode: code ?? 1 });
33
- });
34
- git.on('error', (err) => {
35
- resolve({ stdout: '', stderr: err.message, exitCode: 1 });
36
- });
37
- });
38
- }
39
- /** Map of simple escape sequences to their character values */
40
- const ESCAPE_CHARS = {
41
- '\\': '\\',
42
- '"': '"',
43
- 'n': '\n',
44
- 't': '\t',
45
- 'r': '\r',
46
- };
47
- /** Check if position i starts an octal escape sequence (\nnn) */
48
- function isOctalEscape(str, i) {
49
- return i + 3 < str.length &&
50
- /[0-7]/.test(str[i + 1]) &&
51
- /[0-7]{2}/.test(str.slice(i + 2, i + 4));
52
- }
53
- /**
54
- * Unquote a git-quoted path (C-style quoting)
55
- */
56
- export function unquoteGitPath(path) {
57
- if (!path.startsWith('"') || !path.endsWith('"')) {
58
- return path;
59
- }
60
- const inner = path.slice(1, -1);
61
- let result = '';
62
- let i = 0;
63
- while (i < inner.length) {
64
- if (inner[i] !== '\\' || i + 1 >= inner.length) {
65
- result += inner[i];
66
- i++;
67
- continue;
68
- }
69
- const next = inner[i + 1];
70
- const escaped = ESCAPE_CHARS[next];
71
- if (escaped !== undefined) {
72
- result += escaped;
73
- i += 2;
74
- }
75
- else if (isOctalEscape(inner, i)) {
76
- result += String.fromCharCode(parseInt(inner.slice(i + 1, i + 4), 8));
77
- i += 4;
78
- }
79
- else {
80
- result += inner[i];
81
- i++;
82
- }
83
- }
84
- return result;
85
- }
86
- /** Parse git status --porcelain output into structured format */
87
- export function parseGitStatus(porcelainOutput) {
88
- const staged = [];
89
- const unstaged = [];
90
- const untracked = [];
91
- const lines = porcelainOutput.split('\n').filter(line => line.length >= 4);
92
- for (const line of lines) {
93
- const indexStatus = line[0];
94
- const workTreeStatus = line[1];
95
- const rawPath = line.slice(3);
96
- const path = unquoteGitPath(rawPath);
97
- let filePath = path;
98
- let originalPath;
99
- if (rawPath.includes(' -> ')) {
100
- const parts = rawPath.split(' -> ');
101
- originalPath = unquoteGitPath(parts[0]);
102
- filePath = unquoteGitPath(parts[1]);
103
- }
104
- if (indexStatus === '?' && workTreeStatus === '?') {
105
- untracked.push({ path: filePath, status: '?', staged: false });
106
- continue;
107
- }
108
- if (indexStatus !== ' ' && indexStatus !== '?') {
109
- staged.push({ path: filePath, status: indexStatus, staged: true, originalPath });
110
- }
111
- if (workTreeStatus !== ' ' && workTreeStatus !== '?') {
112
- unstaged.push({ path: filePath, status: workTreeStatus, staged: false, originalPath });
113
- }
114
- }
115
- return { staged, unstaged, untracked };
116
- }
117
- /** Check if a binary runs successfully (exit code 0) */
118
- export function spawnCheck(bin, args) {
119
- return new Promise((resolve) => {
120
- const proc = spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
121
- proc.on('close', (code) => resolve(code === 0));
122
- proc.on('error', () => resolve(false));
123
- });
124
- }
125
- /** Spawn a process and capture stdout/stderr */
126
- export function spawnWithOutput(bin, args, cwd) {
127
- return new Promise((resolve) => {
128
- const proc = spawn(bin, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
129
- let stdout = '';
130
- let stderr = '';
131
- proc.stdout?.on('data', (d) => { stdout += d.toString(); });
132
- proc.stderr?.on('data', (d) => { stderr += d.toString(); });
133
- proc.on('close', (code) => resolve({ stdout, stderr, exitCode: code ?? 1 }));
134
- proc.on('error', (err) => resolve({ stdout: '', stderr: err.message, exitCode: 1 }));
135
- });
136
- }
137
- /**
138
- * Strip injected coauthor/attribution lines from a commit message.
139
- */
140
- export function stripCoauthorLines(message) {
141
- const lines = message.split('\n');
142
- const markers = ['co-authored', 'authored-by', 'haiku', 'noreply@anthropic.com'];
143
- const result = [];
144
- for (let i = 0; i < lines.length; i++) {
145
- const lower = lines[i].toLowerCase();
146
- if (markers.some(m => lower.includes(m))) {
147
- if (result.length > 0 && result[result.length - 1].trim() === '') {
148
- result.pop();
149
- }
150
- continue;
151
- }
152
- result.push(lines[i]);
153
- }
154
- if (result.length === 0)
155
- return '';
156
- return result.join('\n').trimEnd();
157
- }
10
+ // Re-export utilities for backward compatibility (git-pr-handlers, git-worktree-handlers import from here)
11
+ export { detectGitProvider, executeGitCommand, parseGitStatus, sendGitError, spawnCheck, spawnHaikuWithPrompt, spawnWithOutput, stripCoauthorLines, truncateDiff, unquoteGitPath } from './git-utils.js';
158
12
  // PR message types that route to git-pr-handlers
159
13
  const GIT_PR_TYPES = new Set([
160
14
  'gitGetRemoteInfo', 'gitCreatePR', 'gitGeneratePRDescription',
@@ -192,6 +46,8 @@ export async function handleGitMessage(ctx, ws, msg, tabId, workingDir) {
192
46
  gitCreateBranch: () => handleGitCreateBranch(ctx, ws, msg, tabId, gitDir),
193
47
  gitDeleteBranch: () => handleGitDeleteBranch(ctx, ws, msg, tabId, gitDir),
194
48
  gitDiff: () => handleGitDiff(ctx, ws, msg, tabId, gitDir),
49
+ gitShowCommit: () => handleGitShowCommit(ctx, ws, msg, tabId, gitDir),
50
+ gitCommitDiff: () => handleGitCommitDiff(ctx, ws, msg, tabId, gitDir),
195
51
  gitListTags: () => handleGitListTags(ctx, ws, tabId, gitDir),
196
52
  gitCreateTag: () => handleGitCreateTag(ctx, ws, msg, tabId, gitDir),
197
53
  gitPushTag: () => handleGitPushTag(ctx, ws, msg, tabId, gitDir),
@@ -237,7 +93,7 @@ export async function handleGitStatus(ctx, ws, tabId, workingDir) {
237
93
  ctx.send(ws, { type: 'gitStatus', tabId, data: response });
238
94
  }
239
95
  catch (error) {
240
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
96
+ sendGitError(ctx, ws, tabId, error);
241
97
  }
242
98
  }
243
99
  async function handleGitStage(ctx, ws, msg, tabId, workingDir) {
@@ -257,7 +113,7 @@ async function handleGitStage(ctx, ws, msg, tabId, workingDir) {
257
113
  ctx.send(ws, { type: 'gitStaged', tabId, data: { paths: paths || [] } });
258
114
  }
259
115
  catch (error) {
260
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
116
+ sendGitError(ctx, ws, tabId, error);
261
117
  }
262
118
  }
263
119
  async function handleGitUnstage(ctx, ws, msg, tabId, workingDir) {
@@ -275,7 +131,7 @@ async function handleGitUnstage(ctx, ws, msg, tabId, workingDir) {
275
131
  ctx.send(ws, { type: 'gitUnstaged', tabId, data: { paths } });
276
132
  }
277
133
  catch (error) {
278
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
134
+ sendGitError(ctx, ws, tabId, error);
279
135
  }
280
136
  }
281
137
  async function handleGitCommit(ctx, ws, msg, tabId, workingDir) {
@@ -301,7 +157,7 @@ async function handleGitCommit(ctx, ws, msg, tabId, workingDir) {
301
157
  handleGitStatus(ctx, ws, tabId, workingDir);
302
158
  }
303
159
  catch (error) {
304
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
160
+ sendGitError(ctx, ws, tabId, error);
305
161
  }
306
162
  }
307
163
  async function handleGitCommitWithAI(ctx, ws, msg, tabId, workingDir) {
@@ -313,27 +169,17 @@ async function handleGitCommitWithAI(ctx, ws, msg, tabId, workingDir) {
313
169
  return;
314
170
  }
315
171
  const diffResult = await executeGitCommand(['diff', '--cached'], workingDir);
316
- const diff = diffResult.stdout;
317
172
  const logResult = await executeGitCommand(['log', '--oneline', '-5'], workingDir);
318
- const recentCommits = logResult.stdout.trim();
319
- const tempDir = join(workingDir, '.mstro', 'tmp');
320
- if (!existsSync(tempDir)) {
321
- mkdirSync(tempDir, { recursive: true });
322
- }
323
- let truncatedDiff = diff;
324
- if (diff.length > 8000) {
325
- truncatedDiff = `${diff.slice(0, 4000)}\n\n... [diff truncated] ...\n\n${diff.slice(-3500)}`;
326
- }
327
173
  const prompt = `You are generating a git commit message for the following staged changes.
328
174
 
329
175
  RECENT COMMIT MESSAGES (for style reference):
330
- ${recentCommits || 'No recent commits'}
176
+ ${logResult.stdout.trim() || 'No recent commits'}
331
177
 
332
178
  STAGED FILES:
333
179
  ${staged.map(f => `${f.status} ${f.path}`).join('\n')}
334
180
 
335
181
  DIFF OF STAGED CHANGES:
336
- ${truncatedDiff}
182
+ ${truncateDiff(diffResult.stdout)}
337
183
 
338
184
  Generate a commit message following these rules:
339
185
  1. First line: imperative mood, max 72 characters (e.g., "Add user authentication", "Fix memory leak in parser")
@@ -343,64 +189,28 @@ Generate a commit message following these rules:
343
189
  5. No emojis unless the repo already uses them
344
190
 
345
191
  Respond with ONLY the commit message, nothing else.`;
346
- const promptFile = join(tempDir, `commit-msg-${Date.now()}.txt`);
347
- writeFileSync(promptFile, prompt);
348
- const systemPrompt = 'You are a commit message assistant. Respond with only the commit message, no preamble or explanation.';
349
- const args = [
350
- '--print',
351
- '--model', 'haiku',
352
- '--system-prompt', systemPrompt,
353
- promptFile
354
- ];
355
- const claude = spawn('claude', args, {
356
- cwd: workingDir,
357
- stdio: ['ignore', 'pipe', 'pipe']
358
- });
359
- let stdout = '';
360
- let stderr = '';
361
- claude.stdout?.on('data', (data) => {
362
- stdout += data.toString();
363
- });
364
- claude.stderr?.on('data', (data) => {
365
- stderr += data.toString();
366
- });
367
- claude.on('close', async (code) => {
368
- try {
369
- unlinkSync(promptFile);
370
- }
371
- catch {
372
- // Ignore cleanup errors
373
- }
374
- if (code !== 0 || !stdout.trim()) {
375
- console.error('[WebSocketImproviseHandler] Claude commit message error:', stderr || 'No output');
376
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Failed to generate commit message' } });
192
+ const result = await spawnHaikuWithPrompt(prompt, 'You are a commit message assistant. Respond with only the commit message, no preamble or explanation.', workingDir);
193
+ if (result.exitCode !== 0 || !result.stdout.trim()) {
194
+ console.error('[WebSocketImproviseHandler] Claude commit message error:', result.stderr || 'No output');
195
+ ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Failed to generate commit message' } });
196
+ return;
197
+ }
198
+ const commitMessage = extractCommitMessage(result.stdout.trim());
199
+ const autoCommit = !!msg.data?.autoCommit;
200
+ ctx.send(ws, { type: 'gitCommitMessage', tabId, data: { message: commitMessage, autoCommit } });
201
+ if (autoCommit) {
202
+ const commitResult = await executeGitCommand(['commit', '-m', commitMessage], workingDir);
203
+ if (commitResult.exitCode !== 0) {
204
+ ctx.send(ws, { type: 'gitError', tabId, data: { error: commitResult.stderr || commitResult.stdout || 'Failed to commit' } });
377
205
  return;
378
206
  }
379
- const commitMessage = extractCommitMessage(stdout.trim());
380
- const autoCommit = !!msg.data?.autoCommit;
381
- ctx.send(ws, { type: 'gitCommitMessage', tabId, data: { message: commitMessage, autoCommit } });
382
- if (msg.data?.autoCommit) {
383
- const commitResult = await executeGitCommand(['commit', '-m', commitMessage], workingDir);
384
- if (commitResult.exitCode !== 0) {
385
- ctx.send(ws, { type: 'gitError', tabId, data: { error: commitResult.stderr || commitResult.stdout || 'Failed to commit' } });
386
- return;
387
- }
388
- const hashResult = await executeGitCommand(['rev-parse', '--short', 'HEAD'], workingDir);
389
- const hash = hashResult.stdout.trim();
390
- ctx.send(ws, { type: 'gitCommitted', tabId, data: { hash, message: commitMessage } });
391
- handleGitStatus(ctx, ws, tabId, workingDir);
392
- }
393
- });
394
- claude.on('error', (err) => {
395
- console.error('[WebSocketImproviseHandler] Failed to spawn Claude for commit:', err);
396
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Failed to generate commit message' } });
397
- });
398
- setTimeout(() => {
399
- claude.kill();
400
- }, 30000);
207
+ const hashResult = await executeGitCommand(['rev-parse', '--short', 'HEAD'], workingDir);
208
+ ctx.send(ws, { type: 'gitCommitted', tabId, data: { hash: hashResult.stdout.trim(), message: commitMessage } });
209
+ handleGitStatus(ctx, ws, tabId, workingDir);
210
+ }
401
211
  }
402
212
  catch (error) {
403
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
213
+ sendGitError(ctx, ws, tabId, error);
404
214
  }
405
215
  }
406
216
  function extractCommitMessage(output) {
@@ -462,7 +272,7 @@ async function handleGitPush(ctx, ws, tabId, workingDir) {
462
272
  ctx.send(ws, { type: 'gitPushed', tabId, data: { output: result.stdout || result.stderr } });
463
273
  }
464
274
  catch (error) {
465
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
275
+ sendGitError(ctx, ws, tabId, error);
466
276
  }
467
277
  }
468
278
  async function handleGitPull(ctx, ws, tabId, workingDir) {
@@ -475,323 +285,7 @@ async function handleGitPull(ctx, ws, tabId, workingDir) {
475
285
  ctx.send(ws, { type: 'gitPulled', tabId, data: { output: result.stdout || result.stderr } });
476
286
  }
477
287
  catch (error) {
478
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
479
- }
480
- }
481
- async function handleGitLog(ctx, ws, msg, tabId, workingDir) {
482
- const limit = msg.data?.limit ?? 10;
483
- try {
484
- const result = await executeGitCommand([
485
- 'log',
486
- `-${limit}`,
487
- '--format=%H|%h|%s|%an|%aI'
488
- ], workingDir);
489
- if (result.exitCode !== 0) {
490
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || result.stdout || 'Failed to get log' } });
491
- return;
492
- }
493
- const entries = result.stdout.trim().split('\n').filter(Boolean).map(line => {
494
- const [hash, shortHash, subject, author, date] = line.split('|');
495
- const cleanSubject = stripCoauthorLines(subject || '') || subject || '';
496
- return { hash, shortHash, subject: cleanSubject, author, date };
497
- });
498
- ctx.send(ws, { type: 'gitLog', tabId, data: { entries } });
499
- }
500
- catch (error) {
501
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
502
- }
503
- }
504
- /** Directories to skip when scanning for git repos */
505
- const SKIP_DIRS = ['node_modules', 'vendor', '.git'];
506
- function shouldSkipDir(name) {
507
- return name.startsWith('.') || SKIP_DIRS.includes(name);
508
- }
509
- async function getRepoBranch(repoPath) {
510
- const result = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], repoPath);
511
- return result.exitCode === 0 ? result.stdout.trim() : undefined;
512
- }
513
- async function scanForGitRepos(dir, depth, maxDepth, repos) {
514
- if (depth > maxDepth)
515
- return;
516
- let entries;
517
- try {
518
- entries = readdirSync(dir);
519
- }
520
- catch {
521
- return;
522
- }
523
- for (const name of entries) {
524
- if (shouldSkipDir(name))
525
- continue;
526
- const fullPath = join(dir, name);
527
- const gitPath = join(fullPath, '.git');
528
- if (existsSync(gitPath)) {
529
- repos.push({ path: fullPath, name, branch: await getRepoBranch(fullPath) });
530
- }
531
- else {
532
- await scanForGitRepos(fullPath, depth + 1, maxDepth, repos);
533
- }
534
- }
535
- }
536
- async function handleGitDiscoverRepos(ctx, ws, tabId, workingDir) {
537
- try {
538
- const repos = [];
539
- const rootIsGitRepo = existsSync(join(workingDir, '.git'));
540
- if (rootIsGitRepo) {
541
- repos.push({
542
- path: workingDir,
543
- name: workingDir.split('/').pop() || workingDir,
544
- branch: await getRepoBranch(workingDir),
545
- });
546
- }
547
- else {
548
- await scanForGitRepos(workingDir, 1, 3, repos);
549
- }
550
- const response = {
551
- repos,
552
- rootIsGitRepo,
553
- selectedDirectory: ctx.gitDirectories.get(tabId) || null,
554
- };
555
- ctx.send(ws, { type: 'gitReposDiscovered', tabId, data: response });
556
- }
557
- catch (error) {
558
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
559
- }
560
- }
561
- async function handleGitSetDirectory(ctx, ws, msg, tabId, workingDir) {
562
- const directory = msg.data?.directory;
563
- if (!directory) {
564
- ctx.gitDirectories.delete(tabId);
565
- const response = {
566
- directory: workingDir,
567
- isValid: existsSync(join(workingDir, '.git')),
568
- };
569
- ctx.send(ws, { type: 'gitDirectorySet', tabId, data: response });
570
- handleGitStatus(ctx, ws, tabId, workingDir);
571
- return;
572
- }
573
- const gitPath = join(directory, '.git');
574
- const isValid = existsSync(gitPath);
575
- if (isValid) {
576
- ctx.gitDirectories.set(tabId, directory);
577
- }
578
- const response = {
579
- directory,
580
- isValid,
581
- };
582
- ctx.send(ws, { type: 'gitDirectorySet', tabId, data: response });
583
- if (isValid) {
584
- handleGitStatus(ctx, ws, tabId, directory);
585
- handleGitLog(ctx, ws, { type: 'gitLog', data: { limit: 5 } }, tabId, directory);
586
- }
587
- }
588
- async function handleGitListBranches(ctx, ws, tabId, workingDir) {
589
- try {
590
- const result = await executeGitCommand(['branch', '-a', '--format=%(refname:short)|%(objectname:short)|%(upstream:short)|%(HEAD)'], workingDir);
591
- if (result.exitCode !== 0) {
592
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to list branches' } });
593
- return;
594
- }
595
- const currentBranchResult = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], workingDir);
596
- const currentBranch = currentBranchResult.stdout.trim() || 'HEAD';
597
- const branches = result.stdout.trim().split('\n')
598
- .filter(line => line.trim())
599
- .map(line => {
600
- const [name, shortHash, upstream, head] = line.split('|');
601
- const isRemote = name.includes('/') && (name.startsWith('origin/') || name.includes('remotes/'));
602
- return {
603
- name: name.trim(),
604
- shortHash: shortHash?.trim() || '',
605
- isRemote,
606
- isCurrent: head?.trim() === '*',
607
- upstream: upstream?.trim() || undefined,
608
- };
609
- })
610
- .filter(b => b.name !== 'origin/HEAD');
611
- ctx.send(ws, { type: 'gitBranchList', tabId, data: { branches, current: currentBranch } });
612
- }
613
- catch (error) {
614
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
615
- }
616
- }
617
- async function handleGitCheckout(ctx, ws, msg, tabId, workingDir) {
618
- try {
619
- const { branch, create, startPoint } = msg.data || {};
620
- if (!branch) {
621
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Branch name is required' } });
622
- return;
623
- }
624
- const statusResult = await executeGitCommand(['status', '--porcelain'], workingDir);
625
- if (statusResult.stdout.trim()) {
626
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Commit or stash changes before switching branches' } });
627
- return;
628
- }
629
- const prevResult = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], workingDir);
630
- const previous = prevResult.stdout.trim();
631
- const args = create
632
- ? ['checkout', '-b', branch, ...(startPoint ? [startPoint] : [])]
633
- : ['checkout', branch];
634
- const result = await executeGitCommand(args, workingDir);
635
- if (result.exitCode !== 0) {
636
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to checkout branch' } });
637
- return;
638
- }
639
- ctx.send(ws, { type: 'gitCheckedOut', tabId, data: { branch, previous } });
640
- handleGitStatus(ctx, ws, tabId, workingDir);
641
- }
642
- catch (error) {
643
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
644
- }
645
- }
646
- async function handleGitCreateBranch(ctx, ws, msg, tabId, workingDir) {
647
- try {
648
- const { name, startPoint, checkout } = msg.data || {};
649
- if (!name) {
650
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Branch name is required' } });
651
- return;
652
- }
653
- const args = ['branch', name, ...(startPoint ? [startPoint] : [])];
654
- const result = await executeGitCommand(args, workingDir);
655
- if (result.exitCode !== 0) {
656
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to create branch' } });
657
- return;
658
- }
659
- const hashResult = await executeGitCommand(['rev-parse', '--short', name], workingDir);
660
- if (checkout) {
661
- await executeGitCommand(['checkout', name], workingDir);
662
- }
663
- ctx.send(ws, { type: 'gitBranchCreated', tabId, data: { name, hash: hashResult.stdout.trim() } });
664
- }
665
- catch (error) {
666
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
667
- }
668
- }
669
- async function handleGitDeleteBranch(ctx, ws, msg, tabId, workingDir) {
670
- try {
671
- const { name, force } = msg.data || {};
672
- if (!name) {
673
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Branch name is required' } });
674
- return;
675
- }
676
- const currentResult = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], workingDir);
677
- if (currentResult.stdout.trim() === name) {
678
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Cannot delete the currently checked out branch' } });
679
- return;
680
- }
681
- const result = await executeGitCommand(['branch', force ? '-D' : '-d', name], workingDir);
682
- if (result.exitCode !== 0) {
683
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to delete branch' } });
684
- return;
685
- }
686
- ctx.send(ws, { type: 'gitBranchDeleted', tabId, data: { name } });
687
- }
688
- catch (error) {
689
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
690
- }
691
- }
692
- async function handleGitDiff(ctx, ws, msg, tabId, workingDir) {
693
- try {
694
- const { path, staged } = msg.data || {};
695
- if (!path) {
696
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'File path is required' } });
697
- return;
698
- }
699
- const originalResult = await executeGitCommand(['show', `HEAD:${path}`], workingDir);
700
- const original = originalResult.exitCode === 0 ? originalResult.stdout : '';
701
- let modified;
702
- if (staged) {
703
- const indexResult = await executeGitCommand(['show', `:${path}`], workingDir);
704
- modified = indexResult.exitCode === 0 ? indexResult.stdout : '';
705
- }
706
- else {
707
- const fullPath = join(workingDir, path);
708
- try {
709
- modified = readFileSync(fullPath, 'utf-8');
710
- }
711
- catch {
712
- modified = '';
713
- }
714
- }
715
- ctx.send(ws, {
716
- type: 'gitDiffResult',
717
- tabId,
718
- data: { path, original, modified, staged: !!staged },
719
- });
720
- }
721
- catch (error) {
722
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
723
- }
724
- }
725
- async function handleGitListTags(ctx, ws, tabId, workingDir) {
726
- try {
727
- const result = await executeGitCommand(['tag', '-l', '--sort=-creatordate', '--format=%(refname:short)|%(objectname:short)|%(creatordate:iso-strict)|%(subject)'], workingDir);
728
- if (result.exitCode !== 0) {
729
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to list tags' } });
730
- return;
731
- }
732
- const tags = result.stdout.trim().split('\n')
733
- .filter(line => line.trim())
734
- .slice(0, 50)
735
- .map(line => {
736
- const parts = line.split('|');
737
- return {
738
- name: parts[0]?.trim() || '',
739
- shortHash: parts[1]?.trim() || '',
740
- date: parts[2]?.trim() || '',
741
- message: parts[3]?.trim() || '',
742
- };
743
- });
744
- ctx.send(ws, { type: 'gitTagList', tabId, data: { tags } });
745
- }
746
- catch (error) {
747
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
748
- }
749
- }
750
- async function handleGitCreateTag(ctx, ws, msg, tabId, workingDir) {
751
- try {
752
- const { name, message, commit } = msg.data || {};
753
- if (!name) {
754
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Tag name is required' } });
755
- return;
756
- }
757
- if (/\s/.test(name) || name.includes('..')) {
758
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Invalid tag name: no spaces or ".." allowed' } });
759
- return;
760
- }
761
- const args = message
762
- ? ['tag', '-a', name, '-m', message, ...(commit ? [commit] : [])]
763
- : ['tag', name, ...(commit ? [commit] : [])];
764
- const result = await executeGitCommand(args, workingDir);
765
- if (result.exitCode !== 0) {
766
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to create tag' } });
767
- return;
768
- }
769
- const hashResult = await executeGitCommand(['rev-parse', '--short', name], workingDir);
770
- ctx.send(ws, { type: 'gitTagCreated', tabId, data: { name, hash: hashResult.stdout.trim() } });
771
- }
772
- catch (error) {
773
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
774
- }
775
- }
776
- async function handleGitPushTag(ctx, ws, msg, tabId, workingDir) {
777
- try {
778
- const { name, all } = msg.data || {};
779
- const args = all
780
- ? ['push', 'origin', '--tags']
781
- : ['push', 'origin', name];
782
- if (!all && !name) {
783
- ctx.send(ws, { type: 'gitError', tabId, data: { error: 'Tag name is required' } });
784
- return;
785
- }
786
- const result = await executeGitCommand(args, workingDir);
787
- if (result.exitCode !== 0) {
788
- ctx.send(ws, { type: 'gitError', tabId, data: { error: result.stderr || 'Failed to push tag' } });
789
- return;
790
- }
791
- ctx.send(ws, { type: 'gitTagPushed', tabId, data: { name: name || 'all', output: result.stderr || result.stdout } });
792
- }
793
- catch (error) {
794
- ctx.send(ws, { type: 'gitError', tabId, data: { error: error instanceof Error ? error.message : String(error) } });
288
+ sendGitError(ctx, ws, tabId, error);
795
289
  }
796
290
  }
797
291
  //# sourceMappingURL=git-handlers.js.map