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.
- package/README.md +143 -447
- package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
- package/{npx-cli/automagik-forge-0.0.55.tgz → dist/linux-x64/automagik-forge.zip} +0 -0
- package/package.json +13 -23
- package/.cargo/config.toml +0 -13
- package/.claude/commands/commit.md +0 -376
- package/.claude/commands/prompt.md +0 -871
- package/.env.example +0 -20
- package/.github/actions/setup-node/action.yml +0 -29
- package/.github/images/automagik-logo.png +0 -0
- package/.github/workflows/pre-release.yml +0 -470
- package/.github/workflows/publish.yml +0 -145
- package/.github/workflows/test.yml +0 -63
- package/.mcp.json +0 -57
- package/AGENT.md +0 -40
- package/CLAUDE.md +0 -40
- package/CODE-OF-CONDUCT.md +0 -89
- package/Cargo.toml +0 -19
- package/Dockerfile +0 -43
- package/LICENSE +0 -201
- package/Makefile +0 -97
- package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +0 -12
- package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +0 -12
- package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +0 -12
- package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +0 -68
- package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +0 -12
- package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +0 -12
- package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +0 -12
- package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +0 -104
- package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +0 -12
- package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +0 -32
- package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +0 -56
- package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +0 -68
- package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +0 -92
- package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +0 -12
- package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +0 -56
- package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +0 -62
- package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +0 -62
- package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +0 -56
- package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +0 -32
- package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +0 -104
- package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +0 -62
- package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +0 -56
- package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +0 -92
- package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +0 -62
- package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +0 -12
- package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +0 -104
- package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +0 -104
- package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +0 -68
- package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +0 -62
- package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +0 -62
- package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +0 -62
- package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +0 -50
- package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +0 -68
- package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +0 -12
- package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +0 -68
- package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +0 -12
- package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +0 -12
- package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +0 -62
- package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +0 -104
- package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +0 -20
- package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +0 -104
- package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +0 -56
- package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +0 -104
- package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +0 -26
- package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +0 -62
- package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +0 -12
- package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +0 -104
- package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +0 -104
- package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +0 -12
- package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +0 -68
- package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +0 -62
- package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +0 -20
- package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +0 -12
- package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +0 -12
- package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +0 -12
- package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +0 -20
- package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +0 -12
- package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +0 -12
- package/backend/Cargo.toml +0 -71
- package/backend/build.rs +0 -32
- package/backend/migrations/20250617183714_init.sql +0 -44
- package/backend/migrations/20250620212427_execution_processes.sql +0 -25
- package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +0 -28
- package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +0 -23
- package/backend/migrations/20250623120000_executor_sessions.sql +0 -17
- package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +0 -4
- package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +0 -4
- package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +0 -5
- package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +0 -2
- package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +0 -2
- package/backend/migrations/20250710000000_add_setup_completion.sql +0 -3
- package/backend/migrations/20250715154859_add_task_templates.sql +0 -25
- package/backend/migrations/20250716143725_add_default_templates.sql +0 -174
- package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +0 -20
- package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +0 -7
- package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +0 -9
- package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +0 -2
- package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +0 -25
- package/backend/migrations/20250723000000_add_wish_to_tasks.sql +0 -7
- package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +0 -5
- package/backend/scripts/toast-notification.ps1 +0 -23
- package/backend/sounds/abstract-sound1.wav +0 -0
- package/backend/sounds/abstract-sound2.wav +0 -0
- package/backend/sounds/abstract-sound3.wav +0 -0
- package/backend/sounds/abstract-sound4.wav +0 -0
- package/backend/sounds/cow-mooing.wav +0 -0
- package/backend/sounds/phone-vibration.wav +0 -0
- package/backend/sounds/rooster.wav +0 -0
- package/backend/src/app_state.rs +0 -218
- package/backend/src/bin/generate_types.rs +0 -189
- package/backend/src/bin/mcp_task_server.rs +0 -191
- package/backend/src/execution_monitor.rs +0 -1193
- package/backend/src/executor.rs +0 -1053
- package/backend/src/executors/amp.rs +0 -697
- package/backend/src/executors/ccr.rs +0 -91
- package/backend/src/executors/charm_opencode.rs +0 -113
- package/backend/src/executors/claude.rs +0 -887
- package/backend/src/executors/cleanup_script.rs +0 -124
- package/backend/src/executors/dev_server.rs +0 -53
- package/backend/src/executors/echo.rs +0 -79
- package/backend/src/executors/gemini/config.rs +0 -67
- package/backend/src/executors/gemini/streaming.rs +0 -363
- package/backend/src/executors/gemini.rs +0 -765
- package/backend/src/executors/mod.rs +0 -23
- package/backend/src/executors/opencode_ai.rs +0 -113
- package/backend/src/executors/setup_script.rs +0 -130
- package/backend/src/executors/sst_opencode/filter.rs +0 -184
- package/backend/src/executors/sst_opencode/tools.rs +0 -139
- package/backend/src/executors/sst_opencode.rs +0 -756
- package/backend/src/lib.rs +0 -45
- package/backend/src/main.rs +0 -324
- package/backend/src/mcp/mod.rs +0 -1
- package/backend/src/mcp/task_server.rs +0 -850
- package/backend/src/middleware/mod.rs +0 -3
- package/backend/src/middleware/model_loaders.rs +0 -242
- package/backend/src/models/api_response.rs +0 -36
- package/backend/src/models/config.rs +0 -375
- package/backend/src/models/execution_process.rs +0 -430
- package/backend/src/models/executor_session.rs +0 -225
- package/backend/src/models/mod.rs +0 -12
- package/backend/src/models/project.rs +0 -356
- package/backend/src/models/task.rs +0 -345
- package/backend/src/models/task_attempt.rs +0 -1214
- package/backend/src/models/task_template.rs +0 -146
- package/backend/src/openapi.rs +0 -93
- package/backend/src/routes/auth.rs +0 -297
- package/backend/src/routes/config.rs +0 -385
- package/backend/src/routes/filesystem.rs +0 -228
- package/backend/src/routes/health.rs +0 -16
- package/backend/src/routes/mod.rs +0 -9
- package/backend/src/routes/projects.rs +0 -562
- package/backend/src/routes/stream.rs +0 -244
- package/backend/src/routes/task_attempts.rs +0 -1172
- package/backend/src/routes/task_templates.rs +0 -229
- package/backend/src/routes/tasks.rs +0 -353
- package/backend/src/services/analytics.rs +0 -216
- package/backend/src/services/git_service.rs +0 -1321
- package/backend/src/services/github_service.rs +0 -307
- package/backend/src/services/mod.rs +0 -13
- package/backend/src/services/notification_service.rs +0 -263
- package/backend/src/services/pr_monitor.rs +0 -214
- package/backend/src/services/process_service.rs +0 -940
- package/backend/src/utils/path.rs +0 -96
- package/backend/src/utils/shell.rs +0 -19
- package/backend/src/utils/text.rs +0 -24
- package/backend/src/utils/worktree_manager.rs +0 -578
- package/backend/src/utils.rs +0 -125
- package/backend/test.db +0 -0
- package/build-npm-package.sh +0 -61
- package/dev_assets_seed/config.json +0 -19
- package/frontend/.eslintrc.json +0 -25
- package/frontend/.prettierrc.json +0 -8
- package/frontend/components.json +0 -17
- package/frontend/index.html +0 -19
- package/frontend/package-lock.json +0 -7321
- package/frontend/package.json +0 -61
- package/frontend/postcss.config.js +0 -6
- package/frontend/public/android-chrome-192x192.png +0 -0
- package/frontend/public/android-chrome-512x512.png +0 -0
- package/frontend/public/apple-touch-icon.png +0 -0
- package/frontend/public/automagik-forge-logo-dark.svg +0 -3
- package/frontend/public/automagik-forge-logo.svg +0 -3
- package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
- package/frontend/public/favicon-16x16.png +0 -0
- package/frontend/public/favicon-32x32.png +0 -0
- package/frontend/public/favicon.ico +0 -0
- package/frontend/public/site.webmanifest +0 -1
- package/frontend/public/viba-kanban-favicon.png +0 -0
- package/frontend/src/App.tsx +0 -157
- package/frontend/src/components/DisclaimerDialog.tsx +0 -106
- package/frontend/src/components/GitHubLoginDialog.tsx +0 -314
- package/frontend/src/components/OnboardingDialog.tsx +0 -185
- package/frontend/src/components/PrivacyOptInDialog.tsx +0 -130
- package/frontend/src/components/ProvidePatDialog.tsx +0 -98
- package/frontend/src/components/TaskTemplateManager.tsx +0 -336
- package/frontend/src/components/config-provider.tsx +0 -119
- package/frontend/src/components/context/TaskDetailsContextProvider.tsx +0 -470
- package/frontend/src/components/context/taskDetailsContext.ts +0 -125
- package/frontend/src/components/keyboard-shortcuts-demo.tsx +0 -35
- package/frontend/src/components/layout/navbar.tsx +0 -86
- package/frontend/src/components/logo.tsx +0 -44
- package/frontend/src/components/projects/ProjectCard.tsx +0 -155
- package/frontend/src/components/projects/project-detail.tsx +0 -251
- package/frontend/src/components/projects/project-form-fields.tsx +0 -238
- package/frontend/src/components/projects/project-form.tsx +0 -301
- package/frontend/src/components/projects/project-list.tsx +0 -200
- package/frontend/src/components/projects/projects-page.tsx +0 -20
- package/frontend/src/components/tasks/BranchSelector.tsx +0 -169
- package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +0 -94
- package/frontend/src/components/tasks/EditorSelectionDialog.tsx +0 -119
- package/frontend/src/components/tasks/TaskCard.tsx +0 -154
- package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +0 -33
- package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +0 -109
- package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +0 -135
- package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +0 -296
- package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +0 -32
- package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +0 -392
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +0 -256
- package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +0 -56
- package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +0 -92
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +0 -22
- package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +0 -49
- package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +0 -186
- package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +0 -288
- package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +0 -216
- package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +0 -93
- package/frontend/src/components/tasks/TaskDetailsHeader.tsx +0 -169
- package/frontend/src/components/tasks/TaskDetailsPanel.tsx +0 -126
- package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +0 -302
- package/frontend/src/components/tasks/TaskFollowUpSection.tsx +0 -130
- package/frontend/src/components/tasks/TaskFormDialog.tsx +0 -400
- package/frontend/src/components/tasks/TaskKanbanBoard.tsx +0 -180
- package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +0 -259
- package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +0 -243
- package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +0 -899
- package/frontend/src/components/tasks/index.ts +0 -2
- package/frontend/src/components/theme-provider.tsx +0 -82
- package/frontend/src/components/theme-toggle.tsx +0 -36
- package/frontend/src/components/ui/alert.tsx +0 -59
- package/frontend/src/components/ui/auto-expanding-textarea.tsx +0 -70
- package/frontend/src/components/ui/badge.tsx +0 -36
- package/frontend/src/components/ui/button.tsx +0 -56
- package/frontend/src/components/ui/card.tsx +0 -86
- package/frontend/src/components/ui/checkbox.tsx +0 -44
- package/frontend/src/components/ui/chip.tsx +0 -25
- package/frontend/src/components/ui/dialog.tsx +0 -124
- package/frontend/src/components/ui/dropdown-menu.tsx +0 -198
- package/frontend/src/components/ui/file-search-textarea.tsx +0 -292
- package/frontend/src/components/ui/folder-picker.tsx +0 -279
- package/frontend/src/components/ui/input.tsx +0 -25
- package/frontend/src/components/ui/label.tsx +0 -24
- package/frontend/src/components/ui/loader.tsx +0 -26
- package/frontend/src/components/ui/markdown-renderer.tsx +0 -75
- package/frontend/src/components/ui/select.tsx +0 -160
- package/frontend/src/components/ui/separator.tsx +0 -31
- package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +0 -185
- package/frontend/src/components/ui/table.tsx +0 -117
- package/frontend/src/components/ui/tabs.tsx +0 -53
- package/frontend/src/components/ui/textarea.tsx +0 -22
- package/frontend/src/components/ui/tooltip.tsx +0 -28
- package/frontend/src/hooks/useNormalizedConversation.ts +0 -440
- package/frontend/src/index.css +0 -225
- package/frontend/src/lib/api.ts +0 -630
- package/frontend/src/lib/keyboard-shortcuts.ts +0 -266
- package/frontend/src/lib/responsive-config.ts +0 -70
- package/frontend/src/lib/types.ts +0 -39
- package/frontend/src/lib/utils.ts +0 -10
- package/frontend/src/main.tsx +0 -50
- package/frontend/src/pages/McpServers.tsx +0 -418
- package/frontend/src/pages/Settings.tsx +0 -610
- package/frontend/src/pages/project-tasks.tsx +0 -575
- package/frontend/src/pages/projects.tsx +0 -18
- package/frontend/src/vite-env.d.ts +0 -1
- package/frontend/tailwind.config.js +0 -125
- package/frontend/tsconfig.json +0 -26
- package/frontend/tsconfig.node.json +0 -10
- package/frontend/vite.config.ts +0 -33
- package/npx-cli/README.md +0 -159
- package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
- package/npx-cli/automagik-forge-0.1.10.tgz +0 -0
- package/npx-cli/package.json +0 -17
- package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
- package/pnpm-workspace.yaml +0 -2
- package/rust-toolchain.toml +0 -11
- package/rustfmt.toml +0 -3
- package/scripts/load-env.js +0 -43
- package/scripts/mcp_test.js +0 -374
- package/scripts/prepare-db.js +0 -45
- package/scripts/setup-dev-environment.js +0 -274
- package/scripts/start-mcp-sse.js +0 -70
- package/scripts/test-debug.js +0 -32
- package/scripts/test-mcp-sse.js +0 -138
- package/scripts/test-simple.js +0 -44
- package/scripts/test-wish-final.js +0 -179
- package/scripts/test-wish-system.js +0 -221
- package/shared/types.ts +0 -182
- package/test-npm-package.sh +0 -42
- /package/{npx-cli/bin → bin}/cli.js +0 -0
@@ -1,940 +0,0 @@
|
|
1
|
-
use sqlx::SqlitePool;
|
2
|
-
use tracing::{debug, info};
|
3
|
-
use uuid::Uuid;
|
4
|
-
|
5
|
-
use crate::{
|
6
|
-
executor::Executor,
|
7
|
-
models::{
|
8
|
-
execution_process::{CreateExecutionProcess, ExecutionProcess, ExecutionProcessType},
|
9
|
-
executor_session::{CreateExecutorSession, ExecutorSession},
|
10
|
-
project::Project,
|
11
|
-
task::Task,
|
12
|
-
task_attempt::{TaskAttempt, TaskAttemptError},
|
13
|
-
},
|
14
|
-
utils::shell::get_shell_command,
|
15
|
-
};
|
16
|
-
|
17
|
-
/// Service responsible for managing process execution lifecycle
|
18
|
-
pub struct ProcessService;
|
19
|
-
|
20
|
-
impl ProcessService {
|
21
|
-
/// Run cleanup script if project has one configured
|
22
|
-
pub async fn run_cleanup_script_if_configured(
|
23
|
-
pool: &SqlitePool,
|
24
|
-
app_state: &crate::app_state::AppState,
|
25
|
-
attempt_id: Uuid,
|
26
|
-
task_id: Uuid,
|
27
|
-
project_id: Uuid,
|
28
|
-
) -> Result<(), TaskAttemptError> {
|
29
|
-
// Get project to check if cleanup script exists
|
30
|
-
let project = Project::find_by_id(pool, project_id)
|
31
|
-
.await?
|
32
|
-
.ok_or(TaskAttemptError::ProjectNotFound)?;
|
33
|
-
|
34
|
-
if Self::should_run_cleanup_script(&project) {
|
35
|
-
// Get worktree path
|
36
|
-
let task_attempt = TaskAttempt::find_by_id(pool, attempt_id).await?.ok_or(
|
37
|
-
TaskAttemptError::ValidationError("Task attempt not found".to_string()),
|
38
|
-
)?;
|
39
|
-
|
40
|
-
tracing::info!(
|
41
|
-
"Running cleanup script for project {} in attempt {}",
|
42
|
-
project_id,
|
43
|
-
attempt_id
|
44
|
-
);
|
45
|
-
|
46
|
-
Self::start_cleanup_script(
|
47
|
-
pool,
|
48
|
-
app_state,
|
49
|
-
attempt_id,
|
50
|
-
task_id,
|
51
|
-
&project,
|
52
|
-
&task_attempt.worktree_path,
|
53
|
-
)
|
54
|
-
.await?;
|
55
|
-
} else {
|
56
|
-
tracing::debug!("No cleanup script configured for project {}", project_id);
|
57
|
-
}
|
58
|
-
|
59
|
-
Ok(())
|
60
|
-
}
|
61
|
-
|
62
|
-
/// Automatically run setup if needed, then continue with the specified operation
|
63
|
-
pub async fn auto_setup_and_execute(
|
64
|
-
pool: &SqlitePool,
|
65
|
-
app_state: &crate::app_state::AppState,
|
66
|
-
attempt_id: Uuid,
|
67
|
-
task_id: Uuid,
|
68
|
-
project_id: Uuid,
|
69
|
-
operation: &str, // "dev_server", "coding_agent", or "followup"
|
70
|
-
operation_params: Option<serde_json::Value>,
|
71
|
-
) -> Result<(), TaskAttemptError> {
|
72
|
-
// Check if setup is completed for this worktree
|
73
|
-
let setup_completed = TaskAttempt::is_setup_completed(pool, attempt_id).await?;
|
74
|
-
|
75
|
-
// Get project to check if setup script exists
|
76
|
-
let project = Project::find_by_id(pool, project_id)
|
77
|
-
.await?
|
78
|
-
.ok_or(TaskAttemptError::ProjectNotFound)?;
|
79
|
-
|
80
|
-
let needs_setup = Self::should_run_setup_script(&project) && !setup_completed;
|
81
|
-
|
82
|
-
if needs_setup {
|
83
|
-
// Run setup with delegation to the original operation
|
84
|
-
Self::execute_setup_with_delegation(
|
85
|
-
pool,
|
86
|
-
app_state,
|
87
|
-
attempt_id,
|
88
|
-
task_id,
|
89
|
-
project_id,
|
90
|
-
operation,
|
91
|
-
operation_params,
|
92
|
-
)
|
93
|
-
.await
|
94
|
-
} else {
|
95
|
-
// Setup not needed or already completed, continue with original operation
|
96
|
-
match operation {
|
97
|
-
"dev_server" => {
|
98
|
-
Self::start_dev_server_direct(pool, app_state, attempt_id, task_id, project_id)
|
99
|
-
.await
|
100
|
-
}
|
101
|
-
"coding_agent" => {
|
102
|
-
Self::start_coding_agent(pool, app_state, attempt_id, task_id, project_id).await
|
103
|
-
}
|
104
|
-
"followup" => {
|
105
|
-
let prompt = operation_params
|
106
|
-
.as_ref()
|
107
|
-
.and_then(|p| p.get("prompt"))
|
108
|
-
.and_then(|p| p.as_str())
|
109
|
-
.unwrap_or("");
|
110
|
-
Self::start_followup_execution_direct(
|
111
|
-
pool, app_state, attempt_id, task_id, project_id, prompt,
|
112
|
-
)
|
113
|
-
.await
|
114
|
-
.map(|_| ())
|
115
|
-
}
|
116
|
-
_ => Err(TaskAttemptError::ValidationError(format!(
|
117
|
-
"Unknown operation: {}",
|
118
|
-
operation
|
119
|
-
))),
|
120
|
-
}
|
121
|
-
}
|
122
|
-
}
|
123
|
-
|
124
|
-
/// Execute setup script with delegation context for continuing after completion
|
125
|
-
async fn execute_setup_with_delegation(
|
126
|
-
pool: &SqlitePool,
|
127
|
-
app_state: &crate::app_state::AppState,
|
128
|
-
attempt_id: Uuid,
|
129
|
-
task_id: Uuid,
|
130
|
-
project_id: Uuid,
|
131
|
-
delegate_to: &str,
|
132
|
-
operation_params: Option<serde_json::Value>,
|
133
|
-
) -> Result<(), TaskAttemptError> {
|
134
|
-
let (task_attempt, project) =
|
135
|
-
Self::load_execution_context(pool, attempt_id, project_id).await?;
|
136
|
-
|
137
|
-
// Create delegation context for execution monitor
|
138
|
-
let delegation_context = serde_json::json!({
|
139
|
-
"delegate_to": delegate_to,
|
140
|
-
"operation_params": {
|
141
|
-
"task_id": task_id,
|
142
|
-
"project_id": project_id,
|
143
|
-
"attempt_id": attempt_id,
|
144
|
-
"additional": operation_params
|
145
|
-
}
|
146
|
-
});
|
147
|
-
|
148
|
-
// Create modified setup script execution with delegation context in args
|
149
|
-
let setup_script = project.setup_script.as_ref().unwrap();
|
150
|
-
let process_id = Uuid::new_v4();
|
151
|
-
|
152
|
-
// Create execution process record with delegation context
|
153
|
-
let _execution_process = Self::create_execution_process_record_with_delegation(
|
154
|
-
pool,
|
155
|
-
attempt_id,
|
156
|
-
process_id,
|
157
|
-
setup_script,
|
158
|
-
&task_attempt.worktree_path,
|
159
|
-
delegation_context,
|
160
|
-
)
|
161
|
-
.await?;
|
162
|
-
|
163
|
-
// Setup script starting with delegation
|
164
|
-
|
165
|
-
tracing::info!(
|
166
|
-
"Starting setup script with delegation to {} for task attempt {}",
|
167
|
-
delegate_to,
|
168
|
-
attempt_id
|
169
|
-
);
|
170
|
-
|
171
|
-
// Execute the setup script
|
172
|
-
let child = Self::execute_setup_script_process(
|
173
|
-
setup_script,
|
174
|
-
pool,
|
175
|
-
task_id,
|
176
|
-
attempt_id,
|
177
|
-
process_id,
|
178
|
-
&task_attempt.worktree_path,
|
179
|
-
)
|
180
|
-
.await?;
|
181
|
-
|
182
|
-
// Register for monitoring
|
183
|
-
Self::register_for_monitoring(
|
184
|
-
app_state,
|
185
|
-
process_id,
|
186
|
-
attempt_id,
|
187
|
-
&ExecutionProcessType::SetupScript,
|
188
|
-
child,
|
189
|
-
)
|
190
|
-
.await;
|
191
|
-
|
192
|
-
tracing::info!(
|
193
|
-
"Started setup execution with delegation {} for task attempt {}",
|
194
|
-
process_id,
|
195
|
-
attempt_id
|
196
|
-
);
|
197
|
-
Ok(())
|
198
|
-
}
|
199
|
-
|
200
|
-
/// Start the execution flow for a task attempt (setup script + executor)
|
201
|
-
pub async fn start_execution(
|
202
|
-
pool: &SqlitePool,
|
203
|
-
app_state: &crate::app_state::AppState,
|
204
|
-
attempt_id: Uuid,
|
205
|
-
task_id: Uuid,
|
206
|
-
project_id: Uuid,
|
207
|
-
) -> Result<(), TaskAttemptError> {
|
208
|
-
use crate::models::task::{Task, TaskStatus};
|
209
|
-
|
210
|
-
// Load required entities
|
211
|
-
let (task_attempt, project) =
|
212
|
-
Self::load_execution_context(pool, attempt_id, project_id).await?;
|
213
|
-
|
214
|
-
// Update task status to indicate execution has started
|
215
|
-
Task::update_status(pool, task_id, project_id, TaskStatus::InProgress).await?;
|
216
|
-
|
217
|
-
// Determine execution sequence based on project configuration
|
218
|
-
if Self::should_run_setup_script(&project) {
|
219
|
-
Self::start_setup_script(
|
220
|
-
pool,
|
221
|
-
app_state,
|
222
|
-
attempt_id,
|
223
|
-
task_id,
|
224
|
-
&project,
|
225
|
-
&task_attempt.worktree_path,
|
226
|
-
)
|
227
|
-
.await
|
228
|
-
} else {
|
229
|
-
Self::start_coding_agent(pool, app_state, attempt_id, task_id, project_id).await
|
230
|
-
}
|
231
|
-
}
|
232
|
-
|
233
|
-
/// Start the coding agent after setup is complete or if no setup is needed
|
234
|
-
pub async fn start_coding_agent(
|
235
|
-
pool: &SqlitePool,
|
236
|
-
app_state: &crate::app_state::AppState,
|
237
|
-
attempt_id: Uuid,
|
238
|
-
task_id: Uuid,
|
239
|
-
_project_id: Uuid,
|
240
|
-
) -> Result<(), TaskAttemptError> {
|
241
|
-
let task_attempt = TaskAttempt::find_by_id(pool, attempt_id)
|
242
|
-
.await?
|
243
|
-
.ok_or(TaskAttemptError::TaskNotFound)?;
|
244
|
-
|
245
|
-
let executor_config = Self::resolve_executor_config(&task_attempt.executor);
|
246
|
-
|
247
|
-
Self::start_process_execution(
|
248
|
-
pool,
|
249
|
-
app_state,
|
250
|
-
attempt_id,
|
251
|
-
task_id,
|
252
|
-
crate::executor::ExecutorType::CodingAgent {
|
253
|
-
config: executor_config,
|
254
|
-
follow_up: None,
|
255
|
-
},
|
256
|
-
"Starting executor".to_string(),
|
257
|
-
ExecutionProcessType::CodingAgent,
|
258
|
-
&task_attempt.worktree_path,
|
259
|
-
)
|
260
|
-
.await
|
261
|
-
}
|
262
|
-
|
263
|
-
/// Start a dev server for this task attempt (with automatic setup)
|
264
|
-
pub async fn start_dev_server(
|
265
|
-
pool: &SqlitePool,
|
266
|
-
app_state: &crate::app_state::AppState,
|
267
|
-
attempt_id: Uuid,
|
268
|
-
task_id: Uuid,
|
269
|
-
project_id: Uuid,
|
270
|
-
) -> Result<(), TaskAttemptError> {
|
271
|
-
// Ensure worktree exists (recreate if needed for cold task support)
|
272
|
-
let _worktree_path =
|
273
|
-
TaskAttempt::ensure_worktree_exists(pool, attempt_id, project_id, "dev server").await?;
|
274
|
-
|
275
|
-
// Use automatic setup logic
|
276
|
-
Self::auto_setup_and_execute(
|
277
|
-
pool,
|
278
|
-
app_state,
|
279
|
-
attempt_id,
|
280
|
-
task_id,
|
281
|
-
project_id,
|
282
|
-
"dev_server",
|
283
|
-
None,
|
284
|
-
)
|
285
|
-
.await
|
286
|
-
}
|
287
|
-
|
288
|
-
/// Start a dev server directly without setup check (internal method)
|
289
|
-
pub async fn start_dev_server_direct(
|
290
|
-
pool: &SqlitePool,
|
291
|
-
app_state: &crate::app_state::AppState,
|
292
|
-
attempt_id: Uuid,
|
293
|
-
task_id: Uuid,
|
294
|
-
project_id: Uuid,
|
295
|
-
) -> Result<(), TaskAttemptError> {
|
296
|
-
// Ensure worktree exists (recreate if needed for cold task support)
|
297
|
-
let worktree_path =
|
298
|
-
TaskAttempt::ensure_worktree_exists(pool, attempt_id, project_id, "dev server").await?;
|
299
|
-
|
300
|
-
// Get the project to access the dev_script
|
301
|
-
let project = Project::find_by_id(pool, project_id)
|
302
|
-
.await?
|
303
|
-
.ok_or(TaskAttemptError::TaskNotFound)?;
|
304
|
-
|
305
|
-
let dev_script = project.dev_script.ok_or_else(|| {
|
306
|
-
TaskAttemptError::ValidationError(
|
307
|
-
"No dev script configured for this project".to_string(),
|
308
|
-
)
|
309
|
-
})?;
|
310
|
-
|
311
|
-
if dev_script.trim().is_empty() {
|
312
|
-
return Err(TaskAttemptError::ValidationError(
|
313
|
-
"Dev script is empty".to_string(),
|
314
|
-
));
|
315
|
-
}
|
316
|
-
|
317
|
-
let result = Self::start_process_execution(
|
318
|
-
pool,
|
319
|
-
app_state,
|
320
|
-
attempt_id,
|
321
|
-
task_id,
|
322
|
-
crate::executor::ExecutorType::DevServer(dev_script),
|
323
|
-
"Starting dev server".to_string(),
|
324
|
-
ExecutionProcessType::DevServer,
|
325
|
-
&worktree_path,
|
326
|
-
)
|
327
|
-
.await;
|
328
|
-
|
329
|
-
if result.is_ok() {
|
330
|
-
app_state
|
331
|
-
.track_analytics_event(
|
332
|
-
"dev_server_started",
|
333
|
-
Some(serde_json::json!({
|
334
|
-
"task_id": task_id.to_string(),
|
335
|
-
"project_id": project_id.to_string(),
|
336
|
-
"attempt_id": attempt_id.to_string()
|
337
|
-
})),
|
338
|
-
)
|
339
|
-
.await;
|
340
|
-
}
|
341
|
-
|
342
|
-
result
|
343
|
-
}
|
344
|
-
|
345
|
-
/// Start a follow-up execution using the same executor type as the first process (with automatic setup)
|
346
|
-
/// Returns the attempt_id that was actually used (always the original attempt_id for session continuity)
|
347
|
-
pub async fn start_followup_execution(
|
348
|
-
pool: &SqlitePool,
|
349
|
-
app_state: &crate::app_state::AppState,
|
350
|
-
attempt_id: Uuid,
|
351
|
-
task_id: Uuid,
|
352
|
-
project_id: Uuid,
|
353
|
-
prompt: &str,
|
354
|
-
) -> Result<Uuid, TaskAttemptError> {
|
355
|
-
use crate::models::task::{Task, TaskStatus};
|
356
|
-
|
357
|
-
// Get the current task attempt to check if worktree is deleted
|
358
|
-
let current_attempt = TaskAttempt::find_by_id(pool, attempt_id)
|
359
|
-
.await?
|
360
|
-
.ok_or(TaskAttemptError::TaskNotFound)?;
|
361
|
-
|
362
|
-
let actual_attempt_id = attempt_id;
|
363
|
-
|
364
|
-
if current_attempt.worktree_deleted {
|
365
|
-
info!(
|
366
|
-
"Resurrecting deleted attempt {} (branch: {}) for followup execution - maintaining session continuity",
|
367
|
-
attempt_id, current_attempt.branch
|
368
|
-
);
|
369
|
-
} else {
|
370
|
-
info!(
|
371
|
-
"Continuing followup execution on active attempt {} (branch: {})",
|
372
|
-
attempt_id, current_attempt.branch
|
373
|
-
);
|
374
|
-
}
|
375
|
-
|
376
|
-
// Update task status to indicate follow-up execution has started
|
377
|
-
Task::update_status(pool, task_id, project_id, TaskStatus::InProgress).await?;
|
378
|
-
|
379
|
-
// Ensure worktree exists (recreate if needed for cold task support)
|
380
|
-
// This will resurrect the worktree at the exact same path for session continuity
|
381
|
-
let _worktree_path =
|
382
|
-
TaskAttempt::ensure_worktree_exists(pool, actual_attempt_id, project_id, "followup")
|
383
|
-
.await?;
|
384
|
-
|
385
|
-
// Use automatic setup logic with followup parameters
|
386
|
-
let operation_params = serde_json::json!({
|
387
|
-
"prompt": prompt
|
388
|
-
});
|
389
|
-
|
390
|
-
Self::auto_setup_and_execute(
|
391
|
-
pool,
|
392
|
-
app_state,
|
393
|
-
attempt_id,
|
394
|
-
task_id,
|
395
|
-
project_id,
|
396
|
-
"followup",
|
397
|
-
Some(operation_params),
|
398
|
-
)
|
399
|
-
.await?;
|
400
|
-
|
401
|
-
Ok(actual_attempt_id)
|
402
|
-
}
|
403
|
-
|
404
|
-
/// Start a follow-up execution directly without setup check (internal method)
|
405
|
-
pub async fn start_followup_execution_direct(
|
406
|
-
pool: &SqlitePool,
|
407
|
-
app_state: &crate::app_state::AppState,
|
408
|
-
attempt_id: Uuid,
|
409
|
-
task_id: Uuid,
|
410
|
-
project_id: Uuid,
|
411
|
-
prompt: &str,
|
412
|
-
) -> Result<Uuid, TaskAttemptError> {
|
413
|
-
// Ensure worktree exists (recreate if needed for cold task support)
|
414
|
-
// This will resurrect the worktree at the exact same path for session continuity
|
415
|
-
let worktree_path =
|
416
|
-
TaskAttempt::ensure_worktree_exists(pool, attempt_id, project_id, "followup").await?;
|
417
|
-
|
418
|
-
// Find the most recent coding agent execution process to get the executor type
|
419
|
-
// Look up processes from the ORIGINAL attempt to find the session
|
420
|
-
let execution_processes =
|
421
|
-
ExecutionProcess::find_by_task_attempt_id(pool, attempt_id).await?;
|
422
|
-
let most_recent_coding_agent = execution_processes
|
423
|
-
.iter()
|
424
|
-
.rev() // Reverse to get most recent first (since they're ordered by created_at ASC)
|
425
|
-
.find(|p| matches!(p.process_type, ExecutionProcessType::CodingAgent))
|
426
|
-
.ok_or_else(|| {
|
427
|
-
tracing::error!(
|
428
|
-
"No previous coding agent execution found for task attempt {}. Found {} processes: {:?}",
|
429
|
-
attempt_id,
|
430
|
-
execution_processes.len(),
|
431
|
-
execution_processes.iter().map(|p| format!("{:?}", p.process_type)).collect::<Vec<_>>()
|
432
|
-
);
|
433
|
-
TaskAttemptError::ValidationError("No previous coding agent execution found for follow-up".to_string())
|
434
|
-
})?;
|
435
|
-
|
436
|
-
// Get the executor session to find the session ID
|
437
|
-
// This looks up the session from the original attempt's processes
|
438
|
-
let executor_session =
|
439
|
-
ExecutorSession::find_by_execution_process_id(pool, most_recent_coding_agent.id)
|
440
|
-
.await?
|
441
|
-
.ok_or_else(|| {
|
442
|
-
tracing::error!(
|
443
|
-
"No executor session found for execution process {} (task attempt {})",
|
444
|
-
most_recent_coding_agent.id,
|
445
|
-
attempt_id
|
446
|
-
);
|
447
|
-
TaskAttemptError::ValidationError(
|
448
|
-
"No executor session found for follow-up".to_string(),
|
449
|
-
)
|
450
|
-
})?;
|
451
|
-
|
452
|
-
let executor_config: crate::executor::ExecutorConfig = match most_recent_coding_agent
|
453
|
-
.executor_type
|
454
|
-
.as_deref()
|
455
|
-
{
|
456
|
-
Some(executor_str) => executor_str.parse().unwrap(),
|
457
|
-
_ => {
|
458
|
-
tracing::error!(
|
459
|
-
"Invalid or missing executor type '{}' for execution process {} (task attempt {})",
|
460
|
-
most_recent_coding_agent.executor_type.as_deref().unwrap_or("None"),
|
461
|
-
most_recent_coding_agent.id,
|
462
|
-
attempt_id
|
463
|
-
);
|
464
|
-
return Err(TaskAttemptError::ValidationError(format!(
|
465
|
-
"Invalid executor type for follow-up: {}",
|
466
|
-
most_recent_coding_agent
|
467
|
-
.executor_type
|
468
|
-
.as_deref()
|
469
|
-
.unwrap_or("None")
|
470
|
-
)));
|
471
|
-
}
|
472
|
-
};
|
473
|
-
|
474
|
-
// Try to use follow-up with session ID, but fall back to new session if it fails
|
475
|
-
let followup_executor = if let Some(session_id) = &executor_session.session_id {
|
476
|
-
// First try with session ID for continuation
|
477
|
-
debug!(
|
478
|
-
"SESSION_FOLLOWUP: Attempting follow-up execution with session ID: {} (attempt: {}, worktree: {})",
|
479
|
-
session_id, attempt_id, worktree_path
|
480
|
-
);
|
481
|
-
crate::executor::ExecutorType::CodingAgent {
|
482
|
-
config: executor_config.clone(),
|
483
|
-
follow_up: Some(crate::executor::FollowUpInfo {
|
484
|
-
session_id: session_id.clone(),
|
485
|
-
prompt: prompt.to_string(),
|
486
|
-
}),
|
487
|
-
}
|
488
|
-
} else {
|
489
|
-
// No session ID available, start new session
|
490
|
-
tracing::warn!(
|
491
|
-
"SESSION_FOLLOWUP: No session ID available for follow-up execution on attempt {}, starting new session (worktree: {})",
|
492
|
-
attempt_id, worktree_path
|
493
|
-
);
|
494
|
-
crate::executor::ExecutorType::CodingAgent {
|
495
|
-
config: executor_config.clone(),
|
496
|
-
follow_up: None,
|
497
|
-
}
|
498
|
-
};
|
499
|
-
|
500
|
-
// Try to start the follow-up execution
|
501
|
-
let execution_result = Self::start_process_execution(
|
502
|
-
pool,
|
503
|
-
app_state,
|
504
|
-
attempt_id,
|
505
|
-
task_id,
|
506
|
-
followup_executor,
|
507
|
-
"Starting follow-up executor".to_string(),
|
508
|
-
ExecutionProcessType::CodingAgent,
|
509
|
-
&worktree_path,
|
510
|
-
)
|
511
|
-
.await;
|
512
|
-
|
513
|
-
// If follow-up execution failed and we tried to use a session ID,
|
514
|
-
// fall back to a new session
|
515
|
-
if execution_result.is_err() && executor_session.session_id.is_some() {
|
516
|
-
tracing::warn!(
|
517
|
-
"SESSION_FOLLOWUP: Follow-up execution with session ID '{}' failed for attempt {}, falling back to new session. Error: {:?}",
|
518
|
-
executor_session.session_id.as_ref().unwrap(),
|
519
|
-
attempt_id,
|
520
|
-
execution_result.as_ref().err()
|
521
|
-
);
|
522
|
-
|
523
|
-
// Create a new session instead of trying to resume
|
524
|
-
let new_session_executor = crate::executor::ExecutorType::CodingAgent {
|
525
|
-
config: executor_config,
|
526
|
-
follow_up: None,
|
527
|
-
};
|
528
|
-
|
529
|
-
Self::start_process_execution(
|
530
|
-
pool,
|
531
|
-
app_state,
|
532
|
-
attempt_id,
|
533
|
-
task_id,
|
534
|
-
new_session_executor,
|
535
|
-
"Starting new executor session (follow-up session failed)".to_string(),
|
536
|
-
ExecutionProcessType::CodingAgent,
|
537
|
-
&worktree_path,
|
538
|
-
)
|
539
|
-
.await?;
|
540
|
-
} else {
|
541
|
-
// Either it succeeded or we already tried without session ID
|
542
|
-
execution_result?;
|
543
|
-
}
|
544
|
-
|
545
|
-
Ok(attempt_id)
|
546
|
-
}
|
547
|
-
|
548
|
-
/// Unified function to start any type of process execution
|
549
|
-
#[allow(clippy::too_many_arguments)]
|
550
|
-
pub async fn start_process_execution(
|
551
|
-
pool: &SqlitePool,
|
552
|
-
app_state: &crate::app_state::AppState,
|
553
|
-
attempt_id: Uuid,
|
554
|
-
task_id: Uuid,
|
555
|
-
executor_type: crate::executor::ExecutorType,
|
556
|
-
activity_note: String,
|
557
|
-
process_type: ExecutionProcessType,
|
558
|
-
worktree_path: &str,
|
559
|
-
) -> Result<(), TaskAttemptError> {
|
560
|
-
let process_id = Uuid::new_v4();
|
561
|
-
|
562
|
-
// Create execution process record
|
563
|
-
let _execution_process = Self::create_execution_process_record(
|
564
|
-
pool,
|
565
|
-
attempt_id,
|
566
|
-
process_id,
|
567
|
-
&executor_type,
|
568
|
-
process_type.clone(),
|
569
|
-
worktree_path,
|
570
|
-
)
|
571
|
-
.await?;
|
572
|
-
|
573
|
-
// Create executor session for coding agents
|
574
|
-
if matches!(process_type, ExecutionProcessType::CodingAgent) {
|
575
|
-
// Extract follow-up prompt if this is a follow-up execution
|
576
|
-
let followup_prompt = match &executor_type {
|
577
|
-
crate::executor::ExecutorType::CodingAgent {
|
578
|
-
follow_up: Some(ref info),
|
579
|
-
..
|
580
|
-
} => Some(info.prompt.clone()),
|
581
|
-
_ => None,
|
582
|
-
};
|
583
|
-
Self::create_executor_session_record(
|
584
|
-
pool,
|
585
|
-
attempt_id,
|
586
|
-
task_id,
|
587
|
-
process_id,
|
588
|
-
followup_prompt,
|
589
|
-
)
|
590
|
-
.await?;
|
591
|
-
}
|
592
|
-
|
593
|
-
// Process started successfully
|
594
|
-
|
595
|
-
tracing::info!("Starting {} for task attempt {}", activity_note, attempt_id);
|
596
|
-
|
597
|
-
// Execute the process
|
598
|
-
let child = Self::execute_process(
|
599
|
-
&executor_type,
|
600
|
-
pool,
|
601
|
-
task_id,
|
602
|
-
attempt_id,
|
603
|
-
process_id,
|
604
|
-
worktree_path,
|
605
|
-
)
|
606
|
-
.await?;
|
607
|
-
|
608
|
-
// Register for monitoring
|
609
|
-
Self::register_for_monitoring(app_state, process_id, attempt_id, &process_type, child)
|
610
|
-
.await;
|
611
|
-
|
612
|
-
tracing::info!(
|
613
|
-
"Started execution {} for task attempt {}",
|
614
|
-
process_id,
|
615
|
-
attempt_id
|
616
|
-
);
|
617
|
-
Ok(())
|
618
|
-
}
|
619
|
-
|
620
|
-
/// Load the execution context (task attempt and project) with validation
|
621
|
-
async fn load_execution_context(
|
622
|
-
pool: &SqlitePool,
|
623
|
-
attempt_id: Uuid,
|
624
|
-
project_id: Uuid,
|
625
|
-
) -> Result<(TaskAttempt, Project), TaskAttemptError> {
|
626
|
-
let task_attempt = TaskAttempt::find_by_id(pool, attempt_id)
|
627
|
-
.await?
|
628
|
-
.ok_or(TaskAttemptError::TaskNotFound)?;
|
629
|
-
|
630
|
-
let project = Project::find_by_id(pool, project_id)
|
631
|
-
.await?
|
632
|
-
.ok_or(TaskAttemptError::ProjectNotFound)?;
|
633
|
-
|
634
|
-
Ok((task_attempt, project))
|
635
|
-
}
|
636
|
-
|
637
|
-
/// Check if setup script should be executed
|
638
|
-
fn should_run_setup_script(project: &Project) -> bool {
|
639
|
-
project
|
640
|
-
.setup_script
|
641
|
-
.as_ref()
|
642
|
-
.map(|script| !script.trim().is_empty())
|
643
|
-
.unwrap_or(false)
|
644
|
-
}
|
645
|
-
|
646
|
-
fn should_run_cleanup_script(project: &Project) -> bool {
|
647
|
-
project
|
648
|
-
.cleanup_script
|
649
|
-
.as_ref()
|
650
|
-
.map(|script| !script.trim().is_empty())
|
651
|
-
.unwrap_or(false)
|
652
|
-
}
|
653
|
-
|
654
|
-
/// Start the setup script execution
|
655
|
-
async fn start_setup_script(
|
656
|
-
pool: &SqlitePool,
|
657
|
-
app_state: &crate::app_state::AppState,
|
658
|
-
attempt_id: Uuid,
|
659
|
-
task_id: Uuid,
|
660
|
-
project: &Project,
|
661
|
-
worktree_path: &str,
|
662
|
-
) -> Result<(), TaskAttemptError> {
|
663
|
-
let setup_script = project.setup_script.as_ref().unwrap();
|
664
|
-
|
665
|
-
Self::start_process_execution(
|
666
|
-
pool,
|
667
|
-
app_state,
|
668
|
-
attempt_id,
|
669
|
-
task_id,
|
670
|
-
crate::executor::ExecutorType::SetupScript(setup_script.clone()),
|
671
|
-
"Starting setup script".to_string(),
|
672
|
-
ExecutionProcessType::SetupScript,
|
673
|
-
worktree_path,
|
674
|
-
)
|
675
|
-
.await
|
676
|
-
}
|
677
|
-
|
678
|
-
/// Start the cleanup script execution
|
679
|
-
async fn start_cleanup_script(
|
680
|
-
pool: &SqlitePool,
|
681
|
-
app_state: &crate::app_state::AppState,
|
682
|
-
attempt_id: Uuid,
|
683
|
-
task_id: Uuid,
|
684
|
-
project: &Project,
|
685
|
-
worktree_path: &str,
|
686
|
-
) -> Result<(), TaskAttemptError> {
|
687
|
-
let cleanup_script = project.cleanup_script.as_ref().unwrap();
|
688
|
-
|
689
|
-
Self::start_process_execution(
|
690
|
-
pool,
|
691
|
-
app_state,
|
692
|
-
attempt_id,
|
693
|
-
task_id,
|
694
|
-
crate::executor::ExecutorType::CleanupScript(cleanup_script.clone()),
|
695
|
-
"Starting cleanup script".to_string(),
|
696
|
-
ExecutionProcessType::CleanupScript,
|
697
|
-
worktree_path,
|
698
|
-
)
|
699
|
-
.await
|
700
|
-
}
|
701
|
-
|
702
|
-
/// Resolve executor configuration from string name
|
703
|
-
fn resolve_executor_config(executor_name: &Option<String>) -> crate::executor::ExecutorConfig {
|
704
|
-
match executor_name.as_ref().map(|s| s.as_str()) {
|
705
|
-
Some("claude") => crate::executor::ExecutorConfig::Claude,
|
706
|
-
Some("claude-plan") => crate::executor::ExecutorConfig::ClaudePlan,
|
707
|
-
Some("claude-code-router") => crate::executor::ExecutorConfig::ClaudeCodeRouter,
|
708
|
-
Some("amp") => crate::executor::ExecutorConfig::Amp,
|
709
|
-
Some("gemini") => crate::executor::ExecutorConfig::Gemini,
|
710
|
-
Some("charm-opencode") => crate::executor::ExecutorConfig::CharmOpencode,
|
711
|
-
Some("sst-opencode") => crate::executor::ExecutorConfig::SstOpencode,
|
712
|
-
Some("opencode-ai") => crate::executor::ExecutorConfig::OpencodeAi,
|
713
|
-
_ => crate::executor::ExecutorConfig::Echo, // Default for "echo" or None
|
714
|
-
}
|
715
|
-
}
|
716
|
-
|
717
|
-
/// Create execution process database record
|
718
|
-
async fn create_execution_process_record(
|
719
|
-
pool: &SqlitePool,
|
720
|
-
attempt_id: Uuid,
|
721
|
-
process_id: Uuid,
|
722
|
-
executor_type: &crate::executor::ExecutorType,
|
723
|
-
process_type: ExecutionProcessType,
|
724
|
-
worktree_path: &str,
|
725
|
-
) -> Result<ExecutionProcess, TaskAttemptError> {
|
726
|
-
let (shell_cmd, shell_arg) = get_shell_command();
|
727
|
-
let (command, args, executor_type_string) = match executor_type {
|
728
|
-
crate::executor::ExecutorType::SetupScript(_) => (
|
729
|
-
shell_cmd.to_string(),
|
730
|
-
Some(serde_json::to_string(&[shell_arg, "setup-script"]).unwrap()),
|
731
|
-
Some("setup-script".to_string()),
|
732
|
-
),
|
733
|
-
crate::executor::ExecutorType::CleanupScript(_) => (
|
734
|
-
shell_cmd.to_string(),
|
735
|
-
Some(serde_json::to_string(&[shell_arg, "cleanup-script"]).unwrap()),
|
736
|
-
Some("cleanup-script".to_string()),
|
737
|
-
),
|
738
|
-
crate::executor::ExecutorType::DevServer(_) => (
|
739
|
-
shell_cmd.to_string(),
|
740
|
-
Some(serde_json::to_string(&[shell_arg, "dev_server"]).unwrap()),
|
741
|
-
None, // Dev servers don't have an executor type
|
742
|
-
),
|
743
|
-
crate::executor::ExecutorType::CodingAgent { config, follow_up } => {
|
744
|
-
let command = if follow_up.is_some() {
|
745
|
-
"followup_executor".to_string()
|
746
|
-
} else {
|
747
|
-
"executor".to_string()
|
748
|
-
};
|
749
|
-
(command, None, Some(format!("{}", config)))
|
750
|
-
}
|
751
|
-
};
|
752
|
-
|
753
|
-
let create_process = CreateExecutionProcess {
|
754
|
-
task_attempt_id: attempt_id,
|
755
|
-
process_type,
|
756
|
-
executor_type: executor_type_string,
|
757
|
-
command,
|
758
|
-
args,
|
759
|
-
working_directory: worktree_path.to_string(),
|
760
|
-
};
|
761
|
-
|
762
|
-
ExecutionProcess::create(pool, &create_process, process_id)
|
763
|
-
.await
|
764
|
-
.map_err(TaskAttemptError::from)
|
765
|
-
}
|
766
|
-
|
767
|
-
/// Create executor session record for coding agents
|
768
|
-
async fn create_executor_session_record(
|
769
|
-
pool: &SqlitePool,
|
770
|
-
attempt_id: Uuid,
|
771
|
-
task_id: Uuid,
|
772
|
-
process_id: Uuid,
|
773
|
-
followup_prompt: Option<String>,
|
774
|
-
) -> Result<(), TaskAttemptError> {
|
775
|
-
// Use follow-up prompt if provided, otherwise get the task to create prompt
|
776
|
-
let prompt = if let Some(followup_prompt) = followup_prompt {
|
777
|
-
followup_prompt
|
778
|
-
} else {
|
779
|
-
let task = Task::find_by_id(pool, task_id)
|
780
|
-
.await?
|
781
|
-
.ok_or(TaskAttemptError::TaskNotFound)?;
|
782
|
-
format!("{}\n\n{}", task.title, task.description.unwrap_or_default())
|
783
|
-
};
|
784
|
-
|
785
|
-
let session_id = Uuid::new_v4();
|
786
|
-
let create_session = CreateExecutorSession {
|
787
|
-
task_attempt_id: attempt_id,
|
788
|
-
execution_process_id: process_id,
|
789
|
-
prompt: Some(prompt),
|
790
|
-
};
|
791
|
-
|
792
|
-
ExecutorSession::create(pool, &create_session, session_id)
|
793
|
-
.await
|
794
|
-
.map(|_| ())
|
795
|
-
.map_err(TaskAttemptError::from)
|
796
|
-
}
|
797
|
-
|
798
|
-
/// Execute the process based on type
|
799
|
-
async fn execute_process(
|
800
|
-
executor_type: &crate::executor::ExecutorType,
|
801
|
-
pool: &SqlitePool,
|
802
|
-
task_id: Uuid,
|
803
|
-
attempt_id: Uuid,
|
804
|
-
process_id: Uuid,
|
805
|
-
worktree_path: &str,
|
806
|
-
) -> Result<command_group::AsyncGroupChild, TaskAttemptError> {
|
807
|
-
use crate::executors::{CleanupScriptExecutor, DevServerExecutor, SetupScriptExecutor};
|
808
|
-
|
809
|
-
let result = match executor_type {
|
810
|
-
crate::executor::ExecutorType::SetupScript(script) => {
|
811
|
-
let executor = SetupScriptExecutor {
|
812
|
-
script: script.clone(),
|
813
|
-
};
|
814
|
-
executor
|
815
|
-
.execute_streaming(pool, task_id, attempt_id, process_id, worktree_path)
|
816
|
-
.await
|
817
|
-
}
|
818
|
-
crate::executor::ExecutorType::CleanupScript(script) => {
|
819
|
-
let executor = CleanupScriptExecutor {
|
820
|
-
script: script.clone(),
|
821
|
-
};
|
822
|
-
executor
|
823
|
-
.execute_streaming(pool, task_id, attempt_id, process_id, worktree_path)
|
824
|
-
.await
|
825
|
-
}
|
826
|
-
crate::executor::ExecutorType::DevServer(script) => {
|
827
|
-
let executor = DevServerExecutor {
|
828
|
-
script: script.clone(),
|
829
|
-
};
|
830
|
-
executor
|
831
|
-
.execute_streaming(pool, task_id, attempt_id, process_id, worktree_path)
|
832
|
-
.await
|
833
|
-
}
|
834
|
-
crate::executor::ExecutorType::CodingAgent { config, follow_up } => {
|
835
|
-
let executor = config.create_executor();
|
836
|
-
|
837
|
-
if let Some(ref follow_up_info) = follow_up {
|
838
|
-
executor
|
839
|
-
.execute_followup_streaming(
|
840
|
-
pool,
|
841
|
-
task_id,
|
842
|
-
attempt_id,
|
843
|
-
process_id,
|
844
|
-
&follow_up_info.session_id,
|
845
|
-
&follow_up_info.prompt,
|
846
|
-
worktree_path,
|
847
|
-
)
|
848
|
-
.await
|
849
|
-
} else {
|
850
|
-
executor
|
851
|
-
.execute_streaming(pool, task_id, attempt_id, process_id, worktree_path)
|
852
|
-
.await
|
853
|
-
}
|
854
|
-
}
|
855
|
-
};
|
856
|
-
|
857
|
-
result.map_err(|e| TaskAttemptError::Git(git2::Error::from_str(&e.to_string())))
|
858
|
-
}
|
859
|
-
|
860
|
-
/// Register process for monitoring
|
861
|
-
async fn register_for_monitoring(
|
862
|
-
app_state: &crate::app_state::AppState,
|
863
|
-
process_id: Uuid,
|
864
|
-
attempt_id: Uuid,
|
865
|
-
process_type: &ExecutionProcessType,
|
866
|
-
child: command_group::AsyncGroupChild,
|
867
|
-
) {
|
868
|
-
let execution_type = match process_type {
|
869
|
-
ExecutionProcessType::SetupScript => crate::app_state::ExecutionType::SetupScript,
|
870
|
-
ExecutionProcessType::CleanupScript => crate::app_state::ExecutionType::CleanupScript,
|
871
|
-
ExecutionProcessType::CodingAgent => crate::app_state::ExecutionType::CodingAgent,
|
872
|
-
ExecutionProcessType::DevServer => crate::app_state::ExecutionType::DevServer,
|
873
|
-
};
|
874
|
-
|
875
|
-
app_state
|
876
|
-
.add_running_execution(
|
877
|
-
process_id,
|
878
|
-
crate::app_state::RunningExecution {
|
879
|
-
task_attempt_id: attempt_id,
|
880
|
-
_execution_type: execution_type,
|
881
|
-
child,
|
882
|
-
},
|
883
|
-
)
|
884
|
-
.await;
|
885
|
-
}
|
886
|
-
|
887
|
-
/// Create execution process database record with delegation context
|
888
|
-
async fn create_execution_process_record_with_delegation(
|
889
|
-
pool: &SqlitePool,
|
890
|
-
attempt_id: Uuid,
|
891
|
-
process_id: Uuid,
|
892
|
-
_setup_script: &str,
|
893
|
-
worktree_path: &str,
|
894
|
-
delegation_context: serde_json::Value,
|
895
|
-
) -> Result<ExecutionProcess, TaskAttemptError> {
|
896
|
-
let (shell_cmd, shell_arg) = get_shell_command();
|
897
|
-
|
898
|
-
// Store delegation context in args for execution monitor to read
|
899
|
-
let args_with_delegation = serde_json::json!([
|
900
|
-
shell_arg,
|
901
|
-
"setup-script",
|
902
|
-
"--delegation-context",
|
903
|
-
delegation_context.to_string()
|
904
|
-
]);
|
905
|
-
|
906
|
-
let create_process = CreateExecutionProcess {
|
907
|
-
task_attempt_id: attempt_id,
|
908
|
-
process_type: ExecutionProcessType::SetupScript,
|
909
|
-
executor_type: Some("setup-script".to_string()),
|
910
|
-
command: shell_cmd.to_string(),
|
911
|
-
args: Some(args_with_delegation.to_string()),
|
912
|
-
working_directory: worktree_path.to_string(),
|
913
|
-
};
|
914
|
-
|
915
|
-
ExecutionProcess::create(pool, &create_process, process_id)
|
916
|
-
.await
|
917
|
-
.map_err(TaskAttemptError::from)
|
918
|
-
}
|
919
|
-
|
920
|
-
/// Execute setup script process specifically
|
921
|
-
async fn execute_setup_script_process(
|
922
|
-
setup_script: &str,
|
923
|
-
pool: &SqlitePool,
|
924
|
-
task_id: Uuid,
|
925
|
-
attempt_id: Uuid,
|
926
|
-
process_id: Uuid,
|
927
|
-
worktree_path: &str,
|
928
|
-
) -> Result<command_group::AsyncGroupChild, TaskAttemptError> {
|
929
|
-
use crate::executors::SetupScriptExecutor;
|
930
|
-
|
931
|
-
let executor = SetupScriptExecutor {
|
932
|
-
script: setup_script.to_string(),
|
933
|
-
};
|
934
|
-
|
935
|
-
executor
|
936
|
-
.execute_streaming(pool, task_id, attempt_id, process_id, worktree_path)
|
937
|
-
.await
|
938
|
-
.map_err(|e| TaskAttemptError::Git(git2::Error::from_str(&e.to_string())))
|
939
|
-
}
|
940
|
-
}
|