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,562 @@
1
+ use std::collections::HashMap;
2
+
3
+ use axum::{
4
+ extract::{Query, State},
5
+ http::StatusCode,
6
+ response::Json as ResponseJson,
7
+ routing::get,
8
+ Extension, Json, Router,
9
+ };
10
+ use utoipa;
11
+ use uuid::Uuid;
12
+
13
+ use crate::{
14
+ app_state::AppState,
15
+ models::{
16
+ project::{
17
+ CreateBranch, CreateProject, GitBranch, Project, ProjectWithBranch, SearchMatchType,
18
+ SearchResult, UpdateProject,
19
+ },
20
+ ApiResponse,
21
+ },
22
+ };
23
+
24
+ #[utoipa::path(
25
+ get,
26
+ path = "/api/projects",
27
+ responses(
28
+ (status = 200, description = "List all projects", body = ApiResponse<Vec<Project>>),
29
+ (status = 500, description = "Internal server error")
30
+ ),
31
+ tag = "projects"
32
+ )]
33
+ pub async fn get_projects(
34
+ State(app_state): State<AppState>,
35
+ ) -> Result<ResponseJson<ApiResponse<Vec<Project>>>, StatusCode> {
36
+ match Project::find_all(&app_state.db_pool).await {
37
+ Ok(projects) => Ok(ResponseJson(ApiResponse::success(projects))),
38
+ Err(e) => {
39
+ tracing::error!("Failed to fetch projects: {}", e);
40
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
41
+ }
42
+ }
43
+ }
44
+
45
+ #[utoipa::path(
46
+ get,
47
+ path = "/api/projects/{id}",
48
+ params(
49
+ ("id" = String, Path, description = "Project ID")
50
+ ),
51
+ responses(
52
+ (status = 200, description = "Get project by ID", body = ApiResponse<Project>),
53
+ (status = 404, description = "Project not found"),
54
+ (status = 500, description = "Internal server error")
55
+ ),
56
+ tag = "projects"
57
+ )]
58
+ pub async fn get_project(
59
+ Extension(project): Extension<Project>,
60
+ ) -> Result<ResponseJson<ApiResponse<Project>>, StatusCode> {
61
+ Ok(ResponseJson(ApiResponse::success(project)))
62
+ }
63
+
64
+ pub async fn get_project_with_branch(
65
+ Extension(project): Extension<Project>,
66
+ ) -> Result<ResponseJson<ApiResponse<ProjectWithBranch>>, StatusCode> {
67
+ Ok(ResponseJson(ApiResponse::success(
68
+ project.with_branch_info(),
69
+ )))
70
+ }
71
+
72
+ pub async fn get_project_branches(
73
+ Extension(project): Extension<Project>,
74
+ ) -> Result<ResponseJson<ApiResponse<Vec<GitBranch>>>, StatusCode> {
75
+ match project.get_all_branches() {
76
+ Ok(branches) => Ok(ResponseJson(ApiResponse::success(branches))),
77
+ Err(e) => {
78
+ tracing::error!("Failed to get branches for project {}: {}", project.id, e);
79
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
80
+ }
81
+ }
82
+ }
83
+
84
+ pub async fn create_project_branch(
85
+ Extension(project): Extension<Project>,
86
+ Json(payload): Json<CreateBranch>,
87
+ ) -> Result<ResponseJson<ApiResponse<GitBranch>>, StatusCode> {
88
+ // Validate branch name
89
+ if payload.name.trim().is_empty() {
90
+ return Ok(ResponseJson(ApiResponse::error(
91
+ "Branch name cannot be empty",
92
+ )));
93
+ }
94
+
95
+ // Check if branch name contains invalid characters
96
+ if payload.name.contains(' ') {
97
+ return Ok(ResponseJson(ApiResponse::error(
98
+ "Branch name cannot contain spaces",
99
+ )));
100
+ }
101
+
102
+ match project.create_branch(&payload.name, payload.base_branch.as_deref()) {
103
+ Ok(branch) => Ok(ResponseJson(ApiResponse::success(branch))),
104
+ Err(e) => {
105
+ tracing::error!(
106
+ "Failed to create branch '{}' for project {}: {}",
107
+ payload.name,
108
+ project.id,
109
+ e
110
+ );
111
+ Ok(ResponseJson(ApiResponse::error(&format!(
112
+ "Failed to create branch: {}",
113
+ e
114
+ ))))
115
+ }
116
+ }
117
+ }
118
+
119
+ #[utoipa::path(
120
+ post,
121
+ path = "/api/projects",
122
+ request_body = CreateProject,
123
+ responses(
124
+ (status = 200, description = "Project created successfully", body = ApiResponse<Project>),
125
+ (status = 400, description = "Invalid input"),
126
+ (status = 500, description = "Internal server error")
127
+ ),
128
+ tag = "projects"
129
+ )]
130
+ pub async fn create_project(
131
+ State(app_state): State<AppState>,
132
+ Json(payload): Json<CreateProject>,
133
+ ) -> Result<ResponseJson<ApiResponse<Project>>, StatusCode> {
134
+ let id = Uuid::new_v4();
135
+
136
+ tracing::debug!("Creating project '{}'", payload.name);
137
+
138
+ // Check if git repo path is already used by another project
139
+ match Project::find_by_git_repo_path(&app_state.db_pool, &payload.git_repo_path).await {
140
+ Ok(Some(_)) => {
141
+ return Ok(ResponseJson(ApiResponse::error(
142
+ "A project with this git repository path already exists",
143
+ )));
144
+ }
145
+ Ok(None) => {
146
+ // Path is available, continue
147
+ }
148
+ Err(e) => {
149
+ tracing::error!("Failed to check for existing git repo path: {}", e);
150
+ return Err(StatusCode::INTERNAL_SERVER_ERROR);
151
+ }
152
+ }
153
+
154
+ // Validate and setup git repository
155
+ let path = std::path::Path::new(&payload.git_repo_path);
156
+
157
+ if payload.use_existing_repo {
158
+ // For existing repos, validate that the path exists and is a git repository
159
+ if !path.exists() {
160
+ return Ok(ResponseJson(ApiResponse::error(
161
+ "The specified path does not exist",
162
+ )));
163
+ }
164
+
165
+ if !path.is_dir() {
166
+ return Ok(ResponseJson(ApiResponse::error(
167
+ "The specified path is not a directory",
168
+ )));
169
+ }
170
+
171
+ if !path.join(".git").exists() {
172
+ return Ok(ResponseJson(ApiResponse::error(
173
+ "The specified directory is not a git repository",
174
+ )));
175
+ }
176
+ } else {
177
+ // For new repos, create directory and initialize git
178
+
179
+ // Create directory if it doesn't exist
180
+ if !path.exists() {
181
+ if let Err(e) = std::fs::create_dir_all(path) {
182
+ tracing::error!("Failed to create directory: {}", e);
183
+ return Ok(ResponseJson(ApiResponse::error(&format!(
184
+ "Failed to create directory: {}",
185
+ e
186
+ ))));
187
+ }
188
+ }
189
+
190
+ // Check if it's already a git repo, if not initialize it
191
+ if !path.join(".git").exists() {
192
+ match std::process::Command::new("git")
193
+ .arg("init")
194
+ .current_dir(path)
195
+ .output()
196
+ {
197
+ Ok(output) => {
198
+ if !output.status.success() {
199
+ let error_msg = String::from_utf8_lossy(&output.stderr);
200
+ tracing::error!("Git init failed: {}", error_msg);
201
+ return Ok(ResponseJson(ApiResponse::error(&format!(
202
+ "Git init failed: {}",
203
+ error_msg
204
+ ))));
205
+ }
206
+ }
207
+ Err(e) => {
208
+ tracing::error!("Failed to run git init: {}", e);
209
+ return Ok(ResponseJson(ApiResponse::error(&format!(
210
+ "Failed to run git init: {}",
211
+ e
212
+ ))));
213
+ }
214
+ }
215
+ }
216
+ }
217
+
218
+ match Project::create(&app_state.db_pool, &payload, id).await {
219
+ Ok(project) => {
220
+ // Track project creation event
221
+ app_state
222
+ .track_analytics_event(
223
+ "project_created",
224
+ Some(serde_json::json!({
225
+ "project_id": project.id.to_string(),
226
+ "use_existing_repo": payload.use_existing_repo,
227
+ "has_setup_script": payload.setup_script.is_some(),
228
+ "has_dev_script": payload.dev_script.is_some(),
229
+ })),
230
+ )
231
+ .await;
232
+
233
+ Ok(ResponseJson(ApiResponse::success(project)))
234
+ }
235
+ Err(e) => {
236
+ tracing::error!("Failed to create project: {}", e);
237
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
238
+ }
239
+ }
240
+ }
241
+
242
+ #[utoipa::path(
243
+ put,
244
+ path = "/api/projects/{id}",
245
+ params(
246
+ ("id" = String, Path, description = "Project ID")
247
+ ),
248
+ request_body = UpdateProject,
249
+ responses(
250
+ (status = 200, description = "Project updated successfully", body = ApiResponse<Project>),
251
+ (status = 404, description = "Project not found"),
252
+ (status = 400, description = "Invalid input"),
253
+ (status = 500, description = "Internal server error")
254
+ ),
255
+ tag = "projects"
256
+ )]
257
+ pub async fn update_project(
258
+ Extension(existing_project): Extension<Project>,
259
+ State(app_state): State<AppState>,
260
+ Json(payload): Json<UpdateProject>,
261
+ ) -> Result<ResponseJson<ApiResponse<Project>>, StatusCode> {
262
+ // If git_repo_path is being changed, check if the new path is already used by another project
263
+ if let Some(new_git_repo_path) = &payload.git_repo_path {
264
+ if new_git_repo_path != &existing_project.git_repo_path {
265
+ match Project::find_by_git_repo_path_excluding_id(
266
+ &app_state.db_pool,
267
+ new_git_repo_path,
268
+ existing_project.id,
269
+ )
270
+ .await
271
+ {
272
+ Ok(Some(_)) => {
273
+ return Ok(ResponseJson(ApiResponse::error(
274
+ "A project with this git repository path already exists",
275
+ )));
276
+ }
277
+ Ok(None) => {
278
+ // Path is available, continue
279
+ }
280
+ Err(e) => {
281
+ tracing::error!("Failed to check for existing git repo path: {}", e);
282
+ return Err(StatusCode::INTERNAL_SERVER_ERROR);
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ // Destructure payload to handle field updates.
289
+ // This allows us to treat `None` from the payload as an explicit `null` to clear a field,
290
+ // as the frontend currently sends all fields on update.
291
+ let UpdateProject {
292
+ name,
293
+ git_repo_path,
294
+ setup_script,
295
+ dev_script,
296
+ cleanup_script,
297
+ } = payload;
298
+
299
+ let name = name.unwrap_or(existing_project.name);
300
+ let git_repo_path = git_repo_path.unwrap_or(existing_project.git_repo_path);
301
+
302
+ match Project::update(
303
+ &app_state.db_pool,
304
+ existing_project.id,
305
+ name,
306
+ git_repo_path,
307
+ setup_script,
308
+ dev_script,
309
+ cleanup_script,
310
+ )
311
+ .await
312
+ {
313
+ Ok(project) => Ok(ResponseJson(ApiResponse::success(project))),
314
+ Err(e) => {
315
+ tracing::error!("Failed to update project: {}", e);
316
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
317
+ }
318
+ }
319
+ }
320
+
321
+ #[utoipa::path(
322
+ delete,
323
+ path = "/api/projects/{id}",
324
+ params(
325
+ ("id" = String, Path, description = "Project ID")
326
+ ),
327
+ responses(
328
+ (status = 200, description = "Project deleted successfully"),
329
+ (status = 404, description = "Project not found"),
330
+ (status = 500, description = "Internal server error")
331
+ ),
332
+ tag = "projects"
333
+ )]
334
+ pub async fn delete_project(
335
+ Extension(project): Extension<Project>,
336
+ State(app_state): State<AppState>,
337
+ ) -> Result<ResponseJson<ApiResponse<()>>, StatusCode> {
338
+ match Project::delete(&app_state.db_pool, project.id).await {
339
+ Ok(rows_affected) => {
340
+ if rows_affected == 0 {
341
+ Err(StatusCode::NOT_FOUND)
342
+ } else {
343
+ Ok(ResponseJson(ApiResponse::success(())))
344
+ }
345
+ }
346
+ Err(e) => {
347
+ tracing::error!("Failed to delete project: {}", e);
348
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
349
+ }
350
+ }
351
+ }
352
+
353
+ #[derive(serde::Deserialize)]
354
+ pub struct OpenEditorRequest {
355
+ editor_type: Option<String>,
356
+ }
357
+
358
+ pub async fn open_project_in_editor(
359
+ Extension(project): Extension<Project>,
360
+ State(app_state): State<AppState>,
361
+ Json(payload): Json<Option<OpenEditorRequest>>,
362
+ ) -> Result<ResponseJson<ApiResponse<()>>, StatusCode> {
363
+ // Get editor command from config or override
364
+ let editor_command = {
365
+ let config_guard = app_state.get_config().read().await;
366
+ if let Some(ref request) = payload {
367
+ if let Some(ref editor_type) = request.editor_type {
368
+ // Create a temporary editor config with the override
369
+ use crate::models::config::{EditorConfig, EditorType};
370
+ let override_editor_type = match editor_type.as_str() {
371
+ "vscode" => EditorType::VSCode,
372
+ "cursor" => EditorType::Cursor,
373
+ "windsurf" => EditorType::Windsurf,
374
+ "intellij" => EditorType::IntelliJ,
375
+ "zed" => EditorType::Zed,
376
+ "custom" => EditorType::Custom,
377
+ _ => config_guard.editor.editor_type.clone(),
378
+ };
379
+ let temp_config = EditorConfig {
380
+ editor_type: override_editor_type,
381
+ custom_command: config_guard.editor.custom_command.clone(),
382
+ };
383
+ temp_config.get_command()
384
+ } else {
385
+ config_guard.editor.get_command()
386
+ }
387
+ } else {
388
+ config_guard.editor.get_command()
389
+ }
390
+ };
391
+
392
+ // Open editor in the project directory
393
+ let mut cmd = std::process::Command::new(&editor_command[0]);
394
+ for arg in &editor_command[1..] {
395
+ cmd.arg(arg);
396
+ }
397
+ cmd.arg(&project.git_repo_path);
398
+
399
+ match cmd.spawn() {
400
+ Ok(_) => {
401
+ tracing::info!(
402
+ "Opened editor ({}) for project {} at path: {}",
403
+ editor_command.join(" "),
404
+ project.id,
405
+ project.git_repo_path
406
+ );
407
+ Ok(ResponseJson(ApiResponse::success(())))
408
+ }
409
+ Err(e) => {
410
+ tracing::error!(
411
+ "Failed to open editor ({}) for project {}: {}",
412
+ editor_command.join(" "),
413
+ project.id,
414
+ e
415
+ );
416
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
417
+ }
418
+ }
419
+ }
420
+
421
+ pub async fn search_project_files(
422
+ Extension(project): Extension<Project>,
423
+ Query(params): Query<HashMap<String, String>>,
424
+ ) -> Result<ResponseJson<ApiResponse<Vec<SearchResult>>>, StatusCode> {
425
+ let query = match params.get("q") {
426
+ Some(q) if !q.trim().is_empty() => q.trim(),
427
+ _ => {
428
+ return Ok(ResponseJson(ApiResponse::error(
429
+ "Query parameter 'q' is required and cannot be empty",
430
+ )));
431
+ }
432
+ };
433
+
434
+ // Search files in the project repository
435
+ match search_files_in_repo(&project.git_repo_path, query).await {
436
+ Ok(results) => Ok(ResponseJson(ApiResponse::success(results))),
437
+ Err(e) => {
438
+ tracing::error!("Failed to search files: {}", e);
439
+ Err(StatusCode::INTERNAL_SERVER_ERROR)
440
+ }
441
+ }
442
+ }
443
+
444
+ async fn search_files_in_repo(
445
+ repo_path: &str,
446
+ query: &str,
447
+ ) -> Result<Vec<SearchResult>, Box<dyn std::error::Error + Send + Sync>> {
448
+ use std::path::Path;
449
+
450
+ use ignore::WalkBuilder;
451
+
452
+ let repo_path = Path::new(repo_path);
453
+
454
+ if !repo_path.exists() {
455
+ return Err("Repository path does not exist".into());
456
+ }
457
+
458
+ let mut results = Vec::new();
459
+ let query_lower = query.to_lowercase();
460
+
461
+ // Use ignore::WalkBuilder to respect gitignore files
462
+ let walker = WalkBuilder::new(repo_path)
463
+ .git_ignore(true)
464
+ .git_global(true)
465
+ .git_exclude(true)
466
+ .hidden(false)
467
+ .build();
468
+
469
+ for result in walker {
470
+ let entry = result?;
471
+ let path = entry.path();
472
+
473
+ // Skip the root directory itself
474
+ if path == repo_path {
475
+ continue;
476
+ }
477
+
478
+ let relative_path = path.strip_prefix(repo_path)?;
479
+
480
+ // Skip .git directory and its contents
481
+ if relative_path
482
+ .components()
483
+ .any(|component| component.as_os_str() == ".git")
484
+ {
485
+ continue;
486
+ }
487
+ let relative_path_str = relative_path.to_string_lossy().to_lowercase();
488
+
489
+ let file_name = path
490
+ .file_name()
491
+ .map(|name| name.to_string_lossy().to_lowercase())
492
+ .unwrap_or_default();
493
+
494
+ // Check for matches
495
+ if file_name.contains(&query_lower) {
496
+ results.push(SearchResult {
497
+ path: relative_path.to_string_lossy().to_string(),
498
+ is_file: path.is_file(),
499
+ match_type: SearchMatchType::FileName,
500
+ });
501
+ } else if relative_path_str.contains(&query_lower) {
502
+ // Check if it's a directory name match or full path match
503
+ let match_type = if path
504
+ .parent()
505
+ .and_then(|p| p.file_name())
506
+ .map(|name| name.to_string_lossy().to_lowercase())
507
+ .unwrap_or_default()
508
+ .contains(&query_lower)
509
+ {
510
+ SearchMatchType::DirectoryName
511
+ } else {
512
+ SearchMatchType::FullPath
513
+ };
514
+
515
+ results.push(SearchResult {
516
+ path: relative_path.to_string_lossy().to_string(),
517
+ is_file: path.is_file(),
518
+ match_type,
519
+ });
520
+ }
521
+ }
522
+
523
+ // Sort results by priority: FileName > DirectoryName > FullPath
524
+ results.sort_by(|a, b| {
525
+ use SearchMatchType::*;
526
+ let priority = |match_type: &SearchMatchType| match match_type {
527
+ FileName => 0,
528
+ DirectoryName => 1,
529
+ FullPath => 2,
530
+ };
531
+
532
+ priority(&a.match_type)
533
+ .cmp(&priority(&b.match_type))
534
+ .then_with(|| a.path.cmp(&b.path))
535
+ });
536
+
537
+ // Limit to top 10 results
538
+ results.truncate(10);
539
+
540
+ Ok(results)
541
+ }
542
+
543
+ pub fn projects_base_router() -> Router<AppState> {
544
+ Router::new().route("/projects", get(get_projects).post(create_project))
545
+ }
546
+
547
+ pub fn projects_with_id_router() -> Router<AppState> {
548
+ use axum::routing::post;
549
+
550
+ Router::new()
551
+ .route(
552
+ "/projects/:id",
553
+ get(get_project).put(update_project).delete(delete_project),
554
+ )
555
+ .route("/projects/:id/with-branch", get(get_project_with_branch))
556
+ .route(
557
+ "/projects/:id/branches",
558
+ get(get_project_branches).post(create_project_branch),
559
+ )
560
+ .route("/projects/:id/search", get(search_project_files))
561
+ .route("/projects/:id/open-editor", post(open_project_in_editor))
562
+ }