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,1193 +0,0 @@
|
|
1
|
-
use git2::Repository;
|
2
|
-
use uuid::Uuid;
|
3
|
-
|
4
|
-
use crate::{
|
5
|
-
app_state::AppState,
|
6
|
-
models::{
|
7
|
-
execution_process::{ExecutionProcess, ExecutionProcessStatus, ExecutionProcessType},
|
8
|
-
task::{Task, TaskStatus},
|
9
|
-
task_attempt::TaskAttempt,
|
10
|
-
},
|
11
|
-
services::{NotificationConfig, NotificationService, ProcessService},
|
12
|
-
utils::worktree_manager::WorktreeManager,
|
13
|
-
};
|
14
|
-
|
15
|
-
/// Delegation context structure
|
16
|
-
#[derive(Debug, serde::Deserialize)]
|
17
|
-
struct DelegationContext {
|
18
|
-
delegate_to: String,
|
19
|
-
operation_params: DelegationOperationParams,
|
20
|
-
}
|
21
|
-
|
22
|
-
#[derive(Debug, serde::Deserialize)]
|
23
|
-
struct DelegationOperationParams {
|
24
|
-
task_id: uuid::Uuid,
|
25
|
-
project_id: uuid::Uuid,
|
26
|
-
attempt_id: uuid::Uuid,
|
27
|
-
additional: Option<serde_json::Value>,
|
28
|
-
}
|
29
|
-
|
30
|
-
/// Parse delegation context from process args JSON
|
31
|
-
fn parse_delegation_context(args_json: &str) -> Option<DelegationContext> {
|
32
|
-
// Parse the args JSON array
|
33
|
-
if let Ok(args_array) = serde_json::from_str::<serde_json::Value>(args_json) {
|
34
|
-
if let Some(args) = args_array.as_array() {
|
35
|
-
// Look for --delegation-context flag
|
36
|
-
for (i, arg) in args.iter().enumerate() {
|
37
|
-
if let Some(arg_str) = arg.as_str() {
|
38
|
-
if arg_str == "--delegation-context" && i + 1 < args.len() {
|
39
|
-
// Next argument should be the delegation context JSON
|
40
|
-
if let Some(context_str) = args[i + 1].as_str() {
|
41
|
-
if let Ok(context) =
|
42
|
-
serde_json::from_str::<DelegationContext>(context_str)
|
43
|
-
{
|
44
|
-
return Some(context);
|
45
|
-
}
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
}
|
52
|
-
None
|
53
|
-
}
|
54
|
-
|
55
|
-
/// Handle delegation after setup completion
|
56
|
-
async fn handle_setup_delegation(app_state: &AppState, delegation_context: DelegationContext) {
|
57
|
-
let params = &delegation_context.operation_params;
|
58
|
-
let task_id = params.task_id;
|
59
|
-
let project_id = params.project_id;
|
60
|
-
let attempt_id = params.attempt_id;
|
61
|
-
|
62
|
-
tracing::info!(
|
63
|
-
"Delegating to {} after setup completion for attempt {}",
|
64
|
-
delegation_context.delegate_to,
|
65
|
-
attempt_id
|
66
|
-
);
|
67
|
-
|
68
|
-
let result = match delegation_context.delegate_to.as_str() {
|
69
|
-
"dev_server" => {
|
70
|
-
ProcessService::start_dev_server_direct(
|
71
|
-
&app_state.db_pool,
|
72
|
-
app_state,
|
73
|
-
attempt_id,
|
74
|
-
task_id,
|
75
|
-
project_id,
|
76
|
-
)
|
77
|
-
.await
|
78
|
-
}
|
79
|
-
"coding_agent" => {
|
80
|
-
ProcessService::start_coding_agent(
|
81
|
-
&app_state.db_pool,
|
82
|
-
app_state,
|
83
|
-
attempt_id,
|
84
|
-
task_id,
|
85
|
-
project_id,
|
86
|
-
)
|
87
|
-
.await
|
88
|
-
}
|
89
|
-
"followup" => {
|
90
|
-
let prompt = params
|
91
|
-
.additional
|
92
|
-
.as_ref()
|
93
|
-
.and_then(|a| a.get("prompt"))
|
94
|
-
.and_then(|p| p.as_str())
|
95
|
-
.unwrap_or("");
|
96
|
-
|
97
|
-
ProcessService::start_followup_execution_direct(
|
98
|
-
&app_state.db_pool,
|
99
|
-
app_state,
|
100
|
-
attempt_id,
|
101
|
-
task_id,
|
102
|
-
project_id,
|
103
|
-
prompt,
|
104
|
-
)
|
105
|
-
.await
|
106
|
-
.map(|_| ())
|
107
|
-
}
|
108
|
-
_ => {
|
109
|
-
tracing::error!(
|
110
|
-
"Unknown delegation target: {}",
|
111
|
-
delegation_context.delegate_to
|
112
|
-
);
|
113
|
-
return;
|
114
|
-
}
|
115
|
-
};
|
116
|
-
|
117
|
-
if let Err(e) = result {
|
118
|
-
tracing::error!(
|
119
|
-
"Failed to delegate to {} after setup completion: {}",
|
120
|
-
delegation_context.delegate_to,
|
121
|
-
e
|
122
|
-
);
|
123
|
-
} else {
|
124
|
-
tracing::info!(
|
125
|
-
"Successfully delegated to {} after setup completion",
|
126
|
-
delegation_context.delegate_to
|
127
|
-
);
|
128
|
-
}
|
129
|
-
}
|
130
|
-
|
131
|
-
/// Commit any unstaged changes in the worktree after execution completion
|
132
|
-
async fn commit_execution_changes(
|
133
|
-
worktree_path: &str,
|
134
|
-
attempt_id: Uuid,
|
135
|
-
summary: Option<&str>,
|
136
|
-
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
137
|
-
// Run git operations in a blocking task since git2 is synchronous
|
138
|
-
let worktree_path = worktree_path.to_string();
|
139
|
-
let summary = summary.map(|s| s.to_string());
|
140
|
-
tokio::task::spawn_blocking(move || {
|
141
|
-
let worktree_repo = Repository::open(&worktree_path)?;
|
142
|
-
|
143
|
-
// Check if there are any changes to commit
|
144
|
-
let status = worktree_repo.statuses(None)?;
|
145
|
-
let has_changes = status.iter().any(|entry| {
|
146
|
-
let flags = entry.status();
|
147
|
-
flags.contains(git2::Status::INDEX_NEW)
|
148
|
-
|| flags.contains(git2::Status::INDEX_MODIFIED)
|
149
|
-
|| flags.contains(git2::Status::INDEX_DELETED)
|
150
|
-
|| flags.contains(git2::Status::WT_NEW)
|
151
|
-
|| flags.contains(git2::Status::WT_MODIFIED)
|
152
|
-
|| flags.contains(git2::Status::WT_DELETED)
|
153
|
-
});
|
154
|
-
|
155
|
-
if !has_changes {
|
156
|
-
return Ok::<(), Box<dyn std::error::Error + Send + Sync>>(());
|
157
|
-
}
|
158
|
-
|
159
|
-
// Get the current signature for commits
|
160
|
-
let signature = worktree_repo.signature()?;
|
161
|
-
|
162
|
-
// Get the current HEAD commit
|
163
|
-
let head = worktree_repo.head()?;
|
164
|
-
let parent_commit = head.peel_to_commit()?;
|
165
|
-
|
166
|
-
// Stage all changes
|
167
|
-
let mut worktree_index = worktree_repo.index()?;
|
168
|
-
worktree_index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
|
169
|
-
worktree_index.write()?;
|
170
|
-
|
171
|
-
let tree_id = worktree_index.write_tree()?;
|
172
|
-
let tree = worktree_repo.find_tree(tree_id)?;
|
173
|
-
|
174
|
-
// Create commit for the changes
|
175
|
-
let commit_message = if let Some(ref summary_msg) = summary {
|
176
|
-
summary_msg.clone()
|
177
|
-
} else {
|
178
|
-
format!("Task attempt {} - Final changes", attempt_id)
|
179
|
-
};
|
180
|
-
worktree_repo.commit(
|
181
|
-
Some("HEAD"),
|
182
|
-
&signature,
|
183
|
-
&signature,
|
184
|
-
&commit_message,
|
185
|
-
&tree,
|
186
|
-
&[&parent_commit],
|
187
|
-
)?;
|
188
|
-
|
189
|
-
Ok(())
|
190
|
-
})
|
191
|
-
.await??;
|
192
|
-
|
193
|
-
Ok(())
|
194
|
-
}
|
195
|
-
|
196
|
-
/// Check if worktree has uncommitted changes and warn if so
|
197
|
-
fn check_uncommitted_changes(worktree_path: &str) {
|
198
|
-
if let Ok(repo) = Repository::open(worktree_path) {
|
199
|
-
if let Ok(statuses) = repo.statuses(None) {
|
200
|
-
// Simplified check: any status entry indicates changes
|
201
|
-
if !statuses.is_empty() {
|
202
|
-
tracing::warn!(
|
203
|
-
"Deleting worktree {} with uncommitted changes",
|
204
|
-
worktree_path
|
205
|
-
);
|
206
|
-
}
|
207
|
-
}
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
/// Delete a single git worktree and its filesystem directory using WorktreeManager
|
212
|
-
async fn delete_worktree(
|
213
|
-
worktree_path: &str,
|
214
|
-
main_repo_path: &str,
|
215
|
-
attempt_id: Uuid,
|
216
|
-
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
217
|
-
let worktree_path_buf = std::path::PathBuf::from(worktree_path);
|
218
|
-
|
219
|
-
// Check if worktree directory exists first - no-op if already gone
|
220
|
-
if !worktree_path_buf.exists() {
|
221
|
-
tracing::debug!(
|
222
|
-
"Worktree {} already doesn't exist, skipping cleanup",
|
223
|
-
worktree_path
|
224
|
-
);
|
225
|
-
return Ok(());
|
226
|
-
}
|
227
|
-
|
228
|
-
// Check for uncommitted changes and warn
|
229
|
-
check_uncommitted_changes(worktree_path);
|
230
|
-
|
231
|
-
match WorktreeManager::cleanup_worktree(&worktree_path_buf, Some(main_repo_path)).await {
|
232
|
-
Ok(_) => {
|
233
|
-
tracing::info!(
|
234
|
-
"Successfully cleaned up worktree for attempt {}",
|
235
|
-
attempt_id
|
236
|
-
);
|
237
|
-
Ok(())
|
238
|
-
}
|
239
|
-
Err(e) => {
|
240
|
-
tracing::error!(
|
241
|
-
"Failed to cleanup worktree for attempt {}: {}",
|
242
|
-
attempt_id,
|
243
|
-
e
|
244
|
-
);
|
245
|
-
Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
246
|
-
}
|
247
|
-
}
|
248
|
-
}
|
249
|
-
|
250
|
-
/// Clean up all worktrees for a specific task (immediate cleanup)
|
251
|
-
pub async fn cleanup_task_worktrees(
|
252
|
-
pool: &sqlx::SqlitePool,
|
253
|
-
task_id: Uuid,
|
254
|
-
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
255
|
-
let task_attempts_with_project =
|
256
|
-
TaskAttempt::find_by_task_id_with_project(pool, task_id).await?;
|
257
|
-
|
258
|
-
if task_attempts_with_project.is_empty() {
|
259
|
-
tracing::debug!("No worktrees found for task {} to clean up", task_id);
|
260
|
-
return Ok(());
|
261
|
-
}
|
262
|
-
|
263
|
-
tracing::info!(
|
264
|
-
"Starting immediate cleanup of {} worktrees for task {}",
|
265
|
-
task_attempts_with_project.len(),
|
266
|
-
task_id
|
267
|
-
);
|
268
|
-
|
269
|
-
let mut cleaned_count = 0;
|
270
|
-
let mut failed_count = 0;
|
271
|
-
|
272
|
-
for (attempt_id, worktree_path, git_repo_path) in task_attempts_with_project {
|
273
|
-
if let Err(e) = delete_worktree(&worktree_path, &git_repo_path, attempt_id).await {
|
274
|
-
tracing::error!(
|
275
|
-
"Failed to cleanup worktree for attempt {}: {}",
|
276
|
-
attempt_id,
|
277
|
-
e
|
278
|
-
);
|
279
|
-
failed_count += 1;
|
280
|
-
// Continue with other attempts even if one fails
|
281
|
-
} else {
|
282
|
-
// Mark worktree as deleted in database after successful cleanup
|
283
|
-
if let Err(e) =
|
284
|
-
crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(pool, attempt_id)
|
285
|
-
.await
|
286
|
-
{
|
287
|
-
tracing::error!(
|
288
|
-
"Failed to mark worktree as deleted in database for attempt {}: {}",
|
289
|
-
attempt_id,
|
290
|
-
e
|
291
|
-
);
|
292
|
-
} else {
|
293
|
-
cleaned_count += 1;
|
294
|
-
}
|
295
|
-
}
|
296
|
-
}
|
297
|
-
|
298
|
-
tracing::info!(
|
299
|
-
"Completed immediate cleanup for task {}: {} worktrees cleaned, {} failed",
|
300
|
-
task_id,
|
301
|
-
cleaned_count,
|
302
|
-
failed_count
|
303
|
-
);
|
304
|
-
|
305
|
-
Ok(())
|
306
|
-
}
|
307
|
-
|
308
|
-
/// Defensively check for externally deleted worktrees and mark them as deleted in the database
|
309
|
-
async fn check_externally_deleted_worktrees(pool: &sqlx::SqlitePool) {
|
310
|
-
let active_attempts = match sqlx::query!(
|
311
|
-
r#"SELECT id as "id!: Uuid", worktree_path FROM task_attempts WHERE worktree_deleted = FALSE"#
|
312
|
-
)
|
313
|
-
.fetch_all(pool)
|
314
|
-
.await
|
315
|
-
{
|
316
|
-
Ok(attempts) => attempts,
|
317
|
-
Err(e) => {
|
318
|
-
tracing::error!("Failed to query active task attempts for external deletion check: {}", e);
|
319
|
-
return;
|
320
|
-
}
|
321
|
-
};
|
322
|
-
|
323
|
-
tracing::debug!(
|
324
|
-
"Checking {} active worktrees for external deletion...",
|
325
|
-
active_attempts.len()
|
326
|
-
);
|
327
|
-
|
328
|
-
let mut externally_deleted_count = 0;
|
329
|
-
for record in active_attempts {
|
330
|
-
let attempt_id = record.id;
|
331
|
-
let worktree_path = &record.worktree_path;
|
332
|
-
|
333
|
-
// Check if worktree directory exists
|
334
|
-
if !std::path::Path::new(worktree_path).exists() {
|
335
|
-
// Worktree was deleted externally, mark as deleted in database
|
336
|
-
if let Err(e) =
|
337
|
-
crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(pool, attempt_id)
|
338
|
-
.await
|
339
|
-
{
|
340
|
-
tracing::error!(
|
341
|
-
"Failed to mark externally deleted worktree as deleted for attempt {}: {}",
|
342
|
-
attempt_id,
|
343
|
-
e
|
344
|
-
);
|
345
|
-
} else {
|
346
|
-
externally_deleted_count += 1;
|
347
|
-
tracing::debug!(
|
348
|
-
"Marked externally deleted worktree as deleted for attempt {} (path: {})",
|
349
|
-
attempt_id,
|
350
|
-
worktree_path
|
351
|
-
);
|
352
|
-
}
|
353
|
-
}
|
354
|
-
}
|
355
|
-
|
356
|
-
if externally_deleted_count > 0 {
|
357
|
-
tracing::info!(
|
358
|
-
"Found and marked {} externally deleted worktrees",
|
359
|
-
externally_deleted_count
|
360
|
-
);
|
361
|
-
} else {
|
362
|
-
tracing::debug!("No externally deleted worktrees found");
|
363
|
-
}
|
364
|
-
}
|
365
|
-
|
366
|
-
/// Find and delete orphaned worktrees that don't correspond to any task attempts
|
367
|
-
async fn cleanup_orphaned_worktrees(pool: &sqlx::SqlitePool) {
|
368
|
-
// Check if orphan cleanup is disabled via environment variable
|
369
|
-
if std::env::var("DISABLE_WORKTREE_ORPHAN_CLEANUP").is_ok() {
|
370
|
-
tracing::debug!("Orphan worktree cleanup is disabled via DISABLE_WORKTREE_ORPHAN_CLEANUP environment variable");
|
371
|
-
return;
|
372
|
-
}
|
373
|
-
let worktree_base_dir = crate::models::task_attempt::TaskAttempt::get_worktree_base_dir();
|
374
|
-
|
375
|
-
// Check if base directory exists
|
376
|
-
if !worktree_base_dir.exists() {
|
377
|
-
tracing::debug!(
|
378
|
-
"Worktree base directory {} does not exist, skipping orphan cleanup",
|
379
|
-
worktree_base_dir.display()
|
380
|
-
);
|
381
|
-
return;
|
382
|
-
}
|
383
|
-
|
384
|
-
// Read all directories in the base directory
|
385
|
-
let entries = match std::fs::read_dir(&worktree_base_dir) {
|
386
|
-
Ok(entries) => entries,
|
387
|
-
Err(e) => {
|
388
|
-
tracing::error!(
|
389
|
-
"Failed to read worktree base directory {}: {}",
|
390
|
-
worktree_base_dir.display(),
|
391
|
-
e
|
392
|
-
);
|
393
|
-
return;
|
394
|
-
}
|
395
|
-
};
|
396
|
-
|
397
|
-
let mut orphaned_count = 0;
|
398
|
-
let mut checked_count = 0;
|
399
|
-
|
400
|
-
for entry in entries {
|
401
|
-
let entry = match entry {
|
402
|
-
Ok(entry) => entry,
|
403
|
-
Err(e) => {
|
404
|
-
tracing::warn!("Failed to read directory entry: {}", e);
|
405
|
-
continue;
|
406
|
-
}
|
407
|
-
};
|
408
|
-
|
409
|
-
let path = entry.path();
|
410
|
-
|
411
|
-
// Only process directories
|
412
|
-
if !path.is_dir() {
|
413
|
-
continue;
|
414
|
-
}
|
415
|
-
|
416
|
-
let worktree_path_str = path.to_string_lossy().to_string();
|
417
|
-
checked_count += 1;
|
418
|
-
|
419
|
-
// Check if this worktree path exists in the database
|
420
|
-
let exists_in_db = match sqlx::query!(
|
421
|
-
"SELECT COUNT(*) as count FROM task_attempts WHERE worktree_path = ?",
|
422
|
-
worktree_path_str
|
423
|
-
)
|
424
|
-
.fetch_one(pool)
|
425
|
-
.await
|
426
|
-
{
|
427
|
-
Ok(row) => row.count > 0,
|
428
|
-
Err(e) => {
|
429
|
-
tracing::error!(
|
430
|
-
"Failed to check database for worktree path {}: {}",
|
431
|
-
worktree_path_str,
|
432
|
-
e
|
433
|
-
);
|
434
|
-
continue;
|
435
|
-
}
|
436
|
-
};
|
437
|
-
|
438
|
-
if !exists_in_db {
|
439
|
-
// This is an orphaned worktree - delete it
|
440
|
-
tracing::info!("Found orphaned worktree: {}", worktree_path_str);
|
441
|
-
|
442
|
-
// For orphaned worktrees, we try to clean up git metadata if possible
|
443
|
-
// then remove the directory
|
444
|
-
if let Err(e) = cleanup_orphaned_worktree_directory(&path).await {
|
445
|
-
tracing::error!(
|
446
|
-
"Failed to remove orphaned worktree {}: {}",
|
447
|
-
worktree_path_str,
|
448
|
-
e
|
449
|
-
);
|
450
|
-
} else {
|
451
|
-
orphaned_count += 1;
|
452
|
-
tracing::info!(
|
453
|
-
"Successfully removed orphaned worktree: {}",
|
454
|
-
worktree_path_str
|
455
|
-
);
|
456
|
-
}
|
457
|
-
}
|
458
|
-
}
|
459
|
-
|
460
|
-
if orphaned_count > 0 {
|
461
|
-
tracing::info!(
|
462
|
-
"Cleaned up {} orphaned worktrees (checked {} total directories)",
|
463
|
-
orphaned_count,
|
464
|
-
checked_count
|
465
|
-
);
|
466
|
-
} else {
|
467
|
-
tracing::debug!(
|
468
|
-
"No orphaned worktrees found (checked {} directories)",
|
469
|
-
checked_count
|
470
|
-
);
|
471
|
-
}
|
472
|
-
}
|
473
|
-
|
474
|
-
/// Clean up an orphaned worktree directory, attempting to clean git metadata if possible
|
475
|
-
async fn cleanup_orphaned_worktree_directory(
|
476
|
-
worktree_path: &std::path::Path,
|
477
|
-
) -> Result<(), std::io::Error> {
|
478
|
-
// Use WorktreeManager for proper cleanup - it will try to infer the repo path
|
479
|
-
// and clean up what it can, even if the main repo is gone
|
480
|
-
match WorktreeManager::cleanup_worktree(worktree_path, None).await {
|
481
|
-
Ok(()) => {
|
482
|
-
tracing::debug!(
|
483
|
-
"WorktreeManager successfully cleaned up orphaned worktree: {}",
|
484
|
-
worktree_path.display()
|
485
|
-
);
|
486
|
-
}
|
487
|
-
Err(e) => {
|
488
|
-
tracing::warn!(
|
489
|
-
"WorktreeManager cleanup failed for orphaned worktree {}: {}",
|
490
|
-
worktree_path.display(),
|
491
|
-
e
|
492
|
-
);
|
493
|
-
|
494
|
-
// If WorktreeManager cleanup failed, fall back to simple directory removal
|
495
|
-
// This ensures we delete as much as we can
|
496
|
-
if worktree_path.exists() {
|
497
|
-
tracing::debug!(
|
498
|
-
"Falling back to simple directory removal for orphaned worktree: {}",
|
499
|
-
worktree_path.display()
|
500
|
-
);
|
501
|
-
std::fs::remove_dir_all(worktree_path).map_err(|e| {
|
502
|
-
std::io::Error::new(
|
503
|
-
e.kind(),
|
504
|
-
format!("Failed to remove orphaned worktree directory: {}", e),
|
505
|
-
)
|
506
|
-
})?;
|
507
|
-
}
|
508
|
-
}
|
509
|
-
}
|
510
|
-
|
511
|
-
Ok(())
|
512
|
-
}
|
513
|
-
|
514
|
-
pub async fn execution_monitor(app_state: AppState) {
|
515
|
-
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5));
|
516
|
-
let mut cleanup_interval = tokio::time::interval(tokio::time::Duration::from_secs(1800)); // 30 minutes
|
517
|
-
|
518
|
-
loop {
|
519
|
-
tokio::select! {
|
520
|
-
_ = interval.tick() => {
|
521
|
-
// Check for completed processes FIRST to avoid race conditions
|
522
|
-
let completed_executions = app_state.get_running_executions_for_monitor().await;
|
523
|
-
|
524
|
-
// Handle completed executions
|
525
|
-
for (execution_process_id, task_attempt_id, success, exit_code) in completed_executions {
|
526
|
-
let status_text = if success {
|
527
|
-
"completed successfully"
|
528
|
-
} else {
|
529
|
-
"failed"
|
530
|
-
};
|
531
|
-
let exit_text = if let Some(code) = exit_code {
|
532
|
-
format!(" with exit code {}", code)
|
533
|
-
} else {
|
534
|
-
String::new()
|
535
|
-
};
|
536
|
-
|
537
|
-
tracing::info!(
|
538
|
-
"Execution {} {}{}",
|
539
|
-
execution_process_id,
|
540
|
-
status_text,
|
541
|
-
exit_text
|
542
|
-
);
|
543
|
-
|
544
|
-
// Update the execution process record
|
545
|
-
let execution_status = if success {
|
546
|
-
ExecutionProcessStatus::Completed
|
547
|
-
} else {
|
548
|
-
ExecutionProcessStatus::Failed
|
549
|
-
};
|
550
|
-
|
551
|
-
if let Err(e) = ExecutionProcess::update_completion(
|
552
|
-
&app_state.db_pool,
|
553
|
-
execution_process_id,
|
554
|
-
execution_status,
|
555
|
-
exit_code,
|
556
|
-
)
|
557
|
-
.await
|
558
|
-
{
|
559
|
-
tracing::error!(
|
560
|
-
"Failed to update execution process {} completion: {}",
|
561
|
-
execution_process_id,
|
562
|
-
e
|
563
|
-
);
|
564
|
-
}
|
565
|
-
|
566
|
-
// Get the execution process to determine next steps
|
567
|
-
if let Ok(Some(execution_process)) =
|
568
|
-
ExecutionProcess::find_by_id(&app_state.db_pool, execution_process_id).await
|
569
|
-
{
|
570
|
-
match execution_process.process_type {
|
571
|
-
ExecutionProcessType::SetupScript => {
|
572
|
-
handle_setup_completion(
|
573
|
-
&app_state,
|
574
|
-
task_attempt_id,
|
575
|
-
execution_process,
|
576
|
-
success,
|
577
|
-
)
|
578
|
-
.await;
|
579
|
-
}
|
580
|
-
ExecutionProcessType::CleanupScript => {
|
581
|
-
handle_cleanup_completion(
|
582
|
-
&app_state,
|
583
|
-
task_attempt_id,
|
584
|
-
execution_process_id,
|
585
|
-
execution_process,
|
586
|
-
success,
|
587
|
-
exit_code,
|
588
|
-
)
|
589
|
-
.await;
|
590
|
-
}
|
591
|
-
ExecutionProcessType::CodingAgent => {
|
592
|
-
handle_coding_agent_completion(
|
593
|
-
&app_state,
|
594
|
-
task_attempt_id,
|
595
|
-
execution_process_id,
|
596
|
-
execution_process,
|
597
|
-
success,
|
598
|
-
exit_code,
|
599
|
-
)
|
600
|
-
.await;
|
601
|
-
}
|
602
|
-
ExecutionProcessType::DevServer => {
|
603
|
-
handle_dev_server_completion(
|
604
|
-
&app_state,
|
605
|
-
task_attempt_id,
|
606
|
-
execution_process_id,
|
607
|
-
execution_process,
|
608
|
-
success,
|
609
|
-
exit_code,
|
610
|
-
)
|
611
|
-
.await;
|
612
|
-
}
|
613
|
-
}
|
614
|
-
} else {
|
615
|
-
tracing::error!(
|
616
|
-
"Failed to find execution process {} for completion handling",
|
617
|
-
execution_process_id
|
618
|
-
);
|
619
|
-
}
|
620
|
-
}
|
621
|
-
|
622
|
-
// Check for orphaned execution processes AFTER handling completions
|
623
|
-
// Add a small delay to ensure completed processes are properly handled first
|
624
|
-
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
625
|
-
|
626
|
-
let running_processes = match ExecutionProcess::find_running(&app_state.db_pool).await {
|
627
|
-
Ok(processes) => processes,
|
628
|
-
Err(e) => {
|
629
|
-
tracing::error!("Failed to query running execution processes: {}", e);
|
630
|
-
continue;
|
631
|
-
}
|
632
|
-
};
|
633
|
-
|
634
|
-
for process in running_processes {
|
635
|
-
// Check if this process is not actually running in the app state
|
636
|
-
if !app_state.has_running_execution(process.task_attempt_id).await {
|
637
|
-
// Additional check: if the process was recently updated, skip it to prevent race conditions
|
638
|
-
let now = chrono::Utc::now();
|
639
|
-
let time_since_update = now - process.updated_at;
|
640
|
-
if time_since_update.num_seconds() < 10 {
|
641
|
-
// Process was updated within last 10 seconds, likely just completed
|
642
|
-
tracing::debug!(
|
643
|
-
"Skipping recently updated orphaned process {} (updated {} seconds ago)",
|
644
|
-
process.id,
|
645
|
-
time_since_update.num_seconds()
|
646
|
-
);
|
647
|
-
continue;
|
648
|
-
}
|
649
|
-
|
650
|
-
// This is truly an orphaned execution process - mark it as failed
|
651
|
-
tracing::info!(
|
652
|
-
"Found orphaned execution process {} for task attempt {}",
|
653
|
-
process.id,
|
654
|
-
process.task_attempt_id
|
655
|
-
);
|
656
|
-
// This is truly an orphaned execution process - mark it as failed
|
657
|
-
tracing::info!(
|
658
|
-
"Found orphaned execution process {} for task attempt {}",
|
659
|
-
process.id,
|
660
|
-
process.task_attempt_id
|
661
|
-
);
|
662
|
-
|
663
|
-
// Update the execution process status first
|
664
|
-
if let Err(e) = ExecutionProcess::update_completion(
|
665
|
-
&app_state.db_pool,
|
666
|
-
process.id,
|
667
|
-
ExecutionProcessStatus::Failed,
|
668
|
-
None, // No exit code for orphaned processes
|
669
|
-
)
|
670
|
-
.await
|
671
|
-
{
|
672
|
-
tracing::error!(
|
673
|
-
"Failed to update orphaned execution process {} status: {}",
|
674
|
-
process.id,
|
675
|
-
e
|
676
|
-
);
|
677
|
-
continue;
|
678
|
-
}
|
679
|
-
|
680
|
-
// Process marked as failed
|
681
|
-
|
682
|
-
tracing::info!("Marked orphaned execution process {} as failed", process.id);
|
683
|
-
|
684
|
-
// Update task status to InReview for coding agent and setup script failures
|
685
|
-
if matches!(
|
686
|
-
process.process_type,
|
687
|
-
ExecutionProcessType::CodingAgent | ExecutionProcessType::SetupScript
|
688
|
-
) {
|
689
|
-
if let Ok(Some(task_attempt)) =
|
690
|
-
TaskAttempt::find_by_id(&app_state.db_pool, process.task_attempt_id).await
|
691
|
-
{
|
692
|
-
if let Ok(Some(task)) =
|
693
|
-
Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
|
694
|
-
{
|
695
|
-
if let Err(e) = Task::update_status(
|
696
|
-
&app_state.db_pool,
|
697
|
-
task.id,
|
698
|
-
task.project_id,
|
699
|
-
TaskStatus::InReview,
|
700
|
-
)
|
701
|
-
.await
|
702
|
-
{
|
703
|
-
tracing::error!("Failed to update task status to InReview for orphaned attempt: {}", e);
|
704
|
-
}
|
705
|
-
}
|
706
|
-
}
|
707
|
-
}
|
708
|
-
}
|
709
|
-
}
|
710
|
-
}
|
711
|
-
_ = cleanup_interval.tick() => {
|
712
|
-
tracing::info!("Starting periodic worktree cleanup...");
|
713
|
-
|
714
|
-
// First, defensively check for externally deleted worktrees
|
715
|
-
check_externally_deleted_worktrees(&app_state.db_pool).await;
|
716
|
-
|
717
|
-
// Then, find and delete orphaned worktrees that don't belong to any task
|
718
|
-
cleanup_orphaned_worktrees(&app_state.db_pool).await;
|
719
|
-
|
720
|
-
// Then, proceed with normal expired worktree cleanup
|
721
|
-
match TaskAttempt::find_expired_for_cleanup(&app_state.db_pool).await {
|
722
|
-
Ok(expired_attempts) => {
|
723
|
-
if expired_attempts.is_empty() {
|
724
|
-
tracing::debug!("No expired worktrees found");
|
725
|
-
} else {
|
726
|
-
tracing::info!("Found {} expired worktrees to clean up", expired_attempts.len());
|
727
|
-
for (attempt_id, worktree_path, git_repo_path) in expired_attempts {
|
728
|
-
if let Err(e) = delete_worktree(&worktree_path, &git_repo_path, attempt_id).await {
|
729
|
-
tracing::error!("Failed to cleanup expired worktree {}: {}", attempt_id, e);
|
730
|
-
} else {
|
731
|
-
// Mark worktree as deleted in database after successful cleanup
|
732
|
-
if let Err(e) = crate::models::task_attempt::TaskAttempt::mark_worktree_deleted(&app_state.db_pool, attempt_id).await {
|
733
|
-
tracing::error!("Failed to mark worktree as deleted in database for attempt {}: {}", attempt_id, e);
|
734
|
-
} else {
|
735
|
-
tracing::info!("Successfully marked worktree as deleted for attempt {}", attempt_id);
|
736
|
-
}
|
737
|
-
}
|
738
|
-
}
|
739
|
-
}
|
740
|
-
}
|
741
|
-
Err(e) => {
|
742
|
-
tracing::error!("Failed to query expired task attempts: {}", e);
|
743
|
-
}
|
744
|
-
}
|
745
|
-
}
|
746
|
-
}
|
747
|
-
}
|
748
|
-
}
|
749
|
-
|
750
|
-
/// Handle setup script completion
|
751
|
-
async fn handle_setup_completion(
|
752
|
-
app_state: &AppState,
|
753
|
-
task_attempt_id: Uuid,
|
754
|
-
execution_process: ExecutionProcess,
|
755
|
-
success: bool,
|
756
|
-
) {
|
757
|
-
if success {
|
758
|
-
// Mark setup as completed in database
|
759
|
-
if let Err(e) = TaskAttempt::mark_setup_completed(&app_state.db_pool, task_attempt_id).await
|
760
|
-
{
|
761
|
-
tracing::error!(
|
762
|
-
"Failed to mark setup as completed for attempt {}: {}",
|
763
|
-
task_attempt_id,
|
764
|
-
e
|
765
|
-
);
|
766
|
-
}
|
767
|
-
|
768
|
-
// Setup completed successfully
|
769
|
-
|
770
|
-
// Check for delegation context in process args
|
771
|
-
let delegation_result = if let Some(args_json) = &execution_process.args {
|
772
|
-
parse_delegation_context(args_json)
|
773
|
-
} else {
|
774
|
-
None
|
775
|
-
};
|
776
|
-
|
777
|
-
if let Some(delegation_context) = delegation_result {
|
778
|
-
// Delegate to the original operation
|
779
|
-
handle_setup_delegation(app_state, delegation_context).await;
|
780
|
-
} else {
|
781
|
-
// Fallback to original behavior - start coding agent
|
782
|
-
if let Ok(Some(task_attempt)) =
|
783
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
784
|
-
{
|
785
|
-
if let Ok(Some(task)) =
|
786
|
-
Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
|
787
|
-
{
|
788
|
-
// Start the coding agent
|
789
|
-
if let Err(e) = ProcessService::start_coding_agent(
|
790
|
-
&app_state.db_pool,
|
791
|
-
app_state,
|
792
|
-
task_attempt_id,
|
793
|
-
task.id,
|
794
|
-
task.project_id,
|
795
|
-
)
|
796
|
-
.await
|
797
|
-
{
|
798
|
-
tracing::error!(
|
799
|
-
"Failed to start coding agent after setup completion: {}",
|
800
|
-
e
|
801
|
-
);
|
802
|
-
}
|
803
|
-
}
|
804
|
-
}
|
805
|
-
}
|
806
|
-
} else {
|
807
|
-
// Setup failed, update task status
|
808
|
-
|
809
|
-
// Update task status to InReview since setup failed
|
810
|
-
if let Ok(Some(task_attempt)) =
|
811
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
812
|
-
{
|
813
|
-
if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await
|
814
|
-
{
|
815
|
-
if let Err(e) = Task::update_status(
|
816
|
-
&app_state.db_pool,
|
817
|
-
task.id,
|
818
|
-
task.project_id,
|
819
|
-
TaskStatus::InReview,
|
820
|
-
)
|
821
|
-
.await
|
822
|
-
{
|
823
|
-
tracing::error!(
|
824
|
-
"Failed to update task status to InReview after setup failure: {}",
|
825
|
-
e
|
826
|
-
);
|
827
|
-
}
|
828
|
-
}
|
829
|
-
}
|
830
|
-
}
|
831
|
-
}
|
832
|
-
|
833
|
-
/// Handle coding agent completion
|
834
|
-
async fn handle_coding_agent_completion(
|
835
|
-
app_state: &AppState,
|
836
|
-
task_attempt_id: Uuid,
|
837
|
-
execution_process_id: Uuid,
|
838
|
-
execution_process: ExecutionProcess,
|
839
|
-
success: bool,
|
840
|
-
exit_code: Option<i64>,
|
841
|
-
) {
|
842
|
-
// Extract and store assistant message from execution logs
|
843
|
-
let summary = if let Some(stdout) = &execution_process.stdout {
|
844
|
-
if let Some(assistant_message) = crate::executor::parse_assistant_message_from_logs(stdout)
|
845
|
-
{
|
846
|
-
if let Err(e) = crate::models::executor_session::ExecutorSession::update_summary(
|
847
|
-
&app_state.db_pool,
|
848
|
-
execution_process_id,
|
849
|
-
&assistant_message,
|
850
|
-
)
|
851
|
-
.await
|
852
|
-
{
|
853
|
-
tracing::error!(
|
854
|
-
"Failed to update summary for execution process {}: {}",
|
855
|
-
execution_process_id,
|
856
|
-
e
|
857
|
-
);
|
858
|
-
None
|
859
|
-
} else {
|
860
|
-
tracing::info!(
|
861
|
-
"Successfully stored summary for execution process {}",
|
862
|
-
execution_process_id
|
863
|
-
);
|
864
|
-
Some(assistant_message)
|
865
|
-
}
|
866
|
-
} else {
|
867
|
-
None
|
868
|
-
}
|
869
|
-
} else {
|
870
|
-
None
|
871
|
-
};
|
872
|
-
|
873
|
-
// Note: Notifications and status updates moved to cleanup completion handler
|
874
|
-
// to ensure they only fire after all processing (including cleanup) is complete
|
875
|
-
|
876
|
-
// Get task attempt to access worktree path for committing changes
|
877
|
-
if let Ok(Some(task_attempt)) =
|
878
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
879
|
-
{
|
880
|
-
// Commit any unstaged changes after execution completion
|
881
|
-
if let Err(e) = commit_execution_changes(
|
882
|
-
&task_attempt.worktree_path,
|
883
|
-
task_attempt_id,
|
884
|
-
summary.as_deref(),
|
885
|
-
)
|
886
|
-
.await
|
887
|
-
{
|
888
|
-
tracing::error!(
|
889
|
-
"Failed to commit execution changes for attempt {}: {}",
|
890
|
-
task_attempt_id,
|
891
|
-
e
|
892
|
-
);
|
893
|
-
} else {
|
894
|
-
tracing::info!(
|
895
|
-
"Successfully committed execution changes for attempt {}",
|
896
|
-
task_attempt_id
|
897
|
-
);
|
898
|
-
}
|
899
|
-
|
900
|
-
// Coding agent execution completed
|
901
|
-
tracing::info!(
|
902
|
-
"Task attempt {} set to paused after coding agent completion",
|
903
|
-
task_attempt_id
|
904
|
-
);
|
905
|
-
|
906
|
-
// Run cleanup script if configured, otherwise immediately finalize task
|
907
|
-
if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await {
|
908
|
-
// Check if cleanup script should run
|
909
|
-
let should_run_cleanup = if let Ok(Some(project)) =
|
910
|
-
crate::models::project::Project::find_by_id(&app_state.db_pool, task.project_id)
|
911
|
-
.await
|
912
|
-
{
|
913
|
-
project
|
914
|
-
.cleanup_script
|
915
|
-
.as_ref()
|
916
|
-
.map(|script| !script.trim().is_empty())
|
917
|
-
.unwrap_or(false)
|
918
|
-
} else {
|
919
|
-
false
|
920
|
-
};
|
921
|
-
|
922
|
-
if should_run_cleanup {
|
923
|
-
// Run cleanup script - completion will be handled in cleanup completion handler
|
924
|
-
if let Err(e) =
|
925
|
-
crate::services::process_service::ProcessService::run_cleanup_script_if_configured(
|
926
|
-
&app_state.db_pool,
|
927
|
-
app_state,
|
928
|
-
task_attempt_id,
|
929
|
-
task_attempt.task_id,
|
930
|
-
task.project_id,
|
931
|
-
)
|
932
|
-
.await
|
933
|
-
{
|
934
|
-
tracing::error!(
|
935
|
-
"Failed to run cleanup script for attempt {}: {}",
|
936
|
-
task_attempt_id,
|
937
|
-
e
|
938
|
-
);
|
939
|
-
// Even if cleanup fails to start, finalize the task
|
940
|
-
finalize_task_completion(app_state, task_attempt_id, &task, success, exit_code).await;
|
941
|
-
}
|
942
|
-
} else {
|
943
|
-
// No cleanup script configured, immediately finalize task
|
944
|
-
finalize_task_completion(app_state, task_attempt_id, &task, success, exit_code)
|
945
|
-
.await;
|
946
|
-
}
|
947
|
-
}
|
948
|
-
} else {
|
949
|
-
tracing::error!(
|
950
|
-
"Failed to find task attempt {} for coding agent completion",
|
951
|
-
task_attempt_id
|
952
|
-
);
|
953
|
-
}
|
954
|
-
}
|
955
|
-
|
956
|
-
/// Finalize task completion with notifications and status updates
|
957
|
-
async fn finalize_task_completion(
|
958
|
-
app_state: &AppState,
|
959
|
-
task_attempt_id: Uuid,
|
960
|
-
task: &crate::models::task::Task,
|
961
|
-
success: bool,
|
962
|
-
exit_code: Option<i64>,
|
963
|
-
) {
|
964
|
-
// Send notifications if enabled
|
965
|
-
let sound_enabled = app_state.get_sound_alerts_enabled().await;
|
966
|
-
let push_enabled = app_state.get_push_notifications_enabled().await;
|
967
|
-
|
968
|
-
if sound_enabled || push_enabled {
|
969
|
-
let sound_file = app_state.get_sound_file().await;
|
970
|
-
let notification_config = NotificationConfig {
|
971
|
-
sound_enabled,
|
972
|
-
push_enabled,
|
973
|
-
};
|
974
|
-
|
975
|
-
let notification_service = NotificationService::new(notification_config);
|
976
|
-
|
977
|
-
// Get task attempt for notification details
|
978
|
-
if let Ok(Some(task_attempt)) =
|
979
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
980
|
-
{
|
981
|
-
let title = format!("Task Complete: {}", task.title);
|
982
|
-
let message = if success {
|
983
|
-
format!(
|
984
|
-
"✅ '{}' completed successfully\nBranch: {}\nExecutor: {}",
|
985
|
-
task.title,
|
986
|
-
task_attempt.branch,
|
987
|
-
task_attempt.executor.as_deref().unwrap_or("default")
|
988
|
-
)
|
989
|
-
} else {
|
990
|
-
format!(
|
991
|
-
"❌ '{}' execution failed\nBranch: {}\nExecutor: {}",
|
992
|
-
task.title,
|
993
|
-
task_attempt.branch,
|
994
|
-
task_attempt.executor.as_deref().unwrap_or("default")
|
995
|
-
)
|
996
|
-
};
|
997
|
-
|
998
|
-
notification_service
|
999
|
-
.notify(&title, &message, &sound_file)
|
1000
|
-
.await;
|
1001
|
-
}
|
1002
|
-
}
|
1003
|
-
|
1004
|
-
// Track analytics event
|
1005
|
-
app_state
|
1006
|
-
.track_analytics_event(
|
1007
|
-
"task_attempt_finished",
|
1008
|
-
Some(serde_json::json!({
|
1009
|
-
"task_id": task.id.to_string(),
|
1010
|
-
"project_id": task.project_id.to_string(),
|
1011
|
-
"attempt_id": task_attempt_id.to_string(),
|
1012
|
-
"execution_success": success,
|
1013
|
-
"exit_code": exit_code,
|
1014
|
-
})),
|
1015
|
-
)
|
1016
|
-
.await;
|
1017
|
-
|
1018
|
-
// Update task status to InReview
|
1019
|
-
if let Err(e) = Task::update_status(
|
1020
|
-
&app_state.db_pool,
|
1021
|
-
task.id,
|
1022
|
-
task.project_id,
|
1023
|
-
TaskStatus::InReview,
|
1024
|
-
)
|
1025
|
-
.await
|
1026
|
-
{
|
1027
|
-
tracing::error!(
|
1028
|
-
"Failed to update task status to InReview for completed attempt: {}",
|
1029
|
-
e
|
1030
|
-
);
|
1031
|
-
}
|
1032
|
-
}
|
1033
|
-
|
1034
|
-
/// Handle cleanup script completion
|
1035
|
-
async fn handle_cleanup_completion(
|
1036
|
-
app_state: &AppState,
|
1037
|
-
task_attempt_id: Uuid,
|
1038
|
-
execution_process_id: Uuid,
|
1039
|
-
_execution_process: ExecutionProcess,
|
1040
|
-
success: bool,
|
1041
|
-
exit_code: Option<i64>,
|
1042
|
-
) {
|
1043
|
-
let exit_text = if let Some(code) = exit_code {
|
1044
|
-
format!(" with exit code {}", code)
|
1045
|
-
} else {
|
1046
|
-
String::new()
|
1047
|
-
};
|
1048
|
-
|
1049
|
-
tracing::info!(
|
1050
|
-
"Cleanup script for task attempt {} completed{}",
|
1051
|
-
task_attempt_id,
|
1052
|
-
exit_text
|
1053
|
-
);
|
1054
|
-
|
1055
|
-
// Update execution process status
|
1056
|
-
let process_status = if success {
|
1057
|
-
ExecutionProcessStatus::Completed
|
1058
|
-
} else {
|
1059
|
-
ExecutionProcessStatus::Failed
|
1060
|
-
};
|
1061
|
-
|
1062
|
-
if let Err(e) = ExecutionProcess::update_completion(
|
1063
|
-
&app_state.db_pool,
|
1064
|
-
execution_process_id,
|
1065
|
-
process_status,
|
1066
|
-
exit_code,
|
1067
|
-
)
|
1068
|
-
.await
|
1069
|
-
{
|
1070
|
-
tracing::error!(
|
1071
|
-
"Failed to update cleanup script execution process status: {}",
|
1072
|
-
e
|
1073
|
-
);
|
1074
|
-
}
|
1075
|
-
|
1076
|
-
// Auto-commit changes after successful cleanup script execution
|
1077
|
-
if success {
|
1078
|
-
if let Ok(Some(task_attempt)) =
|
1079
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
1080
|
-
{
|
1081
|
-
let commit_message = "Cleanup script";
|
1082
|
-
|
1083
|
-
if let Err(e) = commit_execution_changes(
|
1084
|
-
&task_attempt.worktree_path,
|
1085
|
-
task_attempt_id,
|
1086
|
-
Some(commit_message),
|
1087
|
-
)
|
1088
|
-
.await
|
1089
|
-
{
|
1090
|
-
tracing::error!(
|
1091
|
-
"Failed to commit changes after cleanup script for attempt {}: {}",
|
1092
|
-
task_attempt_id,
|
1093
|
-
e
|
1094
|
-
);
|
1095
|
-
} else {
|
1096
|
-
tracing::info!(
|
1097
|
-
"Successfully committed changes after cleanup script for attempt {}",
|
1098
|
-
task_attempt_id
|
1099
|
-
);
|
1100
|
-
}
|
1101
|
-
} else {
|
1102
|
-
tracing::error!(
|
1103
|
-
"Failed to retrieve task attempt {} for cleanup commit",
|
1104
|
-
task_attempt_id
|
1105
|
-
);
|
1106
|
-
}
|
1107
|
-
}
|
1108
|
-
|
1109
|
-
// Finalize task completion after cleanup (whether successful or failed)
|
1110
|
-
if let Ok(Some(task_attempt)) =
|
1111
|
-
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await
|
1112
|
-
{
|
1113
|
-
if let Ok(Some(task)) = Task::find_by_id(&app_state.db_pool, task_attempt.task_id).await {
|
1114
|
-
// Get the coding agent execution process to determine original success status
|
1115
|
-
let coding_success = if let Ok(processes) =
|
1116
|
-
ExecutionProcess::find_by_task_attempt_id(&app_state.db_pool, task_attempt_id).await
|
1117
|
-
{
|
1118
|
-
// Find the most recent completed coding agent process
|
1119
|
-
processes
|
1120
|
-
.iter()
|
1121
|
-
.filter(|p| {
|
1122
|
-
p.process_type
|
1123
|
-
== crate::models::execution_process::ExecutionProcessType::CodingAgent
|
1124
|
-
})
|
1125
|
-
.filter(|p| {
|
1126
|
-
p.status
|
1127
|
-
== crate::models::execution_process::ExecutionProcessStatus::Completed
|
1128
|
-
})
|
1129
|
-
.next_back()
|
1130
|
-
.map(|p| p.exit_code == Some(0))
|
1131
|
-
.unwrap_or(false)
|
1132
|
-
} else {
|
1133
|
-
false
|
1134
|
-
};
|
1135
|
-
|
1136
|
-
finalize_task_completion(app_state, task_attempt_id, &task, coding_success, exit_code)
|
1137
|
-
.await;
|
1138
|
-
} else {
|
1139
|
-
tracing::error!(
|
1140
|
-
"Failed to retrieve task {} for cleanup completion finalization",
|
1141
|
-
task_attempt.task_id
|
1142
|
-
);
|
1143
|
-
}
|
1144
|
-
} else {
|
1145
|
-
tracing::error!(
|
1146
|
-
"Failed to retrieve task attempt {} for cleanup completion finalization",
|
1147
|
-
task_attempt_id
|
1148
|
-
);
|
1149
|
-
}
|
1150
|
-
}
|
1151
|
-
|
1152
|
-
/// Handle dev server completion (future functionality)
|
1153
|
-
async fn handle_dev_server_completion(
|
1154
|
-
app_state: &AppState,
|
1155
|
-
task_attempt_id: Uuid,
|
1156
|
-
execution_process_id: Uuid,
|
1157
|
-
_execution_process: ExecutionProcess,
|
1158
|
-
success: bool,
|
1159
|
-
exit_code: Option<i64>,
|
1160
|
-
) {
|
1161
|
-
let exit_text = if let Some(code) = exit_code {
|
1162
|
-
format!(" with exit code {}", code)
|
1163
|
-
} else {
|
1164
|
-
String::new()
|
1165
|
-
};
|
1166
|
-
|
1167
|
-
tracing::info!(
|
1168
|
-
"Dev server for task attempt {} completed{}",
|
1169
|
-
task_attempt_id,
|
1170
|
-
exit_text
|
1171
|
-
);
|
1172
|
-
|
1173
|
-
// Update execution process status instead of creating activity
|
1174
|
-
let process_status = if success {
|
1175
|
-
ExecutionProcessStatus::Completed
|
1176
|
-
} else {
|
1177
|
-
ExecutionProcessStatus::Failed
|
1178
|
-
};
|
1179
|
-
|
1180
|
-
if let Err(e) = ExecutionProcess::update_completion(
|
1181
|
-
&app_state.db_pool,
|
1182
|
-
execution_process_id,
|
1183
|
-
process_status,
|
1184
|
-
exit_code,
|
1185
|
-
)
|
1186
|
-
.await
|
1187
|
-
{
|
1188
|
-
tracing::error!(
|
1189
|
-
"Failed to update dev server execution process status: {}",
|
1190
|
-
e
|
1191
|
-
);
|
1192
|
-
}
|
1193
|
-
}
|