automagik-forge 0.1.11 → 0.1.13

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/.cargo/config.toml +13 -0
  2. package/.claude/commands/commit.md +376 -0
  3. package/.claude/commands/prompt.md +871 -0
  4. package/.env.example +20 -0
  5. package/.github/actions/setup-node/action.yml +29 -0
  6. package/.github/images/automagik-logo.png +0 -0
  7. package/.github/workflows/pre-release.yml +470 -0
  8. package/.github/workflows/publish.yml +145 -0
  9. package/.github/workflows/test.yml +63 -0
  10. package/.mcp.json +57 -0
  11. package/AGENT.md +40 -0
  12. package/CLAUDE.md +40 -0
  13. package/CODE-OF-CONDUCT.md +89 -0
  14. package/Cargo.toml +19 -0
  15. package/Dockerfile +43 -0
  16. package/LICENSE +201 -0
  17. package/Makefile +97 -0
  18. package/README.md +447 -143
  19. package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +12 -0
  20. package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +12 -0
  21. package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +12 -0
  22. package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +68 -0
  23. package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +12 -0
  24. package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +12 -0
  25. package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +12 -0
  26. package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +104 -0
  27. package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +12 -0
  28. package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +32 -0
  29. package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +56 -0
  30. package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +68 -0
  31. package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +92 -0
  32. package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +12 -0
  33. package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +56 -0
  34. package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +62 -0
  35. package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +62 -0
  36. package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +56 -0
  37. package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +32 -0
  38. package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +104 -0
  39. package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +62 -0
  40. package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +56 -0
  41. package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +92 -0
  42. package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +62 -0
  43. package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +12 -0
  44. package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +104 -0
  45. package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +104 -0
  46. package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +68 -0
  47. package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +62 -0
  48. package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +62 -0
  49. package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +62 -0
  50. package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +50 -0
  51. package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +68 -0
  52. package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +12 -0
  53. package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +68 -0
  54. package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +12 -0
  55. package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +12 -0
  56. package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +62 -0
  57. package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +104 -0
  58. package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +20 -0
  59. package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +104 -0
  60. package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +56 -0
  61. package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +104 -0
  62. package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +26 -0
  63. package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +62 -0
  64. package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +12 -0
  65. package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +104 -0
  66. package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +104 -0
  67. package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +12 -0
  68. package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +68 -0
  69. package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +62 -0
  70. package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +20 -0
  71. package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +12 -0
  72. package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +12 -0
  73. package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +12 -0
  74. package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +20 -0
  75. package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +12 -0
  76. package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +12 -0
  77. package/backend/Cargo.toml +71 -0
  78. package/backend/build.rs +32 -0
  79. package/backend/migrations/20250617183714_init.sql +44 -0
  80. package/backend/migrations/20250620212427_execution_processes.sql +25 -0
  81. package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +28 -0
  82. package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +23 -0
  83. package/backend/migrations/20250623120000_executor_sessions.sql +17 -0
  84. package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +4 -0
  85. package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +4 -0
  86. package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +2 -0
  87. package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +5 -0
  88. package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +2 -0
  89. package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +2 -0
  90. package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +2 -0
  91. package/backend/migrations/20250710000000_add_setup_completion.sql +3 -0
  92. package/backend/migrations/20250715154859_add_task_templates.sql +25 -0
  93. package/backend/migrations/20250716143725_add_default_templates.sql +174 -0
  94. package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +20 -0
  95. package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +7 -0
  96. package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +9 -0
  97. package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +2 -0
  98. package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +25 -0
  99. package/backend/migrations/20250723000000_add_wish_to_tasks.sql +7 -0
  100. package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +5 -0
  101. package/backend/scripts/toast-notification.ps1 +23 -0
  102. package/backend/sounds/abstract-sound1.wav +0 -0
  103. package/backend/sounds/abstract-sound2.wav +0 -0
  104. package/backend/sounds/abstract-sound3.wav +0 -0
  105. package/backend/sounds/abstract-sound4.wav +0 -0
  106. package/backend/sounds/cow-mooing.wav +0 -0
  107. package/backend/sounds/phone-vibration.wav +0 -0
  108. package/backend/sounds/rooster.wav +0 -0
  109. package/backend/src/app_state.rs +218 -0
  110. package/backend/src/bin/generate_types.rs +189 -0
  111. package/backend/src/bin/mcp_task_server.rs +191 -0
  112. package/backend/src/execution_monitor.rs +1193 -0
  113. package/backend/src/executor.rs +1053 -0
  114. package/backend/src/executors/amp.rs +697 -0
  115. package/backend/src/executors/ccr.rs +91 -0
  116. package/backend/src/executors/charm_opencode.rs +113 -0
  117. package/backend/src/executors/claude.rs +887 -0
  118. package/backend/src/executors/cleanup_script.rs +124 -0
  119. package/backend/src/executors/dev_server.rs +53 -0
  120. package/backend/src/executors/echo.rs +79 -0
  121. package/backend/src/executors/gemini/config.rs +67 -0
  122. package/backend/src/executors/gemini/streaming.rs +363 -0
  123. package/backend/src/executors/gemini.rs +765 -0
  124. package/backend/src/executors/mod.rs +23 -0
  125. package/backend/src/executors/opencode_ai.rs +113 -0
  126. package/backend/src/executors/setup_script.rs +130 -0
  127. package/backend/src/executors/sst_opencode/filter.rs +184 -0
  128. package/backend/src/executors/sst_opencode/tools.rs +139 -0
  129. package/backend/src/executors/sst_opencode.rs +756 -0
  130. package/backend/src/lib.rs +45 -0
  131. package/backend/src/main.rs +324 -0
  132. package/backend/src/mcp/mod.rs +1 -0
  133. package/backend/src/mcp/task_server.rs +850 -0
  134. package/backend/src/middleware/mod.rs +3 -0
  135. package/backend/src/middleware/model_loaders.rs +242 -0
  136. package/backend/src/models/api_response.rs +36 -0
  137. package/backend/src/models/config.rs +375 -0
  138. package/backend/src/models/execution_process.rs +430 -0
  139. package/backend/src/models/executor_session.rs +225 -0
  140. package/backend/src/models/mod.rs +12 -0
  141. package/backend/src/models/project.rs +356 -0
  142. package/backend/src/models/task.rs +345 -0
  143. package/backend/src/models/task_attempt.rs +1214 -0
  144. package/backend/src/models/task_template.rs +146 -0
  145. package/backend/src/openapi.rs +93 -0
  146. package/backend/src/routes/auth.rs +297 -0
  147. package/backend/src/routes/config.rs +385 -0
  148. package/backend/src/routes/filesystem.rs +228 -0
  149. package/backend/src/routes/health.rs +16 -0
  150. package/backend/src/routes/mod.rs +9 -0
  151. package/backend/src/routes/projects.rs +562 -0
  152. package/backend/src/routes/stream.rs +244 -0
  153. package/backend/src/routes/task_attempts.rs +1172 -0
  154. package/backend/src/routes/task_templates.rs +229 -0
  155. package/backend/src/routes/tasks.rs +353 -0
  156. package/backend/src/services/analytics.rs +216 -0
  157. package/backend/src/services/git_service.rs +1321 -0
  158. package/backend/src/services/github_service.rs +307 -0
  159. package/backend/src/services/mod.rs +13 -0
  160. package/backend/src/services/notification_service.rs +263 -0
  161. package/backend/src/services/pr_monitor.rs +214 -0
  162. package/backend/src/services/process_service.rs +940 -0
  163. package/backend/src/utils/path.rs +96 -0
  164. package/backend/src/utils/shell.rs +19 -0
  165. package/backend/src/utils/text.rs +24 -0
  166. package/backend/src/utils/worktree_manager.rs +578 -0
  167. package/backend/src/utils.rs +125 -0
  168. package/backend/test.db +0 -0
  169. package/build-npm-package.sh +61 -0
  170. package/dev_assets_seed/config.json +19 -0
  171. package/frontend/.eslintrc.json +25 -0
  172. package/frontend/.prettierrc.json +8 -0
  173. package/frontend/components.json +17 -0
  174. package/frontend/index.html +19 -0
  175. package/frontend/package-lock.json +7321 -0
  176. package/frontend/package.json +61 -0
  177. package/frontend/postcss.config.js +6 -0
  178. package/frontend/public/android-chrome-192x192.png +0 -0
  179. package/frontend/public/android-chrome-512x512.png +0 -0
  180. package/frontend/public/apple-touch-icon.png +0 -0
  181. package/frontend/public/automagik-forge-logo-dark.svg +3 -0
  182. package/frontend/public/automagik-forge-logo.svg +3 -0
  183. package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
  184. package/frontend/public/favicon-16x16.png +0 -0
  185. package/frontend/public/favicon-32x32.png +0 -0
  186. package/frontend/public/favicon.ico +0 -0
  187. package/frontend/public/site.webmanifest +1 -0
  188. package/frontend/public/viba-kanban-favicon.png +0 -0
  189. package/frontend/src/App.tsx +157 -0
  190. package/frontend/src/components/DisclaimerDialog.tsx +106 -0
  191. package/frontend/src/components/GitHubLoginDialog.tsx +314 -0
  192. package/frontend/src/components/OnboardingDialog.tsx +185 -0
  193. package/frontend/src/components/PrivacyOptInDialog.tsx +130 -0
  194. package/frontend/src/components/ProvidePatDialog.tsx +98 -0
  195. package/frontend/src/components/TaskTemplateManager.tsx +336 -0
  196. package/frontend/src/components/config-provider.tsx +119 -0
  197. package/frontend/src/components/context/TaskDetailsContextProvider.tsx +470 -0
  198. package/frontend/src/components/context/taskDetailsContext.ts +125 -0
  199. package/frontend/src/components/keyboard-shortcuts-demo.tsx +35 -0
  200. package/frontend/src/components/layout/navbar.tsx +86 -0
  201. package/frontend/src/components/logo.tsx +44 -0
  202. package/frontend/src/components/projects/ProjectCard.tsx +155 -0
  203. package/frontend/src/components/projects/project-detail.tsx +251 -0
  204. package/frontend/src/components/projects/project-form-fields.tsx +238 -0
  205. package/frontend/src/components/projects/project-form.tsx +301 -0
  206. package/frontend/src/components/projects/project-list.tsx +200 -0
  207. package/frontend/src/components/projects/projects-page.tsx +20 -0
  208. package/frontend/src/components/tasks/BranchSelector.tsx +169 -0
  209. package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +94 -0
  210. package/frontend/src/components/tasks/EditorSelectionDialog.tsx +119 -0
  211. package/frontend/src/components/tasks/TaskCard.tsx +154 -0
  212. package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +33 -0
  213. package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +109 -0
  214. package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +135 -0
  215. package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +296 -0
  216. package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +32 -0
  217. package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +392 -0
  218. package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +256 -0
  219. package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +56 -0
  220. package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +92 -0
  221. package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +22 -0
  222. package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +49 -0
  223. package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +186 -0
  224. package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +288 -0
  225. package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +216 -0
  226. package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +93 -0
  227. package/frontend/src/components/tasks/TaskDetailsHeader.tsx +169 -0
  228. package/frontend/src/components/tasks/TaskDetailsPanel.tsx +126 -0
  229. package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +302 -0
  230. package/frontend/src/components/tasks/TaskFollowUpSection.tsx +130 -0
  231. package/frontend/src/components/tasks/TaskFormDialog.tsx +400 -0
  232. package/frontend/src/components/tasks/TaskKanbanBoard.tsx +180 -0
  233. package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +259 -0
  234. package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +243 -0
  235. package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +899 -0
  236. package/frontend/src/components/tasks/index.ts +2 -0
  237. package/frontend/src/components/theme-provider.tsx +82 -0
  238. package/frontend/src/components/theme-toggle.tsx +36 -0
  239. package/frontend/src/components/ui/alert.tsx +59 -0
  240. package/frontend/src/components/ui/auto-expanding-textarea.tsx +70 -0
  241. package/frontend/src/components/ui/badge.tsx +36 -0
  242. package/frontend/src/components/ui/button.tsx +56 -0
  243. package/frontend/src/components/ui/card.tsx +86 -0
  244. package/frontend/src/components/ui/checkbox.tsx +44 -0
  245. package/frontend/src/components/ui/chip.tsx +25 -0
  246. package/frontend/src/components/ui/dialog.tsx +124 -0
  247. package/frontend/src/components/ui/dropdown-menu.tsx +198 -0
  248. package/frontend/src/components/ui/file-search-textarea.tsx +292 -0
  249. package/frontend/src/components/ui/folder-picker.tsx +279 -0
  250. package/frontend/src/components/ui/input.tsx +25 -0
  251. package/frontend/src/components/ui/label.tsx +24 -0
  252. package/frontend/src/components/ui/loader.tsx +26 -0
  253. package/frontend/src/components/ui/markdown-renderer.tsx +75 -0
  254. package/frontend/src/components/ui/select.tsx +160 -0
  255. package/frontend/src/components/ui/separator.tsx +31 -0
  256. package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +185 -0
  257. package/frontend/src/components/ui/table.tsx +117 -0
  258. package/frontend/src/components/ui/tabs.tsx +53 -0
  259. package/frontend/src/components/ui/textarea.tsx +22 -0
  260. package/frontend/src/components/ui/tooltip.tsx +28 -0
  261. package/frontend/src/hooks/useNormalizedConversation.ts +440 -0
  262. package/frontend/src/index.css +225 -0
  263. package/frontend/src/lib/api.ts +630 -0
  264. package/frontend/src/lib/keyboard-shortcuts.ts +266 -0
  265. package/frontend/src/lib/responsive-config.ts +70 -0
  266. package/frontend/src/lib/types.ts +39 -0
  267. package/frontend/src/lib/utils.ts +10 -0
  268. package/frontend/src/main.tsx +50 -0
  269. package/frontend/src/pages/McpServers.tsx +418 -0
  270. package/frontend/src/pages/Settings.tsx +610 -0
  271. package/frontend/src/pages/project-tasks.tsx +575 -0
  272. package/frontend/src/pages/projects.tsx +18 -0
  273. package/frontend/src/vite-env.d.ts +1 -0
  274. package/frontend/tailwind.config.js +125 -0
  275. package/frontend/tsconfig.json +26 -0
  276. package/frontend/tsconfig.node.json +10 -0
  277. package/frontend/vite.config.ts +33 -0
  278. package/npx-cli/README.md +159 -0
  279. package/npx-cli/automagik-forge-0.0.55.tgz +0 -0
  280. package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
  281. package/{dist/linux-x64/automagik-forge.zip → npx-cli/automagik-forge-0.1.10.tgz} +0 -0
  282. package/npx-cli/package.json +17 -0
  283. package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
  284. package/package.json +23 -13
  285. package/pnpm-workspace.yaml +2 -0
  286. package/rust-toolchain.toml +11 -0
  287. package/rustfmt.toml +3 -0
  288. package/scripts/load-env.js +43 -0
  289. package/scripts/mcp_test.js +374 -0
  290. package/scripts/prepare-db.js +45 -0
  291. package/scripts/setup-dev-environment.js +274 -0
  292. package/scripts/start-mcp-sse.js +70 -0
  293. package/scripts/test-debug.js +32 -0
  294. package/scripts/test-mcp-sse.js +138 -0
  295. package/scripts/test-simple.js +44 -0
  296. package/scripts/test-wish-final.js +179 -0
  297. package/scripts/test-wish-system.js +221 -0
  298. package/shared/types.ts +182 -0
  299. package/test-npm-package.sh +42 -0
  300. package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
  301. /package/{bin → npx-cli/bin}/cli.js +0 -0
@@ -0,0 +1,229 @@
1
+ use axum::{
2
+ extract::{Path, State},
3
+ http::StatusCode,
4
+ response::IntoResponse,
5
+ Extension, Json,
6
+ };
7
+ use utoipa;
8
+ use uuid::Uuid;
9
+
10
+ use crate::{
11
+ app_state::AppState,
12
+ models::{
13
+ api_response::ApiResponse,
14
+ task_template::{CreateTaskTemplate, TaskTemplate, UpdateTaskTemplate},
15
+ },
16
+ };
17
+
18
+ #[utoipa::path(
19
+ get,
20
+ path = "/api/task-templates",
21
+ responses(
22
+ (status = 200, description = "List all task templates", body = ApiResponse<Vec<TaskTemplate>>),
23
+ (status = 500, description = "Internal server error")
24
+ ),
25
+ tag = "task_templates"
26
+ )]
27
+ pub async fn list_templates(
28
+ State(state): State<AppState>,
29
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
30
+ match TaskTemplate::find_all(&state.db_pool).await {
31
+ Ok(templates) => Ok(Json(ApiResponse::success(templates))),
32
+ Err(e) => Err((
33
+ StatusCode::INTERNAL_SERVER_ERROR,
34
+ Json(ApiResponse::error(&format!(
35
+ "Failed to fetch templates: {}",
36
+ e
37
+ ))),
38
+ )),
39
+ }
40
+ }
41
+
42
+ #[utoipa::path(
43
+ get,
44
+ path = "/api/projects/{project_id}/task-templates",
45
+ params(
46
+ ("project_id" = String, Path, description = "Project ID")
47
+ ),
48
+ responses(
49
+ (status = 200, description = "List project-specific task templates", body = ApiResponse<Vec<TaskTemplate>>),
50
+ (status = 500, description = "Internal server error")
51
+ ),
52
+ tag = "task_templates"
53
+ )]
54
+ pub async fn list_project_templates(
55
+ State(state): State<AppState>,
56
+ Path(project_id): Path<Uuid>,
57
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
58
+ match TaskTemplate::find_by_project_id(&state.db_pool, Some(project_id)).await {
59
+ Ok(templates) => Ok(Json(ApiResponse::success(templates))),
60
+ Err(e) => Err((
61
+ StatusCode::INTERNAL_SERVER_ERROR,
62
+ Json(ApiResponse::error(&format!(
63
+ "Failed to fetch templates: {}",
64
+ e
65
+ ))),
66
+ )),
67
+ }
68
+ }
69
+
70
+ #[utoipa::path(
71
+ get,
72
+ path = "/api/task-templates/global",
73
+ responses(
74
+ (status = 200, description = "List global task templates", body = ApiResponse<Vec<TaskTemplate>>),
75
+ (status = 500, description = "Internal server error")
76
+ ),
77
+ tag = "task_templates"
78
+ )]
79
+ pub async fn list_global_templates(
80
+ State(state): State<AppState>,
81
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
82
+ match TaskTemplate::find_by_project_id(&state.db_pool, None).await {
83
+ Ok(templates) => Ok(Json(ApiResponse::success(templates))),
84
+ Err(e) => Err((
85
+ StatusCode::INTERNAL_SERVER_ERROR,
86
+ Json(ApiResponse::error(&format!(
87
+ "Failed to fetch global templates: {}",
88
+ e
89
+ ))),
90
+ )),
91
+ }
92
+ }
93
+
94
+ #[utoipa::path(
95
+ get,
96
+ path = "/api/task-templates/{template_id}",
97
+ params(
98
+ ("template_id" = String, Path, description = "Template ID")
99
+ ),
100
+ responses(
101
+ (status = 200, description = "Get task template by ID", body = ApiResponse<TaskTemplate>),
102
+ (status = 404, description = "Template not found")
103
+ ),
104
+ tag = "task_templates"
105
+ )]
106
+ pub async fn get_template(
107
+ Extension(template): Extension<TaskTemplate>,
108
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
109
+ Ok(Json(ApiResponse::success(template)))
110
+ }
111
+
112
+ #[utoipa::path(
113
+ post,
114
+ path = "/api/task-templates",
115
+ request_body = CreateTaskTemplate,
116
+ responses(
117
+ (status = 201, description = "Task template created successfully", body = ApiResponse<TaskTemplate>),
118
+ (status = 409, description = "Template name already exists"),
119
+ (status = 500, description = "Internal server error")
120
+ ),
121
+ tag = "task_templates"
122
+ )]
123
+ pub async fn create_template(
124
+ State(state): State<AppState>,
125
+ Json(payload): Json<CreateTaskTemplate>,
126
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
127
+ match TaskTemplate::create(&state.db_pool, &payload).await {
128
+ Ok(template) => Ok((StatusCode::CREATED, Json(ApiResponse::success(template)))),
129
+ Err(e) => {
130
+ if e.to_string().contains("UNIQUE constraint failed") {
131
+ Err((
132
+ StatusCode::CONFLICT,
133
+ Json(ApiResponse::error(
134
+ "A template with this name already exists in this scope",
135
+ )),
136
+ ))
137
+ } else {
138
+ Err((
139
+ StatusCode::INTERNAL_SERVER_ERROR,
140
+ Json(ApiResponse::error(&format!(
141
+ "Failed to create template: {}",
142
+ e
143
+ ))),
144
+ ))
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ #[utoipa::path(
151
+ put,
152
+ path = "/api/task-templates/{template_id}",
153
+ params(
154
+ ("template_id" = String, Path, description = "Template ID")
155
+ ),
156
+ request_body = UpdateTaskTemplate,
157
+ responses(
158
+ (status = 200, description = "Task template updated successfully", body = ApiResponse<TaskTemplate>),
159
+ (status = 404, description = "Template not found"),
160
+ (status = 409, description = "Template name already exists"),
161
+ (status = 500, description = "Internal server error")
162
+ ),
163
+ tag = "task_templates"
164
+ )]
165
+ pub async fn update_template(
166
+ Extension(template): Extension<TaskTemplate>,
167
+ State(state): State<AppState>,
168
+ Json(payload): Json<UpdateTaskTemplate>,
169
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
170
+ match TaskTemplate::update(&state.db_pool, template.id, &payload).await {
171
+ Ok(template) => Ok(Json(ApiResponse::success(template))),
172
+ Err(e) => {
173
+ if matches!(e, sqlx::Error::RowNotFound) {
174
+ Err((
175
+ StatusCode::NOT_FOUND,
176
+ Json(ApiResponse::error("Template not found")),
177
+ ))
178
+ } else if e.to_string().contains("UNIQUE constraint failed") {
179
+ Err((
180
+ StatusCode::CONFLICT,
181
+ Json(ApiResponse::error(
182
+ "A template with this name already exists in this scope",
183
+ )),
184
+ ))
185
+ } else {
186
+ Err((
187
+ StatusCode::INTERNAL_SERVER_ERROR,
188
+ Json(ApiResponse::error(&format!(
189
+ "Failed to update template: {}",
190
+ e
191
+ ))),
192
+ ))
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ #[utoipa::path(
199
+ delete,
200
+ path = "/api/task-templates/{template_id}",
201
+ params(
202
+ ("template_id" = String, Path, description = "Template ID")
203
+ ),
204
+ responses(
205
+ (status = 200, description = "Task template deleted successfully"),
206
+ (status = 404, description = "Template not found"),
207
+ (status = 500, description = "Internal server error")
208
+ ),
209
+ tag = "task_templates"
210
+ )]
211
+ pub async fn delete_template(
212
+ Extension(template): Extension<TaskTemplate>,
213
+ State(state): State<AppState>,
214
+ ) -> Result<impl IntoResponse, (StatusCode, Json<ApiResponse<()>>)> {
215
+ match TaskTemplate::delete(&state.db_pool, template.id).await {
216
+ Ok(0) => Err((
217
+ StatusCode::NOT_FOUND,
218
+ Json(ApiResponse::error("Template not found")),
219
+ )),
220
+ Ok(_) => Ok(Json(ApiResponse::success(()))),
221
+ Err(e) => Err((
222
+ StatusCode::INTERNAL_SERVER_ERROR,
223
+ Json(ApiResponse::error(&format!(
224
+ "Failed to delete template: {}",
225
+ e
226
+ ))),
227
+ )),
228
+ }
229
+ }
@@ -0,0 +1,353 @@
1
+ use axum::{
2
+ extract::State, http::StatusCode, response::Json as ResponseJson, routing::get, Extension,
3
+ Json, Router,
4
+ };
5
+ use utoipa;
6
+ use uuid::Uuid;
7
+
8
+ use crate::{
9
+ app_state::AppState,
10
+ execution_monitor,
11
+ models::{
12
+ project::Project,
13
+ task::{CreateTask, CreateTaskAndStart, Task, TaskWithAttemptStatus, UpdateTask},
14
+ task_attempt::{CreateTaskAttempt, TaskAttempt},
15
+ ApiResponse,
16
+ },
17
+ };
18
+
19
+ #[utoipa::path(
20
+ get,
21
+ path = "/api/projects/{project_id}/tasks",
22
+ params(
23
+ ("project_id" = String, Path, description = "Project ID")
24
+ ),
25
+ responses(
26
+ (status = 200, description = "List all tasks for a project", body = ApiResponse<Vec<TaskWithAttemptStatus>>),
27
+ (status = 404, description = "Project not found"),
28
+ (status = 500, description = "Internal server error")
29
+ ),
30
+ tag = "tasks"
31
+ )]
32
+ pub async fn get_project_tasks(
33
+ Extension(project): Extension<Project>,
34
+ State(app_state): State<AppState>,
35
+ ) -> Result<ResponseJson<ApiResponse<Vec<TaskWithAttemptStatus>>>, StatusCode> {
36
+ match Task::find_by_project_id_with_attempt_status(&app_state.db_pool, project.id).await {
37
+ Ok(tasks) => Ok(ResponseJson(ApiResponse::success(tasks))),
38
+ Err(e) => {
39
+ tracing::error!("Failed to fetch tasks for project {}: {}", project.id, e);
40
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
41
+ }
42
+ }
43
+ }
44
+
45
+ #[utoipa::path(
46
+ get,
47
+ path = "/api/projects/{project_id}/tasks/{task_id}",
48
+ params(
49
+ ("project_id" = String, Path, description = "Project ID"),
50
+ ("task_id" = String, Path, description = "Task ID")
51
+ ),
52
+ responses(
53
+ (status = 200, description = "Get task by ID", body = ApiResponse<Task>),
54
+ (status = 404, description = "Task not found"),
55
+ (status = 500, description = "Internal server error")
56
+ ),
57
+ tag = "tasks"
58
+ )]
59
+ pub async fn get_task(
60
+ Extension(task): Extension<Task>,
61
+ ) -> Result<ResponseJson<ApiResponse<Task>>, StatusCode> {
62
+ Ok(ResponseJson(ApiResponse::success(task)))
63
+ }
64
+
65
+ #[utoipa::path(
66
+ post,
67
+ path = "/api/projects/{project_id}/tasks",
68
+ params(
69
+ ("project_id" = String, Path, description = "Project ID")
70
+ ),
71
+ request_body = CreateTask,
72
+ responses(
73
+ (status = 200, description = "Task created successfully", body = ApiResponse<Task>),
74
+ (status = 400, description = "Invalid input"),
75
+ (status = 404, description = "Project not found"),
76
+ (status = 500, description = "Internal server error")
77
+ ),
78
+ tag = "tasks"
79
+ )]
80
+ pub async fn create_task(
81
+ Extension(project): Extension<Project>,
82
+ State(app_state): State<AppState>,
83
+ Json(mut payload): Json<CreateTask>,
84
+ ) -> Result<ResponseJson<ApiResponse<Task>>, StatusCode> {
85
+ let id = Uuid::new_v4();
86
+
87
+ // Ensure the project_id in the payload matches the project from middleware
88
+ payload.project_id = project.id;
89
+
90
+ tracing::debug!(
91
+ "Creating task '{}' in project {}",
92
+ payload.title,
93
+ project.id
94
+ );
95
+
96
+ match Task::create(&app_state.db_pool, &payload, id).await {
97
+ Ok(task) => {
98
+ // Track task creation event
99
+ app_state
100
+ .track_analytics_event(
101
+ "task_created",
102
+ Some(serde_json::json!({
103
+ "task_id": task.id.to_string(),
104
+ "project_id": project.id.to_string(),
105
+ "has_description": task.description.is_some(),
106
+ })),
107
+ )
108
+ .await;
109
+
110
+ Ok(ResponseJson(ApiResponse::success(task)))
111
+ }
112
+ Err(e) => {
113
+ tracing::error!("Failed to create task: {}", e);
114
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
115
+ }
116
+ }
117
+ }
118
+
119
+ pub async fn create_task_and_start(
120
+ Extension(project): Extension<Project>,
121
+ State(app_state): State<AppState>,
122
+ Json(mut payload): Json<CreateTaskAndStart>,
123
+ ) -> Result<ResponseJson<ApiResponse<Task>>, StatusCode> {
124
+ let task_id = Uuid::new_v4();
125
+
126
+ // Ensure the project_id in the payload matches the project from middleware
127
+ payload.project_id = project.id;
128
+
129
+ tracing::debug!(
130
+ "Creating and starting task '{}' in project {}",
131
+ payload.title,
132
+ project.id
133
+ );
134
+
135
+ // Create the task first
136
+ let create_task_payload = CreateTask {
137
+ project_id: payload.project_id,
138
+ title: payload.title.clone(),
139
+ description: payload.description.clone(),
140
+ wish_id: payload.wish_id.clone(),
141
+ parent_task_attempt: payload.parent_task_attempt,
142
+ };
143
+ let task = match Task::create(&app_state.db_pool, &create_task_payload, task_id).await {
144
+ Ok(task) => task,
145
+ Err(e) => {
146
+ tracing::error!("Failed to create task: {}", e);
147
+ return Err(StatusCode::INTERNAL_SERVER_ERROR);
148
+ }
149
+ };
150
+
151
+ // Create task attempt
152
+ let executor_string = payload.executor.as_ref().map(|exec| exec.to_string());
153
+ let attempt_payload = CreateTaskAttempt {
154
+ executor: executor_string.clone(),
155
+ base_branch: None, // Not supported in task creation endpoint, only in task attempts
156
+ };
157
+
158
+ match TaskAttempt::create(&app_state.db_pool, &attempt_payload, task_id).await {
159
+ Ok(attempt) => {
160
+ app_state
161
+ .track_analytics_event(
162
+ "task_created",
163
+ Some(serde_json::json!({
164
+ "task_id": task.id.to_string(),
165
+ "project_id": project.id.to_string(),
166
+ "has_description": task.description.is_some(),
167
+ })),
168
+ )
169
+ .await;
170
+
171
+ app_state
172
+ .track_analytics_event(
173
+ "task_attempt_started",
174
+ Some(serde_json::json!({
175
+ "task_id": task.id.to_string(),
176
+ "executor_type": executor_string.as_deref().unwrap_or("default"),
177
+ "attempt_id": attempt.id.to_string(),
178
+ })),
179
+ )
180
+ .await;
181
+
182
+ // Start execution asynchronously (don't block the response)
183
+ let app_state_clone = app_state.clone();
184
+ let attempt_id = attempt.id;
185
+ tokio::spawn(async move {
186
+ if let Err(e) = TaskAttempt::start_execution(
187
+ &app_state_clone.db_pool,
188
+ &app_state_clone,
189
+ attempt_id,
190
+ task_id,
191
+ project.id,
192
+ )
193
+ .await
194
+ {
195
+ tracing::error!(
196
+ "Failed to start execution for task attempt {}: {}",
197
+ attempt_id,
198
+ e
199
+ );
200
+ }
201
+ });
202
+
203
+ Ok(ResponseJson(ApiResponse::success(task)))
204
+ }
205
+ Err(e) => {
206
+ tracing::error!("Failed to create task attempt: {}", e);
207
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
208
+ }
209
+ }
210
+ }
211
+
212
+ #[utoipa::path(
213
+ put,
214
+ path = "/api/projects/{project_id}/tasks/{task_id}",
215
+ params(
216
+ ("project_id" = String, Path, description = "Project ID"),
217
+ ("task_id" = String, Path, description = "Task ID")
218
+ ),
219
+ request_body = UpdateTask,
220
+ responses(
221
+ (status = 200, description = "Task updated successfully", body = ApiResponse<Task>),
222
+ (status = 404, description = "Task or project not found"),
223
+ (status = 400, description = "Invalid input"),
224
+ (status = 500, description = "Internal server error")
225
+ ),
226
+ tag = "tasks"
227
+ )]
228
+ pub async fn update_task(
229
+ Extension(project): Extension<Project>,
230
+ Extension(existing_task): Extension<Task>,
231
+ State(app_state): State<AppState>,
232
+ Json(payload): Json<UpdateTask>,
233
+ ) -> Result<ResponseJson<ApiResponse<Task>>, StatusCode> {
234
+ // Use existing values if not provided in update
235
+ let title = payload.title.unwrap_or(existing_task.title);
236
+ let description = payload.description.or(existing_task.description);
237
+ let status = payload.status.unwrap_or(existing_task.status);
238
+ let wish_id = payload.wish_id.unwrap_or(existing_task.wish_id);
239
+ let parent_task_attempt = payload
240
+ .parent_task_attempt
241
+ .or(existing_task.parent_task_attempt);
242
+
243
+ match Task::update(
244
+ &app_state.db_pool,
245
+ existing_task.id,
246
+ project.id,
247
+ title,
248
+ description,
249
+ status,
250
+ wish_id,
251
+ parent_task_attempt,
252
+ )
253
+ .await
254
+ {
255
+ Ok(task) => Ok(ResponseJson(ApiResponse::success(task))),
256
+ Err(e) => {
257
+ tracing::error!("Failed to update task: {}", e);
258
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
259
+ }
260
+ }
261
+ }
262
+
263
+ #[utoipa::path(
264
+ delete,
265
+ path = "/api/projects/{project_id}/tasks/{task_id}",
266
+ params(
267
+ ("project_id" = String, Path, description = "Project ID"),
268
+ ("task_id" = String, Path, description = "Task ID")
269
+ ),
270
+ responses(
271
+ (status = 200, description = "Task deleted successfully"),
272
+ (status = 404, description = "Task or project not found"),
273
+ (status = 500, description = "Internal server error")
274
+ ),
275
+ tag = "tasks"
276
+ )]
277
+ pub async fn delete_task(
278
+ Extension(project): Extension<Project>,
279
+ Extension(task): Extension<Task>,
280
+ State(app_state): State<AppState>,
281
+ ) -> Result<ResponseJson<ApiResponse<()>>, StatusCode> {
282
+ // Clean up all worktrees for this task before deletion
283
+ if let Err(e) = execution_monitor::cleanup_task_worktrees(&app_state.db_pool, task.id).await {
284
+ tracing::error!("Failed to cleanup worktrees for task {}: {}", task.id, e);
285
+ // Continue with deletion even if cleanup fails
286
+ }
287
+
288
+ // Clean up all executor sessions for this task before deletion
289
+ match TaskAttempt::find_by_task_id(&app_state.db_pool, task.id).await {
290
+ Ok(task_attempts) => {
291
+ for attempt in task_attempts {
292
+ if let Err(e) =
293
+ crate::models::executor_session::ExecutorSession::delete_by_task_attempt_id(
294
+ &app_state.db_pool,
295
+ attempt.id,
296
+ )
297
+ .await
298
+ {
299
+ tracing::error!(
300
+ "Failed to cleanup executor sessions for task attempt {}: {}",
301
+ attempt.id,
302
+ e
303
+ );
304
+ // Continue with deletion even if session cleanup fails
305
+ } else {
306
+ tracing::debug!(
307
+ "Cleaned up executor sessions for task attempt {}",
308
+ attempt.id
309
+ );
310
+ }
311
+ }
312
+ }
313
+ Err(e) => {
314
+ tracing::error!("Failed to get task attempts for session cleanup: {}", e);
315
+ // Continue with deletion even if we can't get task attempts
316
+ }
317
+ }
318
+
319
+ match Task::delete(&app_state.db_pool, task.id, project.id).await {
320
+ Ok(rows_affected) => {
321
+ if rows_affected == 0 {
322
+ Err(StatusCode::NOT_FOUND)
323
+ } else {
324
+ Ok(ResponseJson(ApiResponse::success(())))
325
+ }
326
+ }
327
+ Err(e) => {
328
+ tracing::error!("Failed to delete task: {}", e);
329
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
330
+ }
331
+ }
332
+ }
333
+
334
+ pub fn tasks_project_router() -> Router<AppState> {
335
+ use axum::routing::post;
336
+
337
+ Router::new()
338
+ .route(
339
+ "/projects/:project_id/tasks",
340
+ get(get_project_tasks).post(create_task),
341
+ )
342
+ .route(
343
+ "/projects/:project_id/tasks/create-and-start",
344
+ post(create_task_and_start),
345
+ )
346
+ }
347
+
348
+ pub fn tasks_with_id_router() -> Router<AppState> {
349
+ Router::new().route(
350
+ "/projects/:project_id/tasks/:task_id",
351
+ get(get_task).put(update_task).delete(delete_task),
352
+ )
353
+ }