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,23 @@
1
+ pub mod amp;
2
+ pub mod ccr;
3
+ pub mod charm_opencode;
4
+ pub mod claude;
5
+ pub mod cleanup_script;
6
+ pub mod dev_server;
7
+ pub mod echo;
8
+ pub mod gemini;
9
+ pub mod opencode_ai;
10
+ pub mod setup_script;
11
+ pub mod sst_opencode;
12
+
13
+ pub use amp::AmpExecutor;
14
+ pub use ccr::CCRExecutor;
15
+ pub use charm_opencode::CharmOpencodeExecutor;
16
+ pub use claude::ClaudeExecutor;
17
+ pub use cleanup_script::CleanupScriptExecutor;
18
+ pub use dev_server::DevServerExecutor;
19
+ pub use echo::EchoExecutor;
20
+ pub use gemini::GeminiExecutor;
21
+ pub use opencode_ai::OpencodeAiExecutor;
22
+ pub use setup_script::SetupScriptExecutor;
23
+ pub use sst_opencode::SstOpencodeExecutor;
@@ -0,0 +1,113 @@
1
+ use async_trait::async_trait;
2
+ use command_group::{AsyncCommandGroup, AsyncGroupChild};
3
+ use uuid::Uuid;
4
+
5
+ use crate::{
6
+ executor::{Executor, ExecutorError},
7
+ models::task::Task,
8
+ utils::shell::get_shell_command,
9
+ };
10
+
11
+ /// An executor that uses OpenCode to process tasks
12
+ pub struct OpencodeAiExecutor;
13
+
14
+ #[async_trait]
15
+ impl Executor for OpencodeAiExecutor {
16
+ async fn spawn(
17
+ &self,
18
+ pool: &sqlx::SqlitePool,
19
+ task_id: Uuid,
20
+ worktree_path: &str,
21
+ ) -> Result<AsyncGroupChild, ExecutorError> {
22
+ // Get the task to fetch its description
23
+ let task = Task::find_by_id(pool, task_id)
24
+ .await?
25
+ .ok_or(ExecutorError::TaskNotFound)?;
26
+
27
+ use std::process::Stdio;
28
+
29
+ use tokio::process::Command;
30
+
31
+ let prompt = if let Some(task_description) = task.description {
32
+ format!(
33
+ r#"project_id: {}
34
+
35
+ Task title: {}
36
+ Task description: {}"#,
37
+ task.project_id, task.title, task_description
38
+ )
39
+ } else {
40
+ format!(
41
+ r#"project_id: {}
42
+
43
+ Task title: {}"#,
44
+ task.project_id, task.title
45
+ )
46
+ };
47
+
48
+ // Use shell command for cross-platform compatibility
49
+ let (shell_cmd, shell_arg) = get_shell_command();
50
+ let opencode_command = format!(
51
+ "opencode -p \"{}\" --output-format=json",
52
+ prompt.replace('"', "\\\"")
53
+ );
54
+
55
+ let mut command = Command::new(shell_cmd);
56
+ command
57
+ .kill_on_drop(true)
58
+ .stdout(Stdio::piped())
59
+ .stderr(Stdio::piped())
60
+ .current_dir(worktree_path)
61
+ .arg(shell_arg)
62
+ .arg(opencode_command);
63
+
64
+ let child = command
65
+ .group_spawn() // Create new process group so we can kill entire tree
66
+ .map_err(|e| {
67
+ crate::executor::SpawnContext::from_command(&command, "OpenCode AI")
68
+ .with_task(task_id, Some(task.title.clone()))
69
+ .with_context("OpenCode AI CLI execution for new task")
70
+ .spawn_error(e)
71
+ })?;
72
+
73
+ Ok(child)
74
+ }
75
+
76
+ async fn spawn_followup(
77
+ &self,
78
+ _pool: &sqlx::SqlitePool,
79
+ _task_id: Uuid,
80
+ _session_id: &str,
81
+ prompt: &str,
82
+ worktree_path: &str,
83
+ ) -> Result<AsyncGroupChild, ExecutorError> {
84
+ use std::process::Stdio;
85
+
86
+ use tokio::process::Command;
87
+
88
+ // CharmOpencode doesn't support session-based followup, so we ignore session_id
89
+ // and just run with the new prompt
90
+ let (shell_cmd, shell_arg) = get_shell_command();
91
+ let opencode_command = format!(
92
+ "opencode -p \"{}\" --output-format=json",
93
+ prompt.replace('"', "\\\"")
94
+ );
95
+
96
+ let mut command = Command::new(shell_cmd);
97
+ command
98
+ .kill_on_drop(true)
99
+ .stdout(Stdio::piped())
100
+ .stderr(Stdio::piped())
101
+ .current_dir(worktree_path)
102
+ .arg(shell_arg)
103
+ .arg(&opencode_command);
104
+
105
+ let child = command.group_spawn().map_err(|e| {
106
+ crate::executor::SpawnContext::from_command(&command, "OpenCode AI")
107
+ .with_context("OpenCode AI CLI followup execution")
108
+ .spawn_error(e)
109
+ })?;
110
+
111
+ Ok(child)
112
+ }
113
+ }
@@ -0,0 +1,130 @@
1
+ use async_trait::async_trait;
2
+ use command_group::{AsyncCommandGroup, AsyncGroupChild};
3
+ use tokio::process::Command;
4
+ use uuid::Uuid;
5
+
6
+ use crate::{
7
+ executor::{Executor, ExecutorError},
8
+ models::{project::Project, task::Task},
9
+ utils::shell::get_shell_command,
10
+ };
11
+
12
+ /// Executor for running project setup scripts
13
+ pub struct SetupScriptExecutor {
14
+ pub script: String,
15
+ }
16
+
17
+ impl SetupScriptExecutor {
18
+ pub fn new(script: String) -> Self {
19
+ Self { script }
20
+ }
21
+ }
22
+
23
+ #[async_trait]
24
+ impl Executor for SetupScriptExecutor {
25
+ async fn spawn(
26
+ &self,
27
+ pool: &sqlx::SqlitePool,
28
+ task_id: Uuid,
29
+ worktree_path: &str,
30
+ ) -> Result<AsyncGroupChild, ExecutorError> {
31
+ // Validate the task and project exist
32
+ let task = Task::find_by_id(pool, task_id)
33
+ .await?
34
+ .ok_or(ExecutorError::TaskNotFound)?;
35
+
36
+ let _project = Project::find_by_id(pool, task.project_id)
37
+ .await?
38
+ .ok_or(ExecutorError::TaskNotFound)?; // Reuse TaskNotFound for simplicity
39
+
40
+ let (shell_cmd, shell_arg) = get_shell_command();
41
+ let mut command = Command::new(shell_cmd);
42
+ command
43
+ .kill_on_drop(true)
44
+ .stdout(std::process::Stdio::piped())
45
+ .stderr(std::process::Stdio::piped())
46
+ .arg(shell_arg)
47
+ .arg(&self.script)
48
+ .current_dir(worktree_path);
49
+
50
+ let child = command.group_spawn().map_err(|e| {
51
+ crate::executor::SpawnContext::from_command(&command, "SetupScript")
52
+ .with_task(task_id, Some(task.title.clone()))
53
+ .with_context("Setup script execution")
54
+ .spawn_error(e)
55
+ })?;
56
+
57
+ Ok(child)
58
+ }
59
+
60
+ /// Normalize setup script logs into a readable format
61
+ fn normalize_logs(
62
+ &self,
63
+ logs: &str,
64
+ _worktree_path: &str,
65
+ ) -> Result<crate::executor::NormalizedConversation, String> {
66
+ let mut entries = Vec::new();
67
+
68
+ // Add script command as first entry
69
+ entries.push(crate::executor::NormalizedEntry {
70
+ timestamp: None,
71
+ entry_type: crate::executor::NormalizedEntryType::SystemMessage,
72
+ content: format!("Executing setup script:\n{}", self.script),
73
+ metadata: None,
74
+ });
75
+
76
+ // Process the logs - split by lines and create entries
77
+ if !logs.trim().is_empty() {
78
+ let lines: Vec<&str> = logs.lines().collect();
79
+ let mut current_chunk = String::new();
80
+
81
+ for line in lines {
82
+ current_chunk.push_str(line);
83
+ current_chunk.push('\n');
84
+
85
+ // Create entry for every 10 lines or when we encounter an error-like line
86
+ if current_chunk.lines().count() >= 10
87
+ || line.to_lowercase().contains("error")
88
+ || line.to_lowercase().contains("failed")
89
+ || line.to_lowercase().contains("exception")
90
+ {
91
+ let entry_type = if line.to_lowercase().contains("error")
92
+ || line.to_lowercase().contains("failed")
93
+ || line.to_lowercase().contains("exception")
94
+ {
95
+ crate::executor::NormalizedEntryType::ErrorMessage
96
+ } else {
97
+ crate::executor::NormalizedEntryType::SystemMessage
98
+ };
99
+
100
+ entries.push(crate::executor::NormalizedEntry {
101
+ timestamp: Some(chrono::Utc::now().to_rfc3339()),
102
+ entry_type,
103
+ content: current_chunk.trim().to_string(),
104
+ metadata: None,
105
+ });
106
+
107
+ current_chunk.clear();
108
+ }
109
+ }
110
+
111
+ // Add any remaining content
112
+ if !current_chunk.trim().is_empty() {
113
+ entries.push(crate::executor::NormalizedEntry {
114
+ timestamp: Some(chrono::Utc::now().to_rfc3339()),
115
+ entry_type: crate::executor::NormalizedEntryType::SystemMessage,
116
+ content: current_chunk.trim().to_string(),
117
+ metadata: None,
118
+ });
119
+ }
120
+ }
121
+
122
+ Ok(crate::executor::NormalizedConversation {
123
+ entries,
124
+ session_id: None,
125
+ executor_type: "setup-script".to_string(),
126
+ prompt: Some(self.script.clone()),
127
+ summary: None,
128
+ })
129
+ }
130
+ }
@@ -0,0 +1,184 @@
1
+ use lazy_static::lazy_static;
2
+ use regex::Regex;
3
+
4
+ lazy_static! {
5
+ static ref OPENCODE_LOG_REGEX: Regex = Regex::new(r"^(INFO|DEBUG|WARN|ERROR)\s+.*").unwrap();
6
+ static ref SESSION_ID_REGEX: Regex = Regex::new(r".*\b(id|session|sessionID)=([^ ]+)").unwrap();
7
+ static ref TOOL_USAGE_REGEX: Regex = Regex::new(r"^\|\s*([a-zA-Z]+)\s*(.*)").unwrap();
8
+ static ref NPM_WARN_REGEX: Regex = Regex::new(r"^npm warn .*").unwrap();
9
+ }
10
+
11
+ /// Filter for OpenCode stderr output
12
+ pub struct OpenCodeFilter;
13
+
14
+ impl OpenCodeFilter {
15
+ /// Check if a line should be skipped as noise
16
+ pub fn is_noise(line: &str) -> bool {
17
+ let trimmed = line.trim();
18
+
19
+ // Empty lines are noise
20
+ if trimmed.is_empty() {
21
+ return true;
22
+ }
23
+
24
+ // Strip ANSI escape codes for analysis
25
+ let cleaned = Self::strip_ansi_codes(trimmed);
26
+ let cleaned_trim = cleaned.trim();
27
+
28
+ // Skip tool calls - they are NOT noise
29
+ if TOOL_USAGE_REGEX.is_match(cleaned_trim) {
30
+ return false;
31
+ }
32
+
33
+ // OpenCode log lines are noise (includes session logs)
34
+ if is_opencode_log_line(cleaned_trim) {
35
+ return true;
36
+ }
37
+
38
+ if NPM_WARN_REGEX.is_match(cleaned_trim) {
39
+ return true;
40
+ }
41
+
42
+ // Spinner glyphs
43
+ if cleaned_trim.len() == 1 && "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏".contains(cleaned_trim) {
44
+ return true;
45
+ }
46
+
47
+ // Banner lines containing block glyphs (Unicode Block Elements range)
48
+ if cleaned_trim
49
+ .chars()
50
+ .any(|c| ('\u{2580}'..='\u{259F}').contains(&c))
51
+ {
52
+ return true;
53
+ }
54
+
55
+ // UI/stats frames using Box Drawing glyphs (U+2500-257F)
56
+ if cleaned_trim
57
+ .chars()
58
+ .any(|c| ('\u{2500}'..='\u{257F}').contains(&c))
59
+ {
60
+ return true;
61
+ }
62
+
63
+ // Model banner (@ with spaces)
64
+ if cleaned_trim.starts_with("@ ") {
65
+ return true;
66
+ }
67
+
68
+ // Share link
69
+ if cleaned_trim.starts_with("~") && cleaned_trim.contains("https://opencode.ai/s/") {
70
+ return true;
71
+ }
72
+
73
+ // Everything else (assistant messages) is NOT noise
74
+ false
75
+ }
76
+
77
+ pub fn is_stderr(_line: &str) -> bool {
78
+ false
79
+ }
80
+
81
+ /// Strip ANSI escape codes from text (conservative)
82
+ pub fn strip_ansi_codes(text: &str) -> String {
83
+ // Handle both unicode escape sequences and raw ANSI codes
84
+ let result = text.replace("\\u001b", "\x1b");
85
+
86
+ let mut cleaned = String::new();
87
+ let mut chars = result.chars().peekable();
88
+
89
+ while let Some(ch) = chars.next() {
90
+ if ch == '\x1b' {
91
+ // Skip ANSI escape sequence
92
+ if chars.peek() == Some(&'[') {
93
+ chars.next(); // consume '['
94
+ // Skip until we find a letter (end of ANSI sequence)
95
+ for next_ch in chars.by_ref() {
96
+ if next_ch.is_ascii_alphabetic() {
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ } else {
102
+ cleaned.push(ch);
103
+ }
104
+ }
105
+
106
+ cleaned
107
+ }
108
+ }
109
+
110
+ /// Detect if a line is an OpenCode log line format using regex
111
+ pub fn is_opencode_log_line(line: &str) -> bool {
112
+ OPENCODE_LOG_REGEX.is_match(line)
113
+ }
114
+
115
+ /// Parse session_id from OpenCode log lines
116
+ pub fn parse_session_id_from_line(line: &str) -> Option<String> {
117
+ // Only apply to OpenCode log lines
118
+ if !is_opencode_log_line(line) {
119
+ return None;
120
+ }
121
+
122
+ // Try regex for session ID extraction from service=session logs
123
+ if let Some(captures) = SESSION_ID_REGEX.captures(line) {
124
+ if let Some(id) = captures.get(2) {
125
+ return Some(id.as_str().to_string());
126
+ }
127
+ }
128
+
129
+ None
130
+ }
131
+
132
+ /// Get the tool usage regex for parsing tool patterns
133
+ pub fn tool_usage_regex() -> &'static Regex {
134
+ &TOOL_USAGE_REGEX
135
+ }
136
+
137
+ #[cfg(test)]
138
+ mod tests {
139
+ #[test]
140
+ fn test_session_id_extraction() {
141
+ use crate::executors::sst_opencode::filter::parse_session_id_from_line;
142
+
143
+ // Test session ID extraction from session= format (only works on OpenCode log lines)
144
+ assert_eq!(
145
+ parse_session_id_from_line("INFO session=ses_abc123 starting"),
146
+ Some("ses_abc123".to_string())
147
+ );
148
+
149
+ assert_eq!(
150
+ parse_session_id_from_line("DEBUG id=debug_id process"),
151
+ Some("debug_id".to_string())
152
+ );
153
+
154
+ // Test lines without log prefix (should return None)
155
+ assert_eq!(
156
+ parse_session_id_from_line("session=simple_id chatting"),
157
+ None
158
+ );
159
+
160
+ // Test no session ID
161
+ assert_eq!(parse_session_id_from_line("No session here"), None);
162
+ assert_eq!(parse_session_id_from_line(""), None);
163
+ }
164
+
165
+ #[test]
166
+ fn test_ansi_code_stripping() {
167
+ use crate::executors::sst_opencode::filter::OpenCodeFilter;
168
+
169
+ // Test ANSI escape sequence removal
170
+ let ansi_text = "\x1b[31mRed text\x1b[0m normal text";
171
+ let cleaned = OpenCodeFilter::strip_ansi_codes(ansi_text);
172
+ assert_eq!(cleaned, "Red text normal text");
173
+
174
+ // Test unicode escape sequences
175
+ let unicode_ansi = "Text with \\u001b[32mgreen\\u001b[0m color";
176
+ let cleaned = OpenCodeFilter::strip_ansi_codes(unicode_ansi);
177
+ assert_eq!(cleaned, "Text with green color");
178
+
179
+ // Test text without ANSI codes (unchanged)
180
+ let plain_text = "Regular text without codes";
181
+ let cleaned = OpenCodeFilter::strip_ansi_codes(plain_text);
182
+ assert_eq!(cleaned, plain_text);
183
+ }
184
+ }
@@ -0,0 +1,139 @@
1
+ use serde_json::{json, Value};
2
+
3
+ use crate::utils::path::make_path_relative;
4
+
5
+ /// Normalize tool names to match frontend expectations for purple box styling
6
+ pub fn normalize_tool_name(tool_name: &str) -> String {
7
+ match tool_name {
8
+ "Todo" => "todowrite".to_string(), // Generic TODO tool → todowrite
9
+ "TodoWrite" => "todowrite".to_string(),
10
+ "TodoRead" => "todoread".to_string(),
11
+ _ => tool_name.to_string(),
12
+ }
13
+ }
14
+
15
+ /// Helper function to determine action type for tool usage
16
+ pub fn determine_action_type(tool_name: &str, input: &Value, worktree_path: &str) -> Value {
17
+ match tool_name.to_lowercase().as_str() {
18
+ "read" => {
19
+ if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
20
+ json!({
21
+ "action": "file_read",
22
+ "path": make_path_relative(file_path, worktree_path)
23
+ })
24
+ } else {
25
+ json!({"action": "other", "description": "File read operation"})
26
+ }
27
+ }
28
+ "write" | "edit" => {
29
+ if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
30
+ json!({
31
+ "action": "file_write",
32
+ "path": make_path_relative(file_path, worktree_path)
33
+ })
34
+ } else {
35
+ json!({"action": "other", "description": "File write operation"})
36
+ }
37
+ }
38
+ "bash" => {
39
+ if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
40
+ json!({"action": "command_run", "command": command})
41
+ } else {
42
+ json!({"action": "other", "description": "Command execution"})
43
+ }
44
+ }
45
+ "grep" => {
46
+ if let Some(pattern) = input.get("pattern").and_then(|p| p.as_str()) {
47
+ json!({"action": "search", "query": pattern})
48
+ } else {
49
+ json!({"action": "other", "description": "Search operation"})
50
+ }
51
+ }
52
+ "todowrite" | "todoread" => {
53
+ json!({"action": "other", "description": "TODO list management"})
54
+ }
55
+ _ => json!({"action": "other", "description": format!("Tool: {}", tool_name)}),
56
+ }
57
+ }
58
+
59
+ /// Helper function to generate concise content for tool usage
60
+ pub fn generate_tool_content(tool_name: &str, input: &Value, worktree_path: &str) -> String {
61
+ match tool_name.to_lowercase().as_str() {
62
+ "read" => {
63
+ if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
64
+ format!("`{}`", make_path_relative(file_path, worktree_path))
65
+ } else {
66
+ "Read file".to_string()
67
+ }
68
+ }
69
+ "write" | "edit" => {
70
+ if let Some(file_path) = input.get("filePath").and_then(|p| p.as_str()) {
71
+ format!("`{}`", make_path_relative(file_path, worktree_path))
72
+ } else {
73
+ "Write file".to_string()
74
+ }
75
+ }
76
+ "bash" => {
77
+ if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
78
+ format!("`{}`", command)
79
+ } else {
80
+ "Execute command".to_string()
81
+ }
82
+ }
83
+ "todowrite" | "todoread" => generate_todo_content(input),
84
+ _ => format!("`{}`", tool_name),
85
+ }
86
+ }
87
+
88
+ /// Generate formatted content for TODO tools
89
+ fn generate_todo_content(input: &Value) -> String {
90
+ // Extract todo list from input to show actual todos
91
+ if let Some(todos) = input.get("todos").and_then(|t| t.as_array()) {
92
+ let mut todo_items = Vec::new();
93
+ for todo in todos {
94
+ if let Some(content) = todo.get("content").and_then(|c| c.as_str()) {
95
+ let status = todo
96
+ .get("status")
97
+ .and_then(|s| s.as_str())
98
+ .unwrap_or("pending");
99
+ let status_emoji = match status {
100
+ "completed" => "✅",
101
+ "in_progress" => "🔄",
102
+ "pending" | "todo" => "⏳",
103
+ _ => "📝",
104
+ };
105
+ let priority = todo
106
+ .get("priority")
107
+ .and_then(|p| p.as_str())
108
+ .unwrap_or("medium");
109
+ todo_items.push(format!("{} {} ({})", status_emoji, content, priority));
110
+ }
111
+ }
112
+ if !todo_items.is_empty() {
113
+ format!("TODO List:\n{}", todo_items.join("\n"))
114
+ } else {
115
+ "Managing TODO list".to_string()
116
+ }
117
+ } else {
118
+ "Managing TODO list".to_string()
119
+ }
120
+ }
121
+
122
+ #[cfg(test)]
123
+ mod tests {
124
+ #[test]
125
+ fn test_normalize_tool_name() {
126
+ use crate::executors::sst_opencode::tools::normalize_tool_name;
127
+
128
+ // Test TODO tool normalization
129
+ assert_eq!(normalize_tool_name("Todo"), "todowrite");
130
+ assert_eq!(normalize_tool_name("TodoWrite"), "todowrite");
131
+ assert_eq!(normalize_tool_name("TodoRead"), "todoread");
132
+
133
+ // Test other tools remain unchanged
134
+ assert_eq!(normalize_tool_name("Read"), "Read");
135
+ assert_eq!(normalize_tool_name("Write"), "Write");
136
+ assert_eq!(normalize_tool_name("bash"), "bash");
137
+ assert_eq!(normalize_tool_name("SomeOtherTool"), "SomeOtherTool");
138
+ }
139
+ }