automagik-forge 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +143 -447
  2. package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
  3. package/{npx-cli/automagik-forge-0.0.55.tgz → dist/linux-x64/automagik-forge.zip} +0 -0
  4. package/package.json +13 -23
  5. package/.cargo/config.toml +0 -13
  6. package/.claude/commands/commit.md +0 -376
  7. package/.claude/commands/prompt.md +0 -871
  8. package/.env.example +0 -20
  9. package/.github/actions/setup-node/action.yml +0 -29
  10. package/.github/images/automagik-logo.png +0 -0
  11. package/.github/workflows/pre-release.yml +0 -470
  12. package/.github/workflows/publish.yml +0 -145
  13. package/.github/workflows/test.yml +0 -63
  14. package/.mcp.json +0 -57
  15. package/AGENT.md +0 -40
  16. package/CLAUDE.md +0 -40
  17. package/CODE-OF-CONDUCT.md +0 -89
  18. package/Cargo.toml +0 -19
  19. package/Dockerfile +0 -43
  20. package/LICENSE +0 -201
  21. package/Makefile +0 -97
  22. package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +0 -12
  23. package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +0 -12
  24. package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +0 -12
  25. package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +0 -68
  26. package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +0 -12
  27. package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +0 -12
  28. package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +0 -12
  29. package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +0 -104
  30. package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +0 -12
  31. package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +0 -32
  32. package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +0 -56
  33. package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +0 -68
  34. package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +0 -92
  35. package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +0 -12
  36. package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +0 -56
  37. package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +0 -62
  38. package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +0 -62
  39. package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +0 -56
  40. package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +0 -32
  41. package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +0 -104
  42. package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +0 -62
  43. package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +0 -56
  44. package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +0 -92
  45. package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +0 -62
  46. package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +0 -12
  47. package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +0 -104
  48. package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +0 -104
  49. package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +0 -68
  50. package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +0 -62
  51. package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +0 -62
  52. package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +0 -62
  53. package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +0 -50
  54. package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +0 -68
  55. package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +0 -12
  56. package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +0 -68
  57. package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +0 -12
  58. package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +0 -12
  59. package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +0 -62
  60. package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +0 -104
  61. package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +0 -20
  62. package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +0 -104
  63. package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +0 -56
  64. package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +0 -104
  65. package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +0 -26
  66. package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +0 -62
  67. package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +0 -12
  68. package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +0 -104
  69. package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +0 -104
  70. package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +0 -12
  71. package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +0 -68
  72. package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +0 -62
  73. package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +0 -20
  74. package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +0 -12
  75. package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +0 -12
  76. package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +0 -12
  77. package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +0 -20
  78. package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +0 -12
  79. package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +0 -12
  80. package/backend/Cargo.toml +0 -71
  81. package/backend/build.rs +0 -32
  82. package/backend/migrations/20250617183714_init.sql +0 -44
  83. package/backend/migrations/20250620212427_execution_processes.sql +0 -25
  84. package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +0 -28
  85. package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +0 -23
  86. package/backend/migrations/20250623120000_executor_sessions.sql +0 -17
  87. package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +0 -4
  88. package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +0 -4
  89. package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +0 -2
  90. package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +0 -5
  91. package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +0 -2
  92. package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +0 -2
  93. package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +0 -2
  94. package/backend/migrations/20250710000000_add_setup_completion.sql +0 -3
  95. package/backend/migrations/20250715154859_add_task_templates.sql +0 -25
  96. package/backend/migrations/20250716143725_add_default_templates.sql +0 -174
  97. package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +0 -20
  98. package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +0 -7
  99. package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +0 -9
  100. package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +0 -2
  101. package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +0 -25
  102. package/backend/migrations/20250723000000_add_wish_to_tasks.sql +0 -7
  103. package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +0 -5
  104. package/backend/scripts/toast-notification.ps1 +0 -23
  105. package/backend/sounds/abstract-sound1.wav +0 -0
  106. package/backend/sounds/abstract-sound2.wav +0 -0
  107. package/backend/sounds/abstract-sound3.wav +0 -0
  108. package/backend/sounds/abstract-sound4.wav +0 -0
  109. package/backend/sounds/cow-mooing.wav +0 -0
  110. package/backend/sounds/phone-vibration.wav +0 -0
  111. package/backend/sounds/rooster.wav +0 -0
  112. package/backend/src/app_state.rs +0 -218
  113. package/backend/src/bin/generate_types.rs +0 -189
  114. package/backend/src/bin/mcp_task_server.rs +0 -191
  115. package/backend/src/execution_monitor.rs +0 -1193
  116. package/backend/src/executor.rs +0 -1053
  117. package/backend/src/executors/amp.rs +0 -697
  118. package/backend/src/executors/ccr.rs +0 -91
  119. package/backend/src/executors/charm_opencode.rs +0 -113
  120. package/backend/src/executors/claude.rs +0 -887
  121. package/backend/src/executors/cleanup_script.rs +0 -124
  122. package/backend/src/executors/dev_server.rs +0 -53
  123. package/backend/src/executors/echo.rs +0 -79
  124. package/backend/src/executors/gemini/config.rs +0 -67
  125. package/backend/src/executors/gemini/streaming.rs +0 -363
  126. package/backend/src/executors/gemini.rs +0 -765
  127. package/backend/src/executors/mod.rs +0 -23
  128. package/backend/src/executors/opencode_ai.rs +0 -113
  129. package/backend/src/executors/setup_script.rs +0 -130
  130. package/backend/src/executors/sst_opencode/filter.rs +0 -184
  131. package/backend/src/executors/sst_opencode/tools.rs +0 -139
  132. package/backend/src/executors/sst_opencode.rs +0 -756
  133. package/backend/src/lib.rs +0 -45
  134. package/backend/src/main.rs +0 -324
  135. package/backend/src/mcp/mod.rs +0 -1
  136. package/backend/src/mcp/task_server.rs +0 -850
  137. package/backend/src/middleware/mod.rs +0 -3
  138. package/backend/src/middleware/model_loaders.rs +0 -242
  139. package/backend/src/models/api_response.rs +0 -36
  140. package/backend/src/models/config.rs +0 -375
  141. package/backend/src/models/execution_process.rs +0 -430
  142. package/backend/src/models/executor_session.rs +0 -225
  143. package/backend/src/models/mod.rs +0 -12
  144. package/backend/src/models/project.rs +0 -356
  145. package/backend/src/models/task.rs +0 -345
  146. package/backend/src/models/task_attempt.rs +0 -1214
  147. package/backend/src/models/task_template.rs +0 -146
  148. package/backend/src/openapi.rs +0 -93
  149. package/backend/src/routes/auth.rs +0 -297
  150. package/backend/src/routes/config.rs +0 -385
  151. package/backend/src/routes/filesystem.rs +0 -228
  152. package/backend/src/routes/health.rs +0 -16
  153. package/backend/src/routes/mod.rs +0 -9
  154. package/backend/src/routes/projects.rs +0 -562
  155. package/backend/src/routes/stream.rs +0 -244
  156. package/backend/src/routes/task_attempts.rs +0 -1172
  157. package/backend/src/routes/task_templates.rs +0 -229
  158. package/backend/src/routes/tasks.rs +0 -353
  159. package/backend/src/services/analytics.rs +0 -216
  160. package/backend/src/services/git_service.rs +0 -1321
  161. package/backend/src/services/github_service.rs +0 -307
  162. package/backend/src/services/mod.rs +0 -13
  163. package/backend/src/services/notification_service.rs +0 -263
  164. package/backend/src/services/pr_monitor.rs +0 -214
  165. package/backend/src/services/process_service.rs +0 -940
  166. package/backend/src/utils/path.rs +0 -96
  167. package/backend/src/utils/shell.rs +0 -19
  168. package/backend/src/utils/text.rs +0 -24
  169. package/backend/src/utils/worktree_manager.rs +0 -578
  170. package/backend/src/utils.rs +0 -125
  171. package/backend/test.db +0 -0
  172. package/build-npm-package.sh +0 -61
  173. package/dev_assets_seed/config.json +0 -19
  174. package/frontend/.eslintrc.json +0 -25
  175. package/frontend/.prettierrc.json +0 -8
  176. package/frontend/components.json +0 -17
  177. package/frontend/index.html +0 -19
  178. package/frontend/package-lock.json +0 -7321
  179. package/frontend/package.json +0 -61
  180. package/frontend/postcss.config.js +0 -6
  181. package/frontend/public/android-chrome-192x192.png +0 -0
  182. package/frontend/public/android-chrome-512x512.png +0 -0
  183. package/frontend/public/apple-touch-icon.png +0 -0
  184. package/frontend/public/automagik-forge-logo-dark.svg +0 -3
  185. package/frontend/public/automagik-forge-logo.svg +0 -3
  186. package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
  187. package/frontend/public/favicon-16x16.png +0 -0
  188. package/frontend/public/favicon-32x32.png +0 -0
  189. package/frontend/public/favicon.ico +0 -0
  190. package/frontend/public/site.webmanifest +0 -1
  191. package/frontend/public/viba-kanban-favicon.png +0 -0
  192. package/frontend/src/App.tsx +0 -157
  193. package/frontend/src/components/DisclaimerDialog.tsx +0 -106
  194. package/frontend/src/components/GitHubLoginDialog.tsx +0 -314
  195. package/frontend/src/components/OnboardingDialog.tsx +0 -185
  196. package/frontend/src/components/PrivacyOptInDialog.tsx +0 -130
  197. package/frontend/src/components/ProvidePatDialog.tsx +0 -98
  198. package/frontend/src/components/TaskTemplateManager.tsx +0 -336
  199. package/frontend/src/components/config-provider.tsx +0 -119
  200. package/frontend/src/components/context/TaskDetailsContextProvider.tsx +0 -470
  201. package/frontend/src/components/context/taskDetailsContext.ts +0 -125
  202. package/frontend/src/components/keyboard-shortcuts-demo.tsx +0 -35
  203. package/frontend/src/components/layout/navbar.tsx +0 -86
  204. package/frontend/src/components/logo.tsx +0 -44
  205. package/frontend/src/components/projects/ProjectCard.tsx +0 -155
  206. package/frontend/src/components/projects/project-detail.tsx +0 -251
  207. package/frontend/src/components/projects/project-form-fields.tsx +0 -238
  208. package/frontend/src/components/projects/project-form.tsx +0 -301
  209. package/frontend/src/components/projects/project-list.tsx +0 -200
  210. package/frontend/src/components/projects/projects-page.tsx +0 -20
  211. package/frontend/src/components/tasks/BranchSelector.tsx +0 -169
  212. package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +0 -94
  213. package/frontend/src/components/tasks/EditorSelectionDialog.tsx +0 -119
  214. package/frontend/src/components/tasks/TaskCard.tsx +0 -154
  215. package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +0 -33
  216. package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +0 -109
  217. package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +0 -135
  218. package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +0 -296
  219. package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +0 -32
  220. package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +0 -392
  221. package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +0 -256
  222. package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +0 -56
  223. package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +0 -92
  224. package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +0 -22
  225. package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +0 -49
  226. package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +0 -186
  227. package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +0 -288
  228. package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +0 -216
  229. package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +0 -93
  230. package/frontend/src/components/tasks/TaskDetailsHeader.tsx +0 -169
  231. package/frontend/src/components/tasks/TaskDetailsPanel.tsx +0 -126
  232. package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +0 -302
  233. package/frontend/src/components/tasks/TaskFollowUpSection.tsx +0 -130
  234. package/frontend/src/components/tasks/TaskFormDialog.tsx +0 -400
  235. package/frontend/src/components/tasks/TaskKanbanBoard.tsx +0 -180
  236. package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +0 -259
  237. package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +0 -243
  238. package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +0 -899
  239. package/frontend/src/components/tasks/index.ts +0 -2
  240. package/frontend/src/components/theme-provider.tsx +0 -82
  241. package/frontend/src/components/theme-toggle.tsx +0 -36
  242. package/frontend/src/components/ui/alert.tsx +0 -59
  243. package/frontend/src/components/ui/auto-expanding-textarea.tsx +0 -70
  244. package/frontend/src/components/ui/badge.tsx +0 -36
  245. package/frontend/src/components/ui/button.tsx +0 -56
  246. package/frontend/src/components/ui/card.tsx +0 -86
  247. package/frontend/src/components/ui/checkbox.tsx +0 -44
  248. package/frontend/src/components/ui/chip.tsx +0 -25
  249. package/frontend/src/components/ui/dialog.tsx +0 -124
  250. package/frontend/src/components/ui/dropdown-menu.tsx +0 -198
  251. package/frontend/src/components/ui/file-search-textarea.tsx +0 -292
  252. package/frontend/src/components/ui/folder-picker.tsx +0 -279
  253. package/frontend/src/components/ui/input.tsx +0 -25
  254. package/frontend/src/components/ui/label.tsx +0 -24
  255. package/frontend/src/components/ui/loader.tsx +0 -26
  256. package/frontend/src/components/ui/markdown-renderer.tsx +0 -75
  257. package/frontend/src/components/ui/select.tsx +0 -160
  258. package/frontend/src/components/ui/separator.tsx +0 -31
  259. package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +0 -185
  260. package/frontend/src/components/ui/table.tsx +0 -117
  261. package/frontend/src/components/ui/tabs.tsx +0 -53
  262. package/frontend/src/components/ui/textarea.tsx +0 -22
  263. package/frontend/src/components/ui/tooltip.tsx +0 -28
  264. package/frontend/src/hooks/useNormalizedConversation.ts +0 -440
  265. package/frontend/src/index.css +0 -225
  266. package/frontend/src/lib/api.ts +0 -630
  267. package/frontend/src/lib/keyboard-shortcuts.ts +0 -266
  268. package/frontend/src/lib/responsive-config.ts +0 -70
  269. package/frontend/src/lib/types.ts +0 -39
  270. package/frontend/src/lib/utils.ts +0 -10
  271. package/frontend/src/main.tsx +0 -50
  272. package/frontend/src/pages/McpServers.tsx +0 -418
  273. package/frontend/src/pages/Settings.tsx +0 -610
  274. package/frontend/src/pages/project-tasks.tsx +0 -575
  275. package/frontend/src/pages/projects.tsx +0 -18
  276. package/frontend/src/vite-env.d.ts +0 -1
  277. package/frontend/tailwind.config.js +0 -125
  278. package/frontend/tsconfig.json +0 -26
  279. package/frontend/tsconfig.node.json +0 -10
  280. package/frontend/vite.config.ts +0 -33
  281. package/npx-cli/README.md +0 -159
  282. package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
  283. package/npx-cli/automagik-forge-0.1.10.tgz +0 -0
  284. package/npx-cli/package.json +0 -17
  285. package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
  286. package/pnpm-workspace.yaml +0 -2
  287. package/rust-toolchain.toml +0 -11
  288. package/rustfmt.toml +0 -3
  289. package/scripts/load-env.js +0 -43
  290. package/scripts/mcp_test.js +0 -374
  291. package/scripts/prepare-db.js +0 -45
  292. package/scripts/setup-dev-environment.js +0 -274
  293. package/scripts/start-mcp-sse.js +0 -70
  294. package/scripts/test-debug.js +0 -32
  295. package/scripts/test-mcp-sse.js +0 -138
  296. package/scripts/test-simple.js +0 -44
  297. package/scripts/test-wish-final.js +0 -179
  298. package/scripts/test-wish-system.js +0 -221
  299. package/shared/types.ts +0 -182
  300. package/test-npm-package.sh +0 -42
  301. /package/{npx-cli/bin → bin}/cli.js +0 -0
@@ -1,697 +0,0 @@
1
- use std::path::Path;
2
-
3
- use async_trait::async_trait;
4
- use command_group::{AsyncCommandGroup, AsyncGroupChild};
5
- use serde::{Deserialize, Serialize};
6
- use uuid::Uuid;
7
-
8
- use crate::{
9
- executor::{
10
- ActionType, Executor, ExecutorError, NormalizedConversation, NormalizedEntry,
11
- NormalizedEntryType,
12
- },
13
- models::task::Task,
14
- utils::shell::get_shell_command,
15
- };
16
-
17
- /// An executor that uses Amp to process tasks
18
- pub struct AmpExecutor;
19
-
20
- #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
21
- #[serde(tag = "type")]
22
- pub enum AmpJson {
23
- #[serde(rename = "messages")]
24
- Messages {
25
- messages: Vec<(usize, AmpMessage)>,
26
- #[serde(rename = "toolResults")]
27
- tool_results: Vec<serde_json::Value>,
28
- },
29
- #[serde(rename = "initial")]
30
- Initial {
31
- #[serde(rename = "threadID")]
32
- thread_id: Option<String>,
33
- },
34
- #[serde(rename = "token-usage")]
35
- TokenUsage(serde_json::Value),
36
- #[serde(rename = "state")]
37
- State { state: String },
38
- #[serde(rename = "shutdown")]
39
- Shutdown,
40
- #[serde(rename = "tool-status")]
41
- ToolStatus(serde_json::Value),
42
- }
43
-
44
- #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
45
- pub struct AmpMessage {
46
- pub role: String,
47
- pub content: Vec<AmpContentItem>,
48
- pub state: Option<serde_json::Value>,
49
- pub meta: Option<AmpMeta>,
50
- }
51
-
52
- #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
53
- pub struct AmpMeta {
54
- #[serde(rename = "sentAt")]
55
- pub sent_at: u64,
56
- }
57
-
58
- #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
59
- #[serde(tag = "type")]
60
- pub enum AmpContentItem {
61
- #[serde(rename = "text")]
62
- Text { text: String },
63
- #[serde(rename = "thinking")]
64
- Thinking { thinking: String },
65
- #[serde(rename = "tool_use")]
66
- ToolUse {
67
- id: String,
68
- name: String,
69
- input: serde_json::Value,
70
- },
71
- #[serde(rename = "tool_result")]
72
- ToolResult {
73
- #[serde(rename = "toolUseID")]
74
- tool_use_id: String,
75
- run: serde_json::Value,
76
- },
77
- }
78
-
79
- impl AmpJson {
80
- pub fn should_process(&self) -> bool {
81
- matches!(self, AmpJson::Messages { .. })
82
- }
83
-
84
- pub fn extract_session_id(&self) -> Option<String> {
85
- match self {
86
- AmpJson::Initial { thread_id } => thread_id.clone(),
87
- _ => None,
88
- }
89
- }
90
-
91
- pub fn has_streaming_content(&self) -> bool {
92
- match self {
93
- AmpJson::Messages { messages, .. } => messages.iter().any(|(_index, message)| {
94
- if let Some(state) = &message.state {
95
- if let Some(state_type) = state.get("type").and_then(|t| t.as_str()) {
96
- state_type == "streaming"
97
- } else {
98
- false
99
- }
100
- } else {
101
- false
102
- }
103
- }),
104
- _ => false,
105
- }
106
- }
107
-
108
- pub fn to_normalized_entries(
109
- &self,
110
- executor: &AmpExecutor,
111
- worktree_path: &str,
112
- ) -> Vec<NormalizedEntry> {
113
- match self {
114
- AmpJson::Messages { messages, .. } => {
115
- if self.has_streaming_content() {
116
- return vec![];
117
- }
118
-
119
- let mut entries = Vec::new();
120
- for (_index, message) in messages {
121
- let role = &message.role;
122
- for content_item in &message.content {
123
- if let Some(entry) =
124
- content_item.to_normalized_entry(role, message, executor, worktree_path)
125
- {
126
- entries.push(entry);
127
- }
128
- }
129
- }
130
- entries
131
- }
132
- _ => vec![],
133
- }
134
- }
135
- }
136
-
137
- impl AmpContentItem {
138
- pub fn to_normalized_entry(
139
- &self,
140
- role: &str,
141
- message: &AmpMessage,
142
- executor: &AmpExecutor,
143
- worktree_path: &str,
144
- ) -> Option<NormalizedEntry> {
145
- use serde_json::Value;
146
-
147
- let timestamp = message.meta.as_ref().map(|meta| meta.sent_at.to_string());
148
-
149
- match self {
150
- AmpContentItem::Text { text } => {
151
- let entry_type = match role {
152
- "user" => NormalizedEntryType::UserMessage,
153
- "assistant" => NormalizedEntryType::AssistantMessage,
154
- _ => return None,
155
- };
156
- Some(NormalizedEntry {
157
- timestamp,
158
- entry_type,
159
- content: text.clone(),
160
- metadata: Some(serde_json::to_value(self).unwrap_or(Value::Null)),
161
- })
162
- }
163
- AmpContentItem::Thinking { thinking } => Some(NormalizedEntry {
164
- timestamp,
165
- entry_type: NormalizedEntryType::Thinking,
166
- content: thinking.clone(),
167
- metadata: Some(serde_json::to_value(self).unwrap_or(Value::Null)),
168
- }),
169
- AmpContentItem::ToolUse { name, input, .. } => {
170
- let action_type = executor.extract_action_type(name, input, worktree_path);
171
- let content =
172
- executor.generate_concise_content(name, input, &action_type, worktree_path);
173
-
174
- Some(NormalizedEntry {
175
- timestamp,
176
- entry_type: NormalizedEntryType::ToolUse {
177
- tool_name: name.clone(),
178
- action_type,
179
- },
180
- content,
181
- metadata: Some(serde_json::to_value(self).unwrap_or(Value::Null)),
182
- })
183
- }
184
- AmpContentItem::ToolResult { .. } => None,
185
- }
186
- }
187
- }
188
-
189
- #[async_trait]
190
- impl Executor for AmpExecutor {
191
- async fn spawn(
192
- &self,
193
- pool: &sqlx::SqlitePool,
194
- task_id: Uuid,
195
- worktree_path: &str,
196
- ) -> Result<AsyncGroupChild, ExecutorError> {
197
- // Get the task to fetch its description
198
- let task = Task::find_by_id(pool, task_id)
199
- .await?
200
- .ok_or(ExecutorError::TaskNotFound)?;
201
-
202
- use std::process::Stdio;
203
-
204
- use tokio::{io::AsyncWriteExt, process::Command};
205
-
206
- let prompt = if let Some(task_description) = task.description {
207
- format!(
208
- r#"project_id: {}
209
-
210
- Task title: {}
211
- Task description: {}"#,
212
- task.project_id, task.title, task_description
213
- )
214
- } else {
215
- format!(
216
- r#"project_id: {}
217
-
218
- Task title: {}"#,
219
- task.project_id, task.title
220
- )
221
- };
222
-
223
- // Use shell command for cross-platform compatibility
224
- let (shell_cmd, shell_arg) = get_shell_command();
225
- // --format=jsonl is deprecated in latest versions of Amp CLI
226
- let amp_command = "npx @sourcegraph/amp@0.0.1752148945-gd8844f --format=jsonl";
227
-
228
- let mut command = Command::new(shell_cmd);
229
- command
230
- .kill_on_drop(true)
231
- .stdin(Stdio::piped()) // <-- open a pipe
232
- .stdout(Stdio::piped())
233
- .stderr(Stdio::piped())
234
- .current_dir(worktree_path)
235
- .arg(shell_arg)
236
- .arg(amp_command);
237
-
238
- let mut child = command
239
- .group_spawn() // Create new process group so we can kill entire tree
240
- .map_err(|e| {
241
- crate::executor::SpawnContext::from_command(&command, "Amp")
242
- .with_task(task_id, Some(task.title.clone()))
243
- .with_context("Amp CLI execution for new task")
244
- .spawn_error(e)
245
- })?;
246
-
247
- // feed the prompt in, then close the pipe so `amp` sees EOF
248
- if let Some(mut stdin) = child.inner().stdin.take() {
249
- stdin.write_all(prompt.as_bytes()).await.unwrap();
250
- stdin.shutdown().await.unwrap(); // or `drop(stdin);`
251
- }
252
-
253
- Ok(child)
254
- }
255
-
256
- async fn spawn_followup(
257
- &self,
258
- _pool: &sqlx::SqlitePool,
259
- _task_id: Uuid,
260
- session_id: &str,
261
- prompt: &str,
262
- worktree_path: &str,
263
- ) -> Result<AsyncGroupChild, ExecutorError> {
264
- use std::process::Stdio;
265
-
266
- use tokio::{io::AsyncWriteExt, process::Command};
267
-
268
- // Use shell command for cross-platform compatibility
269
- let (shell_cmd, shell_arg) = get_shell_command();
270
- let amp_command = format!(
271
- "npx @sourcegraph/amp@0.0.1752148945-gd8844f threads continue {} --format=jsonl",
272
- session_id
273
- );
274
-
275
- let mut command = Command::new(shell_cmd);
276
- command
277
- .kill_on_drop(true)
278
- .stdin(Stdio::piped())
279
- .stdout(Stdio::piped())
280
- .stderr(Stdio::piped())
281
- .current_dir(worktree_path)
282
- .arg(shell_arg)
283
- .arg(&amp_command);
284
-
285
- let mut child = command.group_spawn().map_err(|e| {
286
- crate::executor::SpawnContext::from_command(&command, "Amp")
287
- .with_context(format!(
288
- "Amp CLI followup execution for thread {}",
289
- session_id
290
- ))
291
- .spawn_error(e)
292
- })?;
293
-
294
- // Feed the prompt in, then close the pipe so amp sees EOF
295
- if let Some(mut stdin) = child.inner().stdin.take() {
296
- stdin.write_all(prompt.as_bytes()).await.map_err(|e| {
297
- crate::executor::SpawnContext::from_command(&command, "Amp")
298
- .with_context(format!(
299
- "Failed to write prompt to Amp CLI stdin for thread {}",
300
- session_id
301
- ))
302
- .spawn_error(e)
303
- })?;
304
- stdin.shutdown().await.map_err(|e| {
305
- crate::executor::SpawnContext::from_command(&command, "Amp")
306
- .with_context(format!(
307
- "Failed to close Amp CLI stdin for thread {}",
308
- session_id
309
- ))
310
- .spawn_error(e)
311
- })?;
312
- }
313
-
314
- Ok(child)
315
- }
316
-
317
- fn normalize_logs(
318
- &self,
319
- logs: &str,
320
- worktree_path: &str,
321
- ) -> Result<NormalizedConversation, String> {
322
- let mut entries = Vec::new();
323
- let mut session_id = None;
324
-
325
- for line in logs.lines() {
326
- let trimmed = line.trim();
327
- if trimmed.is_empty() {
328
- continue;
329
- }
330
-
331
- // Try to parse as AmpMessage
332
- let amp_message: AmpJson = match serde_json::from_str(trimmed) {
333
- Ok(msg) => msg,
334
- Err(_) => {
335
- // If line isn't valid JSON, add it as raw text
336
- entries.push(NormalizedEntry {
337
- timestamp: None,
338
- entry_type: NormalizedEntryType::SystemMessage,
339
- content: format!("Raw output: {}", trimmed),
340
- metadata: None,
341
- });
342
- continue;
343
- }
344
- };
345
-
346
- // Extract session ID if available
347
- if session_id.is_none() {
348
- if let Some(id) = amp_message.extract_session_id() {
349
- session_id = Some(id);
350
- }
351
- }
352
-
353
- // Process the message if it's a type we care about
354
- if amp_message.should_process() {
355
- let new_entries = amp_message.to_normalized_entries(self, worktree_path);
356
- entries.extend(new_entries);
357
- }
358
- }
359
-
360
- Ok(NormalizedConversation {
361
- entries,
362
- session_id,
363
- executor_type: "amp".to_string(),
364
- prompt: None,
365
- summary: None,
366
- })
367
- }
368
- }
369
-
370
- impl AmpExecutor {
371
- /// Convert absolute paths to relative paths based on worktree path
372
- fn make_path_relative(&self, path: &str, worktree_path: &str) -> String {
373
- let path_obj = Path::new(path);
374
- let worktree_obj = Path::new(worktree_path);
375
-
376
- // If path is already relative, return as is
377
- if path_obj.is_relative() {
378
- return path.to_string();
379
- }
380
-
381
- // Try to make path relative to worktree path
382
- if let Ok(relative_path) = path_obj.strip_prefix(worktree_obj) {
383
- return relative_path.to_string_lossy().to_string();
384
- }
385
-
386
- // If we can't make it relative, return the original path
387
- path.to_string()
388
- }
389
-
390
- fn generate_concise_content(
391
- &self,
392
- tool_name: &str,
393
- input: &serde_json::Value,
394
- action_type: &ActionType,
395
- worktree_path: &str,
396
- ) -> String {
397
- match action_type {
398
- ActionType::FileRead { path } => format!("`{}`", path),
399
- ActionType::FileWrite { path } => format!("`{}`", path),
400
- ActionType::CommandRun { command } => format!("`{}`", command),
401
- ActionType::Search { query } => format!("`{}`", query),
402
- ActionType::WebFetch { url } => format!("`{}`", url),
403
- ActionType::PlanPresentation { plan } => format!("Plan Presentation: `{}`", plan),
404
- ActionType::TaskCreate { description } => description.clone(),
405
- ActionType::Other { description: _ } => {
406
- // For other tools, try to extract key information or fall back to tool name
407
- match tool_name.to_lowercase().as_str() {
408
- "todowrite" | "todoread" | "todo_write" | "todo_read" => {
409
- if let Some(todos) = input.get("todos").and_then(|t| t.as_array()) {
410
- let mut todo_items = Vec::new();
411
- for todo in todos {
412
- if let (Some(content), Some(status)) = (
413
- todo.get("content").and_then(|c| c.as_str()),
414
- todo.get("status").and_then(|s| s.as_str()),
415
- ) {
416
- let emoji = match status {
417
- "completed" => "✅",
418
- "in_progress" | "in-progress" => "🔄",
419
- "pending" | "todo" => "⏳",
420
- _ => "📝",
421
- };
422
- let priority = todo
423
- .get("priority")
424
- .and_then(|p| p.as_str())
425
- .unwrap_or("medium");
426
- todo_items
427
- .push(format!("{} {} ({})", emoji, content, priority));
428
- }
429
- }
430
- if !todo_items.is_empty() {
431
- format!("TODO List:\n{}", todo_items.join("\n"))
432
- } else {
433
- "Managing TODO list".to_string()
434
- }
435
- } else {
436
- "Managing TODO list".to_string()
437
- }
438
- }
439
- "ls" => {
440
- if let Some(path) = input.get("path").and_then(|p| p.as_str()) {
441
- let relative_path = self.make_path_relative(path, worktree_path);
442
- if relative_path.is_empty() {
443
- "List directory".to_string()
444
- } else {
445
- format!("List directory: `{}`", relative_path)
446
- }
447
- } else {
448
- "List directory".to_string()
449
- }
450
- }
451
- "glob" => {
452
- let pattern = input.get("pattern").and_then(|p| p.as_str()).unwrap_or("*");
453
- let path = input.get("path").and_then(|p| p.as_str());
454
-
455
- if let Some(path) = path {
456
- let relative_path = self.make_path_relative(path, worktree_path);
457
- format!("Find files: `{}` in `{}`", pattern, relative_path)
458
- } else {
459
- format!("Find files: `{}`", pattern)
460
- }
461
- }
462
- "grep" => {
463
- let pattern = input.get("pattern").and_then(|p| p.as_str()).unwrap_or("");
464
- let include = input.get("include").and_then(|i| i.as_str());
465
- let path = input.get("path").and_then(|p| p.as_str());
466
-
467
- let mut parts = vec![format!("Search: `{}`", pattern)];
468
- if let Some(include) = include {
469
- parts.push(format!("in `{}`", include));
470
- }
471
- if let Some(path) = path {
472
- let relative_path = self.make_path_relative(path, worktree_path);
473
- parts.push(format!("at `{}`", relative_path));
474
- }
475
- parts.join(" ")
476
- }
477
- "read" => {
478
- if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
479
- let relative_path = self.make_path_relative(file_path, worktree_path);
480
- format!("Read file: `{}`", relative_path)
481
- } else {
482
- "Read file".to_string()
483
- }
484
- }
485
- "write" => {
486
- if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
487
- let relative_path = self.make_path_relative(file_path, worktree_path);
488
- format!("Write file: `{}`", relative_path)
489
- } else {
490
- "Write file".to_string()
491
- }
492
- }
493
- "edit" => {
494
- if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
495
- let relative_path = self.make_path_relative(file_path, worktree_path);
496
- format!("Edit file: `{}`", relative_path)
497
- } else {
498
- "Edit file".to_string()
499
- }
500
- }
501
- "multiedit" => {
502
- if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
503
- let relative_path = self.make_path_relative(file_path, worktree_path);
504
- format!("Multi-edit file: `{}`", relative_path)
505
- } else {
506
- "Multi-edit file".to_string()
507
- }
508
- }
509
- "bash" => {
510
- if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
511
- format!("Run command: `{}`", command)
512
- } else {
513
- "Run command".to_string()
514
- }
515
- }
516
- "webfetch" => {
517
- if let Some(url) = input.get("url").and_then(|u| u.as_str()) {
518
- format!("Fetch URL: `{}`", url)
519
- } else {
520
- "Fetch URL".to_string()
521
- }
522
- }
523
- "task" => {
524
- if let Some(description) = input.get("description").and_then(|d| d.as_str())
525
- {
526
- format!("Task: {}", description)
527
- } else if let Some(prompt) = input.get("prompt").and_then(|p| p.as_str()) {
528
- format!("Task: {}", prompt)
529
- } else {
530
- "Task".to_string()
531
- }
532
- }
533
- _ => tool_name.to_string(),
534
- }
535
- }
536
- }
537
- }
538
-
539
- fn extract_action_type(
540
- &self,
541
- tool_name: &str,
542
- input: &serde_json::Value,
543
- worktree_path: &str,
544
- ) -> ActionType {
545
- match tool_name.to_lowercase().as_str() {
546
- "read_file" | "read" => {
547
- if let Some(path) = input.get("path").and_then(|p| p.as_str()) {
548
- ActionType::FileRead {
549
- path: self.make_path_relative(path, worktree_path),
550
- }
551
- } else if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
552
- ActionType::FileRead {
553
- path: self.make_path_relative(file_path, worktree_path),
554
- }
555
- } else {
556
- ActionType::Other {
557
- description: "File read operation".to_string(),
558
- }
559
- }
560
- }
561
- "edit_file" | "write" | "create_file" | "edit" | "multiedit" => {
562
- if let Some(path) = input.get("path").and_then(|p| p.as_str()) {
563
- ActionType::FileWrite {
564
- path: self.make_path_relative(path, worktree_path),
565
- }
566
- } else if let Some(file_path) = input.get("file_path").and_then(|p| p.as_str()) {
567
- ActionType::FileWrite {
568
- path: self.make_path_relative(file_path, worktree_path),
569
- }
570
- } else {
571
- ActionType::Other {
572
- description: "File write operation".to_string(),
573
- }
574
- }
575
- }
576
- "bash" | "run_command" => {
577
- if let Some(cmd) = input.get("cmd").and_then(|c| c.as_str()) {
578
- ActionType::CommandRun {
579
- command: cmd.to_string(),
580
- }
581
- } else if let Some(command) = input.get("command").and_then(|c| c.as_str()) {
582
- ActionType::CommandRun {
583
- command: command.to_string(),
584
- }
585
- } else {
586
- ActionType::Other {
587
- description: "Command execution".to_string(),
588
- }
589
- }
590
- }
591
- "grep" | "search" => {
592
- if let Some(pattern) = input.get("pattern").and_then(|p| p.as_str()) {
593
- ActionType::Search {
594
- query: pattern.to_string(),
595
- }
596
- } else if let Some(query) = input.get("query").and_then(|q| q.as_str()) {
597
- ActionType::Search {
598
- query: query.to_string(),
599
- }
600
- } else {
601
- ActionType::Other {
602
- description: "Search operation".to_string(),
603
- }
604
- }
605
- }
606
- "web_fetch" | "webfetch" => {
607
- if let Some(url) = input.get("url").and_then(|u| u.as_str()) {
608
- ActionType::WebFetch {
609
- url: url.to_string(),
610
- }
611
- } else {
612
- ActionType::Other {
613
- description: "Web fetch operation".to_string(),
614
- }
615
- }
616
- }
617
- "task" => {
618
- if let Some(description) = input.get("description").and_then(|d| d.as_str()) {
619
- ActionType::TaskCreate {
620
- description: description.to_string(),
621
- }
622
- } else if let Some(prompt) = input.get("prompt").and_then(|p| p.as_str()) {
623
- ActionType::TaskCreate {
624
- description: prompt.to_string(),
625
- }
626
- } else {
627
- ActionType::Other {
628
- description: "Task creation".to_string(),
629
- }
630
- }
631
- }
632
- "glob" => ActionType::Other {
633
- description: "File pattern search".to_string(),
634
- },
635
- "ls" => ActionType::Other {
636
- description: "List directory".to_string(),
637
- },
638
- "todowrite" | "todoread" | "todo_write" | "todo_read" => ActionType::Other {
639
- description: "Manage TODO list".to_string(),
640
- },
641
- _ => ActionType::Other {
642
- description: format!("Tool: {}", tool_name),
643
- },
644
- }
645
- }
646
- }
647
-
648
- #[cfg(test)]
649
- mod tests {
650
- use super::*;
651
-
652
- #[test]
653
- fn test_filter_streaming_messages() {
654
- // Test logs that simulate the actual normalize_logs behavior
655
- let amp_executor = AmpExecutor;
656
- let logs = r#"{"type":"messages","messages":[[7,{"role":"assistant","content":[{"type":"text","text":"Created all three files: test1.txt, test2.txt, and test3.txt"}],"state":{"type":"streaming"}}]],"toolResults":[]}
657
- {"type":"messages","messages":[[7,{"role":"assistant","content":[{"type":"text","text":"Created all three files: test1.txt, test2.txt, and test3.txt, each with a line of text."}],"state":{"type":"streaming"}}]],"toolResults":[]}
658
- {"type":"messages","messages":[[7,{"role":"assistant","content":[{"type":"text","text":"Created all three files: test1.txt, test2.txt, and test3.txt, each with a line of text."}],"state":{"type":"complete","stopReason":"end_turn"}}]],"toolResults":[]}"#;
659
-
660
- let result = amp_executor.normalize_logs(logs, "/tmp/test");
661
- assert!(result.is_ok());
662
-
663
- let conversation = result.unwrap();
664
-
665
- // Should only have 1 assistant message (the complete one)
666
- let assistant_messages: Vec<_> = conversation
667
- .entries
668
- .iter()
669
- .filter(|e| matches!(e.entry_type, NormalizedEntryType::AssistantMessage))
670
- .collect();
671
-
672
- assert_eq!(assistant_messages.len(), 1);
673
- assert_eq!(assistant_messages[0].content, "Created all three files: test1.txt, test2.txt, and test3.txt, each with a line of text.");
674
- }
675
-
676
- #[test]
677
- fn test_filter_preserves_messages_without_state() {
678
- // Test that messages without state metadata are preserved (for compatibility)
679
- let amp_executor = AmpExecutor;
680
- let logs = r#"{"type":"messages","messages":[[1,{"role":"assistant","content":[{"type":"text","text":"Regular message"}]}]],"toolResults":[]}"#;
681
-
682
- let result = amp_executor.normalize_logs(logs, "/tmp/test");
683
- assert!(result.is_ok());
684
-
685
- let conversation = result.unwrap();
686
-
687
- // Should have 1 assistant message
688
- let assistant_messages: Vec<_> = conversation
689
- .entries
690
- .iter()
691
- .filter(|e| matches!(e.entry_type, NormalizedEntryType::AssistantMessage))
692
- .collect();
693
-
694
- assert_eq!(assistant_messages.len(), 1);
695
- assert_eq!(assistant_messages[0].content, "Regular message");
696
- }
697
- }