automagik-forge 0.1.13 → 0.1.14

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 (301) hide show
  1. package/README.md +143 -447
  2. package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
  3. package/{npx-cli/automagik-forge-0.0.55.tgz → dist/linux-x64/automagik-forge.zip} +0 -0
  4. package/package.json +13 -23
  5. package/.cargo/config.toml +0 -13
  6. package/.claude/commands/commit.md +0 -376
  7. package/.claude/commands/prompt.md +0 -871
  8. package/.env.example +0 -20
  9. package/.github/actions/setup-node/action.yml +0 -29
  10. package/.github/images/automagik-logo.png +0 -0
  11. package/.github/workflows/pre-release.yml +0 -470
  12. package/.github/workflows/publish.yml +0 -145
  13. package/.github/workflows/test.yml +0 -63
  14. package/.mcp.json +0 -57
  15. package/AGENT.md +0 -40
  16. package/CLAUDE.md +0 -40
  17. package/CODE-OF-CONDUCT.md +0 -89
  18. package/Cargo.toml +0 -19
  19. package/Dockerfile +0 -43
  20. package/LICENSE +0 -201
  21. package/Makefile +0 -97
  22. package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +0 -12
  23. package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +0 -12
  24. package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +0 -12
  25. package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +0 -68
  26. package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +0 -12
  27. package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +0 -12
  28. package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +0 -12
  29. package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +0 -104
  30. package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +0 -12
  31. package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +0 -32
  32. package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +0 -56
  33. package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +0 -68
  34. package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +0 -92
  35. package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +0 -12
  36. package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +0 -56
  37. package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +0 -62
  38. package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +0 -62
  39. package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +0 -56
  40. package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +0 -32
  41. package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +0 -104
  42. package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +0 -62
  43. package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +0 -56
  44. package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +0 -92
  45. package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +0 -62
  46. package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +0 -12
  47. package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +0 -104
  48. package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +0 -104
  49. package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +0 -68
  50. package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +0 -62
  51. package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +0 -62
  52. package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +0 -62
  53. package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +0 -50
  54. package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +0 -68
  55. package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +0 -12
  56. package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +0 -68
  57. package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +0 -12
  58. package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +0 -12
  59. package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +0 -62
  60. package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +0 -104
  61. package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +0 -20
  62. package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +0 -104
  63. package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +0 -56
  64. package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +0 -104
  65. package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +0 -26
  66. package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +0 -62
  67. package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +0 -12
  68. package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +0 -104
  69. package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +0 -104
  70. package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +0 -12
  71. package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +0 -68
  72. package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +0 -62
  73. package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +0 -20
  74. package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +0 -12
  75. package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +0 -12
  76. package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +0 -12
  77. package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +0 -20
  78. package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +0 -12
  79. package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +0 -12
  80. package/backend/Cargo.toml +0 -71
  81. package/backend/build.rs +0 -32
  82. package/backend/migrations/20250617183714_init.sql +0 -44
  83. package/backend/migrations/20250620212427_execution_processes.sql +0 -25
  84. package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +0 -28
  85. package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +0 -23
  86. package/backend/migrations/20250623120000_executor_sessions.sql +0 -17
  87. package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +0 -4
  88. package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +0 -4
  89. package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +0 -2
  90. package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +0 -5
  91. package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +0 -2
  92. package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +0 -2
  93. package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +0 -2
  94. package/backend/migrations/20250710000000_add_setup_completion.sql +0 -3
  95. package/backend/migrations/20250715154859_add_task_templates.sql +0 -25
  96. package/backend/migrations/20250716143725_add_default_templates.sql +0 -174
  97. package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +0 -20
  98. package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +0 -7
  99. package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +0 -9
  100. package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +0 -2
  101. package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +0 -25
  102. package/backend/migrations/20250723000000_add_wish_to_tasks.sql +0 -7
  103. package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +0 -5
  104. package/backend/scripts/toast-notification.ps1 +0 -23
  105. package/backend/sounds/abstract-sound1.wav +0 -0
  106. package/backend/sounds/abstract-sound2.wav +0 -0
  107. package/backend/sounds/abstract-sound3.wav +0 -0
  108. package/backend/sounds/abstract-sound4.wav +0 -0
  109. package/backend/sounds/cow-mooing.wav +0 -0
  110. package/backend/sounds/phone-vibration.wav +0 -0
  111. package/backend/sounds/rooster.wav +0 -0
  112. package/backend/src/app_state.rs +0 -218
  113. package/backend/src/bin/generate_types.rs +0 -189
  114. package/backend/src/bin/mcp_task_server.rs +0 -191
  115. package/backend/src/execution_monitor.rs +0 -1193
  116. package/backend/src/executor.rs +0 -1053
  117. package/backend/src/executors/amp.rs +0 -697
  118. package/backend/src/executors/ccr.rs +0 -91
  119. package/backend/src/executors/charm_opencode.rs +0 -113
  120. package/backend/src/executors/claude.rs +0 -887
  121. package/backend/src/executors/cleanup_script.rs +0 -124
  122. package/backend/src/executors/dev_server.rs +0 -53
  123. package/backend/src/executors/echo.rs +0 -79
  124. package/backend/src/executors/gemini/config.rs +0 -67
  125. package/backend/src/executors/gemini/streaming.rs +0 -363
  126. package/backend/src/executors/gemini.rs +0 -765
  127. package/backend/src/executors/mod.rs +0 -23
  128. package/backend/src/executors/opencode_ai.rs +0 -113
  129. package/backend/src/executors/setup_script.rs +0 -130
  130. package/backend/src/executors/sst_opencode/filter.rs +0 -184
  131. package/backend/src/executors/sst_opencode/tools.rs +0 -139
  132. package/backend/src/executors/sst_opencode.rs +0 -756
  133. package/backend/src/lib.rs +0 -45
  134. package/backend/src/main.rs +0 -324
  135. package/backend/src/mcp/mod.rs +0 -1
  136. package/backend/src/mcp/task_server.rs +0 -850
  137. package/backend/src/middleware/mod.rs +0 -3
  138. package/backend/src/middleware/model_loaders.rs +0 -242
  139. package/backend/src/models/api_response.rs +0 -36
  140. package/backend/src/models/config.rs +0 -375
  141. package/backend/src/models/execution_process.rs +0 -430
  142. package/backend/src/models/executor_session.rs +0 -225
  143. package/backend/src/models/mod.rs +0 -12
  144. package/backend/src/models/project.rs +0 -356
  145. package/backend/src/models/task.rs +0 -345
  146. package/backend/src/models/task_attempt.rs +0 -1214
  147. package/backend/src/models/task_template.rs +0 -146
  148. package/backend/src/openapi.rs +0 -93
  149. package/backend/src/routes/auth.rs +0 -297
  150. package/backend/src/routes/config.rs +0 -385
  151. package/backend/src/routes/filesystem.rs +0 -228
  152. package/backend/src/routes/health.rs +0 -16
  153. package/backend/src/routes/mod.rs +0 -9
  154. package/backend/src/routes/projects.rs +0 -562
  155. package/backend/src/routes/stream.rs +0 -244
  156. package/backend/src/routes/task_attempts.rs +0 -1172
  157. package/backend/src/routes/task_templates.rs +0 -229
  158. package/backend/src/routes/tasks.rs +0 -353
  159. package/backend/src/services/analytics.rs +0 -216
  160. package/backend/src/services/git_service.rs +0 -1321
  161. package/backend/src/services/github_service.rs +0 -307
  162. package/backend/src/services/mod.rs +0 -13
  163. package/backend/src/services/notification_service.rs +0 -263
  164. package/backend/src/services/pr_monitor.rs +0 -214
  165. package/backend/src/services/process_service.rs +0 -940
  166. package/backend/src/utils/path.rs +0 -96
  167. package/backend/src/utils/shell.rs +0 -19
  168. package/backend/src/utils/text.rs +0 -24
  169. package/backend/src/utils/worktree_manager.rs +0 -578
  170. package/backend/src/utils.rs +0 -125
  171. package/backend/test.db +0 -0
  172. package/build-npm-package.sh +0 -61
  173. package/dev_assets_seed/config.json +0 -19
  174. package/frontend/.eslintrc.json +0 -25
  175. package/frontend/.prettierrc.json +0 -8
  176. package/frontend/components.json +0 -17
  177. package/frontend/index.html +0 -19
  178. package/frontend/package-lock.json +0 -7321
  179. package/frontend/package.json +0 -61
  180. package/frontend/postcss.config.js +0 -6
  181. package/frontend/public/android-chrome-192x192.png +0 -0
  182. package/frontend/public/android-chrome-512x512.png +0 -0
  183. package/frontend/public/apple-touch-icon.png +0 -0
  184. package/frontend/public/automagik-forge-logo-dark.svg +0 -3
  185. package/frontend/public/automagik-forge-logo.svg +0 -3
  186. package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
  187. package/frontend/public/favicon-16x16.png +0 -0
  188. package/frontend/public/favicon-32x32.png +0 -0
  189. package/frontend/public/favicon.ico +0 -0
  190. package/frontend/public/site.webmanifest +0 -1
  191. package/frontend/public/viba-kanban-favicon.png +0 -0
  192. package/frontend/src/App.tsx +0 -157
  193. package/frontend/src/components/DisclaimerDialog.tsx +0 -106
  194. package/frontend/src/components/GitHubLoginDialog.tsx +0 -314
  195. package/frontend/src/components/OnboardingDialog.tsx +0 -185
  196. package/frontend/src/components/PrivacyOptInDialog.tsx +0 -130
  197. package/frontend/src/components/ProvidePatDialog.tsx +0 -98
  198. package/frontend/src/components/TaskTemplateManager.tsx +0 -336
  199. package/frontend/src/components/config-provider.tsx +0 -119
  200. package/frontend/src/components/context/TaskDetailsContextProvider.tsx +0 -470
  201. package/frontend/src/components/context/taskDetailsContext.ts +0 -125
  202. package/frontend/src/components/keyboard-shortcuts-demo.tsx +0 -35
  203. package/frontend/src/components/layout/navbar.tsx +0 -86
  204. package/frontend/src/components/logo.tsx +0 -44
  205. package/frontend/src/components/projects/ProjectCard.tsx +0 -155
  206. package/frontend/src/components/projects/project-detail.tsx +0 -251
  207. package/frontend/src/components/projects/project-form-fields.tsx +0 -238
  208. package/frontend/src/components/projects/project-form.tsx +0 -301
  209. package/frontend/src/components/projects/project-list.tsx +0 -200
  210. package/frontend/src/components/projects/projects-page.tsx +0 -20
  211. package/frontend/src/components/tasks/BranchSelector.tsx +0 -169
  212. package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +0 -94
  213. package/frontend/src/components/tasks/EditorSelectionDialog.tsx +0 -119
  214. package/frontend/src/components/tasks/TaskCard.tsx +0 -154
  215. package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +0 -33
  216. package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +0 -109
  217. package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +0 -135
  218. package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +0 -296
  219. package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +0 -32
  220. package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +0 -392
  221. package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +0 -256
  222. package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +0 -56
  223. package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +0 -92
  224. package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +0 -22
  225. package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +0 -49
  226. package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +0 -186
  227. package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +0 -288
  228. package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +0 -216
  229. package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +0 -93
  230. package/frontend/src/components/tasks/TaskDetailsHeader.tsx +0 -169
  231. package/frontend/src/components/tasks/TaskDetailsPanel.tsx +0 -126
  232. package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +0 -302
  233. package/frontend/src/components/tasks/TaskFollowUpSection.tsx +0 -130
  234. package/frontend/src/components/tasks/TaskFormDialog.tsx +0 -400
  235. package/frontend/src/components/tasks/TaskKanbanBoard.tsx +0 -180
  236. package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +0 -259
  237. package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +0 -243
  238. package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +0 -899
  239. package/frontend/src/components/tasks/index.ts +0 -2
  240. package/frontend/src/components/theme-provider.tsx +0 -82
  241. package/frontend/src/components/theme-toggle.tsx +0 -36
  242. package/frontend/src/components/ui/alert.tsx +0 -59
  243. package/frontend/src/components/ui/auto-expanding-textarea.tsx +0 -70
  244. package/frontend/src/components/ui/badge.tsx +0 -36
  245. package/frontend/src/components/ui/button.tsx +0 -56
  246. package/frontend/src/components/ui/card.tsx +0 -86
  247. package/frontend/src/components/ui/checkbox.tsx +0 -44
  248. package/frontend/src/components/ui/chip.tsx +0 -25
  249. package/frontend/src/components/ui/dialog.tsx +0 -124
  250. package/frontend/src/components/ui/dropdown-menu.tsx +0 -198
  251. package/frontend/src/components/ui/file-search-textarea.tsx +0 -292
  252. package/frontend/src/components/ui/folder-picker.tsx +0 -279
  253. package/frontend/src/components/ui/input.tsx +0 -25
  254. package/frontend/src/components/ui/label.tsx +0 -24
  255. package/frontend/src/components/ui/loader.tsx +0 -26
  256. package/frontend/src/components/ui/markdown-renderer.tsx +0 -75
  257. package/frontend/src/components/ui/select.tsx +0 -160
  258. package/frontend/src/components/ui/separator.tsx +0 -31
  259. package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +0 -185
  260. package/frontend/src/components/ui/table.tsx +0 -117
  261. package/frontend/src/components/ui/tabs.tsx +0 -53
  262. package/frontend/src/components/ui/textarea.tsx +0 -22
  263. package/frontend/src/components/ui/tooltip.tsx +0 -28
  264. package/frontend/src/hooks/useNormalizedConversation.ts +0 -440
  265. package/frontend/src/index.css +0 -225
  266. package/frontend/src/lib/api.ts +0 -630
  267. package/frontend/src/lib/keyboard-shortcuts.ts +0 -266
  268. package/frontend/src/lib/responsive-config.ts +0 -70
  269. package/frontend/src/lib/types.ts +0 -39
  270. package/frontend/src/lib/utils.ts +0 -10
  271. package/frontend/src/main.tsx +0 -50
  272. package/frontend/src/pages/McpServers.tsx +0 -418
  273. package/frontend/src/pages/Settings.tsx +0 -610
  274. package/frontend/src/pages/project-tasks.tsx +0 -575
  275. package/frontend/src/pages/projects.tsx +0 -18
  276. package/frontend/src/vite-env.d.ts +0 -1
  277. package/frontend/tailwind.config.js +0 -125
  278. package/frontend/tsconfig.json +0 -26
  279. package/frontend/tsconfig.node.json +0 -10
  280. package/frontend/vite.config.ts +0 -33
  281. package/npx-cli/README.md +0 -159
  282. package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
  283. package/npx-cli/automagik-forge-0.1.10.tgz +0 -0
  284. package/npx-cli/package.json +0 -17
  285. package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
  286. package/pnpm-workspace.yaml +0 -2
  287. package/rust-toolchain.toml +0 -11
  288. package/rustfmt.toml +0 -3
  289. package/scripts/load-env.js +0 -43
  290. package/scripts/mcp_test.js +0 -374
  291. package/scripts/prepare-db.js +0 -45
  292. package/scripts/setup-dev-environment.js +0 -274
  293. package/scripts/start-mcp-sse.js +0 -70
  294. package/scripts/test-debug.js +0 -32
  295. package/scripts/test-mcp-sse.js +0 -138
  296. package/scripts/test-simple.js +0 -44
  297. package/scripts/test-wish-final.js +0 -179
  298. package/scripts/test-wish-system.js +0 -221
  299. package/shared/types.ts +0 -182
  300. package/test-npm-package.sh +0 -42
  301. /package/{npx-cli/bin → bin}/cli.js +0 -0
@@ -1,1214 +0,0 @@
1
- use std::path::Path;
2
-
3
- use chrono::{DateTime, Utc};
4
- use git2::{BranchType, Error as GitError, Repository};
5
- use serde::{Deserialize, Serialize};
6
- use sqlx::{FromRow, SqlitePool, Type};
7
- use tracing::info;
8
- use ts_rs::TS;
9
- use utoipa::ToSchema;
10
- use uuid::Uuid;
11
-
12
- use super::{project::Project, task::Task};
13
- use crate::services::{
14
- CreatePrRequest, GitHubRepoInfo, GitHubService, GitHubServiceError, GitService,
15
- GitServiceError, ProcessService,
16
- };
17
-
18
- // Constants for git diff operations
19
- const GIT_DIFF_CONTEXT_LINES: u32 = 3;
20
- const GIT_DIFF_INTERHUNK_LINES: u32 = 0;
21
-
22
- #[derive(Debug)]
23
- pub enum TaskAttemptError {
24
- Database(sqlx::Error),
25
- Git(GitError),
26
- GitService(GitServiceError),
27
- GitHubService(GitHubServiceError),
28
- TaskNotFound,
29
- ProjectNotFound,
30
- ValidationError(String),
31
- BranchNotFound(String),
32
- }
33
-
34
- impl std::fmt::Display for TaskAttemptError {
35
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36
- match self {
37
- TaskAttemptError::Database(e) => write!(f, "Database error: {}", e),
38
- TaskAttemptError::Git(e) => write!(f, "Git error: {}", e),
39
- TaskAttemptError::GitService(e) => write!(f, "Git service error: {}", e),
40
- TaskAttemptError::GitHubService(e) => write!(f, "GitHub service error: {}", e),
41
- TaskAttemptError::TaskNotFound => write!(f, "Task not found"),
42
- TaskAttemptError::ProjectNotFound => write!(f, "Project not found"),
43
- TaskAttemptError::ValidationError(e) => write!(f, "Validation error: {}", e),
44
- TaskAttemptError::BranchNotFound(branch) => write!(f, "Branch '{}' not found", branch),
45
- }
46
- }
47
- }
48
-
49
- impl std::error::Error for TaskAttemptError {}
50
-
51
- impl From<sqlx::Error> for TaskAttemptError {
52
- fn from(err: sqlx::Error) -> Self {
53
- TaskAttemptError::Database(err)
54
- }
55
- }
56
-
57
- impl From<GitError> for TaskAttemptError {
58
- fn from(err: GitError) -> Self {
59
- TaskAttemptError::Git(err)
60
- }
61
- }
62
-
63
- impl From<GitServiceError> for TaskAttemptError {
64
- fn from(err: GitServiceError) -> Self {
65
- TaskAttemptError::GitService(err)
66
- }
67
- }
68
-
69
- impl From<GitHubServiceError> for TaskAttemptError {
70
- fn from(err: GitHubServiceError) -> Self {
71
- TaskAttemptError::GitHubService(err)
72
- }
73
- }
74
-
75
- #[derive(Debug, Clone, Type, Serialize, Deserialize, PartialEq, TS, ToSchema)]
76
- #[sqlx(type_name = "task_attempt_status", rename_all = "lowercase")]
77
- #[serde(rename_all = "lowercase")]
78
- #[ts(export)]
79
- pub enum TaskAttemptStatus {
80
- SetupRunning,
81
- SetupComplete,
82
- SetupFailed,
83
- ExecutorRunning,
84
- ExecutorComplete,
85
- ExecutorFailed,
86
- }
87
-
88
- #[derive(Debug, Clone, FromRow, Serialize, Deserialize, TS, ToSchema)]
89
- #[ts(export)]
90
- pub struct TaskAttempt {
91
- pub id: Uuid,
92
- pub task_id: Uuid, // Foreign key to Task
93
- pub worktree_path: String,
94
- pub branch: String, // Git branch name for this task attempt
95
- pub base_branch: String, // Base branch this attempt is based on
96
- pub merge_commit: Option<String>,
97
- pub executor: Option<String>, // Name of the executor to use
98
- pub pr_url: Option<String>, // GitHub PR URL
99
- pub pr_number: Option<i64>, // GitHub PR number
100
- pub pr_status: Option<String>, // open, closed, merged
101
- pub pr_merged_at: Option<DateTime<Utc>>, // When PR was merged
102
- pub worktree_deleted: bool, // Flag indicating if worktree has been cleaned up
103
- pub setup_completed_at: Option<DateTime<Utc>>, // When setup script was last completed
104
- pub created_at: DateTime<Utc>,
105
- pub updated_at: DateTime<Utc>,
106
- }
107
-
108
- #[derive(Debug, Deserialize, TS, ToSchema)]
109
- #[ts(export)]
110
- pub struct CreateTaskAttempt {
111
- pub executor: Option<String>, // Optional executor name (defaults to "echo")
112
- pub base_branch: Option<String>, // Optional base branch to checkout (defaults to current HEAD)
113
- }
114
-
115
- #[derive(Debug, Deserialize, TS, ToSchema)]
116
- #[ts(export)]
117
- pub struct UpdateTaskAttempt {
118
- // Currently no updateable fields, but keeping struct for API compatibility
119
- }
120
-
121
- /// GitHub PR creation parameters
122
- pub struct CreatePrParams<'a> {
123
- pub attempt_id: Uuid,
124
- pub task_id: Uuid,
125
- pub project_id: Uuid,
126
- pub github_token: &'a str,
127
- pub title: &'a str,
128
- pub body: Option<&'a str>,
129
- pub base_branch: Option<&'a str>,
130
- }
131
-
132
- #[derive(Debug, Deserialize, TS, ToSchema)]
133
- #[ts(export)]
134
- pub struct CreateFollowUpAttempt {
135
- pub prompt: String,
136
- }
137
-
138
- #[derive(Debug, Clone, Serialize, Deserialize, TS, ToSchema)]
139
- #[ts(export)]
140
- pub enum DiffChunkType {
141
- Equal,
142
- Insert,
143
- Delete,
144
- }
145
-
146
- #[derive(Debug, Clone, Serialize, Deserialize, TS, ToSchema)]
147
- #[ts(export)]
148
- pub struct DiffChunk {
149
- pub chunk_type: DiffChunkType,
150
- pub content: String,
151
- }
152
-
153
- #[derive(Debug, Clone, Serialize, Deserialize, TS)]
154
- #[ts(export)]
155
- pub struct FileDiff {
156
- pub path: String,
157
- pub chunks: Vec<DiffChunk>,
158
- }
159
-
160
- #[derive(Debug, Clone, Serialize, Deserialize, TS)]
161
- #[ts(export)]
162
- pub struct WorktreeDiff {
163
- pub files: Vec<FileDiff>,
164
- }
165
-
166
- #[derive(Debug, Clone, Serialize, Deserialize, TS)]
167
- #[ts(export)]
168
- pub struct BranchStatus {
169
- pub is_behind: bool,
170
- pub commits_behind: usize,
171
- pub commits_ahead: usize,
172
- pub up_to_date: bool,
173
- pub merged: bool,
174
- pub has_uncommitted_changes: bool,
175
- pub base_branch_name: String,
176
- }
177
-
178
- #[derive(Debug, Clone, Serialize, Deserialize, TS)]
179
- #[ts(export)]
180
- pub enum ExecutionState {
181
- NotStarted,
182
- SetupRunning,
183
- SetupComplete,
184
- SetupFailed,
185
- SetupStopped,
186
- CodingAgentRunning,
187
- CodingAgentComplete,
188
- CodingAgentFailed,
189
- CodingAgentStopped,
190
- Complete,
191
- }
192
-
193
- #[derive(Debug, Clone, Serialize, Deserialize, TS)]
194
- #[ts(export)]
195
- pub struct TaskAttemptState {
196
- pub execution_state: ExecutionState,
197
- pub has_changes: bool,
198
- pub has_setup_script: bool,
199
- pub setup_process_id: Option<String>,
200
- pub coding_agent_process_id: Option<String>,
201
- }
202
-
203
- /// Context data for resume operations (simplified)
204
- #[derive(Debug, Clone, Serialize, Deserialize)]
205
- pub struct AttemptResumeContext {
206
- pub execution_history: String,
207
- pub cumulative_diffs: String,
208
- }
209
-
210
- #[derive(Debug)]
211
- pub struct TaskAttemptContext {
212
- pub task_attempt: TaskAttempt,
213
- pub task: Task,
214
- pub project: Project,
215
- }
216
-
217
- impl TaskAttempt {
218
- /// Load task attempt with full validation - ensures task_attempt belongs to task and task belongs to project
219
- pub async fn load_context(
220
- pool: &SqlitePool,
221
- attempt_id: Uuid,
222
- task_id: Uuid,
223
- project_id: Uuid,
224
- ) -> Result<TaskAttemptContext, TaskAttemptError> {
225
- // Single query with JOIN validation to ensure proper relationships
226
- let task_attempt = sqlx::query_as!(
227
- TaskAttempt,
228
- r#"SELECT ta.id AS "id!: Uuid",
229
- ta.task_id AS "task_id!: Uuid",
230
- ta.worktree_path,
231
- ta.branch,
232
- ta.base_branch,
233
- ta.merge_commit,
234
- ta.executor,
235
- ta.pr_url,
236
- ta.pr_number,
237
- ta.pr_status,
238
- ta.pr_merged_at AS "pr_merged_at: DateTime<Utc>",
239
- ta.worktree_deleted AS "worktree_deleted!: bool",
240
- ta.setup_completed_at AS "setup_completed_at: DateTime<Utc>",
241
- ta.created_at AS "created_at!: DateTime<Utc>",
242
- ta.updated_at AS "updated_at!: DateTime<Utc>"
243
- FROM task_attempts ta
244
- JOIN tasks t ON ta.task_id = t.id
245
- JOIN projects p ON t.project_id = p.id
246
- WHERE ta.id = $1 AND t.id = $2 AND p.id = $3"#,
247
- attempt_id,
248
- task_id,
249
- project_id
250
- )
251
- .fetch_optional(pool)
252
- .await?
253
- .ok_or(TaskAttemptError::TaskNotFound)?;
254
-
255
- // Load task and project (we know they exist due to JOIN validation)
256
- let task = Task::find_by_id(pool, task_id)
257
- .await?
258
- .ok_or(TaskAttemptError::TaskNotFound)?;
259
-
260
- let project = Project::find_by_id(pool, project_id)
261
- .await?
262
- .ok_or(TaskAttemptError::ProjectNotFound)?;
263
-
264
- Ok(TaskAttemptContext {
265
- task_attempt,
266
- task,
267
- project,
268
- })
269
- }
270
-
271
- /// Helper function to mark a worktree as deleted in the database
272
- pub async fn mark_worktree_deleted(
273
- pool: &SqlitePool,
274
- attempt_id: Uuid,
275
- ) -> Result<(), sqlx::Error> {
276
- sqlx::query!(
277
- "UPDATE task_attempts SET worktree_deleted = TRUE, updated_at = datetime('now') WHERE id = ?",
278
- attempt_id
279
- )
280
- .execute(pool)
281
- .await?;
282
- Ok(())
283
- }
284
-
285
- /// Get the base directory for automagik-forge worktrees
286
- pub fn get_worktree_base_dir() -> std::path::PathBuf {
287
- let dir_name = if cfg!(debug_assertions) {
288
- "automagik-forge-dev"
289
- } else {
290
- "automagik-forge"
291
- };
292
-
293
- if cfg!(target_os = "macos") {
294
- // macOS already uses /var/folders/... which is persistent storage
295
- std::env::temp_dir().join(dir_name)
296
- } else if cfg!(target_os = "linux") {
297
- // Linux: use /var/tmp instead of /tmp to avoid RAM usage
298
- std::path::PathBuf::from("/var/tmp").join(dir_name)
299
- } else {
300
- // Windows and other platforms: use temp dir with automagik-forge subdirectory
301
- std::env::temp_dir().join(dir_name)
302
- }
303
- }
304
-
305
- pub async fn find_by_id(pool: &SqlitePool, id: Uuid) -> Result<Option<Self>, sqlx::Error> {
306
- sqlx::query_as!(
307
- TaskAttempt,
308
- r#"SELECT id AS "id!: Uuid",
309
- task_id AS "task_id!: Uuid",
310
- worktree_path,
311
- branch,
312
- merge_commit,
313
- base_branch,
314
- executor,
315
- pr_url,
316
- pr_number,
317
- pr_status,
318
- pr_merged_at AS "pr_merged_at: DateTime<Utc>",
319
- worktree_deleted AS "worktree_deleted!: bool",
320
- setup_completed_at AS "setup_completed_at: DateTime<Utc>",
321
- created_at AS "created_at!: DateTime<Utc>",
322
- updated_at AS "updated_at!: DateTime<Utc>"
323
- FROM task_attempts
324
- WHERE id = $1"#,
325
- id
326
- )
327
- .fetch_optional(pool)
328
- .await
329
- }
330
-
331
- pub async fn find_by_task_id(
332
- pool: &SqlitePool,
333
- task_id: Uuid,
334
- ) -> Result<Vec<Self>, sqlx::Error> {
335
- sqlx::query_as!(
336
- TaskAttempt,
337
- r#"SELECT id AS "id!: Uuid",
338
- task_id AS "task_id!: Uuid",
339
- worktree_path,
340
- branch,
341
- base_branch,
342
- merge_commit,
343
- executor,
344
- pr_url,
345
- pr_number,
346
- pr_status,
347
- pr_merged_at AS "pr_merged_at: DateTime<Utc>",
348
- worktree_deleted AS "worktree_deleted!: bool",
349
- setup_completed_at AS "setup_completed_at: DateTime<Utc>",
350
- created_at AS "created_at!: DateTime<Utc>",
351
- updated_at AS "updated_at!: DateTime<Utc>"
352
- FROM task_attempts
353
- WHERE task_id = $1
354
- ORDER BY created_at DESC"#,
355
- task_id
356
- )
357
- .fetch_all(pool)
358
- .await
359
- }
360
-
361
- /// Find task attempts by task_id with project git repo path for cleanup operations
362
- pub async fn find_by_task_id_with_project(
363
- pool: &SqlitePool,
364
- task_id: Uuid,
365
- ) -> Result<Vec<(Uuid, String, String)>, sqlx::Error> {
366
- let records = sqlx::query!(
367
- r#"
368
- SELECT ta.id as "attempt_id!: Uuid", ta.worktree_path, p.git_repo_path as "git_repo_path!"
369
- FROM task_attempts ta
370
- JOIN tasks t ON ta.task_id = t.id
371
- JOIN projects p ON t.project_id = p.id
372
- WHERE ta.task_id = $1
373
- "#,
374
- task_id
375
- )
376
- .fetch_all(pool)
377
- .await?;
378
-
379
- Ok(records
380
- .into_iter()
381
- .map(|r| (r.attempt_id, r.worktree_path, r.git_repo_path))
382
- .collect())
383
- }
384
-
385
- /// Find task attempts that are expired (24+ hours since last activity) and eligible for worktree cleanup
386
- /// Activity includes: execution completion, task attempt updates (including worktree recreation),
387
- /// and any attempts that are currently in progress
388
- pub async fn find_expired_for_cleanup(
389
- pool: &SqlitePool,
390
- ) -> Result<Vec<(Uuid, String, String)>, sqlx::Error> {
391
- let records = sqlx::query!(
392
- r#"
393
- SELECT ta.id as "attempt_id!: Uuid", ta.worktree_path, p.git_repo_path as "git_repo_path!"
394
- FROM task_attempts ta
395
- LEFT JOIN execution_processes ep ON ta.id = ep.task_attempt_id AND ep.completed_at IS NOT NULL
396
- JOIN tasks t ON ta.task_id = t.id
397
- JOIN projects p ON t.project_id = p.id
398
- WHERE ta.worktree_deleted = FALSE
399
- -- Exclude attempts with any running processes (in progress)
400
- AND ta.id NOT IN (
401
- SELECT DISTINCT ep2.task_attempt_id
402
- FROM execution_processes ep2
403
- WHERE ep2.completed_at IS NULL
404
- )
405
- GROUP BY ta.id, ta.worktree_path, p.git_repo_path, ta.updated_at
406
- HAVING datetime('now', '-24 hours') > datetime(
407
- MAX(
408
- CASE
409
- WHEN ep.completed_at IS NOT NULL THEN ep.completed_at
410
- ELSE ta.updated_at
411
- END
412
- )
413
- )
414
- ORDER BY MAX(
415
- CASE
416
- WHEN ep.completed_at IS NOT NULL THEN ep.completed_at
417
- ELSE ta.updated_at
418
- END
419
- ) ASC
420
- "#
421
- )
422
- .fetch_all(pool)
423
- .await?;
424
-
425
- Ok(records
426
- .into_iter()
427
- .filter_map(|r| {
428
- r.worktree_path
429
- .map(|path| (r.attempt_id, path, r.git_repo_path))
430
- })
431
- .collect())
432
- }
433
-
434
- pub async fn create(
435
- pool: &SqlitePool,
436
- data: &CreateTaskAttempt,
437
- task_id: Uuid,
438
- ) -> Result<Self, TaskAttemptError> {
439
- let attempt_id = Uuid::new_v4();
440
- // let prefixed_id = format!("automagik-forge-{}", attempt_id);
441
-
442
- // First, get the task to get the project_id
443
- let task = Task::find_by_id(pool, task_id)
444
- .await?
445
- .ok_or(TaskAttemptError::TaskNotFound)?;
446
-
447
- // Create a unique and helpful branch name
448
- let task_title_id = crate::utils::text::git_branch_id(&task.title);
449
- let task_attempt_branch = format!(
450
- "vk-{}-{}",
451
- crate::utils::text::short_uuid(&attempt_id),
452
- task_title_id
453
- );
454
-
455
- // Generate worktree path using automagik-forge specific directory
456
- let worktree_path = Self::get_worktree_base_dir().join(&task_attempt_branch);
457
- let worktree_path_str = worktree_path.to_string_lossy().to_string();
458
-
459
- // Then get the project using the project_id
460
- let project = Project::find_by_id(pool, task.project_id)
461
- .await?
462
- .ok_or(TaskAttemptError::ProjectNotFound)?;
463
-
464
- // Create GitService instance
465
- let git_service = GitService::new(&project.git_repo_path)?;
466
-
467
- // Determine the resolved base branch name first
468
- let resolved_base_branch = if let Some(ref base_branch) = data.base_branch {
469
- base_branch.clone()
470
- } else {
471
- // Default to current HEAD branch name or "main"
472
- git_service.get_default_branch_name()?
473
- };
474
-
475
- // Create the worktree using GitService
476
- git_service.create_worktree(
477
- &task_attempt_branch,
478
- &worktree_path,
479
- data.base_branch.as_deref(),
480
- )?;
481
-
482
- // Insert the record into the database
483
- Ok(sqlx::query_as!(
484
- TaskAttempt,
485
- r#"INSERT INTO task_attempts (id, task_id, worktree_path, branch, base_branch, merge_commit, executor, pr_url, pr_number, pr_status, pr_merged_at, worktree_deleted, setup_completed_at)
486
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
487
- RETURNING id as "id!: Uuid", task_id as "task_id!: Uuid", worktree_path, branch, base_branch, merge_commit, executor, pr_url, pr_number, pr_status, pr_merged_at as "pr_merged_at: DateTime<Utc>", worktree_deleted as "worktree_deleted!: bool", setup_completed_at as "setup_completed_at: DateTime<Utc>", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
488
- attempt_id,
489
- task_id,
490
- worktree_path_str,
491
- task_attempt_branch,
492
- resolved_base_branch,
493
- Option::<String>::None, // merge_commit is always None during creation
494
- data.executor,
495
- Option::<String>::None, // pr_url is None during creation
496
- Option::<i64>::None, // pr_number is None during creation
497
- Option::<String>::None, // pr_status is None during creation
498
- Option::<DateTime<Utc>>::None, // pr_merged_at is None during creation
499
- false, // worktree_deleted is false during creation
500
- Option::<DateTime<Utc>>::None // setup_completed_at is None during creation
501
- )
502
- .fetch_one(pool)
503
- .await?)
504
- }
505
-
506
- /// Perform the actual merge operation using GitService
507
- fn perform_merge_operation(
508
- worktree_path: &str,
509
- main_repo_path: &str,
510
- branch_name: &str,
511
- base_branch: &str,
512
- task_title: &str,
513
- task_description: &Option<String>,
514
- task_id: Uuid,
515
- ) -> Result<String, TaskAttemptError> {
516
- let git_service = GitService::new(main_repo_path)?;
517
- let worktree_path = Path::new(worktree_path);
518
-
519
- // Extract first section of UUID (before first hyphen)
520
- let task_uuid_str = task_id.to_string();
521
- let first_uuid_section = task_uuid_str.split('-').next().unwrap_or(&task_uuid_str);
522
-
523
- // Create commit message with task title and description
524
- let mut commit_message = format!("{} (automagik-forge {})", task_title, first_uuid_section);
525
-
526
- // Add description on next line if it exists
527
- if let Some(description) = task_description {
528
- if !description.trim().is_empty() {
529
- commit_message.push_str("\n\n");
530
- commit_message.push_str(description);
531
- }
532
- }
533
-
534
- git_service
535
- .merge_changes(worktree_path, branch_name, base_branch, &commit_message)
536
- .map_err(TaskAttemptError::from)
537
- }
538
-
539
- /// Perform the actual git rebase operations using GitService
540
- fn perform_rebase_operation(
541
- worktree_path: &str,
542
- main_repo_path: &str,
543
- new_base_branch: Option<String>,
544
- old_base_branch: String,
545
- ) -> Result<String, TaskAttemptError> {
546
- let git_service = GitService::new(main_repo_path)?;
547
- let worktree_path = Path::new(worktree_path);
548
-
549
- git_service
550
- .rebase_branch(worktree_path, new_base_branch.as_deref(), &old_base_branch)
551
- .map_err(TaskAttemptError::from)
552
- }
553
-
554
- /// Merge the worktree changes back to the main repository
555
- pub async fn merge_changes(
556
- pool: &SqlitePool,
557
- attempt_id: Uuid,
558
- task_id: Uuid,
559
- project_id: Uuid,
560
- ) -> Result<String, TaskAttemptError> {
561
- // Load context with full validation
562
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
563
-
564
- // Ensure worktree exists (recreate if needed for cold task support)
565
- let worktree_path =
566
- Self::ensure_worktree_exists(pool, attempt_id, project_id, "merge").await?;
567
-
568
- // Perform the actual merge operation
569
- let merge_commit_id = Self::perform_merge_operation(
570
- &worktree_path,
571
- &ctx.project.git_repo_path,
572
- &ctx.task_attempt.branch,
573
- &ctx.task_attempt.base_branch,
574
- &ctx.task.title,
575
- &ctx.task.description,
576
- ctx.task.id,
577
- )?;
578
-
579
- // Update the task attempt with the merge commit
580
- sqlx::query!(
581
- "UPDATE task_attempts SET merge_commit = $1, updated_at = datetime('now') WHERE id = $2",
582
- merge_commit_id,
583
- attempt_id
584
- )
585
- .execute(pool)
586
- .await?;
587
-
588
- Ok(merge_commit_id)
589
- }
590
-
591
- /// Start the execution flow for a task attempt (setup script + executor)
592
- pub async fn start_execution(
593
- pool: &SqlitePool,
594
- app_state: &crate::app_state::AppState,
595
- attempt_id: Uuid,
596
- task_id: Uuid,
597
- project_id: Uuid,
598
- ) -> Result<(), TaskAttemptError> {
599
- ProcessService::start_execution(pool, app_state, attempt_id, task_id, project_id).await
600
- }
601
-
602
- /// Start a dev server for this task attempt
603
- pub async fn start_dev_server(
604
- pool: &SqlitePool,
605
- app_state: &crate::app_state::AppState,
606
- attempt_id: Uuid,
607
- task_id: Uuid,
608
- project_id: Uuid,
609
- ) -> Result<(), TaskAttemptError> {
610
- ProcessService::start_dev_server(pool, app_state, attempt_id, task_id, project_id).await
611
- }
612
-
613
- /// Start a follow-up execution using the same executor type as the first process
614
- /// Returns the attempt_id that was actually used (always the original attempt_id for session continuity)
615
- pub async fn start_followup_execution(
616
- pool: &SqlitePool,
617
- app_state: &crate::app_state::AppState,
618
- attempt_id: Uuid,
619
- task_id: Uuid,
620
- project_id: Uuid,
621
- prompt: &str,
622
- ) -> Result<Uuid, TaskAttemptError> {
623
- ProcessService::start_followup_execution(
624
- pool, app_state, attempt_id, task_id, project_id, prompt,
625
- )
626
- .await
627
- }
628
-
629
- /// Ensure worktree exists, recreating from branch if needed (cold task support)
630
- pub async fn ensure_worktree_exists(
631
- pool: &SqlitePool,
632
- attempt_id: Uuid,
633
- project_id: Uuid,
634
- context: &str,
635
- ) -> Result<String, TaskAttemptError> {
636
- let task_attempt = TaskAttempt::find_by_id(pool, attempt_id)
637
- .await?
638
- .ok_or(TaskAttemptError::TaskNotFound)?;
639
-
640
- // Return existing path if worktree still exists
641
- if std::path::Path::new(&task_attempt.worktree_path).exists() {
642
- return Ok(task_attempt.worktree_path);
643
- }
644
-
645
- // Recreate worktree from branch
646
- info!(
647
- "Worktree {} no longer exists, recreating from branch {} for {}",
648
- task_attempt.worktree_path, task_attempt.branch, context
649
- );
650
-
651
- let new_worktree_path =
652
- Self::recreate_worktree_from_branch(pool, &task_attempt, project_id).await?;
653
-
654
- // Update database with new path, reset worktree_deleted flag, and clear setup completion
655
- sqlx::query!(
656
- "UPDATE task_attempts SET worktree_path = $1, worktree_deleted = FALSE, setup_completed_at = NULL, updated_at = datetime('now') WHERE id = $2",
657
- new_worktree_path,
658
- attempt_id
659
- )
660
- .execute(pool)
661
- .await?;
662
-
663
- Ok(new_worktree_path)
664
- }
665
-
666
- /// Recreate a worktree from an existing branch (for cold task support)
667
- pub async fn recreate_worktree_from_branch(
668
- pool: &SqlitePool,
669
- task_attempt: &TaskAttempt,
670
- project_id: Uuid,
671
- ) -> Result<String, TaskAttemptError> {
672
- let project = Project::find_by_id(pool, project_id)
673
- .await?
674
- .ok_or(TaskAttemptError::ProjectNotFound)?;
675
-
676
- // Create GitService instance
677
- let git_service = GitService::new(&project.git_repo_path)?;
678
-
679
- // Use the stored worktree path from database - this ensures we recreate in the exact same location
680
- // where Claude originally created its session, maintaining session continuity
681
- let stored_worktree_path = std::path::PathBuf::from(&task_attempt.worktree_path);
682
-
683
- let result_path = git_service
684
- .recreate_worktree_from_branch(&task_attempt.branch, &stored_worktree_path)
685
- .await?;
686
-
687
- Ok(result_path.to_string_lossy().to_string())
688
- }
689
-
690
- /// Get the git diff between the base commit and the current committed worktree state
691
- pub async fn get_diff(
692
- pool: &SqlitePool,
693
- attempt_id: Uuid,
694
- task_id: Uuid,
695
- project_id: Uuid,
696
- ) -> Result<WorktreeDiff, TaskAttemptError> {
697
- // Load context with full validation
698
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
699
-
700
- // Create GitService instance
701
- let git_service = GitService::new(&ctx.project.git_repo_path)?;
702
-
703
- if let Some(merge_commit_id) = &ctx.task_attempt.merge_commit {
704
- // Task attempt has been merged - show the diff from the merge commit
705
- git_service
706
- .get_enhanced_diff(
707
- Path::new(""),
708
- Some(merge_commit_id),
709
- &ctx.task_attempt.base_branch,
710
- )
711
- .map_err(TaskAttemptError::from)
712
- } else {
713
- // Task attempt not yet merged - get worktree diff
714
- // Ensure worktree exists (recreate if needed for cold task support)
715
- let worktree_path =
716
- Self::ensure_worktree_exists(pool, attempt_id, project_id, "diff").await?;
717
-
718
- git_service
719
- .get_enhanced_diff(
720
- Path::new(&worktree_path),
721
- None,
722
- &ctx.task_attempt.base_branch,
723
- )
724
- .map_err(TaskAttemptError::from)
725
- }
726
- }
727
-
728
- /// Get the branch status for this task attempt
729
- pub async fn get_branch_status(
730
- pool: &SqlitePool,
731
- attempt_id: Uuid,
732
- task_id: Uuid,
733
- project_id: Uuid,
734
- ) -> Result<BranchStatus, TaskAttemptError> {
735
- // Load context with full validation
736
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
737
-
738
- use git2::{Status, StatusOptions};
739
-
740
- // Ensure worktree exists (recreate if needed for cold task support)
741
- let main_repo = Repository::open(&ctx.project.git_repo_path)?;
742
- let attempt_branch = ctx.task_attempt.branch.clone();
743
-
744
- // ── locate the commit pointed to by the attempt branch ───────────────────────
745
- let attempt_ref = main_repo
746
- // try "refs/heads/<name>" first, then raw name
747
- .find_reference(&format!("refs/heads/{}", attempt_branch))
748
- .or_else(|_| main_repo.find_reference(&attempt_branch))?;
749
- let attempt_oid = attempt_ref.target().unwrap();
750
-
751
- // ── determine the base branch & ahead/behind counts ─────────────────────────
752
- let base_branch_name = ctx.task_attempt.base_branch.clone();
753
-
754
- // 1. prefer the branch’s configured upstream, if any
755
- if let Ok(local_branch) = main_repo.find_branch(&attempt_branch, BranchType::Local) {
756
- if let Ok(upstream) = local_branch.upstream() {
757
- if let Some(_name) = upstream.name()? {
758
- if let Some(base_oid) = upstream.get().target() {
759
- let (_ahead, _behind) =
760
- main_repo.graph_ahead_behind(attempt_oid, base_oid)?;
761
- // Ignore upstream since we use stored base branch
762
- }
763
- }
764
- }
765
- }
766
-
767
- // Calculate ahead/behind counts using the stored base branch
768
- let (commits_ahead, commits_behind) =
769
- if let Ok(base_branch) = main_repo.find_branch(&base_branch_name, BranchType::Local) {
770
- if let Some(base_oid) = base_branch.get().target() {
771
- main_repo.graph_ahead_behind(attempt_oid, base_oid)?
772
- } else {
773
- (0, 0) // Base branch has no commits
774
- }
775
- } else {
776
- // Base branch doesn't exist, assume no relationship
777
- (0, 0)
778
- };
779
-
780
- // ── detect any uncommitted / untracked changes ───────────────────────────────
781
- let repo_for_status = Repository::open(&ctx.project.git_repo_path)?;
782
-
783
- let mut status_opts = StatusOptions::new();
784
- status_opts
785
- .include_untracked(true)
786
- .recurse_untracked_dirs(true)
787
- .include_ignored(false);
788
-
789
- let has_uncommitted_changes = repo_for_status
790
- .statuses(Some(&mut status_opts))?
791
- .iter()
792
- .any(|e| e.status() != Status::CURRENT);
793
-
794
- // ── assemble & return ────────────────────────────────────────────────────────
795
- Ok(BranchStatus {
796
- is_behind: commits_behind > 0,
797
- commits_behind,
798
- commits_ahead,
799
- up_to_date: commits_behind == 0 && commits_ahead == 0,
800
- merged: ctx.task_attempt.merge_commit.is_some(),
801
- has_uncommitted_changes,
802
- base_branch_name,
803
- })
804
- }
805
-
806
- /// Rebase the worktree branch onto specified base branch (or current HEAD if none specified)
807
- pub async fn rebase_attempt(
808
- pool: &SqlitePool,
809
- attempt_id: Uuid,
810
- task_id: Uuid,
811
- project_id: Uuid,
812
- new_base_branch: Option<String>,
813
- ) -> Result<String, TaskAttemptError> {
814
- // Load context with full validation
815
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
816
-
817
- // Use the stored base branch if no new base branch is provided
818
- let effective_base_branch =
819
- new_base_branch.or_else(|| Some(ctx.task_attempt.base_branch.clone()));
820
-
821
- // Ensure worktree exists (recreate if needed for cold task support)
822
- let worktree_path =
823
- Self::ensure_worktree_exists(pool, attempt_id, project_id, "rebase").await?;
824
-
825
- let new_base_commit = Self::perform_rebase_operation(
826
- &worktree_path,
827
- &ctx.project.git_repo_path,
828
- effective_base_branch.clone(),
829
- ctx.task_attempt.base_branch.clone(),
830
- )?;
831
-
832
- // Update the database with the new base branch if it was changed
833
- if let Some(new_base_branch) = &effective_base_branch {
834
- if new_base_branch != &ctx.task_attempt.base_branch {
835
- // For remote branches, store the local branch name in the database
836
- let db_branch_name = if new_base_branch.starts_with("origin/") {
837
- new_base_branch.strip_prefix("origin/").unwrap()
838
- } else {
839
- new_base_branch
840
- };
841
-
842
- sqlx::query!(
843
- "UPDATE task_attempts SET base_branch = $1, updated_at = datetime('now') WHERE id = $2",
844
- db_branch_name,
845
- attempt_id
846
- )
847
- .execute(pool)
848
- .await?;
849
- }
850
- }
851
-
852
- Ok(new_base_commit)
853
- }
854
-
855
- /// Delete a file from the worktree and commit the change
856
- pub async fn delete_file(
857
- pool: &SqlitePool,
858
- attempt_id: Uuid,
859
- task_id: Uuid,
860
- project_id: Uuid,
861
- file_path: &str,
862
- ) -> Result<String, TaskAttemptError> {
863
- // Load context with full validation
864
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
865
-
866
- // Ensure worktree exists (recreate if needed for cold task support)
867
- let worktree_path_str =
868
- Self::ensure_worktree_exists(pool, attempt_id, project_id, "delete file").await?;
869
-
870
- // Create GitService instance
871
- let git_service = GitService::new(&ctx.project.git_repo_path)?;
872
-
873
- // Use GitService to delete file and commit
874
- let commit_id =
875
- git_service.delete_file_and_commit(Path::new(&worktree_path_str), file_path)?;
876
-
877
- Ok(commit_id)
878
- }
879
-
880
- /// Create a GitHub PR for this task attempt
881
- pub async fn create_github_pr(
882
- pool: &SqlitePool,
883
- params: CreatePrParams<'_>,
884
- ) -> Result<String, TaskAttemptError> {
885
- // Load context with full validation
886
- let ctx =
887
- TaskAttempt::load_context(pool, params.attempt_id, params.task_id, params.project_id)
888
- .await?;
889
-
890
- // Ensure worktree exists (recreate if needed for cold task support)
891
- let worktree_path =
892
- Self::ensure_worktree_exists(pool, params.attempt_id, params.project_id, "GitHub PR")
893
- .await?;
894
-
895
- // Create GitHub service instance
896
- let github_service = GitHubService::new(params.github_token)?;
897
-
898
- // Use GitService to get the remote URL, then create GitHubRepoInfo
899
- let git_service = GitService::new(&ctx.project.git_repo_path)?;
900
- let (owner, repo_name) = git_service
901
- .get_github_repo_info()
902
- .map_err(|e| TaskAttemptError::ValidationError(e.to_string()))?;
903
- let repo_info = GitHubRepoInfo { owner, repo_name };
904
-
905
- // Push the branch to GitHub first
906
- Self::push_branch_to_github(
907
- &ctx.project.git_repo_path,
908
- &worktree_path,
909
- &ctx.task_attempt.branch,
910
- params.github_token,
911
- )?;
912
-
913
- // Create the PR using GitHub service
914
- let pr_request = CreatePrRequest {
915
- title: params.title.to_string(),
916
- body: params.body.map(|s| s.to_string()),
917
- head_branch: ctx.task_attempt.branch.clone(),
918
- base_branch: params.base_branch.unwrap_or("main").to_string(),
919
- };
920
-
921
- let pr_info = github_service.create_pr(&repo_info, &pr_request).await?;
922
-
923
- // Update the task attempt with PR information
924
- sqlx::query!(
925
- "UPDATE task_attempts SET pr_url = $1, pr_number = $2, pr_status = $3, updated_at = datetime('now') WHERE id = $4",
926
- pr_info.url,
927
- pr_info.number,
928
- pr_info.status,
929
- params.attempt_id
930
- )
931
- .execute(pool)
932
- .await?;
933
-
934
- Ok(pr_info.url)
935
- }
936
-
937
- /// Push the branch to GitHub remote
938
- fn push_branch_to_github(
939
- git_repo_path: &str,
940
- worktree_path: &str,
941
- branch_name: &str,
942
- github_token: &str,
943
- ) -> Result<(), TaskAttemptError> {
944
- // Use GitService to push to GitHub
945
- let git_service = GitService::new(git_repo_path)?;
946
- git_service
947
- .push_to_github(Path::new(worktree_path), branch_name, github_token)
948
- .map_err(TaskAttemptError::from)
949
- }
950
-
951
- /// Update PR status and merge commit
952
- pub async fn update_pr_status(
953
- pool: &SqlitePool,
954
- attempt_id: Uuid,
955
- status: &str,
956
- merged_at: Option<DateTime<Utc>>,
957
- merge_commit_sha: Option<&str>,
958
- ) -> Result<(), sqlx::Error> {
959
- sqlx::query!(
960
- "UPDATE task_attempts SET pr_status = $1, pr_merged_at = $2, merge_commit = $3, updated_at = datetime('now') WHERE id = $4",
961
- status,
962
- merged_at,
963
- merge_commit_sha,
964
- attempt_id
965
- )
966
- .execute(pool)
967
- .await?;
968
-
969
- Ok(())
970
- }
971
-
972
- /// Get the current execution state for a task attempt
973
- pub async fn get_execution_state(
974
- pool: &SqlitePool,
975
- attempt_id: Uuid,
976
- task_id: Uuid,
977
- project_id: Uuid,
978
- ) -> Result<TaskAttemptState, TaskAttemptError> {
979
- // Load context with full validation
980
- let ctx = TaskAttempt::load_context(pool, attempt_id, task_id, project_id).await?;
981
-
982
- let has_setup_script = ctx
983
- .project
984
- .setup_script
985
- .as_ref()
986
- .map(|script| !script.trim().is_empty())
987
- .unwrap_or(false);
988
-
989
- // Get all execution processes for this attempt, ordered by created_at
990
- let processes =
991
- crate::models::execution_process::ExecutionProcess::find_by_task_attempt_id(
992
- pool, attempt_id,
993
- )
994
- .await?;
995
-
996
- // Find setup and coding agent processes
997
- let setup_process = processes.iter().find(|p| {
998
- matches!(
999
- p.process_type,
1000
- crate::models::execution_process::ExecutionProcessType::SetupScript
1001
- )
1002
- });
1003
-
1004
- let coding_agent_process = processes.iter().find(|p| {
1005
- matches!(
1006
- p.process_type,
1007
- crate::models::execution_process::ExecutionProcessType::CodingAgent
1008
- )
1009
- });
1010
-
1011
- // Determine execution state based on processes
1012
- let execution_state = if let Some(setup) = setup_process {
1013
- match setup.status {
1014
- crate::models::execution_process::ExecutionProcessStatus::Running => {
1015
- ExecutionState::SetupRunning
1016
- }
1017
- crate::models::execution_process::ExecutionProcessStatus::Completed => {
1018
- if let Some(agent) = coding_agent_process {
1019
- match agent.status {
1020
- crate::models::execution_process::ExecutionProcessStatus::Running => {
1021
- ExecutionState::CodingAgentRunning
1022
- }
1023
- crate::models::execution_process::ExecutionProcessStatus::Completed => {
1024
- ExecutionState::CodingAgentComplete
1025
- }
1026
- crate::models::execution_process::ExecutionProcessStatus::Failed => {
1027
- ExecutionState::CodingAgentFailed
1028
- }
1029
- crate::models::execution_process::ExecutionProcessStatus::Killed => {
1030
- ExecutionState::CodingAgentStopped
1031
- }
1032
- }
1033
- } else {
1034
- ExecutionState::SetupComplete
1035
- }
1036
- }
1037
- crate::models::execution_process::ExecutionProcessStatus::Failed => {
1038
- ExecutionState::SetupFailed
1039
- }
1040
- crate::models::execution_process::ExecutionProcessStatus::Killed => {
1041
- ExecutionState::SetupStopped
1042
- }
1043
- }
1044
- } else if let Some(agent) = coding_agent_process {
1045
- // No setup script, only coding agent
1046
- match agent.status {
1047
- crate::models::execution_process::ExecutionProcessStatus::Running => {
1048
- ExecutionState::CodingAgentRunning
1049
- }
1050
- crate::models::execution_process::ExecutionProcessStatus::Completed => {
1051
- ExecutionState::CodingAgentComplete
1052
- }
1053
- crate::models::execution_process::ExecutionProcessStatus::Failed => {
1054
- ExecutionState::CodingAgentFailed
1055
- }
1056
- crate::models::execution_process::ExecutionProcessStatus::Killed => {
1057
- ExecutionState::CodingAgentStopped
1058
- }
1059
- }
1060
- } else {
1061
- // No processes started yet
1062
- ExecutionState::NotStarted
1063
- };
1064
-
1065
- // Check if there are any changes (quick diff check)
1066
- let has_changes = match Self::get_diff(pool, attempt_id, task_id, project_id).await {
1067
- Ok(diff) => !diff.files.is_empty(),
1068
- Err(_) => false, // If diff fails, assume no changes
1069
- };
1070
-
1071
- Ok(TaskAttemptState {
1072
- execution_state,
1073
- has_changes,
1074
- has_setup_script,
1075
- setup_process_id: setup_process.map(|p| p.id.to_string()),
1076
- coding_agent_process_id: coding_agent_process.map(|p| p.id.to_string()),
1077
- })
1078
- }
1079
-
1080
- /// Check if setup script has been completed for this worktree
1081
- pub async fn is_setup_completed(
1082
- pool: &SqlitePool,
1083
- attempt_id: Uuid,
1084
- ) -> Result<bool, TaskAttemptError> {
1085
- let task_attempt = Self::find_by_id(pool, attempt_id)
1086
- .await?
1087
- .ok_or(TaskAttemptError::TaskNotFound)?;
1088
-
1089
- Ok(task_attempt.setup_completed_at.is_some())
1090
- }
1091
-
1092
- /// Mark setup script as completed for this worktree
1093
- pub async fn mark_setup_completed(
1094
- pool: &SqlitePool,
1095
- attempt_id: Uuid,
1096
- ) -> Result<(), TaskAttemptError> {
1097
- sqlx::query!(
1098
- "UPDATE task_attempts SET setup_completed_at = datetime('now'), updated_at = datetime('now') WHERE id = ?",
1099
- attempt_id
1100
- )
1101
- .execute(pool)
1102
- .await?;
1103
-
1104
- Ok(())
1105
- }
1106
-
1107
- /// Get execution history from current attempt only (simplified)
1108
- pub async fn get_attempt_execution_history(
1109
- pool: &SqlitePool,
1110
- attempt_id: Uuid,
1111
- ) -> Result<String, TaskAttemptError> {
1112
- // Get all coding agent processes for this attempt
1113
- let processes =
1114
- crate::models::execution_process::ExecutionProcess::find_by_task_attempt_id(
1115
- pool, attempt_id,
1116
- )
1117
- .await?;
1118
-
1119
- // Filter to coding agent processes only and aggregate stdout
1120
- let coding_processes: Vec<_> = processes
1121
- .into_iter()
1122
- .filter(|p| {
1123
- matches!(
1124
- p.process_type,
1125
- crate::models::execution_process::ExecutionProcessType::CodingAgent
1126
- )
1127
- })
1128
- .collect();
1129
-
1130
- let mut history = String::new();
1131
- for process in coding_processes {
1132
- if let Some(stdout) = process.stdout {
1133
- if !stdout.trim().is_empty() {
1134
- history.push_str(&stdout);
1135
- history.push('\n');
1136
- }
1137
- }
1138
- }
1139
-
1140
- Ok(history)
1141
- }
1142
-
1143
- /// Get diff between base_branch and current attempt (simplified)
1144
- pub async fn get_attempt_diff(
1145
- pool: &SqlitePool,
1146
- attempt_id: Uuid,
1147
- project_id: Uuid,
1148
- ) -> Result<String, TaskAttemptError> {
1149
- // Get the task attempt with base_branch
1150
- let attempt = Self::find_by_id(pool, attempt_id)
1151
- .await?
1152
- .ok_or(TaskAttemptError::TaskNotFound)?;
1153
-
1154
- // Get the project
1155
- let project = Project::find_by_id(pool, project_id)
1156
- .await?
1157
- .ok_or(TaskAttemptError::ProjectNotFound)?;
1158
-
1159
- // Open the main repository
1160
- let repo = Repository::open(&project.git_repo_path)?;
1161
-
1162
- // Get base branch commit
1163
- let base_branch = repo
1164
- .find_branch(&attempt.base_branch, git2::BranchType::Local)
1165
- .map_err(|_| TaskAttemptError::BranchNotFound(attempt.base_branch.clone()))?;
1166
- let base_commit = base_branch.get().peel_to_commit()?;
1167
-
1168
- // Get current branch commit
1169
- let current_branch = repo
1170
- .find_branch(&attempt.branch, git2::BranchType::Local)
1171
- .map_err(|_| TaskAttemptError::BranchNotFound(attempt.branch.clone()))?;
1172
- let current_commit = current_branch.get().peel_to_commit()?;
1173
-
1174
- // Create diff between base and current
1175
- let base_tree = base_commit.tree()?;
1176
- let current_tree = current_commit.tree()?;
1177
-
1178
- let mut diff_opts = git2::DiffOptions::new();
1179
- diff_opts.context_lines(GIT_DIFF_CONTEXT_LINES);
1180
- diff_opts.interhunk_lines(GIT_DIFF_INTERHUNK_LINES);
1181
-
1182
- let diff =
1183
- repo.diff_tree_to_tree(Some(&base_tree), Some(&current_tree), Some(&mut diff_opts))?;
1184
-
1185
- // Convert to text format
1186
- let mut diff_text = String::new();
1187
- diff.print(git2::DiffFormat::Patch, |_delta, _hunk, line| {
1188
- let content = std::str::from_utf8(line.content()).unwrap_or("");
1189
- diff_text.push_str(&format!("{}{}", line.origin(), content));
1190
- true
1191
- })?;
1192
-
1193
- Ok(diff_text)
1194
- }
1195
-
1196
- /// Get comprehensive resume context for Gemini followup execution (simplified)
1197
- pub async fn get_attempt_resume_context(
1198
- pool: &SqlitePool,
1199
- attempt_id: Uuid,
1200
- _task_id: Uuid,
1201
- project_id: Uuid,
1202
- ) -> Result<AttemptResumeContext, TaskAttemptError> {
1203
- // Get execution history from current attempt only
1204
- let execution_history = Self::get_attempt_execution_history(pool, attempt_id).await?;
1205
-
1206
- // Get diff between base_branch and current attempt
1207
- let cumulative_diffs = Self::get_attempt_diff(pool, attempt_id, project_id).await?;
1208
-
1209
- Ok(AttemptResumeContext {
1210
- execution_history,
1211
- cumulative_diffs,
1212
- })
1213
- }
1214
- }