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,385 @@
1
+ use std::collections::HashMap;
2
+
3
+ use axum::{
4
+ extract::{Query, State},
5
+ response::Json as ResponseJson,
6
+ routing::{get, post},
7
+ Json, Router,
8
+ };
9
+ use serde::{Deserialize, Serialize};
10
+ use serde_json::Value;
11
+ use tokio::fs;
12
+ use ts_rs::TS;
13
+ use utoipa::ToSchema;
14
+
15
+ use crate::{
16
+ app_state::AppState,
17
+ executor::ExecutorConfig,
18
+ models::{
19
+ config::{Config, EditorConstants, SoundConstants},
20
+ ApiResponse,
21
+ },
22
+ utils,
23
+ };
24
+
25
+ pub fn config_router() -> Router<AppState> {
26
+ Router::new()
27
+ .route("/config", get(get_config))
28
+ .route("/config", post(update_config))
29
+ .route("/config/constants", get(get_config_constants))
30
+ .route("/mcp-servers", get(get_mcp_servers))
31
+ .route("/mcp-servers", post(update_mcp_servers))
32
+ }
33
+
34
+ #[utoipa::path(
35
+ get,
36
+ path = "/config",
37
+ tag = "config",
38
+ summary = "Get application configuration",
39
+ description = "Retrieves the current application configuration settings",
40
+ responses(
41
+ (status = 200, description = "Configuration retrieved successfully", body = ApiResponse<Config>)
42
+ )
43
+ )]
44
+ pub async fn get_config(State(app_state): State<AppState>) -> ResponseJson<ApiResponse<Config>> {
45
+ let config = app_state.get_config().read().await;
46
+ ResponseJson(ApiResponse::success(config.clone()))
47
+ }
48
+
49
+ #[utoipa::path(
50
+ post,
51
+ path = "/config",
52
+ tag = "config",
53
+ summary = "Update application configuration",
54
+ description = "Updates the application configuration with new settings",
55
+ request_body = Config,
56
+ responses(
57
+ (status = 200, description = "Configuration updated successfully", body = ApiResponse<Config>),
58
+ (status = 500, description = "Failed to save configuration", body = ApiResponse<String>)
59
+ )
60
+ )]
61
+ pub async fn update_config(
62
+ State(app_state): State<AppState>,
63
+ Json(new_config): Json<Config>,
64
+ ) -> ResponseJson<ApiResponse<Config>> {
65
+ let config_path = utils::config_path();
66
+
67
+ match new_config.save(&config_path) {
68
+ Ok(_) => {
69
+ let mut config = app_state.get_config().write().await;
70
+ *config = new_config.clone();
71
+ drop(config);
72
+
73
+ app_state
74
+ .update_analytics_config(new_config.analytics_enabled.unwrap_or(true))
75
+ .await;
76
+
77
+ ResponseJson(ApiResponse::success(new_config))
78
+ }
79
+ Err(e) => ResponseJson(ApiResponse::error(&format!("Failed to save config: {}", e))),
80
+ }
81
+ }
82
+
83
+ #[derive(Debug, Serialize, Deserialize, TS, ToSchema)]
84
+ #[ts(export)]
85
+ pub struct ConfigConstants {
86
+ pub editor: EditorConstants,
87
+ pub sound: SoundConstants,
88
+ }
89
+
90
+ #[utoipa::path(
91
+ get,
92
+ path = "/config/constants",
93
+ tag = "config",
94
+ summary = "Get configuration constants",
95
+ description = "Retrieves editor and sound constants for the application",
96
+ responses(
97
+ (status = 200, description = "Constants retrieved successfully", body = ApiResponse<ConfigConstants>)
98
+ )
99
+ )]
100
+ pub async fn get_config_constants() -> ResponseJson<ApiResponse<ConfigConstants>> {
101
+ let constants = ConfigConstants {
102
+ editor: EditorConstants::new(),
103
+ sound: SoundConstants::new(),
104
+ };
105
+
106
+ ResponseJson(ApiResponse::success(constants))
107
+ }
108
+
109
+ #[derive(Debug, Deserialize, ToSchema)]
110
+ pub struct McpServerQuery {
111
+ executor: Option<String>,
112
+ }
113
+
114
+ /// Common logic for resolving executor configuration and validating MCP support
115
+ fn resolve_executor_config(
116
+ query_executor: Option<String>,
117
+ saved_config: &ExecutorConfig,
118
+ ) -> Result<ExecutorConfig, String> {
119
+ let executor_config = match query_executor {
120
+ Some(executor_type) => executor_type
121
+ .parse::<ExecutorConfig>()
122
+ .map_err(|e| e.to_string())?,
123
+ None => saved_config.clone(),
124
+ };
125
+
126
+ if !executor_config.supports_mcp() {
127
+ return Err(format!(
128
+ "{} executor does not support MCP configuration",
129
+ executor_config.display_name()
130
+ ));
131
+ }
132
+
133
+ Ok(executor_config)
134
+ }
135
+
136
+ #[utoipa::path(
137
+ get,
138
+ path = "/mcp-servers",
139
+ tag = "config",
140
+ summary = "Get MCP servers configuration",
141
+ description = "Retrieves MCP (Model Context Protocol) servers configuration for the specified executor",
142
+ params(
143
+ ("executor" = Option<String>, Query, description = "Executor type to get MCP servers for")
144
+ ),
145
+ responses(
146
+ (status = 200, description = "MCP servers retrieved successfully", body = ApiResponse<Value>),
147
+ (status = 400, description = "Executor does not support MCP or invalid configuration", body = ApiResponse<String>)
148
+ )
149
+ )]
150
+ pub async fn get_mcp_servers(
151
+ State(app_state): State<AppState>,
152
+ Query(query): Query<McpServerQuery>,
153
+ ) -> ResponseJson<ApiResponse<Value>> {
154
+ let saved_config = {
155
+ let config = app_state.get_config().read().await;
156
+ config.executor.clone()
157
+ };
158
+
159
+ let executor_config = match resolve_executor_config(query.executor, &saved_config) {
160
+ Ok(config) => config,
161
+ Err(message) => {
162
+ return ResponseJson(ApiResponse::error(&message));
163
+ }
164
+ };
165
+
166
+ // Get the config file path for this executor
167
+ let config_path = match executor_config.config_path() {
168
+ Some(path) => path,
169
+ None => {
170
+ return ResponseJson(ApiResponse::error("Could not determine config file path"));
171
+ }
172
+ };
173
+
174
+ match read_mcp_servers_from_config(&config_path, &executor_config).await {
175
+ Ok(servers) => {
176
+ let response_data = serde_json::json!({
177
+ "servers": servers,
178
+ "config_path": config_path.to_string_lossy().to_string()
179
+ });
180
+ ResponseJson(ApiResponse::success(response_data))
181
+ }
182
+ Err(e) => ResponseJson(ApiResponse::error(&format!(
183
+ "Failed to read MCP servers: {}",
184
+ e
185
+ ))),
186
+ }
187
+ }
188
+
189
+ #[utoipa::path(
190
+ post,
191
+ path = "/mcp-servers",
192
+ tag = "config",
193
+ summary = "Update MCP servers configuration",
194
+ description = "Updates MCP (Model Context Protocol) servers configuration for the specified executor",
195
+ params(
196
+ ("executor" = Option<String>, Query, description = "Executor type to update MCP servers for")
197
+ ),
198
+ request_body = HashMap<String, Value>,
199
+ responses(
200
+ (status = 200, description = "MCP servers updated successfully", body = ApiResponse<String>),
201
+ (status = 400, description = "Executor does not support MCP or update failed", body = ApiResponse<String>)
202
+ )
203
+ )]
204
+ pub async fn update_mcp_servers(
205
+ State(app_state): State<AppState>,
206
+ Query(query): Query<McpServerQuery>,
207
+ Json(new_servers): Json<HashMap<String, Value>>,
208
+ ) -> ResponseJson<ApiResponse<String>> {
209
+ let saved_config = {
210
+ let config = app_state.get_config().read().await;
211
+ config.executor.clone()
212
+ };
213
+
214
+ let executor_config = match resolve_executor_config(query.executor, &saved_config) {
215
+ Ok(config) => config,
216
+ Err(message) => {
217
+ return ResponseJson(ApiResponse::error(&message));
218
+ }
219
+ };
220
+
221
+ // Get the config file path for this executor
222
+ let config_path = match executor_config.config_path() {
223
+ Some(path) => path,
224
+ None => {
225
+ return ResponseJson(ApiResponse::error("Could not determine config file path"));
226
+ }
227
+ };
228
+
229
+ match update_mcp_servers_in_config(&config_path, &executor_config, new_servers).await {
230
+ Ok(message) => ResponseJson(ApiResponse::success(message)),
231
+ Err(e) => ResponseJson(ApiResponse::error(&format!(
232
+ "Failed to update MCP servers: {}",
233
+ e
234
+ ))),
235
+ }
236
+ }
237
+
238
+ async fn update_mcp_servers_in_config(
239
+ file_path: &std::path::Path,
240
+ executor_config: &ExecutorConfig,
241
+ new_servers: HashMap<String, Value>,
242
+ ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
243
+ // Ensure parent directory exists
244
+ if let Some(parent) = file_path.parent() {
245
+ fs::create_dir_all(parent).await?;
246
+ }
247
+
248
+ // Read existing config file or create empty object if it doesn't exist
249
+ let file_content = fs::read_to_string(file_path)
250
+ .await
251
+ .unwrap_or_else(|_| "{}".to_string());
252
+ let mut config: Value = serde_json::from_str(&file_content)?;
253
+
254
+ // Get the attribute path for MCP servers
255
+ let mcp_path = executor_config.mcp_attribute_path().unwrap();
256
+
257
+ // Get the current server count for comparison
258
+ let old_servers = get_mcp_servers_from_config_path(&config, &mcp_path).len();
259
+
260
+ // Set the MCP servers using the correct attribute path
261
+ set_mcp_servers_in_config_path(&mut config, &mcp_path, &new_servers)?;
262
+
263
+ // Write the updated config back to file
264
+ let updated_content = serde_json::to_string_pretty(&config)?;
265
+ fs::write(file_path, updated_content).await?;
266
+
267
+ let new_count = new_servers.len();
268
+ let message = match (old_servers, new_count) {
269
+ (0, 0) => "No MCP servers configured".to_string(),
270
+ (0, n) => format!("Added {} MCP server(s)", n),
271
+ (old, new) if old == new => format!("Updated MCP server configuration ({} server(s))", new),
272
+ (old, new) => format!(
273
+ "Updated MCP server configuration (was {}, now {})",
274
+ old, new
275
+ ),
276
+ };
277
+
278
+ Ok(message)
279
+ }
280
+
281
+ async fn read_mcp_servers_from_config(
282
+ file_path: &std::path::Path,
283
+ executor_config: &ExecutorConfig,
284
+ ) -> Result<HashMap<String, Value>, Box<dyn std::error::Error + Send + Sync>> {
285
+ // Read the config file, return empty if it doesn't exist
286
+ let file_content = fs::read_to_string(file_path)
287
+ .await
288
+ .unwrap_or_else(|_| "{}".to_string());
289
+ let config: Value = serde_json::from_str(&file_content)?;
290
+
291
+ // Get the attribute path for MCP servers
292
+ let mcp_path = executor_config.mcp_attribute_path().unwrap();
293
+
294
+ // Get the servers using the correct attribute path
295
+ let servers = get_mcp_servers_from_config_path(&config, &mcp_path);
296
+
297
+ Ok(servers)
298
+ }
299
+
300
+ /// Helper function to get MCP servers from config using a path
301
+ fn get_mcp_servers_from_config_path(config: &Value, path: &[&str]) -> HashMap<String, Value> {
302
+ // Special handling for AMP - use flat key structure
303
+ if path.len() == 2 && path[0] == "amp" && path[1] == "mcpServers" {
304
+ let flat_key = format!("{}.{}", path[0], path[1]);
305
+ let current = match config.get(&flat_key) {
306
+ Some(val) => val,
307
+ None => return HashMap::new(),
308
+ };
309
+
310
+ // Extract the servers object
311
+ match current.as_object() {
312
+ Some(servers) => servers
313
+ .iter()
314
+ .map(|(k, v)| (k.clone(), v.clone()))
315
+ .collect(),
316
+ None => HashMap::new(),
317
+ }
318
+ } else {
319
+ let mut current = config;
320
+
321
+ // Navigate to the target location
322
+ for &part in path {
323
+ current = match current.get(part) {
324
+ Some(val) => val,
325
+ None => return HashMap::new(),
326
+ };
327
+ }
328
+
329
+ // Extract the servers object
330
+ match current.as_object() {
331
+ Some(servers) => servers
332
+ .iter()
333
+ .map(|(k, v)| (k.clone(), v.clone()))
334
+ .collect(),
335
+ None => HashMap::new(),
336
+ }
337
+ }
338
+ }
339
+
340
+ /// Helper function to set MCP servers in config using a path
341
+ fn set_mcp_servers_in_config_path(
342
+ config: &mut Value,
343
+ path: &[&str],
344
+ servers: &HashMap<String, Value>,
345
+ ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
346
+ // Ensure config is an object
347
+ if !config.is_object() {
348
+ *config = serde_json::json!({});
349
+ }
350
+
351
+ // Special handling for AMP - use flat key structure
352
+ if path.len() == 2 && path[0] == "amp" && path[1] == "mcpServers" {
353
+ let flat_key = format!("{}.{}", path[0], path[1]);
354
+ config
355
+ .as_object_mut()
356
+ .unwrap()
357
+ .insert(flat_key, serde_json::to_value(servers)?);
358
+ return Ok(());
359
+ }
360
+
361
+ let mut current = config;
362
+
363
+ // Navigate/create the nested structure (all parts except the last)
364
+ for &part in &path[..path.len() - 1] {
365
+ if current.get(part).is_none() {
366
+ current
367
+ .as_object_mut()
368
+ .unwrap()
369
+ .insert(part.to_string(), serde_json::json!({}));
370
+ }
371
+ current = current.get_mut(part).unwrap();
372
+ if !current.is_object() {
373
+ *current = serde_json::json!({});
374
+ }
375
+ }
376
+
377
+ // Set the final attribute
378
+ let final_attr = path.last().unwrap();
379
+ current
380
+ .as_object_mut()
381
+ .unwrap()
382
+ .insert(final_attr.to_string(), serde_json::to_value(servers)?);
383
+
384
+ Ok(())
385
+ }
@@ -0,0 +1,228 @@
1
+ use std::{
2
+ fs,
3
+ path::{Path, PathBuf},
4
+ };
5
+
6
+ use axum::{
7
+ extract::Query, http::StatusCode, response::Json as ResponseJson, routing::get, Router,
8
+ };
9
+ use serde::{Deserialize, Serialize};
10
+ use ts_rs::TS;
11
+ use utoipa::ToSchema;
12
+
13
+ use crate::{app_state::AppState, models::ApiResponse};
14
+
15
+ #[derive(Debug, Serialize, TS, ToSchema)]
16
+ #[ts(export)]
17
+ pub struct DirectoryEntry {
18
+ pub name: String,
19
+ pub path: String,
20
+ pub is_directory: bool,
21
+ pub is_git_repo: bool,
22
+ }
23
+
24
+ #[derive(Debug, Serialize, TS, ToSchema)]
25
+ #[ts(export)]
26
+ pub struct DirectoryListResponse {
27
+ pub entries: Vec<DirectoryEntry>,
28
+ pub current_path: String,
29
+ }
30
+
31
+ #[derive(Debug, Deserialize, ToSchema)]
32
+ pub struct ListDirectoryQuery {
33
+ path: Option<String>,
34
+ }
35
+
36
+ #[utoipa::path(
37
+ get,
38
+ path = "/filesystem/list",
39
+ tag = "filesystem",
40
+ summary = "List directory contents",
41
+ description = "Lists files and directories at the specified path, with Git repository detection",
42
+ params(
43
+ ("path" = Option<String>, Query, description = "Directory path to list (defaults to home directory)")
44
+ ),
45
+ responses(
46
+ (status = 200, description = "Directory contents retrieved successfully", body = ApiResponse<DirectoryListResponse>),
47
+ (status = 404, description = "Directory not found or access denied")
48
+ )
49
+ )]
50
+ pub async fn list_directory(
51
+ Query(query): Query<ListDirectoryQuery>,
52
+ ) -> Result<ResponseJson<ApiResponse<DirectoryListResponse>>, StatusCode> {
53
+ let path_str = query.path.unwrap_or_else(|| {
54
+ // Default to user's home directory
55
+ dirs::home_dir()
56
+ .or_else(dirs::desktop_dir)
57
+ .or_else(dirs::document_dir)
58
+ .unwrap_or_else(|| {
59
+ if cfg!(windows) {
60
+ std::env::var("USERPROFILE")
61
+ .map(PathBuf::from)
62
+ .unwrap_or_else(|_| PathBuf::from("C:\\"))
63
+ } else {
64
+ PathBuf::from("/")
65
+ }
66
+ })
67
+ .to_string_lossy()
68
+ .to_string()
69
+ });
70
+
71
+ let path = Path::new(&path_str);
72
+
73
+ if !path.exists() {
74
+ return Ok(ResponseJson(ApiResponse::error("Directory does not exist")));
75
+ }
76
+
77
+ if !path.is_dir() {
78
+ return Ok(ResponseJson(ApiResponse::error("Path is not a directory")));
79
+ }
80
+
81
+ match fs::read_dir(path) {
82
+ Ok(entries) => {
83
+ let mut directory_entries = Vec::new();
84
+
85
+ for entry in entries.flatten() {
86
+ let path = entry.path();
87
+ let metadata = entry.metadata().ok();
88
+
89
+ if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
90
+ // Skip hidden files/directories
91
+ if name.starts_with('.') && name != ".." {
92
+ continue;
93
+ }
94
+
95
+ let is_directory = metadata.is_some_and(|m| m.is_dir());
96
+ let is_git_repo = if is_directory {
97
+ path.join(".git").exists()
98
+ } else {
99
+ false
100
+ };
101
+
102
+ directory_entries.push(DirectoryEntry {
103
+ name: name.to_string(),
104
+ path: path.to_string_lossy().to_string(),
105
+ is_directory,
106
+ is_git_repo,
107
+ });
108
+ }
109
+ }
110
+
111
+ // Sort: directories first, then files, both alphabetically
112
+ directory_entries.sort_by(|a, b| match (a.is_directory, b.is_directory) {
113
+ (true, false) => std::cmp::Ordering::Less,
114
+ (false, true) => std::cmp::Ordering::Greater,
115
+ _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()),
116
+ });
117
+
118
+ Ok(ResponseJson(ApiResponse::success(DirectoryListResponse {
119
+ entries: directory_entries,
120
+ current_path: path.to_string_lossy().to_string(),
121
+ })))
122
+ }
123
+ Err(e) => {
124
+ tracing::error!("Failed to read directory: {}", e);
125
+ Ok(ResponseJson(ApiResponse::error(&format!(
126
+ "Failed to read directory: {}",
127
+ e
128
+ ))))
129
+ }
130
+ }
131
+ }
132
+
133
+ #[utoipa::path(
134
+ get,
135
+ path = "/filesystem/validate-git",
136
+ tag = "filesystem",
137
+ summary = "Validate Git repository path",
138
+ description = "Checks if the specified path exists and is a valid Git repository",
139
+ params(
140
+ ("path" = String, Query, description = "Directory path to validate as Git repository")
141
+ ),
142
+ responses(
143
+ (status = 200, description = "Path validation result", body = ApiResponse<bool>),
144
+ (status = 400, description = "Missing or invalid path parameter")
145
+ )
146
+ )]
147
+ pub async fn validate_git_path(
148
+ Query(query): Query<ListDirectoryQuery>,
149
+ ) -> Result<ResponseJson<ApiResponse<bool>>, StatusCode> {
150
+ let path_str = query.path.ok_or(StatusCode::BAD_REQUEST)?;
151
+ let path = Path::new(&path_str);
152
+
153
+ // Check if path exists and is a git repo
154
+ let is_valid_git_repo = path.exists() && path.is_dir() && path.join(".git").exists();
155
+
156
+ Ok(ResponseJson(ApiResponse::success(is_valid_git_repo)))
157
+ }
158
+
159
+ #[utoipa::path(
160
+ get,
161
+ path = "/filesystem/create-git",
162
+ tag = "filesystem",
163
+ summary = "Initialize Git repository",
164
+ description = "Creates a directory if needed and initializes it as a Git repository",
165
+ params(
166
+ ("path" = String, Query, description = "Directory path where Git repository should be created")
167
+ ),
168
+ responses(
169
+ (status = 200, description = "Git repository created or already exists"),
170
+ (status = 400, description = "Missing path parameter or Git initialization failed")
171
+ )
172
+ )]
173
+ pub async fn create_git_repo(
174
+ Query(query): Query<ListDirectoryQuery>,
175
+ ) -> Result<ResponseJson<ApiResponse<()>>, StatusCode> {
176
+ let path_str = query.path.ok_or(StatusCode::BAD_REQUEST)?;
177
+ let path = Path::new(&path_str);
178
+
179
+ // Create directory if it doesn't exist
180
+ if !path.exists() {
181
+ if let Err(e) = 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
191
+ if path.join(".git").exists() {
192
+ return Ok(ResponseJson(ApiResponse::success(())));
193
+ }
194
+
195
+ // Initialize git repository
196
+ match std::process::Command::new("git")
197
+ .arg("init")
198
+ .current_dir(path)
199
+ .output()
200
+ {
201
+ Ok(output) => {
202
+ if output.status.success() {
203
+ Ok(ResponseJson(ApiResponse::success(())))
204
+ } else {
205
+ let error_msg = String::from_utf8_lossy(&output.stderr);
206
+ tracing::error!("Git init failed: {}", error_msg);
207
+ Ok(ResponseJson(ApiResponse::error(&format!(
208
+ "Git init failed: {}",
209
+ error_msg
210
+ ))))
211
+ }
212
+ }
213
+ Err(e) => {
214
+ tracing::error!("Failed to run git init: {}", e);
215
+ Ok(ResponseJson(ApiResponse::error(&format!(
216
+ "Failed to run git init: {}",
217
+ e
218
+ ))))
219
+ }
220
+ }
221
+ }
222
+
223
+ pub fn filesystem_router() -> Router<AppState> {
224
+ Router::new()
225
+ .route("/filesystem/list", get(list_directory))
226
+ .route("/filesystem/validate-git", get(validate_git_path))
227
+ .route("/filesystem/create-git", get(create_git_repo))
228
+ }
@@ -0,0 +1,16 @@
1
+ use axum::response::Json;
2
+ use utoipa;
3
+
4
+ use crate::models::ApiResponse;
5
+
6
+ #[utoipa::path(
7
+ get,
8
+ path = "/api/health",
9
+ responses(
10
+ (status = 200, description = "Health check successful", body = ApiResponse<String>)
11
+ ),
12
+ tag = "health"
13
+ )]
14
+ pub async fn health_check() -> Json<ApiResponse<String>> {
15
+ Json(ApiResponse::success("OK".to_string()))
16
+ }
@@ -0,0 +1,9 @@
1
+ pub mod auth;
2
+ pub mod config;
3
+ pub mod filesystem;
4
+ pub mod health;
5
+ pub mod projects;
6
+ pub mod stream;
7
+ pub mod task_attempts;
8
+ pub mod task_templates;
9
+ pub mod tasks;