@cloudcli-ai/cloudcli 1.36.2 → 1.37.0

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 (539) hide show
  1. package/dist/assets/index-DSlds633.css +32 -0
  2. package/dist/assets/{index-CDGzV0LN.js → index-eGqPeIEu.js} +375 -267
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/constants/config.js.map +1 -1
  5. package/dist-server/server/index.js +73 -1224
  6. package/dist-server/server/index.js.map +1 -1
  7. package/dist-server/server/load-env.js +17 -4
  8. package/dist-server/server/load-env.js.map +1 -1
  9. package/dist-server/server/modules/agent/agent.module.js +37 -0
  10. package/dist-server/server/modules/agent/agent.module.js.map +1 -0
  11. package/dist-server/server/modules/agent/agent.routes.js +1161 -0
  12. package/dist-server/server/modules/agent/agent.routes.js.map +1 -0
  13. package/dist-server/server/modules/agent/index.js +3 -0
  14. package/dist-server/server/modules/agent/index.js.map +1 -0
  15. package/dist-server/server/modules/agent/tests/agent.routes.test.js +176 -0
  16. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -0
  17. package/dist-server/server/modules/assets/assets.routes.js +56 -15
  18. package/dist-server/server/modules/assets/assets.routes.js.map +1 -1
  19. package/dist-server/server/modules/assets/index.js +2 -2
  20. package/dist-server/server/modules/assets/index.js.map +1 -1
  21. package/dist-server/server/modules/assets/services/image-assets.service.js +39 -1
  22. package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -1
  23. package/dist-server/server/modules/assets/tests/image-assets.service.test.js +21 -1
  24. package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -1
  25. package/dist-server/server/{middleware/auth.js → modules/auth/auth.middleware.js} +30 -8
  26. package/dist-server/server/modules/auth/auth.middleware.js.map +1 -0
  27. package/dist-server/server/modules/auth/auth.module.js +29 -0
  28. package/dist-server/server/modules/auth/auth.module.js.map +1 -0
  29. package/dist-server/server/modules/auth/auth.routes.js +45 -0
  30. package/dist-server/server/modules/auth/auth.routes.js.map +1 -0
  31. package/dist-server/server/modules/auth/auth.service.js +113 -0
  32. package/dist-server/server/modules/auth/auth.service.js.map +1 -0
  33. package/dist-server/server/modules/auth/index.js +9 -0
  34. package/dist-server/server/modules/auth/index.js.map +1 -0
  35. package/dist-server/server/modules/auth/tests/auth.service.test.js +80 -0
  36. package/dist-server/server/modules/auth/tests/auth.service.test.js.map +1 -0
  37. package/dist-server/server/{browser-use-mcp.js → modules/browser-use/browser-use-mcp.js} +3 -1
  38. package/dist-server/server/modules/browser-use/browser-use-mcp.js.map +1 -0
  39. package/dist-server/server/modules/browser-use/browser-use.service.js +3 -4
  40. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  41. package/dist-server/server/modules/browser-use/index.js +9 -0
  42. package/dist-server/server/modules/browser-use/index.js.map +1 -0
  43. package/dist-server/server/modules/cli/cli.js +11 -0
  44. package/dist-server/server/modules/cli/cli.js.map +1 -0
  45. package/dist-server/server/modules/cli/cli.module.js +86 -0
  46. package/dist-server/server/modules/cli/cli.module.js.map +1 -0
  47. package/dist-server/server/modules/cli/cli.service.js +244 -0
  48. package/dist-server/server/modules/cli/cli.service.js.map +1 -0
  49. package/dist-server/server/modules/cli/index.js +3 -0
  50. package/dist-server/server/modules/cli/index.js.map +1 -0
  51. package/dist-server/server/modules/cli/sandbox.service.js +332 -0
  52. package/dist-server/server/modules/cli/sandbox.service.js.map +1 -0
  53. package/dist-server/server/modules/cli/tests/cli.service.test.js +78 -0
  54. package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -0
  55. package/dist-server/server/modules/cli/tests/sandbox.service.test.js +55 -0
  56. package/dist-server/server/modules/cli/tests/sandbox.service.test.js.map +1 -0
  57. package/dist-server/server/modules/commands/commands.module.js +20 -0
  58. package/dist-server/server/modules/commands/commands.module.js.map +1 -0
  59. package/dist-server/server/modules/commands/commands.routes.js +497 -0
  60. package/dist-server/server/modules/commands/commands.routes.js.map +1 -0
  61. package/dist-server/server/modules/commands/index.js +3 -0
  62. package/dist-server/server/modules/commands/index.js.map +1 -0
  63. package/dist-server/server/modules/commands/tests/commands.test.js +96 -0
  64. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -0
  65. package/dist-server/server/modules/database/index.js +1 -0
  66. package/dist-server/server/modules/database/index.js.map +1 -1
  67. package/dist-server/server/modules/database/migrations.js +13 -0
  68. package/dist-server/server/modules/database/migrations.js.map +1 -1
  69. package/dist-server/server/modules/database/repositories/sessions.db.js +14 -1
  70. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  71. package/dist-server/server/modules/database/schema.js +4 -0
  72. package/dist-server/server/modules/database/schema.js.map +1 -1
  73. package/dist-server/server/modules/file-tree/file-tree.module.js +91 -0
  74. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -0
  75. package/dist-server/server/modules/file-tree/file-tree.routes.js +208 -0
  76. package/dist-server/server/modules/file-tree/file-tree.routes.js.map +1 -0
  77. package/dist-server/server/modules/file-tree/file-tree.service.js +544 -0
  78. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -0
  79. package/dist-server/server/modules/file-tree/index.js +3 -0
  80. package/dist-server/server/modules/file-tree/index.js.map +1 -0
  81. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js +123 -0
  82. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js.map +1 -0
  83. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +184 -0
  84. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -0
  85. package/dist-server/server/modules/git/git-parsing.service.js +61 -0
  86. package/dist-server/server/modules/git/git-parsing.service.js.map +1 -0
  87. package/dist-server/server/modules/git/git.module.js +14 -0
  88. package/dist-server/server/modules/git/git.module.js.map +1 -0
  89. package/dist-server/server/modules/git/git.routes.js +1339 -0
  90. package/dist-server/server/modules/git/git.routes.js.map +1 -0
  91. package/dist-server/server/modules/git/index.js +3 -0
  92. package/dist-server/server/modules/git/index.js.map +1 -0
  93. package/dist-server/server/modules/git/tests/git-init.routes.test.js +48 -0
  94. package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -0
  95. package/dist-server/server/{routes → modules/git/tests}/git.test.js +1 -1
  96. package/dist-server/server/modules/git/tests/git.test.js.map +1 -0
  97. package/dist-server/server/modules/notifications/index.js +15 -1
  98. package/dist-server/server/modules/notifications/index.js.map +1 -1
  99. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js +44 -0
  100. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js.map +1 -0
  101. package/dist-server/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.js} +3 -2
  102. package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -0
  103. package/dist-server/server/modules/plugins/index.js +9 -0
  104. package/dist-server/server/modules/plugins/index.js.map +1 -0
  105. package/dist-server/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.js} +3 -2
  106. package/dist-server/server/modules/plugins/plugin-process.service.js.map +1 -0
  107. package/dist-server/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.js} +2 -1
  108. package/dist-server/server/modules/plugins/plugin-registry.service.js.map +1 -0
  109. package/dist-server/server/modules/plugins/plugins.module.js +30 -0
  110. package/dist-server/server/modules/plugins/plugins.module.js.map +1 -0
  111. package/dist-server/server/modules/plugins/plugins.routes.js +70 -0
  112. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -0
  113. package/dist-server/server/modules/plugins/plugins.service.js +123 -0
  114. package/dist-server/server/modules/plugins/plugins.service.js.map +1 -0
  115. package/dist-server/server/modules/plugins/tests/plugins.service.test.js +28 -0
  116. package/dist-server/server/modules/plugins/tests/plugins.service.test.js.map +1 -0
  117. package/dist-server/server/modules/projects/index.js +5 -0
  118. package/dist-server/server/modules/projects/index.js.map +1 -1
  119. package/dist-server/server/modules/providers/index.js +3 -0
  120. package/dist-server/server/modules/providers/index.js.map +1 -1
  121. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +6 -0
  122. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  123. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +1 -4
  124. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  125. package/dist-server/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +62 -41
  126. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -0
  127. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +6 -4
  128. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  129. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +23 -4
  130. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  131. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  132. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  133. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +1 -4
  134. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  135. package/dist-server/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +48 -34
  136. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -0
  137. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +19 -1
  138. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +290 -6
  140. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +5 -0
  142. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
  143. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  144. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  145. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +1 -4
  146. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  147. package/dist-server/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +40 -28
  148. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -0
  149. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +21 -14
  150. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
  152. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +7 -5
  154. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  155. package/dist-server/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +38 -25
  156. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -0
  157. package/dist-server/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +27 -4
  158. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js.map +1 -0
  159. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +11 -5
  160. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +2 -0
  162. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -1
  163. package/dist-server/server/modules/providers/provider.routes.js +33 -9
  164. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  165. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -1
  166. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  167. package/dist-server/server/modules/providers/services/provider-models.service.js +110 -15
  168. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  169. package/dist-server/server/modules/providers/services/provider-runtime.service.js +66 -0
  170. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -0
  171. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +268 -0
  172. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -0
  173. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +2 -0
  174. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  175. package/dist-server/server/modules/providers/services/sessions.service.js +54 -0
  176. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  177. package/dist-server/server/modules/providers/shared/base/abstract.provider.js +2 -3
  178. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
  179. package/dist-server/server/modules/providers/tests/claude-auth.test.js +122 -0
  180. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -0
  181. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +44 -0
  182. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -0
  183. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +70 -0
  184. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  185. package/dist-server/server/modules/providers/tests/{provider-image-history.test.js → provider-attachment-history.test.js} +60 -2
  186. package/dist-server/server/modules/providers/tests/provider-attachment-history.test.js.map +1 -0
  187. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +148 -61
  188. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  189. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +130 -0
  190. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -0
  191. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +165 -0
  192. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -0
  193. package/dist-server/server/modules/providers/tests/sessions-details.test.js +62 -0
  194. package/dist-server/server/modules/providers/tests/sessions-details.test.js.map +1 -0
  195. package/dist-server/server/modules/providers/tests/skills.test.js +4 -2
  196. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  197. package/dist-server/server/modules/settings/index.js +3 -0
  198. package/dist-server/server/modules/settings/index.js.map +1 -0
  199. package/dist-server/server/modules/settings/settings.module.js +35 -0
  200. package/dist-server/server/modules/settings/settings.module.js.map +1 -0
  201. package/dist-server/server/modules/settings/settings.routes.js +34 -0
  202. package/dist-server/server/modules/settings/settings.routes.js.map +1 -0
  203. package/dist-server/server/modules/settings/settings.service.js +116 -0
  204. package/dist-server/server/modules/settings/settings.service.js.map +1 -0
  205. package/dist-server/server/modules/settings/tests/settings.service.test.js +48 -0
  206. package/dist-server/server/modules/settings/tests/settings.service.test.js.map +1 -0
  207. package/dist-server/server/modules/system/index.js +3 -0
  208. package/dist-server/server/modules/system/index.js.map +1 -0
  209. package/dist-server/server/modules/system/system.module.js +44 -0
  210. package/dist-server/server/modules/system/system.module.js.map +1 -0
  211. package/dist-server/server/modules/system/system.routes.js +16 -0
  212. package/dist-server/server/modules/system/system.routes.js.map +1 -0
  213. package/dist-server/server/modules/system/system.service.js +45 -0
  214. package/dist-server/server/modules/system/system.service.js.map +1 -0
  215. package/dist-server/server/modules/system/tests/system.service.test.js +100 -0
  216. package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -0
  217. package/dist-server/server/modules/taskmaster/index.js +3 -0
  218. package/dist-server/server/modules/taskmaster/index.js.map +1 -0
  219. package/dist-server/server/modules/taskmaster/taskmaster.module.js +20 -0
  220. package/dist-server/server/modules/taskmaster/taskmaster.module.js.map +1 -0
  221. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +1372 -0
  222. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -0
  223. package/dist-server/server/modules/taskmaster/taskmaster.service.js +107 -0
  224. package/dist-server/server/modules/taskmaster/taskmaster.service.js.map +1 -0
  225. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +132 -0
  226. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -0
  227. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js +85 -0
  228. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js.map +1 -0
  229. package/dist-server/server/modules/user/index.js +3 -0
  230. package/dist-server/server/modules/user/index.js.map +1 -0
  231. package/dist-server/server/modules/user/tests/user.service.test.js +53 -0
  232. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -0
  233. package/dist-server/server/modules/user/user.module.js +47 -0
  234. package/dist-server/server/modules/user/user.module.js.map +1 -0
  235. package/dist-server/server/modules/user/user.routes.js +44 -0
  236. package/dist-server/server/modules/user/user.routes.js.map +1 -0
  237. package/dist-server/server/modules/user/user.service.js +59 -0
  238. package/dist-server/server/modules/user/user.service.js.map +1 -0
  239. package/dist-server/server/modules/voice/index.js +3 -0
  240. package/dist-server/server/modules/voice/index.js.map +1 -0
  241. package/dist-server/server/modules/voice/tests/voice.service.test.js +86 -0
  242. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -0
  243. package/dist-server/server/modules/voice/voice.module.js +44 -0
  244. package/dist-server/server/modules/voice/voice.module.js.map +1 -0
  245. package/dist-server/server/modules/voice/voice.routes.js +87 -0
  246. package/dist-server/server/modules/voice/voice.routes.js.map +1 -0
  247. package/dist-server/server/modules/voice/voice.service.js +148 -0
  248. package/dist-server/server/modules/voice/voice.service.js.map +1 -0
  249. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +2 -2
  250. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +40 -29
  251. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  252. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +76 -8
  253. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  254. package/dist-server/server/modules/websocket/services/websocket-server.service.js +51 -20
  255. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
  256. package/dist-server/server/modules/websocket/tests/{chat-image-filter.test.js → chat-attachment-filter.test.js} +10 -3
  257. package/dist-server/server/modules/websocket/tests/chat-attachment-filter.test.js.map +1 -0
  258. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +97 -0
  259. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -0
  260. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js +61 -0
  261. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js.map +1 -0
  262. package/dist-server/server/modules/worktrees/index.js +3 -0
  263. package/dist-server/server/modules/worktrees/index.js.map +1 -0
  264. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js +37 -0
  265. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js.map +1 -0
  266. package/dist-server/server/modules/worktrees/services/worktree-create.service.js +67 -0
  267. package/dist-server/server/modules/worktrees/services/worktree-create.service.js.map +1 -0
  268. package/dist-server/server/modules/worktrees/services/worktree-git.service.js +172 -0
  269. package/dist-server/server/modules/worktrees/services/worktree-git.service.js.map +1 -0
  270. package/dist-server/server/modules/worktrees/services/worktree-list.service.js +91 -0
  271. package/dist-server/server/modules/worktrees/services/worktree-list.service.js.map +1 -0
  272. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js +118 -0
  273. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js.map +1 -0
  274. package/dist-server/server/modules/worktrees/services/worktree-open.service.js +58 -0
  275. package/dist-server/server/modules/worktrees/services/worktree-open.service.js.map +1 -0
  276. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +65 -0
  277. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js.map +1 -0
  278. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js +58 -0
  279. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js.map +1 -0
  280. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js +85 -0
  281. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js.map +1 -0
  282. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js +58 -0
  283. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js.map +1 -0
  284. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js +34 -0
  285. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js.map +1 -0
  286. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js +137 -0
  287. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js.map +1 -0
  288. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js +99 -0
  289. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js.map +1 -0
  290. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js +157 -0
  291. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js.map +1 -0
  292. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js +167 -0
  293. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js.map +1 -0
  294. package/dist-server/server/modules/worktrees/worktrees.module.js +97 -0
  295. package/dist-server/server/modules/worktrees/worktrees.module.js.map +1 -0
  296. package/dist-server/server/modules/worktrees/worktrees.routes.js +84 -0
  297. package/dist-server/server/modules/worktrees/worktrees.routes.js.map +1 -0
  298. package/dist-server/server/shared/image-attachments.js +90 -17
  299. package/dist-server/server/shared/image-attachments.js.map +1 -1
  300. package/dist-server/server/shared/tests/image-attachments.test.js +43 -1
  301. package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -1
  302. package/dist-server/server/shared/utils.js +66 -142
  303. package/dist-server/server/shared/utils.js.map +1 -1
  304. package/electron/localServer.js +1 -2
  305. package/package.json +8 -4
  306. package/server/index.ts +401 -0
  307. package/server/{load-env.js → load-env.ts} +18 -5
  308. package/server/modules/agent/agent.module.ts +51 -0
  309. package/server/modules/agent/agent.routes.ts +1287 -0
  310. package/server/modules/agent/index.ts +2 -0
  311. package/server/modules/agent/tests/agent.routes.test.ts +205 -0
  312. package/server/modules/assets/assets.routes.ts +63 -16
  313. package/server/modules/assets/index.ts +2 -2
  314. package/server/modules/assets/services/image-assets.service.ts +46 -1
  315. package/server/modules/assets/tests/image-assets.service.test.ts +28 -0
  316. package/server/{middleware/auth.js → modules/auth/auth.middleware.ts} +38 -7
  317. package/server/modules/auth/auth.module.ts +38 -0
  318. package/server/modules/auth/auth.routes.ts +57 -0
  319. package/server/modules/auth/auth.service.ts +155 -0
  320. package/server/modules/auth/index.ts +9 -0
  321. package/server/modules/auth/tests/auth.service.test.ts +95 -0
  322. package/server/{browser-use-mcp.ts → modules/browser-use/browser-use-mcp.ts} +3 -1
  323. package/server/modules/browser-use/browser-use.service.ts +3 -4
  324. package/server/modules/browser-use/index.ts +8 -0
  325. package/server/modules/cli/cli.module.ts +96 -0
  326. package/server/modules/cli/cli.service.ts +281 -0
  327. package/server/modules/cli/cli.ts +13 -0
  328. package/server/modules/cli/index.ts +2 -0
  329. package/server/modules/cli/sandbox.service.ts +413 -0
  330. package/server/modules/cli/tests/cli.service.test.ts +91 -0
  331. package/server/modules/cli/tests/sandbox.service.test.ts +61 -0
  332. package/server/modules/commands/commands.module.ts +22 -0
  333. package/server/{routes/commands.js → modules/commands/commands.routes.ts} +44 -36
  334. package/server/modules/commands/index.ts +2 -0
  335. package/server/modules/commands/tests/commands.test.ts +119 -0
  336. package/server/modules/database/index.ts +1 -0
  337. package/server/modules/database/migrations.ts +15 -0
  338. package/server/modules/database/repositories/sessions.db.ts +19 -1
  339. package/server/modules/database/schema.ts +4 -0
  340. package/server/modules/file-tree/file-tree.module.ts +112 -0
  341. package/server/modules/file-tree/file-tree.routes.ts +258 -0
  342. package/server/modules/file-tree/file-tree.service.ts +616 -0
  343. package/server/modules/file-tree/index.ts +2 -0
  344. package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -0
  345. package/server/modules/file-tree/tests/file-tree.service.test.ts +220 -0
  346. package/server/modules/git/git-parsing.service.ts +66 -0
  347. package/server/modules/git/git.module.ts +22 -0
  348. package/server/{routes/git.js → modules/git/git.routes.ts} +91 -132
  349. package/server/modules/git/index.ts +2 -0
  350. package/server/modules/git/tests/git-init.routes.test.ts +52 -0
  351. package/server/{routes/git.test.js → modules/git/tests/git.test.ts} +3 -3
  352. package/server/modules/notifications/index.ts +9 -0
  353. package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -0
  354. package/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.ts} +3 -1
  355. package/server/modules/plugins/index.ts +9 -0
  356. package/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.ts} +3 -1
  357. package/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.ts} +1 -0
  358. package/server/modules/plugins/plugins.module.ts +33 -0
  359. package/server/modules/plugins/plugins.routes.ts +66 -0
  360. package/server/modules/plugins/plugins.service.ts +147 -0
  361. package/server/modules/plugins/tests/plugins.service.test.ts +32 -0
  362. package/server/modules/projects/index.ts +5 -0
  363. package/server/modules/providers/README.md +41 -9
  364. package/server/modules/providers/index.ts +4 -0
  365. package/server/modules/providers/list/claude/claude-auth.provider.ts +8 -0
  366. package/server/modules/providers/list/claude/claude-models.provider.ts +1 -12
  367. package/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +66 -44
  368. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +6 -4
  369. package/server/modules/providers/list/claude/claude-sessions.provider.ts +27 -4
  370. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  371. package/server/modules/providers/list/codex/codex-models.provider.ts +0 -9
  372. package/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +54 -36
  373. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +21 -1
  374. package/server/modules/providers/list/codex/codex-sessions.provider.ts +331 -7
  375. package/server/modules/providers/list/codex/codex-skills.provider.ts +5 -0
  376. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  377. package/server/modules/providers/list/cursor/cursor-models.provider.ts +0 -9
  378. package/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +58 -27
  379. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +26 -15
  380. package/server/modules/providers/list/cursor/cursor.provider.ts +3 -0
  381. package/server/modules/providers/list/opencode/opencode-models.provider.ts +7 -10
  382. package/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +56 -28
  383. package/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +41 -3
  384. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +13 -5
  385. package/server/modules/providers/list/opencode/opencode.provider.ts +3 -0
  386. package/server/modules/providers/provider.routes.ts +47 -10
  387. package/server/modules/providers/services/provider-capabilities.service.ts +7 -1
  388. package/server/modules/providers/services/provider-models.service.ts +131 -23
  389. package/server/modules/providers/services/provider-runtime.service.ts +109 -0
  390. package/server/modules/providers/services/provider-token-usage.service.ts +357 -0
  391. package/server/modules/providers/services/sessions-watcher.service.ts +2 -0
  392. package/server/modules/providers/services/sessions.service.ts +79 -0
  393. package/server/modules/providers/shared/base/abstract.provider.ts +4 -3
  394. package/server/modules/providers/tests/claude-auth.test.ts +150 -0
  395. package/server/modules/providers/tests/claude-sessions.test.ts +61 -0
  396. package/server/modules/providers/tests/codex-sessions.test.ts +81 -0
  397. package/server/modules/providers/tests/{provider-image-history.test.ts → provider-attachment-history.test.ts} +68 -1
  398. package/server/modules/providers/tests/provider-models.service.test.ts +450 -349
  399. package/server/modules/providers/tests/provider-runtime.service.test.ts +142 -0
  400. package/server/modules/providers/tests/provider-token-usage.service.test.ts +185 -0
  401. package/server/modules/providers/tests/sessions-details.test.ts +73 -0
  402. package/server/modules/providers/tests/skills.test.ts +9 -2
  403. package/server/modules/settings/index.ts +2 -0
  404. package/server/modules/settings/settings.module.ts +50 -0
  405. package/server/modules/settings/settings.routes.ts +51 -0
  406. package/server/modules/settings/settings.service.ts +186 -0
  407. package/server/modules/settings/tests/settings.service.test.ts +54 -0
  408. package/server/modules/system/index.ts +2 -0
  409. package/server/modules/system/system.module.ts +62 -0
  410. package/server/modules/system/system.routes.ts +21 -0
  411. package/server/modules/system/system.service.ts +76 -0
  412. package/server/modules/system/tests/system.service.test.ts +118 -0
  413. package/server/modules/taskmaster/index.ts +2 -0
  414. package/server/modules/taskmaster/taskmaster.module.ts +24 -0
  415. package/server/modules/taskmaster/taskmaster.routes.ts +1482 -0
  416. package/server/modules/taskmaster/taskmaster.service.ts +132 -0
  417. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -0
  418. package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -0
  419. package/server/modules/user/index.ts +2 -0
  420. package/server/modules/user/tests/user.service.test.ts +61 -0
  421. package/server/modules/user/user.module.ts +58 -0
  422. package/server/modules/user/user.routes.ts +50 -0
  423. package/server/modules/user/user.service.ts +85 -0
  424. package/server/modules/voice/index.ts +2 -0
  425. package/server/modules/voice/tests/voice.service.test.ts +98 -0
  426. package/server/modules/voice/voice.module.ts +47 -0
  427. package/server/modules/voice/voice.routes.ts +113 -0
  428. package/server/modules/voice/voice.service.ts +192 -0
  429. package/server/modules/websocket/README.md +7 -7
  430. package/server/modules/websocket/services/chat-session-writer.service.ts +2 -2
  431. package/server/modules/websocket/services/chat-websocket.service.ts +73 -61
  432. package/server/modules/websocket/services/shell-websocket.service.ts +88 -12
  433. package/server/modules/websocket/services/websocket-server.service.ts +63 -19
  434. package/server/modules/websocket/tests/{chat-image-filter.test.ts → chat-attachment-filter.test.ts} +17 -2
  435. package/server/modules/websocket/tests/shell-websocket.service.test.ts +119 -0
  436. package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -0
  437. package/server/modules/worktrees/index.ts +2 -0
  438. package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -0
  439. package/server/modules/worktrees/services/worktree-create.service.ts +104 -0
  440. package/server/modules/worktrees/services/worktree-git.service.ts +207 -0
  441. package/server/modules/worktrees/services/worktree-list.service.ts +143 -0
  442. package/server/modules/worktrees/services/worktree-merge.service.ts +157 -0
  443. package/server/modules/worktrees/services/worktree-open.service.ts +85 -0
  444. package/server/modules/worktrees/services/worktree-remove.service.ts +88 -0
  445. package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -0
  446. package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -0
  447. package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -0
  448. package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -0
  449. package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -0
  450. package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -0
  451. package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -0
  452. package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -0
  453. package/server/modules/worktrees/worktrees.module.ts +113 -0
  454. package/server/modules/worktrees/worktrees.routes.ts +116 -0
  455. package/server/shared/image-attachments.ts +112 -18
  456. package/server/shared/interfaces.ts +29 -19
  457. package/server/shared/tests/image-attachments.test.ts +55 -0
  458. package/server/shared/types.ts +658 -21
  459. package/server/shared/utils.ts +77 -212
  460. package/dist/assets/index-DLmhQ15L.css +0 -32
  461. package/dist-server/server/browser-use-mcp.js.map +0 -1
  462. package/dist-server/server/claude-sdk.js.map +0 -1
  463. package/dist-server/server/cli.js +0 -647
  464. package/dist-server/server/cli.js.map +0 -1
  465. package/dist-server/server/cursor-cli.js.map +0 -1
  466. package/dist-server/server/middleware/auth.js.map +0 -1
  467. package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +0 -1
  468. package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +0 -1
  469. package/dist-server/server/openai-codex.js.map +0 -1
  470. package/dist-server/server/opencode-cli.js.map +0 -1
  471. package/dist-server/server/opencode-cli.test.js.map +0 -1
  472. package/dist-server/server/routes/agent.js +0 -1155
  473. package/dist-server/server/routes/agent.js.map +0 -1
  474. package/dist-server/server/routes/auth.js +0 -119
  475. package/dist-server/server/routes/auth.js.map +0 -1
  476. package/dist-server/server/routes/commands.js +0 -492
  477. package/dist-server/server/routes/commands.js.map +0 -1
  478. package/dist-server/server/routes/cursor.js +0 -49
  479. package/dist-server/server/routes/cursor.js.map +0 -1
  480. package/dist-server/server/routes/git.js +0 -1389
  481. package/dist-server/server/routes/git.js.map +0 -1
  482. package/dist-server/server/routes/git.test.js.map +0 -1
  483. package/dist-server/server/routes/mcp-utils.js +0 -29
  484. package/dist-server/server/routes/mcp-utils.js.map +0 -1
  485. package/dist-server/server/routes/plugins.js +0 -266
  486. package/dist-server/server/routes/plugins.js.map +0 -1
  487. package/dist-server/server/routes/settings.js +0 -249
  488. package/dist-server/server/routes/settings.js.map +0 -1
  489. package/dist-server/server/routes/taskmaster.js +0 -1362
  490. package/dist-server/server/routes/taskmaster.js.map +0 -1
  491. package/dist-server/server/routes/tests/commands.test.js +0 -76
  492. package/dist-server/server/routes/tests/commands.test.js.map +0 -1
  493. package/dist-server/server/routes/user.js +0 -116
  494. package/dist-server/server/routes/user.js.map +0 -1
  495. package/dist-server/server/services/notification-orchestrator.js +0 -2
  496. package/dist-server/server/services/notification-orchestrator.js.map +0 -1
  497. package/dist-server/server/services/tests/notification-orchestrator.test.js +0 -64
  498. package/dist-server/server/services/tests/notification-orchestrator.test.js.map +0 -1
  499. package/dist-server/server/services/vapid-keys.js.map +0 -1
  500. package/dist-server/server/utils/colors.js +0 -20
  501. package/dist-server/server/utils/colors.js.map +0 -1
  502. package/dist-server/server/utils/commandParser.js +0 -255
  503. package/dist-server/server/utils/commandParser.js.map +0 -1
  504. package/dist-server/server/utils/gitConfig.js +0 -37
  505. package/dist-server/server/utils/gitConfig.js.map +0 -1
  506. package/dist-server/server/utils/mcp-detector.js +0 -134
  507. package/dist-server/server/utils/mcp-detector.js.map +0 -1
  508. package/dist-server/server/utils/plugin-loader.js.map +0 -1
  509. package/dist-server/server/utils/plugin-process-manager.js.map +0 -1
  510. package/dist-server/server/utils/runtime-paths.js +0 -30
  511. package/dist-server/server/utils/runtime-paths.js.map +0 -1
  512. package/dist-server/server/utils/taskmaster-websocket.js +0 -124
  513. package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
  514. package/dist-server/server/utils/url-detection.js +0 -58
  515. package/dist-server/server/utils/url-detection.js.map +0 -1
  516. package/dist-server/server/voice-proxy.js +0 -226
  517. package/dist-server/server/voice-proxy.js.map +0 -1
  518. package/server/cli.js +0 -695
  519. package/server/index.js +0 -1651
  520. package/server/routes/agent.js +0 -1257
  521. package/server/routes/auth.js +0 -137
  522. package/server/routes/cursor.js +0 -52
  523. package/server/routes/mcp-utils.js +0 -31
  524. package/server/routes/plugins.js +0 -307
  525. package/server/routes/settings.js +0 -282
  526. package/server/routes/taskmaster.js +0 -1470
  527. package/server/routes/tests/commands.test.js +0 -82
  528. package/server/routes/user.js +0 -124
  529. package/server/services/notification-orchestrator.js +0 -7
  530. package/server/services/tests/notification-orchestrator.test.js +0 -80
  531. package/server/utils/colors.js +0 -21
  532. package/server/utils/commandParser.js +0 -305
  533. package/server/utils/gitConfig.js +0 -35
  534. package/server/utils/mcp-detector.js +0 -147
  535. package/server/utils/runtime-paths.js +0 -37
  536. package/server/utils/taskmaster-websocket.js +0 -135
  537. package/server/utils/url-detection.js +0 -71
  538. package/server/voice-proxy.js +0 -224
  539. /package/server/constants/{config.js → config.ts} +0 -0
@@ -0,0 +1,157 @@
1
+ import type {
2
+ GitCommandRunner,
3
+ MergeWorktreeInput,
4
+ MergeWorktreeResult,
5
+ RemoveWorktreeInput,
6
+ RemoveWorktreeResult,
7
+ } from '@/shared/types.js';
8
+ import { AppError } from '@/shared/utils.js';
9
+ import {
10
+ countChangedFiles,
11
+ findWorktreeEntryByPath,
12
+ listWorktreePorcelainEntries,
13
+ } from '@/modules/worktrees/services/worktree-git.service.js';
14
+
15
+ /** Reads the paths currently in conflict inside the main worktree (best effort). */
16
+ async function readConflictedFiles(repositoryRoot: string, runGit: GitCommandRunner): Promise<string[]> {
17
+ try {
18
+ const { stdout } = await runGit(['diff', '--name-only', '--diff-filter=U'], repositoryRoot);
19
+ return stdout.split('\n').filter((line) => line.trim().length > 0);
20
+ } catch {
21
+ return [];
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Merges a worktree's branch back into the base branch (the branch checked out
27
+ * in the main worktree). Runs entirely inside the main worktree, so the user's
28
+ * current worktree never changes underneath them.
29
+ *
30
+ * Both worktrees must be clean: the source so no work is silently left behind,
31
+ * the target so a conflict can be rolled back without touching user changes.
32
+ * On conflict the merge is fully aborted and the conflicting paths are
33
+ * reported back to the client.
34
+ */
35
+ export async function mergeWorktree(
36
+ input: MergeWorktreeInput,
37
+ dependencies: {
38
+ runGit: GitCommandRunner;
39
+ removeWorktree: (input: RemoveWorktreeInput) => Promise<RemoveWorktreeResult>;
40
+ },
41
+ ): Promise<MergeWorktreeResult> {
42
+ const { removeWorktree, runGit } = dependencies;
43
+ const entries = await listWorktreePorcelainEntries(input.projectPath, runGit);
44
+ const entry = findWorktreeEntryByPath(entries, input.worktreePath);
45
+ const repositoryRoot = entries[0].path;
46
+ const targetBranch = entries[0].branch;
47
+
48
+ if (entry.path === repositoryRoot) {
49
+ throw new AppError('The main worktree cannot be merged into itself', {
50
+ code: 'WORKTREE_MERGE_MAIN',
51
+ statusCode: 400,
52
+ });
53
+ }
54
+
55
+ if (!entry.branch) {
56
+ throw new AppError('Worktree is on a detached HEAD — check out a branch first', {
57
+ code: 'WORKTREE_DETACHED_HEAD',
58
+ statusCode: 400,
59
+ });
60
+ }
61
+
62
+ if (!targetBranch) {
63
+ throw new AppError('The main worktree is on a detached HEAD — cannot determine merge target', {
64
+ code: 'WORKTREE_TARGET_DETACHED',
65
+ statusCode: 400,
66
+ });
67
+ }
68
+
69
+ const sourceChangedCount = await countChangedFiles(entry.path, runGit);
70
+ if (sourceChangedCount > 0) {
71
+ throw new AppError(
72
+ `Worktree has ${sourceChangedCount} uncommitted change${sourceChangedCount === 1 ? '' : 's'} — commit or discard them first`,
73
+ { code: 'WORKTREE_SOURCE_DIRTY', statusCode: 409 },
74
+ );
75
+ }
76
+
77
+ const targetChangedCount = await countChangedFiles(repositoryRoot, runGit);
78
+ if (targetChangedCount > 0) {
79
+ throw new AppError(
80
+ `The base worktree (${targetBranch}) has uncommitted changes — commit or stash them first`,
81
+ { code: 'WORKTREE_TARGET_DIRTY', statusCode: 409 },
82
+ );
83
+ }
84
+
85
+ const squash = Boolean(input.squash);
86
+ const message =
87
+ input.message?.trim() ||
88
+ (squash ? `Squash merge branch '${entry.branch}'` : `Merge branch '${entry.branch}'`);
89
+
90
+ try {
91
+ if (squash) {
92
+ await runGit(['merge', '--squash', entry.branch], repositoryRoot);
93
+ await runGit(['commit', '-m', message], repositoryRoot);
94
+ } else {
95
+ await runGit(['merge', '--no-ff', entry.branch, '-m', message], repositoryRoot);
96
+ }
97
+ } catch (error) {
98
+ const conflictedFiles = await readConflictedFiles(repositoryRoot, runGit);
99
+ // Roll the main worktree back to its pre-merge state. `--merge` also
100
+ // clears a half-applied squash since the target was verified clean above.
101
+ try {
102
+ await runGit(['reset', '--merge'], repositoryRoot);
103
+ } catch (rollbackError) {
104
+ throw new AppError(
105
+ `Merge of '${entry.branch}' into '${targetBranch}' failed and could not be rolled back`,
106
+ {
107
+ code: 'WORKTREE_MERGE_ROLLBACK_FAILED',
108
+ statusCode: 500,
109
+ details: {
110
+ mergeError: error instanceof Error ? error.message : String(error),
111
+ rollbackError: rollbackError instanceof AppError
112
+ ? rollbackError.details ?? rollbackError.message
113
+ : rollbackError instanceof Error ? rollbackError.message : String(rollbackError),
114
+ },
115
+ },
116
+ );
117
+ }
118
+
119
+ if (conflictedFiles.length > 0) {
120
+ throw new AppError(
121
+ `Merge of '${entry.branch}' into '${targetBranch}' has conflicts — the merge was aborted`,
122
+ {
123
+ code: 'WORKTREE_MERGE_CONFLICT',
124
+ statusCode: 409,
125
+ details: conflictedFiles,
126
+ },
127
+ );
128
+ }
129
+
130
+ throw error;
131
+ }
132
+
133
+ let removedWorktree: RemoveWorktreeResult | null = null;
134
+ let cleanupError: string | null = null;
135
+ if (input.removeAfterMerge) {
136
+ try {
137
+ removedWorktree = await removeWorktree(
138
+ {
139
+ projectPath: repositoryRoot,
140
+ worktreePath: entry.path,
141
+ force: false,
142
+ deleteBranch: true,
143
+ },
144
+ );
145
+ } catch (error) {
146
+ cleanupError = error instanceof Error ? error.message : String(error);
147
+ }
148
+ }
149
+
150
+ return {
151
+ mergedBranch: entry.branch,
152
+ targetBranch,
153
+ squash,
154
+ removedWorktree,
155
+ cleanupError,
156
+ };
157
+ }
@@ -0,0 +1,85 @@
1
+ import path from 'node:path';
2
+
3
+ import type {
4
+ GitCommandRunner,
5
+ OpenWorktreeInput,
6
+ ProjectRepositoryRow,
7
+ WorktreeProjectGateway,
8
+ WorktreeProjectView,
9
+ } from '@/shared/types.js';
10
+ import { AppError, normalizeProjectPath } from '@/shared/utils.js';
11
+ import {
12
+ findWorktreeEntryByPath,
13
+ listWorktreePorcelainEntries,
14
+ } from '@/modules/worktrees/services/worktree-git.service.js';
15
+
16
+ function mapRowToProjectView(row: ProjectRepositoryRow): WorktreeProjectView {
17
+ return {
18
+ projectId: row.project_id,
19
+ path: row.project_path,
20
+ fullPath: row.project_path,
21
+ displayName: row.custom_project_name || path.basename(row.project_path),
22
+ isStarred: Boolean(row.isStarred),
23
+ sessions: [],
24
+ sessionMeta: { hasMore: false, total: 0 },
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Ensures a CloudCLI project exists (and is active) for a worktree directory
30
+ * and returns it, so the caller can switch the UI into that worktree.
31
+ *
32
+ * The path is only accepted when it is a registered worktree of the repository
33
+ * that contains `projectPath` — this endpoint must never become a generic
34
+ * "create project anywhere" backdoor.
35
+ */
36
+ export async function openWorktreeAsProject(
37
+ input: OpenWorktreeInput,
38
+ dependencies: {
39
+ runGit: GitCommandRunner;
40
+ projects: Pick<
41
+ WorktreeProjectGateway,
42
+ 'getProjectByPath' | 'createProject' | 'restoreProject'
43
+ >;
44
+ },
45
+ ): Promise<WorktreeProjectView> {
46
+ const { projects, runGit } = dependencies;
47
+ const entries = await listWorktreePorcelainEntries(input.projectPath, runGit);
48
+ const entry = findWorktreeEntryByPath(entries, input.worktreePath);
49
+
50
+ const normalizedWorktreePath = normalizeProjectPath(entry.path);
51
+ const repoName = path.basename(entries[0].path);
52
+ // "repo · branch" keeps worktree projects visually grouped next to their
53
+ // parent repository in the sidebar.
54
+ const displayName = entry.branch ? `${repoName} · ${entry.branch}` : repoName;
55
+
56
+ const existingRow = projects.getProjectByPath(normalizedWorktreePath);
57
+ if (existingRow) {
58
+ if (existingRow.isArchived) {
59
+ await projects.restoreProject(existingRow.project_id);
60
+ }
61
+ const refreshedRow = projects.getProjectByPath(normalizedWorktreePath) ?? existingRow;
62
+ return mapRowToProjectView(refreshedRow);
63
+ }
64
+
65
+ const created = await projects.createProject({
66
+ projectPath: normalizedWorktreePath,
67
+ customName: displayName,
68
+ });
69
+
70
+ // `createProject` intentionally keeps reactivated archived rows archived;
71
+ // an opened worktree must be active so it shows up in the sidebar.
72
+ if (created.outcome === 'reactivated_archived') {
73
+ await projects.restoreProject(created.project.projectId);
74
+ }
75
+
76
+ const row = projects.getProjectByPath(normalizedWorktreePath);
77
+ if (!row) {
78
+ throw new AppError('Failed to resolve project for worktree', {
79
+ code: 'WORKTREE_PROJECT_RESOLVE_FAILED',
80
+ statusCode: 500,
81
+ });
82
+ }
83
+
84
+ return mapRowToProjectView(row);
85
+ }
@@ -0,0 +1,88 @@
1
+ import type {
2
+ GitCommandRunner,
3
+ RemoveWorktreeInput,
4
+ RemoveWorktreeResult,
5
+ WorktreeProjectGateway,
6
+ } from '@/shared/types.js';
7
+ import { AppError } from '@/shared/utils.js';
8
+ import {
9
+ countChangedFiles,
10
+ findWorktreeEntryByPath,
11
+ listWorktreePorcelainEntries,
12
+ } from '@/modules/worktrees/services/worktree-git.service.js';
13
+
14
+ /**
15
+ * Removes a linked worktree (never the main one), optionally deletes its
16
+ * branch, and archives the CloudCLI project registered for the directory so it
17
+ * disappears from the sidebar while its chat history stays recoverable.
18
+ */
19
+ export async function removeWorktree(
20
+ input: RemoveWorktreeInput,
21
+ dependencies: {
22
+ runGit: GitCommandRunner;
23
+ projects: Pick<WorktreeProjectGateway, 'getProjectByPath' | 'archiveProject'>;
24
+ },
25
+ ): Promise<RemoveWorktreeResult> {
26
+ const { projects, runGit } = dependencies;
27
+ const entries = await listWorktreePorcelainEntries(input.projectPath, runGit);
28
+ const entry = findWorktreeEntryByPath(entries, input.worktreePath);
29
+ const repositoryRoot = entries[0].path;
30
+
31
+ if (entry.path === repositoryRoot) {
32
+ throw new AppError('The main worktree cannot be removed', {
33
+ code: 'WORKTREE_MAIN_NOT_REMOVABLE',
34
+ statusCode: 400,
35
+ });
36
+ }
37
+
38
+ if (!input.force) {
39
+ const changedFileCount = await countChangedFiles(entry.path, runGit);
40
+ if (changedFileCount > 0) {
41
+ throw new AppError(
42
+ `Worktree has ${changedFileCount} uncommitted change${changedFileCount === 1 ? '' : 's'}`,
43
+ {
44
+ code: 'WORKTREE_DIRTY',
45
+ statusCode: 409,
46
+ },
47
+ );
48
+ }
49
+ }
50
+
51
+ const removeArgs = ['worktree', 'remove'];
52
+ if (input.force) {
53
+ removeArgs.push('--force');
54
+ }
55
+ removeArgs.push(entry.path);
56
+ await runGit(removeArgs, repositoryRoot);
57
+
58
+ let branchDeleted = false;
59
+ if (input.deleteBranch && entry.branch) {
60
+ try {
61
+ await runGit(['branch', '-D', entry.branch], repositoryRoot);
62
+ branchDeleted = true;
63
+ } catch {
64
+ // Branch deletion is best-effort cleanup — the worktree itself is gone,
65
+ // which is the operation the user asked for.
66
+ }
67
+ }
68
+
69
+ let archivedProjectId: string | null = null;
70
+ let archivalError: string | null = null;
71
+ try {
72
+ const linkedProject = projects.getProjectByPath(entry.path);
73
+ if (linkedProject && !linkedProject.isArchived) {
74
+ await projects.archiveProject(linkedProject.project_id);
75
+ archivedProjectId = linkedProject.project_id;
76
+ }
77
+ } catch (error) {
78
+ archivalError = error instanceof Error ? error.message : String(error);
79
+ }
80
+
81
+ return {
82
+ removedPath: entry.path,
83
+ branch: entry.branch,
84
+ branchDeleted,
85
+ archivedProjectId,
86
+ archivalError,
87
+ };
88
+ }
@@ -0,0 +1,68 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { createAndOpenWorktree } from '@/modules/worktrees/services/worktree-create-and-open.service.js';
5
+ import type { RemoveWorktreeInput } from '@/shared/types.js';
6
+
7
+ const input = {
8
+ projectPath: '/workspace/repo',
9
+ branch: 'feature/login',
10
+ };
11
+
12
+ test('createAndOpenWorktree returns the registered project', async () => {
13
+ const result = await createAndOpenWorktree(input, {
14
+ createWorktree: async () => ({
15
+ worktreePath: '/workspace/repo-worktrees/feature-login',
16
+ branch: 'feature/login',
17
+ createdBranch: true,
18
+ }),
19
+ openWorktree: async ({ worktreePath }) => ({
20
+ projectId: 'project-2',
21
+ path: worktreePath,
22
+ fullPath: worktreePath,
23
+ displayName: 'feature/login',
24
+ isStarred: false,
25
+ sessions: [],
26
+ sessionMeta: { hasMore: false, total: 0 },
27
+ }),
28
+ removeWorktree: async () => {
29
+ throw new Error('unexpected cleanup');
30
+ },
31
+ });
32
+
33
+ assert.equal(result.project.projectId, 'project-2');
34
+ });
35
+
36
+ test('createAndOpenWorktree compensates registration failure without deleting an existing branch', async () => {
37
+ const removals: RemoveWorktreeInput[] = [];
38
+ const registrationError = new Error('registration failed');
39
+
40
+ await assert.rejects(
41
+ createAndOpenWorktree(input, {
42
+ createWorktree: async () => ({
43
+ worktreePath: '/workspace/repo-worktrees/feature-login',
44
+ branch: 'feature/login',
45
+ createdBranch: false,
46
+ }),
47
+ openWorktree: async () => { throw registrationError; },
48
+ removeWorktree: async (removeInput) => {
49
+ removals.push(removeInput);
50
+ return {
51
+ removedPath: removeInput.worktreePath,
52
+ branch: 'feature/login',
53
+ branchDeleted: false,
54
+ archivedProjectId: null,
55
+ archivalError: null,
56
+ };
57
+ },
58
+ }),
59
+ registrationError,
60
+ );
61
+
62
+ assert.deepEqual(removals, [{
63
+ projectPath: input.projectPath,
64
+ worktreePath: '/workspace/repo-worktrees/feature-login',
65
+ force: true,
66
+ deleteBranch: false,
67
+ }]);
68
+ });
@@ -0,0 +1,142 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { createWorktree } from '@/modules/worktrees/services/worktree-create.service.js';
5
+ import type { GitCommandResult } from '@/shared/types.js';
6
+ import { AppError } from '@/shared/utils.js';
7
+
8
+ const PORCELAIN = [
9
+ 'worktree /home/user/repo',
10
+ 'HEAD 1111111111111111111111111111111111111111',
11
+ 'branch refs/heads/main',
12
+ '',
13
+ 'worktree /home/user/repo-worktrees/existing',
14
+ 'HEAD 2222222222222222222222222222222222222222',
15
+ 'branch refs/heads/existing-branch',
16
+ '',
17
+ ].join('\n');
18
+
19
+ type RecordedCall = { args: string[]; cwd: string };
20
+
21
+ /**
22
+ * Fake GitCommandRunner: records every call and answers `worktree list` /
23
+ * `branch --list` from canned data; everything else succeeds with no output.
24
+ */
25
+ function createFakeRunner(existingBranches: string[]) {
26
+ const calls: RecordedCall[] = [];
27
+ const runner = async (args: string[], cwd: string): Promise<GitCommandResult> => {
28
+ calls.push({ args, cwd });
29
+
30
+ if (args[0] === 'worktree' && args[1] === 'list') {
31
+ return { stdout: PORCELAIN, stderr: '' };
32
+ }
33
+
34
+ if (args[0] === 'branch' && args[1] === '--list') {
35
+ const requested = args[2];
36
+ const matches = existingBranches.filter((branch) => branch === requested);
37
+ return { stdout: matches.join('\n'), stderr: '' };
38
+ }
39
+
40
+ return { stdout: '', stderr: '' };
41
+ };
42
+
43
+ return { calls, runner };
44
+ }
45
+
46
+ function createDependencies(runner: ReturnType<typeof createFakeRunner>['runner'], pathExists = false) {
47
+ return {
48
+ runGit: runner,
49
+ fileSystem: {
50
+ pathExists: async () => pathExists,
51
+ },
52
+ };
53
+ }
54
+
55
+ test('createWorktree creates a new branch from the main branch by default', async () => {
56
+ const { calls, runner } = createFakeRunner([]);
57
+
58
+ const result = await createWorktree(
59
+ { projectPath: '/home/user/repo', branch: 'feature/login' },
60
+ createDependencies(runner),
61
+ );
62
+
63
+ assert.equal(result.branch, 'feature/login');
64
+ assert.equal(result.createdBranch, true);
65
+ // Slashes in the branch become dashes in the folder name.
66
+ assert.ok(result.worktreePath.endsWith('feature-login'));
67
+ assert.ok(result.worktreePath.includes('repo-worktrees'));
68
+
69
+ const addCall = calls.find((call) => call.args[0] === 'worktree' && call.args[1] === 'add');
70
+ assert.ok(addCall, 'expected a worktree add call');
71
+ assert.deepEqual(addCall.args.slice(3), ['-b', 'feature/login', 'main']);
72
+ });
73
+
74
+ test('createWorktree checks out an existing branch without -b', async () => {
75
+ const { calls, runner } = createFakeRunner(['bugfix']);
76
+
77
+ const result = await createWorktree(
78
+ { projectPath: '/home/user/repo', branch: 'bugfix' },
79
+ createDependencies(runner),
80
+ );
81
+
82
+ assert.equal(result.createdBranch, false);
83
+
84
+ const addCall = calls.find((call) => call.args[0] === 'worktree' && call.args[1] === 'add');
85
+ assert.ok(addCall, 'expected a worktree add call');
86
+ assert.ok(!addCall.args.includes('-b'));
87
+ assert.equal(addCall.args.at(-1), 'bugfix');
88
+ });
89
+
90
+ test('createWorktree honors an explicit base branch', async () => {
91
+ const { calls, runner } = createFakeRunner([]);
92
+
93
+ await createWorktree(
94
+ { projectPath: '/home/user/repo', branch: 'hotfix', baseBranch: 'release/1.0' },
95
+ createDependencies(runner),
96
+ );
97
+
98
+ const addCall = calls.find((call) => call.args[0] === 'worktree' && call.args[1] === 'add');
99
+ assert.ok(addCall);
100
+ assert.equal(addCall.args.at(-1), 'release/1.0');
101
+ });
102
+
103
+ test('createWorktree rejects a branch already checked out in another worktree', async () => {
104
+ const { runner } = createFakeRunner(['existing-branch']);
105
+
106
+ await assert.rejects(
107
+ createWorktree(
108
+ { projectPath: '/home/user/repo', branch: 'existing-branch' },
109
+ createDependencies(runner),
110
+ ),
111
+ (error: unknown) =>
112
+ error instanceof AppError && error.code === 'BRANCH_ALREADY_CHECKED_OUT' && error.statusCode === 409,
113
+ );
114
+ });
115
+
116
+ test('createWorktree rejects invalid branch names before touching git state', async () => {
117
+ const { calls, runner } = createFakeRunner([]);
118
+
119
+ await assert.rejects(
120
+ createWorktree(
121
+ { projectPath: '/home/user/repo', branch: '-rf' },
122
+ createDependencies(runner),
123
+ ),
124
+ (error: unknown) => error instanceof AppError && error.code === 'INVALID_BRANCH_NAME',
125
+ );
126
+
127
+ assert.equal(calls.length, 0);
128
+ });
129
+
130
+ test('createWorktree rejects an occupied destination without running a mutating git command', async () => {
131
+ const { calls, runner } = createFakeRunner([]);
132
+
133
+ await assert.rejects(
134
+ createWorktree(
135
+ { projectPath: '/home/user/repo', branch: 'feature/login' },
136
+ createDependencies(runner, true),
137
+ ),
138
+ (error: unknown) => error instanceof AppError && error.code === 'WORKTREE_FOLDER_EXISTS',
139
+ );
140
+
141
+ assert.equal(calls.some((call) => call.args[0] === 'worktree' && call.args[1] === 'add'), false);
142
+ });
@@ -0,0 +1,84 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import {
5
+ findWorktreeEntryByPath,
6
+ parseWorktreeListPorcelain,
7
+ validateWorktreeBranchName,
8
+ } from '@/modules/worktrees/services/worktree-git.service.js';
9
+ import { AppError } from '@/shared/utils.js';
10
+
11
+ const SAMPLE_PORCELAIN = [
12
+ 'worktree /home/user/repo',
13
+ 'HEAD 1111111111111111111111111111111111111111',
14
+ 'branch refs/heads/main',
15
+ '',
16
+ 'worktree /home/user/repo-worktrees/feature-login',
17
+ 'HEAD 2222222222222222222222222222222222222222',
18
+ 'branch refs/heads/feature/login',
19
+ '',
20
+ 'worktree /home/user/repo-worktrees/spike',
21
+ 'HEAD 3333333333333333333333333333333333333333',
22
+ 'detached',
23
+ 'locked reason text',
24
+ '',
25
+ ].join('\n');
26
+
27
+ test('parseWorktreeListPorcelain parses main, branch, detached and locked entries', () => {
28
+ const entries = parseWorktreeListPorcelain(SAMPLE_PORCELAIN);
29
+
30
+ assert.equal(entries.length, 3);
31
+
32
+ assert.equal(entries[0].branch, 'main');
33
+ assert.equal(entries[0].headSha, '1111111111111111111111111111111111111111');
34
+ assert.equal(entries[0].isDetached, false);
35
+
36
+ assert.equal(entries[1].branch, 'feature/login');
37
+ assert.ok(entries[1].path.endsWith('feature-login'));
38
+
39
+ assert.equal(entries[2].branch, null);
40
+ assert.equal(entries[2].isDetached, true);
41
+ assert.equal(entries[2].isLocked, true);
42
+ });
43
+
44
+ test('parseWorktreeListPorcelain handles output without a trailing blank line', () => {
45
+ const entries = parseWorktreeListPorcelain(
46
+ 'worktree /home/user/repo\nHEAD 1111111111111111111111111111111111111111\nbranch refs/heads/main',
47
+ );
48
+
49
+ assert.equal(entries.length, 1);
50
+ assert.equal(entries[0].branch, 'main');
51
+ });
52
+
53
+ test('findWorktreeEntryByPath matches normalized paths', () => {
54
+ const entries = parseWorktreeListPorcelain(SAMPLE_PORCELAIN);
55
+ const found = findWorktreeEntryByPath(entries, '/home/user/repo-worktrees/feature-login/');
56
+ assert.equal(found.branch, 'feature/login');
57
+ });
58
+
59
+ test('findWorktreeEntryByPath throws a 404 AppError for unknown paths', () => {
60
+ const entries = parseWorktreeListPorcelain(SAMPLE_PORCELAIN);
61
+ assert.throws(
62
+ () => findWorktreeEntryByPath(entries, '/home/user/elsewhere'),
63
+ (error: unknown) =>
64
+ error instanceof AppError && error.code === 'WORKTREE_NOT_FOUND' && error.statusCode === 404,
65
+ );
66
+ });
67
+
68
+ test('validateWorktreeBranchName accepts slash-separated branch names', () => {
69
+ assert.equal(validateWorktreeBranchName(' feature/login-form '), 'feature/login-form');
70
+ });
71
+
72
+ test('validateWorktreeBranchName rejects unsafe names', () => {
73
+ for (const invalidName of [
74
+ '', ' ', '-oops', '.', '..', 'bad name', 'bad;name', 'bad$(name)',
75
+ 'foo..bar', 'foo.', 'foo//bar', 'foo.lock', 'feature/foo.LOCK', '/feature', 'feature/',
76
+ '.hidden', 'feature/.hidden', 'feature/./name',
77
+ ]) {
78
+ assert.throws(
79
+ () => validateWorktreeBranchName(invalidName),
80
+ (error: unknown) => error instanceof AppError && error.code === 'INVALID_BRANCH_NAME',
81
+ `expected "${invalidName}" to be rejected`,
82
+ );
83
+ }
84
+ });
@@ -0,0 +1,45 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { listWorktrees } from '@/modules/worktrees/services/worktree-list.service.js';
5
+ import type { GitCommandResult } from '@/shared/types.js';
6
+
7
+ test('listWorktrees bounds concurrent per-worktree Git inspection and preserves ordering', async () => {
8
+ const entryCount = 10;
9
+ const porcelain = Array.from({ length: entryCount }, (_, index) => [
10
+ `worktree /workspace/repo-${index}`,
11
+ `HEAD ${String(index).padStart(40, '0')}`,
12
+ `branch refs/heads/${index === 0 ? 'main' : `feature/${index}`}`,
13
+ '',
14
+ ].join('\n')).join('\n');
15
+ let activeCommands = 0;
16
+ let maximumActiveCommands = 0;
17
+
18
+ const runGit = async (args: string[]): Promise<GitCommandResult> => {
19
+ if (args[0] === 'worktree') {
20
+ return { stdout: porcelain, stderr: '' };
21
+ }
22
+
23
+ activeCommands += 1;
24
+ maximumActiveCommands = Math.max(maximumActiveCommands, activeCommands);
25
+ await new Promise((resolve) => setTimeout(resolve, 5));
26
+ activeCommands -= 1;
27
+
28
+ if (args[0] === 'log') {
29
+ return { stdout: 'subject\0' + '2026-01-01T00:00:00Z', stderr: '' };
30
+ }
31
+ if (args[0] === 'rev-list') {
32
+ return { stdout: '0 1', stderr: '' };
33
+ }
34
+ return { stdout: '', stderr: '' };
35
+ };
36
+
37
+ const result = await listWorktrees(
38
+ { projectPath: '/workspace/repo-0' },
39
+ { runGit, getProjectByPath: () => null },
40
+ );
41
+
42
+ assert.equal(maximumActiveCommands <= 12, true);
43
+ assert.deepEqual(result.worktrees.map((worktree) => worktree.path.replace(/\\/g, '/')),
44
+ Array.from({ length: entryCount }, (_, index) => `/workspace/repo-${index}`));
45
+ });