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,1193 +0,0 @@
1
- use git2::Repository;
2
- use uuid::Uuid;
3
-
4
- use crate::{
5
- app_state::AppState,
6
- models::{
7
- execution_process::{ExecutionProcess, ExecutionProcessStatus, ExecutionProcessType},
8
- task::{Task, TaskStatus},
9
- task_attempt::TaskAttempt,
10
- },
11
- services::{NotificationConfig, NotificationService, ProcessService},
12
- utils::worktree_manager::WorktreeManager,
13
- };
14
-
15
- /// Delegation context structure
16
- #[derive(Debug, serde::Deserialize)]
17
- struct DelegationContext {
18
- delegate_to: String,
19
- operation_params: DelegationOperationParams,
20
- }
21
-
22
- #[derive(Debug, serde::Deserialize)]
23
- struct DelegationOperationParams {
24
- task_id: uuid::Uuid,
25
- project_id: uuid::Uuid,
26
- attempt_id: uuid::Uuid,
27
- additional: Option<serde_json::Value>,
28
- }
29
-
30
- /// Parse delegation context from process args JSON
31
- fn parse_delegation_context(args_json: &str) -> Option<DelegationContext> {
32
- // Parse the args JSON array
33
- if let Ok(args_array) = serde_json::from_str::<serde_json::Value>(args_json) {
34
- if let Some(args) = args_array.as_array() {
35
- // Look for --delegation-context flag
36
- for (i, arg) in args.iter().enumerate() {
37
- if let Some(arg_str) = arg.as_str() {
38
- if arg_str == "--delegation-context" && i + 1 < args.len() {
39
- // Next argument should be the delegation context JSON
40
- if let Some(context_str) = args[i + 1].as_str() {
41
- if let Ok(context) =
42
- serde_json::from_str::<DelegationContext>(context_str)
43
- {
44
- return Some(context);
45
- }
46
- }
47
- }
48
- }
49
- }
50
- }
51
- }
52
- None
53
- }
54
-
55
- /// Handle delegation after setup completion
56
- async fn handle_setup_delegation(app_state: &AppState, delegation_context: DelegationContext) {
57
- let params = &delegation_context.operation_params;
58
- let task_id = params.task_id;
59
- let project_id = params.project_id;
60
- let attempt_id = params.attempt_id;
61
-
62
- tracing::info!(
63
- "Delegating to {} after setup completion for attempt {}",
64
- delegation_context.delegate_to,
65
- attempt_id
66
- );
67
-
68
- let result = match delegation_context.delegate_to.as_str() {
69
- "dev_server" => {
70
- ProcessService::start_dev_server_direct(
71
- &app_state.db_pool,
72
- app_state,
73
- attempt_id,
74
- task_id,
75
- project_id,
76
- )
77
- .await
78
- }
79
- "coding_agent" => {
80
- ProcessService::start_coding_agent(
81
- &app_state.db_pool,
82
- app_state,
83
- attempt_id,
84
- task_id,
85
- project_id,
86
- )
87
- .await
88
- }
89
- "followup" => {
90
- let prompt = params
91
- .additional
92
- .as_ref()
93
- .and_then(|a| a.get("prompt"))
94
- .and_then(|p| p.as_str())
95
- .unwrap_or("");
96
-
97
- ProcessService::start_followup_execution_direct(
98
- &app_state.db_pool,
99
- app_state,
100
- attempt_id,
101
- task_id,
102
- project_id,
103
- prompt,
104
- )
105
- .await
106
- .map(|_| ())
107
- }
108
- _ => {
109
- tracing::error!(
110
- "Unknown delegation target: {}",
111
- delegation_context.delegate_to
112
- );
113
- return;
114
- }
115
- };
116
-
117
- if let Err(e) = result {
118
- tracing::error!(
119
- "Failed to delegate to {} after setup completion: {}",
120
- delegation_context.delegate_to,
121
- e
122
- );
123
- } else {
124
- tracing::info!(
125
- "Successfully delegated to {} after setup completion",
126
- delegation_context.delegate_to
127
- );
128
- }
129
- }
130
-
131
- /// Commit any unstaged changes in the worktree after execution completion
132
- async fn commit_execution_changes(
133
- worktree_path: &str,
134
- attempt_id: Uuid,
135
- summary: Option<&str>,
136
- ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
137
- // Run git operations in a blocking task since git2 is synchronous
138
- let worktree_path = worktree_path.to_string();
139
- let summary = summary.map(|s| s.to_string());
140
- tokio::task::spawn_blocking(move || {
141
- let worktree_repo = Repository::open(&worktree_path)?;
142
-
143
- // Check if there are any changes to commit
144
- let status = worktree_repo.statuses(None)?;
145
- let has_changes = status.iter().any(|entry| {
146
- let flags = entry.status();
147
- flags.contains(git2::Status::INDEX_NEW)
148
- || flags.contains(git2::Status::INDEX_MODIFIED)
149
- || flags.contains(git2::Status::INDEX_DELETED)
150
- || flags.contains(git2::Status::WT_NEW)
151
- || flags.contains(git2::Status::WT_MODIFIED)
152
- || flags.contains(git2::Status::WT_DELETED)
153
- });
154
-
155
- if !has_changes {
156
- return Ok::<(), Box<dyn std::error::Error + Send + Sync>>(());
157
- }
158
-
159
- // Get the current signature for commits
160
- let signature = worktree_repo.signature()?;
161
-
162
- // Get the current HEAD commit
163
- let head = worktree_repo.head()?;
164
- let parent_commit = head.peel_to_commit()?;
165
-
166
- // Stage all changes
167
- let mut worktree_index = worktree_repo.index()?;
168
- worktree_index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
169
- worktree_index.write()?;
170
-
171
- let tree_id = worktree_index.write_tree()?;
172
- let tree = worktree_repo.find_tree(tree_id)?;
173
-
174
- // Create commit for the changes
175
- let commit_message = if let Some(ref summary_msg) = summary {
176
- summary_msg.clone()
177
- } else {
178
- format!("Task attempt {} - Final changes", attempt_id)
179
- };
180
- worktree_repo.commit(
181
- Some("HEAD"),
182
- &signature,
183
- &signature,
184
- &commit_message,
185
- &tree,
186
- &[&parent_commit],
187
- )?;
188
-
189
- Ok(())
190
- })
191
- .await??;
192
-
193
- Ok(())
194
- }
195
-
196
- /// Check if worktree has uncommitted changes and warn if so
197
- fn check_uncommitted_changes(worktree_path: &str) {
198
- if let Ok(repo) = Repository::open(worktree_path) {
199
- if let Ok(statuses) = repo.statuses(None) {
200
- // Simplified check: any status entry indicates changes
201
- if !statuses.is_empty() {
202
- tracing::warn!(
203
- "Deleting worktree {} with uncommitted changes",
204
- worktree_path
205
- );
206
- }
207
- }
208
- }
209
- }
210
-
211
- /// Delete a single git worktree and its filesystem directory using WorktreeManager
212
- async fn delete_worktree(
213
- worktree_path: &str,
214
- main_repo_path: &str,
215
- attempt_id: Uuid,
216
- ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
217
- let worktree_path_buf = std::path::PathBuf::from(worktree_path);
218
-
219
- // Check if worktree directory exists first - no-op if already gone
220
- if !worktree_path_buf.exists() {
221
- tracing::debug!(
222
- "Worktree {} already doesn't exist, skipping cleanup",
223
- worktree_path
224
- );
225
- return Ok(());
226
- }
227
-
228
- // Check for uncommitted changes and warn
229
- check_uncommitted_changes(worktree_path);
230
-
231
- match WorktreeManager::cleanup_worktree(&worktree_path_buf, Some(main_repo_path)).await {
232
- Ok(_) => {
233
- tracing::info!(
234
- "Successfully cleaned up worktree for attempt {}",
235
- attempt_id
236
- );
237
- Ok(())
238
- }
239
- Err(e) => {
240
- tracing::error!(
241
- "Failed to cleanup worktree for attempt {}: {}",
242
- attempt_id,
243
- e
244
- );
245
- Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
246
- }
247
- }
248
- }
249
-
250
- /// Clean up all worktrees for a specific task (immediate cleanup)
251
- pub async fn cleanup_task_worktrees(
252
- pool: &sqlx::SqlitePool,
253
- task_id: Uuid,
254
- ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
255
- let task_attempts_with_project =
256
- TaskAttempt::find_by_task_id_with_project(pool, task_id).await?;
257
-
258
- if task_attempts_with_project.is_empty() {
259
- tracing::debug!("No worktrees found for task {} to clean up", task_id);
260
- return Ok(());
261
- }
262
-
263
- tracing::info!(
264
- "Starting immediate cleanup of {} worktrees for task {}",
265
- task_attempts_with_project.len(),
266
- task_id
267
- );
268
-
269
- let mut cleaned_count = 0;
270
- let mut failed_count = 0;
271
-
272
- for (attempt_id, worktree_path, git_repo_path) in task_attempts_with_project {
273
- if let Err(e) = delete_worktree(&worktree_path, &git_repo_path, attempt_id).await {
274
- tracing::error!(
275
- "Failed to cleanup worktree for attempt {}: {}",
276
- attempt_id,
277
- e
278
- );
279
- failed_count += 1;
280
- // Continue with other attempts even if one fails
281
- } else {
282
- // Mark worktree as deleted in database after successful cleanup
283
- if let Err(e) =
284
- crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(pool, attempt_id)
285
- .await
286
- {
287
- tracing::error!(
288
- "Failed to mark worktree as deleted in database for attempt {}: {}",
289
- attempt_id,
290
- e
291
- );
292
- } else {
293
- cleaned_count += 1;
294
- }
295
- }
296
- }
297
-
298
- tracing::info!(
299
- "Completed immediate cleanup for task {}: {} worktrees cleaned, {} failed",
300
- task_id,
301
- cleaned_count,
302
- failed_count
303
- );
304
-
305
- Ok(())
306
- }
307
-
308
- /// Defensively check for externally deleted worktrees and mark them as deleted in the database
309
- async fn check_externally_deleted_worktrees(pool: &sqlx::SqlitePool) {
310
- let active_attempts = match sqlx::query!(
311
- r#"SELECT id as "id!: Uuid", worktree_path FROM task_attempts WHERE worktree_deleted = FALSE"#
312
- )
313
- .fetch_all(pool)
314
- .await
315
- {
316
- Ok(attempts) => attempts,
317
- Err(e) => {
318
- tracing::error!("Failed to query active task attempts for external deletion check: {}", e);
319
- return;
320
- }
321
- };
322
-
323
- tracing::debug!(
324
- "Checking {} active worktrees for external deletion...",
325
- active_attempts.len()
326
- );
327
-
328
- let mut externally_deleted_count = 0;
329
- for record in active_attempts {
330
- let attempt_id = record.id;
331
- let worktree_path = &record.worktree_path;
332
-
333
- // Check if worktree directory exists
334
- if !std::path::Path::new(worktree_path).exists() {
335
- // Worktree was deleted externally, mark as deleted in database
336
- if let Err(e) =
337
- crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(pool, attempt_id)
338
- .await
339
- {
340
- tracing::error!(
341
- "Failed to mark externally deleted worktree as deleted for attempt {}: {}",
342
- attempt_id,
343
- e
344
- );
345
- } else {
346
- externally_deleted_count += 1;
347
- tracing::debug!(
348
- "Marked externally deleted worktree as deleted for attempt {} (path: {})",
349
- attempt_id,
350
- worktree_path
351
- );
352
- }
353
- }
354
- }
355
-
356
- if externally_deleted_count > 0 {
357
- tracing::info!(
358
- "Found and marked {} externally deleted worktrees",
359
- externally_deleted_count
360
- );
361
- } else {
362
- tracing::debug!("No externally deleted worktrees found");
363
- }
364
- }
365
-
366
- /// Find and delete orphaned worktrees that don't correspond to any task attempts
367
- async fn cleanup_orphaned_worktrees(pool: &sqlx::SqlitePool) {
368
- // Check if orphan cleanup is disabled via environment variable
369
- if std::env::var("DISABLE_WORKTREE_ORPHAN_CLEANUP").is_ok() {
370
- tracing::debug!("Orphan worktree cleanup is disabled via DISABLE_WORKTREE_ORPHAN_CLEANUP environment variable");
371
- return;
372
- }
373
- let worktree_base_dir = crate::models::task_attempt::TaskAttempt::get_worktree_base_dir();
374
-
375
- // Check if base directory exists
376
- if !worktree_base_dir.exists() {
377
- tracing::debug!(
378
- "Worktree base directory {} does not exist, skipping orphan cleanup",
379
- worktree_base_dir.display()
380
- );
381
- return;
382
- }
383
-
384
- // Read all directories in the base directory
385
- let entries = match std::fs::read_dir(&worktree_base_dir) {
386
- Ok(entries) => entries,
387
- Err(e) => {
388
- tracing::error!(
389
- "Failed to read worktree base directory {}: {}",
390
- worktree_base_dir.display(),
391
- e
392
- );
393
- return;
394
- }
395
- };
396
-
397
- let mut orphaned_count = 0;
398
- let mut checked_count = 0;
399
-
400
- for entry in entries {
401
- let entry = match entry {
402
- Ok(entry) => entry,
403
- Err(e) => {
404
- tracing::warn!("Failed to read directory entry: {}", e);
405
- continue;
406
- }
407
- };
408
-
409
- let path = entry.path();
410
-
411
- // Only process directories
412
- if !path.is_dir() {
413
- continue;
414
- }
415
-
416
- let worktree_path_str = path.to_string_lossy().to_string();
417
- checked_count += 1;
418
-
419
- // Check if this worktree path exists in the database
420
- let exists_in_db = match sqlx::query!(
421
- "SELECT COUNT(*) as count FROM task_attempts WHERE worktree_path = ?",
422
- worktree_path_str
423
- )
424
- .fetch_one(pool)
425
- .await
426
- {
427
- Ok(row) => row.count > 0,
428
- Err(e) => {
429
- tracing::error!(
430
- "Failed to check database for worktree path {}: {}",
431
- worktree_path_str,
432
- e
433
- );
434
- continue;
435
- }
436
- };
437
-
438
- if !exists_in_db {
439
- // This is an orphaned worktree - delete it
440
- tracing::info!("Found orphaned worktree: {}", worktree_path_str);
441
-
442
- // For orphaned worktrees, we try to clean up git metadata if possible
443
- // then remove the directory
444
- if let Err(e) = cleanup_orphaned_worktree_directory(&path).await {
445
- tracing::error!(
446
- "Failed to remove orphaned worktree {}: {}",
447
- worktree_path_str,
448
- e
449
- );
450
- } else {
451
- orphaned_count += 1;
452
- tracing::info!(
453
- "Successfully removed orphaned worktree: {}",
454
- worktree_path_str
455
- );
456
- }
457
- }
458
- }
459
-
460
- if orphaned_count > 0 {
461
- tracing::info!(
462
- "Cleaned up {} orphaned worktrees (checked {} total directories)",
463
- orphaned_count,
464
- checked_count
465
- );
466
- } else {
467
- tracing::debug!(
468
- "No orphaned worktrees found (checked {} directories)",
469
- checked_count
470
- );
471
- }
472
- }
473
-
474
- /// Clean up an orphaned worktree directory, attempting to clean git metadata if possible
475
- async fn cleanup_orphaned_worktree_directory(
476
- worktree_path: &std::path::Path,
477
- ) -> Result<(), std::io::Error> {
478
- // Use WorktreeManager for proper cleanup - it will try to infer the repo path
479
- // and clean up what it can, even if the main repo is gone
480
- match WorktreeManager::cleanup_worktree(worktree_path, None).await {
481
- Ok(()) => {
482
- tracing::debug!(
483
- "WorktreeManager successfully cleaned up orphaned worktree: {}",
484
- worktree_path.display()
485
- );
486
- }
487
- Err(e) => {
488
- tracing::warn!(
489
- "WorktreeManager cleanup failed for orphaned worktree {}: {}",
490
- worktree_path.display(),
491
- e
492
- );
493
-
494
- // If WorktreeManager cleanup failed, fall back to simple directory removal
495
- // This ensures we delete as much as we can
496
- if worktree_path.exists() {
497
- tracing::debug!(
498
- "Falling back to simple directory removal for orphaned worktree: {}",
499
- worktree_path.display()
500
- );
501
- std::fs::remove_dir_all(worktree_path).map_err(|e| {
502
- std::io::Error::new(
503
- e.kind(),
504
- format!("Failed to remove orphaned worktree directory: {}", e),
505
- )
506
- })?;
507
- }
508
- }
509
- }
510
-
511
- Ok(())
512
- }
513
-
514
- pub async fn execution_monitor(app_state: AppState) {
515
- let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5));
516
- let mut cleanup_interval = tokio::time::interval(tokio::time::Duration::from_secs(1800)); // 30 minutes
517
-
518
- loop {
519
- tokio::select! {
520
- _ = interval.tick() => {
521
- // Check for completed processes FIRST to avoid race conditions
522
- let completed_executions = app_state.get_running_executions_for_monitor().await;
523
-
524
- // Handle completed executions
525
- for (execution_process_id, task_attempt_id, success, exit_code) in completed_executions {
526
- let status_text = if success {
527
- "completed successfully"
528
- } else {
529
- "failed"
530
- };
531
- let exit_text = if let Some(code) = exit_code {
532
- format!(" with exit code {}", code)
533
- } else {
534
- String::new()
535
- };
536
-
537
- tracing::info!(
538
- "Execution {} {}{}",
539
- execution_process_id,
540
- status_text,
541
- exit_text
542
- );
543
-
544
- // Update the execution process record
545
- let execution_status = if success {
546
- ExecutionProcessStatus::Completed
547
- } else {
548
- ExecutionProcessStatus::Failed
549
- };
550
-
551
- if let Err(e) = ExecutionProcess::update_completion(
552
- &app_state.db_pool,
553
- execution_process_id,
554
- execution_status,
555
- exit_code,
556
- )
557
- .await
558
- {
559
- tracing::error!(
560
- "Failed to update execution process {} completion: {}",
561
- execution_process_id,
562
- e
563
- );
564
- }
565
-
566
- // Get the execution process to determine next steps
567
- if let Ok(Some(execution_process)) =
568
- ExecutionProcess::find_by_id(&app_state.db_pool, execution_process_id).await
569
- {
570
- match execution_process.process_type {
571
- ExecutionProcessType::SetupScript => {
572
- handle_setup_completion(
573
- &app_state,
574
- task_attempt_id,
575
- execution_process,
576
- success,
577
- )
578
- .await;
579
- }
580
- ExecutionProcessType::CleanupScript => {
581
- handle_cleanup_completion(
582
- &app_state,
583
- task_attempt_id,
584
- execution_process_id,
585
- execution_process,
586
- success,
587
- exit_code,
588
- )
589
- .await;
590
- }
591
- ExecutionProcessType::CodingAgent => {
592
- handle_coding_agent_completion(
593
- &app_state,
594
- task_attempt_id,
595
- execution_process_id,
596
- execution_process,
597
- success,
598
- exit_code,
599
- )
600
- .await;
601
- }
602
- ExecutionProcessType::DevServer => {
603
- handle_dev_server_completion(
604
- &app_state,
605
- task_attempt_id,
606
- execution_process_id,
607
- execution_process,
608
- success,
609
- exit_code,
610
- )
611
- .await;
612
- }
613
- }
614
- } else {
615
- tracing::error!(
616
- "Failed to find execution process {} for completion handling",
617
- execution_process_id
618
- );
619
- }
620
- }
621
-
622
- // Check for orphaned execution processes AFTER handling completions
623
- // Add a small delay to ensure completed processes are properly handled first
624
- tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
625
-
626
- let running_processes = match ExecutionProcess::find_running(&app_state.db_pool).await {
627
- Ok(processes) => processes,
628
- Err(e) => {
629
- tracing::error!("Failed to query running execution processes: {}", e);
630
- continue;
631
- }
632
- };
633
-
634
- for process in running_processes {
635
- // Check if this process is not actually running in the app state
636
- if !app_state.has_running_execution(process.task_attempt_id).await {
637
- // Additional check: if the process was recently updated, skip it to prevent race conditions
638
- let now = chrono::Utc::now();
639
- let time_since_update = now - process.updated_at;
640
- if time_since_update.num_seconds() < 10 {
641
- // Process was updated within last 10 seconds, likely just completed
642
- tracing::debug!(
643
- "Skipping recently updated orphaned process {} (updated {} seconds ago)",
644
- process.id,
645
- time_since_update.num_seconds()
646
- );
647
- continue;
648
- }
649
-
650
- // This is truly an orphaned execution process - mark it as failed
651
- tracing::info!(
652
- "Found orphaned execution process {} for task attempt {}",
653
- process.id,
654
- process.task_attempt_id
655
- );
656
- // This is truly an orphaned execution process - mark it as failed
657
- tracing::info!(
658
- "Found orphaned execution process {} for task attempt {}",
659
- process.id,
660
- process.task_attempt_id
661
- );
662
-
663
- // Update the execution process status first
664
- if let Err(e) = ExecutionProcess::update_completion(
665
- &app_state.db_pool,
666
- process.id,
667
- ExecutionProcessStatus::Failed,
668
- None, // No exit code for orphaned processes
669
- )
670
- .await
671
- {
672
- tracing::error!(
673
- "Failed to update orphaned execution process {} status: {}",
674
- process.id,
675
- e
676
- );
677
- continue;
678
- }
679
-
680
- // Process marked as failed
681
-
682
- tracing::info!("Marked orphaned execution process {} as failed", process.id);
683
-
684
- // Update task status to InReview for coding agent and setup script failures
685
- if matches!(
686
- process.process_type,
687
- ExecutionProcessType::CodingAgent | ExecutionProcessType::SetupScript
688
- ) {
689
- if let Ok(Some(task_attempt)) =
690
- TaskAttempt::find_by_id(&app_state.db_pool, process.task_attempt_id).await
691
- {
692
- if let Ok(Some(task)) =
693
- Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
694
- {
695
- if let Err(e) = Task::update_status(
696
- &app_state.db_pool,
697
- task.id,
698
- task.project_id,
699
- TaskStatus::InReview,
700
- )
701
- .await
702
- {
703
- tracing::error!("Failed to update task status to InReview for orphaned attempt: {}", e);
704
- }
705
- }
706
- }
707
- }
708
- }
709
- }
710
- }
711
- _ = cleanup_interval.tick() => {
712
- tracing::info!("Starting periodic worktree cleanup...");
713
-
714
- // First, defensively check for externally deleted worktrees
715
- check_externally_deleted_worktrees(&app_state.db_pool).await;
716
-
717
- // Then, find and delete orphaned worktrees that don't belong to any task
718
- cleanup_orphaned_worktrees(&app_state.db_pool).await;
719
-
720
- // Then, proceed with normal expired worktree cleanup
721
- match TaskAttempt::find_expired_for_cleanup(&app_state.db_pool).await {
722
- Ok(expired_attempts) => {
723
- if expired_attempts.is_empty() {
724
- tracing::debug!("No expired worktrees found");
725
- } else {
726
- tracing::info!("Found {} expired worktrees to clean up", expired_attempts.len());
727
- for (attempt_id, worktree_path, git_repo_path) in expired_attempts {
728
- if let Err(e) = delete_worktree(&worktree_path, &git_repo_path, attempt_id).await {
729
- tracing::error!("Failed to cleanup expired worktree {}: {}", attempt_id, e);
730
- } else {
731
- // Mark worktree as deleted in database after successful cleanup
732
- if let Err(e) = crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(&app_state.db_pool, attempt_id).await {
733
- tracing::error!("Failed to mark worktree as deleted in database for attempt {}: {}", attempt_id, e);
734
- } else {
735
- tracing::info!("Successfully marked worktree as deleted for attempt {}", attempt_id);
736
- }
737
- }
738
- }
739
- }
740
- }
741
- Err(e) => {
742
- tracing::error!("Failed to query expired task attempts: {}", e);
743
- }
744
- }
745
- }
746
- }
747
- }
748
- }
749
-
750
- /// Handle setup script completion
751
- async fn handle_setup_completion(
752
- app_state: &AppState,
753
- task_attempt_id: Uuid,
754
- execution_process: ExecutionProcess,
755
- success: bool,
756
- ) {
757
- if success {
758
- // Mark setup as completed in database
759
- if let Err(e) = TaskAttempt::mark_setup_completed(&app_state.db_pool, task_attempt_id).await
760
- {
761
- tracing::error!(
762
- "Failed to mark setup as completed for attempt {}: {}",
763
- task_attempt_id,
764
- e
765
- );
766
- }
767
-
768
- // Setup completed successfully
769
-
770
- // Check for delegation context in process args
771
- let delegation_result = if let Some(args_json) = &execution_process.args {
772
- parse_delegation_context(args_json)
773
- } else {
774
- None
775
- };
776
-
777
- if let Some(delegation_context) = delegation_result {
778
- // Delegate to the original operation
779
- handle_setup_delegation(app_state, delegation_context).await;
780
- } else {
781
- // Fallback to original behavior - start coding agent
782
- if let Ok(Some(task_attempt)) =
783
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
784
- {
785
- if let Ok(Some(task)) =
786
- Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
787
- {
788
- // Start the coding agent
789
- if let Err(e) = ProcessService::start_coding_agent(
790
- &app_state.db_pool,
791
- app_state,
792
- task_attempt_id,
793
- task.id,
794
- task.project_id,
795
- )
796
- .await
797
- {
798
- tracing::error!(
799
- "Failed to start coding agent after setup completion: {}",
800
- e
801
- );
802
- }
803
- }
804
- }
805
- }
806
- } else {
807
- // Setup failed, update task status
808
-
809
- // Update task status to InReview since setup failed
810
- if let Ok(Some(task_attempt)) =
811
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
812
- {
813
- if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
814
- {
815
- if let Err(e) = Task::update_status(
816
- &app_state.db_pool,
817
- task.id,
818
- task.project_id,
819
- TaskStatus::InReview,
820
- )
821
- .await
822
- {
823
- tracing::error!(
824
- "Failed to update task status to InReview after setup failure: {}",
825
- e
826
- );
827
- }
828
- }
829
- }
830
- }
831
- }
832
-
833
- /// Handle coding agent completion
834
- async fn handle_coding_agent_completion(
835
- app_state: &AppState,
836
- task_attempt_id: Uuid,
837
- execution_process_id: Uuid,
838
- execution_process: ExecutionProcess,
839
- success: bool,
840
- exit_code: Option<i64>,
841
- ) {
842
- // Extract and store assistant message from execution logs
843
- let summary = if let Some(stdout) = &execution_process.stdout {
844
- if let Some(assistant_message) = crate::executor::parse_assistant_message_from_logs(stdout)
845
- {
846
- if let Err(e) = crate::models::executor_session::ExecutorSession::update_summary(
847
- &app_state.db_pool,
848
- execution_process_id,
849
- &assistant_message,
850
- )
851
- .await
852
- {
853
- tracing::error!(
854
- "Failed to update summary for execution process {}: {}",
855
- execution_process_id,
856
- e
857
- );
858
- None
859
- } else {
860
- tracing::info!(
861
- "Successfully stored summary for execution process {}",
862
- execution_process_id
863
- );
864
- Some(assistant_message)
865
- }
866
- } else {
867
- None
868
- }
869
- } else {
870
- None
871
- };
872
-
873
- // Note: Notifications and status updates moved to cleanup completion handler
874
- // to ensure they only fire after all processing (including cleanup) is complete
875
-
876
- // Get task attempt to access worktree path for committing changes
877
- if let Ok(Some(task_attempt)) =
878
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
879
- {
880
- // Commit any unstaged changes after execution completion
881
- if let Err(e) = commit_execution_changes(
882
- &task_attempt.worktree_path,
883
- task_attempt_id,
884
- summary.as_deref(),
885
- )
886
- .await
887
- {
888
- tracing::error!(
889
- "Failed to commit execution changes for attempt {}: {}",
890
- task_attempt_id,
891
- e
892
- );
893
- } else {
894
- tracing::info!(
895
- "Successfully committed execution changes for attempt {}",
896
- task_attempt_id
897
- );
898
- }
899
-
900
- // Coding agent execution completed
901
- tracing::info!(
902
- "Task attempt {} set to paused after coding agent completion",
903
- task_attempt_id
904
- );
905
-
906
- // Run cleanup script if configured, otherwise immediately finalize task
907
- if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await {
908
- // Check if cleanup script should run
909
- let should_run_cleanup = if let Ok(Some(project)) =
910
- crate::models::project::Project::find_by_id(&app_state.db_pool, task.project_id)
911
- .await
912
- {
913
- project
914
- .cleanup_script
915
- .as_ref()
916
- .map(|script| !script.trim().is_empty())
917
- .unwrap_or(false)
918
- } else {
919
- false
920
- };
921
-
922
- if should_run_cleanup {
923
- // Run cleanup script - completion will be handled in cleanup completion handler
924
- if let Err(e) =
925
- crate::services::process_service::ProcessService::run_cleanup_script_if_configured(
926
- &app_state.db_pool,
927
- app_state,
928
- task_attempt_id,
929
- task_attempt.task_id,
930
- task.project_id,
931
- )
932
- .await
933
- {
934
- tracing::error!(
935
- "Failed to run cleanup script for attempt {}: {}",
936
- task_attempt_id,
937
- e
938
- );
939
- // Even if cleanup fails to start, finalize the task
940
- finalize_task_completion(app_state, task_attempt_id, &task, success, exit_code).await;
941
- }
942
- } else {
943
- // No cleanup script configured, immediately finalize task
944
- finalize_task_completion(app_state, task_attempt_id, &task, success, exit_code)
945
- .await;
946
- }
947
- }
948
- } else {
949
- tracing::error!(
950
- "Failed to find task attempt {} for coding agent completion",
951
- task_attempt_id
952
- );
953
- }
954
- }
955
-
956
- /// Finalize task completion with notifications and status updates
957
- async fn finalize_task_completion(
958
- app_state: &AppState,
959
- task_attempt_id: Uuid,
960
- task: &crate::models::task::Task,
961
- success: bool,
962
- exit_code: Option<i64>,
963
- ) {
964
- // Send notifications if enabled
965
- let sound_enabled = app_state.get_sound_alerts_enabled().await;
966
- let push_enabled = app_state.get_push_notifications_enabled().await;
967
-
968
- if sound_enabled || push_enabled {
969
- let sound_file = app_state.get_sound_file().await;
970
- let notification_config = NotificationConfig {
971
- sound_enabled,
972
- push_enabled,
973
- };
974
-
975
- let notification_service = NotificationService::new(notification_config);
976
-
977
- // Get task attempt for notification details
978
- if let Ok(Some(task_attempt)) =
979
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
980
- {
981
- let title = format!("Task Complete: {}", task.title);
982
- let message = if success {
983
- format!(
984
- "✅ '{}' completed successfully\nBranch: {}\nExecutor: {}",
985
- task.title,
986
- task_attempt.branch,
987
- task_attempt.executor.as_deref().unwrap_or("default")
988
- )
989
- } else {
990
- format!(
991
- "❌ '{}' execution failed\nBranch: {}\nExecutor: {}",
992
- task.title,
993
- task_attempt.branch,
994
- task_attempt.executor.as_deref().unwrap_or("default")
995
- )
996
- };
997
-
998
- notification_service
999
- .notify(&title, &message, &sound_file)
1000
- .await;
1001
- }
1002
- }
1003
-
1004
- // Track analytics event
1005
- app_state
1006
- .track_analytics_event(
1007
- "task_attempt_finished",
1008
- Some(serde_json::json!({
1009
- "task_id": task.id.to_string(),
1010
- "project_id": task.project_id.to_string(),
1011
- "attempt_id": task_attempt_id.to_string(),
1012
- "execution_success": success,
1013
- "exit_code": exit_code,
1014
- })),
1015
- )
1016
- .await;
1017
-
1018
- // Update task status to InReview
1019
- if let Err(e) = Task::update_status(
1020
- &app_state.db_pool,
1021
- task.id,
1022
- task.project_id,
1023
- TaskStatus::InReview,
1024
- )
1025
- .await
1026
- {
1027
- tracing::error!(
1028
- "Failed to update task status to InReview for completed attempt: {}",
1029
- e
1030
- );
1031
- }
1032
- }
1033
-
1034
- /// Handle cleanup script completion
1035
- async fn handle_cleanup_completion(
1036
- app_state: &AppState,
1037
- task_attempt_id: Uuid,
1038
- execution_process_id: Uuid,
1039
- _execution_process: ExecutionProcess,
1040
- success: bool,
1041
- exit_code: Option<i64>,
1042
- ) {
1043
- let exit_text = if let Some(code) = exit_code {
1044
- format!(" with exit code {}", code)
1045
- } else {
1046
- String::new()
1047
- };
1048
-
1049
- tracing::info!(
1050
- "Cleanup script for task attempt {} completed{}",
1051
- task_attempt_id,
1052
- exit_text
1053
- );
1054
-
1055
- // Update execution process status
1056
- let process_status = if success {
1057
- ExecutionProcessStatus::Completed
1058
- } else {
1059
- ExecutionProcessStatus::Failed
1060
- };
1061
-
1062
- if let Err(e) = ExecutionProcess::update_completion(
1063
- &app_state.db_pool,
1064
- execution_process_id,
1065
- process_status,
1066
- exit_code,
1067
- )
1068
- .await
1069
- {
1070
- tracing::error!(
1071
- "Failed to update cleanup script execution process status: {}",
1072
- e
1073
- );
1074
- }
1075
-
1076
- // Auto-commit changes after successful cleanup script execution
1077
- if success {
1078
- if let Ok(Some(task_attempt)) =
1079
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
1080
- {
1081
- let commit_message = "Cleanup script";
1082
-
1083
- if let Err(e) = commit_execution_changes(
1084
- &task_attempt.worktree_path,
1085
- task_attempt_id,
1086
- Some(commit_message),
1087
- )
1088
- .await
1089
- {
1090
- tracing::error!(
1091
- "Failed to commit changes after cleanup script for attempt {}: {}",
1092
- task_attempt_id,
1093
- e
1094
- );
1095
- } else {
1096
- tracing::info!(
1097
- "Successfully committed changes after cleanup script for attempt {}",
1098
- task_attempt_id
1099
- );
1100
- }
1101
- } else {
1102
- tracing::error!(
1103
- "Failed to retrieve task attempt {} for cleanup commit",
1104
- task_attempt_id
1105
- );
1106
- }
1107
- }
1108
-
1109
- // Finalize task completion after cleanup (whether successful or failed)
1110
- if let Ok(Some(task_attempt)) =
1111
- TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
1112
- {
1113
- if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await {
1114
- // Get the coding agent execution process to determine original success status
1115
- let coding_success = if let Ok(processes) =
1116
- ExecutionProcess::find_by_task_attempt_id(&app_state.db_pool, task_attempt_id).await
1117
- {
1118
- // Find the most recent completed coding agent process
1119
- processes
1120
- .iter()
1121
- .filter(|p| {
1122
- p.process_type
1123
- == crate::models::execution_process::ExecutionProcessType::CodingAgent
1124
- })
1125
- .filter(|p| {
1126
- p.status
1127
- == crate::models::execution_process::ExecutionProcessStatus::Completed
1128
- })
1129
- .next_back()
1130
- .map(|p| p.exit_code == Some(0))
1131
- .unwrap_or(false)
1132
- } else {
1133
- false
1134
- };
1135
-
1136
- finalize_task_completion(app_state, task_attempt_id, &task, coding_success, exit_code)
1137
- .await;
1138
- } else {
1139
- tracing::error!(
1140
- "Failed to retrieve task {} for cleanup completion finalization",
1141
- task_attempt.task_id
1142
- );
1143
- }
1144
- } else {
1145
- tracing::error!(
1146
- "Failed to retrieve task attempt {} for cleanup completion finalization",
1147
- task_attempt_id
1148
- );
1149
- }
1150
- }
1151
-
1152
- /// Handle dev server completion (future functionality)
1153
- async fn handle_dev_server_completion(
1154
- app_state: &AppState,
1155
- task_attempt_id: Uuid,
1156
- execution_process_id: Uuid,
1157
- _execution_process: ExecutionProcess,
1158
- success: bool,
1159
- exit_code: Option<i64>,
1160
- ) {
1161
- let exit_text = if let Some(code) = exit_code {
1162
- format!(" with exit code {}", code)
1163
- } else {
1164
- String::new()
1165
- };
1166
-
1167
- tracing::info!(
1168
- "Dev server for task attempt {} completed{}",
1169
- task_attempt_id,
1170
- exit_text
1171
- );
1172
-
1173
- // Update execution process status instead of creating activity
1174
- let process_status = if success {
1175
- ExecutionProcessStatus::Completed
1176
- } else {
1177
- ExecutionProcessStatus::Failed
1178
- };
1179
-
1180
- if let Err(e) = ExecutionProcess::update_completion(
1181
- &app_state.db_pool,
1182
- execution_process_id,
1183
- process_status,
1184
- exit_code,
1185
- )
1186
- .await
1187
- {
1188
- tracing::error!(
1189
- "Failed to update dev server execution process status: {}",
1190
- e
1191
- );
1192
- }
1193
- }