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,356 +0,0 @@
|
|
1
|
-
use chrono::{DateTime, Utc};
|
2
|
-
use git2::{BranchType, Repository};
|
3
|
-
use serde::{Deserialize, Serialize};
|
4
|
-
use sqlx::{FromRow, SqlitePool};
|
5
|
-
use ts_rs::TS;
|
6
|
-
use utoipa::ToSchema;
|
7
|
-
use uuid::Uuid;
|
8
|
-
|
9
|
-
#[derive(Debug, Clone, FromRow, Serialize, Deserialize, TS, ToSchema)]
|
10
|
-
#[ts(export)]
|
11
|
-
pub struct Project {
|
12
|
-
pub id: Uuid,
|
13
|
-
pub name: String,
|
14
|
-
pub git_repo_path: String,
|
15
|
-
pub setup_script: Option<String>,
|
16
|
-
pub dev_script: Option<String>,
|
17
|
-
pub cleanup_script: Option<String>,
|
18
|
-
|
19
|
-
#[ts(type = "Date")]
|
20
|
-
#[schema(value_type = String, format = DateTime)]
|
21
|
-
pub created_at: DateTime<Utc>,
|
22
|
-
#[ts(type = "Date")]
|
23
|
-
#[schema(value_type = String, format = DateTime)]
|
24
|
-
pub updated_at: DateTime<Utc>,
|
25
|
-
}
|
26
|
-
|
27
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
28
|
-
#[ts(export)]
|
29
|
-
pub struct CreateProject {
|
30
|
-
pub name: String,
|
31
|
-
pub git_repo_path: String,
|
32
|
-
pub use_existing_repo: bool,
|
33
|
-
pub setup_script: Option<String>,
|
34
|
-
pub dev_script: Option<String>,
|
35
|
-
pub cleanup_script: Option<String>,
|
36
|
-
}
|
37
|
-
|
38
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
39
|
-
#[ts(export)]
|
40
|
-
pub struct UpdateProject {
|
41
|
-
pub name: Option<String>,
|
42
|
-
pub git_repo_path: Option<String>,
|
43
|
-
pub setup_script: Option<String>,
|
44
|
-
pub dev_script: Option<String>,
|
45
|
-
pub cleanup_script: Option<String>,
|
46
|
-
}
|
47
|
-
|
48
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
49
|
-
#[ts(export)]
|
50
|
-
pub struct ProjectWithBranch {
|
51
|
-
pub id: Uuid,
|
52
|
-
pub name: String,
|
53
|
-
pub git_repo_path: String,
|
54
|
-
pub setup_script: Option<String>,
|
55
|
-
pub dev_script: Option<String>,
|
56
|
-
pub cleanup_script: Option<String>,
|
57
|
-
pub current_branch: Option<String>,
|
58
|
-
|
59
|
-
#[ts(type = "Date")]
|
60
|
-
#[schema(value_type = String, format = DateTime)]
|
61
|
-
pub created_at: DateTime<Utc>,
|
62
|
-
#[ts(type = "Date")]
|
63
|
-
#[schema(value_type = String, format = DateTime)]
|
64
|
-
pub updated_at: DateTime<Utc>,
|
65
|
-
}
|
66
|
-
|
67
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
68
|
-
#[ts(export)]
|
69
|
-
pub struct SearchResult {
|
70
|
-
pub path: String,
|
71
|
-
pub is_file: bool,
|
72
|
-
pub match_type: SearchMatchType,
|
73
|
-
}
|
74
|
-
|
75
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
76
|
-
#[ts(export)]
|
77
|
-
pub enum SearchMatchType {
|
78
|
-
FileName,
|
79
|
-
DirectoryName,
|
80
|
-
FullPath,
|
81
|
-
}
|
82
|
-
|
83
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
84
|
-
#[ts(export)]
|
85
|
-
pub struct GitBranch {
|
86
|
-
pub name: String,
|
87
|
-
pub is_current: bool,
|
88
|
-
pub is_remote: bool,
|
89
|
-
#[ts(type = "Date")]
|
90
|
-
pub last_commit_date: DateTime<Utc>,
|
91
|
-
}
|
92
|
-
|
93
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
94
|
-
#[ts(export)]
|
95
|
-
pub struct CreateBranch {
|
96
|
-
pub name: String,
|
97
|
-
pub base_branch: Option<String>,
|
98
|
-
}
|
99
|
-
|
100
|
-
impl Project {
|
101
|
-
pub async fn find_all(pool: &SqlitePool) -> Result<Vec<Self>, sqlx::Error> {
|
102
|
-
sqlx::query_as!(
|
103
|
-
Project,
|
104
|
-
r#"SELECT id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>" FROM projects ORDER BY created_at DESC"#
|
105
|
-
)
|
106
|
-
.fetch_all(pool)
|
107
|
-
.await
|
108
|
-
}
|
109
|
-
|
110
|
-
pub async fn find_by_id(pool: &SqlitePool, id: Uuid) -> Result<Option<Self>, sqlx::Error> {
|
111
|
-
sqlx::query_as!(
|
112
|
-
Project,
|
113
|
-
r#"SELECT id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>" FROM projects WHERE id = $1"#,
|
114
|
-
id
|
115
|
-
)
|
116
|
-
.fetch_optional(pool)
|
117
|
-
.await
|
118
|
-
}
|
119
|
-
|
120
|
-
pub async fn find_by_git_repo_path(
|
121
|
-
pool: &SqlitePool,
|
122
|
-
git_repo_path: &str,
|
123
|
-
) -> Result<Option<Self>, sqlx::Error> {
|
124
|
-
sqlx::query_as!(
|
125
|
-
Project,
|
126
|
-
r#"SELECT id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>" FROM projects WHERE git_repo_path = $1"#,
|
127
|
-
git_repo_path
|
128
|
-
)
|
129
|
-
.fetch_optional(pool)
|
130
|
-
.await
|
131
|
-
}
|
132
|
-
|
133
|
-
pub async fn find_by_git_repo_path_excluding_id(
|
134
|
-
pool: &SqlitePool,
|
135
|
-
git_repo_path: &str,
|
136
|
-
exclude_id: Uuid,
|
137
|
-
) -> Result<Option<Self>, sqlx::Error> {
|
138
|
-
sqlx::query_as!(
|
139
|
-
Project,
|
140
|
-
r#"SELECT id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>" FROM projects WHERE git_repo_path = $1 AND id != $2"#,
|
141
|
-
git_repo_path,
|
142
|
-
exclude_id
|
143
|
-
)
|
144
|
-
.fetch_optional(pool)
|
145
|
-
.await
|
146
|
-
}
|
147
|
-
|
148
|
-
pub async fn create(
|
149
|
-
pool: &SqlitePool,
|
150
|
-
data: &CreateProject,
|
151
|
-
project_id: Uuid,
|
152
|
-
) -> Result<Self, sqlx::Error> {
|
153
|
-
sqlx::query_as!(
|
154
|
-
Project,
|
155
|
-
r#"INSERT INTO projects (id, name, git_repo_path, setup_script, dev_script, cleanup_script) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
|
156
|
-
project_id,
|
157
|
-
data.name,
|
158
|
-
data.git_repo_path,
|
159
|
-
data.setup_script,
|
160
|
-
data.dev_script,
|
161
|
-
data.cleanup_script
|
162
|
-
)
|
163
|
-
.fetch_one(pool)
|
164
|
-
.await
|
165
|
-
}
|
166
|
-
|
167
|
-
pub async fn update(
|
168
|
-
pool: &SqlitePool,
|
169
|
-
id: Uuid,
|
170
|
-
name: String,
|
171
|
-
git_repo_path: String,
|
172
|
-
setup_script: Option<String>,
|
173
|
-
dev_script: Option<String>,
|
174
|
-
cleanup_script: Option<String>,
|
175
|
-
) -> Result<Self, sqlx::Error> {
|
176
|
-
sqlx::query_as!(
|
177
|
-
Project,
|
178
|
-
r#"UPDATE projects SET name = $2, git_repo_path = $3, setup_script = $4, dev_script = $5, cleanup_script = $6 WHERE id = $1 RETURNING id as "id!: Uuid", name, git_repo_path, setup_script, dev_script, cleanup_script, created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
|
179
|
-
id,
|
180
|
-
name,
|
181
|
-
git_repo_path,
|
182
|
-
setup_script,
|
183
|
-
dev_script,
|
184
|
-
cleanup_script
|
185
|
-
)
|
186
|
-
.fetch_one(pool)
|
187
|
-
.await
|
188
|
-
}
|
189
|
-
|
190
|
-
pub async fn delete(pool: &SqlitePool, id: Uuid) -> Result<u64, sqlx::Error> {
|
191
|
-
let result = sqlx::query!("DELETE FROM projects WHERE id = $1", id)
|
192
|
-
.execute(pool)
|
193
|
-
.await?;
|
194
|
-
Ok(result.rows_affected())
|
195
|
-
}
|
196
|
-
|
197
|
-
pub async fn exists(pool: &SqlitePool, id: Uuid) -> Result<bool, sqlx::Error> {
|
198
|
-
let result = sqlx::query!(
|
199
|
-
r#"
|
200
|
-
SELECT COUNT(*) as "count!: i64"
|
201
|
-
FROM projects
|
202
|
-
WHERE id = $1
|
203
|
-
"#,
|
204
|
-
id
|
205
|
-
)
|
206
|
-
.fetch_one(pool)
|
207
|
-
.await?;
|
208
|
-
|
209
|
-
Ok(result.count > 0)
|
210
|
-
}
|
211
|
-
|
212
|
-
pub fn get_current_branch(&self) -> Result<String, git2::Error> {
|
213
|
-
let repo = Repository::open(&self.git_repo_path)?;
|
214
|
-
let head = repo.head()?;
|
215
|
-
|
216
|
-
if let Some(branch_name) = head.shorthand() {
|
217
|
-
Ok(branch_name.to_string())
|
218
|
-
} else {
|
219
|
-
Ok("HEAD".to_string())
|
220
|
-
}
|
221
|
-
}
|
222
|
-
|
223
|
-
pub fn with_branch_info(self) -> ProjectWithBranch {
|
224
|
-
let current_branch = self.get_current_branch().ok();
|
225
|
-
|
226
|
-
ProjectWithBranch {
|
227
|
-
id: self.id,
|
228
|
-
name: self.name,
|
229
|
-
git_repo_path: self.git_repo_path,
|
230
|
-
setup_script: self.setup_script,
|
231
|
-
dev_script: self.dev_script,
|
232
|
-
cleanup_script: self.cleanup_script,
|
233
|
-
current_branch,
|
234
|
-
created_at: self.created_at,
|
235
|
-
updated_at: self.updated_at,
|
236
|
-
}
|
237
|
-
}
|
238
|
-
|
239
|
-
pub fn get_all_branches(&self) -> Result<Vec<GitBranch>, git2::Error> {
|
240
|
-
let repo = Repository::open(&self.git_repo_path)?;
|
241
|
-
let current_branch = self.get_current_branch().unwrap_or_default();
|
242
|
-
let mut branches = Vec::new();
|
243
|
-
|
244
|
-
// Helper function to get last commit date for a branch
|
245
|
-
let get_last_commit_date = |branch: &git2::Branch| -> Result<DateTime<Utc>, git2::Error> {
|
246
|
-
if let Some(target) = branch.get().target() {
|
247
|
-
if let Ok(commit) = repo.find_commit(target) {
|
248
|
-
let timestamp = commit.time().seconds();
|
249
|
-
return Ok(DateTime::from_timestamp(timestamp, 0).unwrap_or_else(Utc::now));
|
250
|
-
}
|
251
|
-
}
|
252
|
-
Ok(Utc::now()) // Default to now if we can't get the commit date
|
253
|
-
};
|
254
|
-
|
255
|
-
// Get local branches
|
256
|
-
let local_branches = repo.branches(Some(BranchType::Local))?;
|
257
|
-
for branch_result in local_branches {
|
258
|
-
let (branch, _) = branch_result?;
|
259
|
-
if let Some(name) = branch.name()? {
|
260
|
-
let last_commit_date = get_last_commit_date(&branch)?;
|
261
|
-
branches.push(GitBranch {
|
262
|
-
name: name.to_string(),
|
263
|
-
is_current: name == current_branch,
|
264
|
-
is_remote: false,
|
265
|
-
last_commit_date,
|
266
|
-
});
|
267
|
-
}
|
268
|
-
}
|
269
|
-
|
270
|
-
// Get remote branches
|
271
|
-
let remote_branches = repo.branches(Some(BranchType::Remote))?;
|
272
|
-
for branch_result in remote_branches {
|
273
|
-
let (branch, _) = branch_result?;
|
274
|
-
if let Some(name) = branch.name()? {
|
275
|
-
// Skip remote HEAD references
|
276
|
-
if !name.ends_with("/HEAD") {
|
277
|
-
let last_commit_date = get_last_commit_date(&branch)?;
|
278
|
-
branches.push(GitBranch {
|
279
|
-
name: name.to_string(),
|
280
|
-
is_current: false,
|
281
|
-
is_remote: true,
|
282
|
-
last_commit_date,
|
283
|
-
});
|
284
|
-
}
|
285
|
-
}
|
286
|
-
}
|
287
|
-
|
288
|
-
// Sort branches: current first, then by most recent commit date
|
289
|
-
branches.sort_by(|a, b| {
|
290
|
-
if a.is_current && !b.is_current {
|
291
|
-
std::cmp::Ordering::Less
|
292
|
-
} else if !a.is_current && b.is_current {
|
293
|
-
std::cmp::Ordering::Greater
|
294
|
-
} else {
|
295
|
-
// Sort by most recent commit date (newest first)
|
296
|
-
b.last_commit_date.cmp(&a.last_commit_date)
|
297
|
-
}
|
298
|
-
});
|
299
|
-
|
300
|
-
Ok(branches)
|
301
|
-
}
|
302
|
-
|
303
|
-
pub fn create_branch(
|
304
|
-
&self,
|
305
|
-
branch_name: &str,
|
306
|
-
base_branch: Option<&str>,
|
307
|
-
) -> Result<GitBranch, git2::Error> {
|
308
|
-
let repo = Repository::open(&self.git_repo_path)?;
|
309
|
-
|
310
|
-
// Get the base branch reference - default to current branch if not specified
|
311
|
-
let base_branch_name = match base_branch {
|
312
|
-
Some(name) => name.to_string(),
|
313
|
-
None => self
|
314
|
-
.get_current_branch()
|
315
|
-
.unwrap_or_else(|_| "HEAD".to_string()),
|
316
|
-
};
|
317
|
-
|
318
|
-
// Find the base commit
|
319
|
-
let base_commit = if base_branch_name == "HEAD" {
|
320
|
-
repo.head()?.peel_to_commit()?
|
321
|
-
} else {
|
322
|
-
// Try to find the branch as local first, then remote
|
323
|
-
let base_ref = if let Ok(local_ref) =
|
324
|
-
repo.find_reference(&format!("refs/heads/{}", base_branch_name))
|
325
|
-
{
|
326
|
-
local_ref
|
327
|
-
} else if let Ok(remote_ref) =
|
328
|
-
repo.find_reference(&format!("refs/remotes/{}", base_branch_name))
|
329
|
-
{
|
330
|
-
remote_ref
|
331
|
-
} else {
|
332
|
-
return Err(git2::Error::from_str(&format!(
|
333
|
-
"Base branch '{}' not found",
|
334
|
-
base_branch_name
|
335
|
-
)));
|
336
|
-
};
|
337
|
-
base_ref.peel_to_commit()?
|
338
|
-
};
|
339
|
-
|
340
|
-
// Create the new branch
|
341
|
-
let _new_branch = repo.branch(branch_name, &base_commit, false)?;
|
342
|
-
|
343
|
-
// Get the commit date for the new branch (same as base commit)
|
344
|
-
let last_commit_date = {
|
345
|
-
let timestamp = base_commit.time().seconds();
|
346
|
-
DateTime::from_timestamp(timestamp, 0).unwrap_or_else(Utc::now)
|
347
|
-
};
|
348
|
-
|
349
|
-
Ok(GitBranch {
|
350
|
-
name: branch_name.to_string(),
|
351
|
-
is_current: false,
|
352
|
-
is_remote: false,
|
353
|
-
last_commit_date,
|
354
|
-
})
|
355
|
-
}
|
356
|
-
}
|
@@ -1,345 +0,0 @@
|
|
1
|
-
use chrono::{DateTime, Utc};
|
2
|
-
use serde::{Deserialize, Serialize};
|
3
|
-
use sqlx::{FromRow, SqlitePool, Type};
|
4
|
-
use ts_rs::TS;
|
5
|
-
use utoipa::ToSchema;
|
6
|
-
use uuid::Uuid;
|
7
|
-
|
8
|
-
#[derive(Debug, Clone, Type, Serialize, Deserialize, PartialEq, TS, ToSchema)]
|
9
|
-
#[sqlx(type_name = "task_status", rename_all = "lowercase")]
|
10
|
-
#[serde(rename_all = "lowercase")]
|
11
|
-
#[ts(export)]
|
12
|
-
pub enum TaskStatus {
|
13
|
-
Todo,
|
14
|
-
InProgress,
|
15
|
-
InReview,
|
16
|
-
Done,
|
17
|
-
Cancelled,
|
18
|
-
}
|
19
|
-
|
20
|
-
#[derive(Debug, Clone, FromRow, Serialize, Deserialize, TS, ToSchema)]
|
21
|
-
#[ts(export)]
|
22
|
-
pub struct Task {
|
23
|
-
pub id: Uuid,
|
24
|
-
pub project_id: Uuid, // Foreign key to Project
|
25
|
-
pub title: String,
|
26
|
-
pub description: Option<String>,
|
27
|
-
pub status: TaskStatus,
|
28
|
-
pub wish_id: String, // Required: Grouping field for task organization
|
29
|
-
pub parent_task_attempt: Option<Uuid>, // Foreign key to parent TaskAttempt
|
30
|
-
pub created_at: DateTime<Utc>,
|
31
|
-
pub updated_at: DateTime<Utc>,
|
32
|
-
}
|
33
|
-
|
34
|
-
#[derive(Debug, Clone, Serialize, Deserialize, TS, ToSchema)]
|
35
|
-
#[ts(export)]
|
36
|
-
pub struct TaskWithAttemptStatus {
|
37
|
-
pub id: Uuid,
|
38
|
-
pub project_id: Uuid,
|
39
|
-
pub title: String,
|
40
|
-
pub description: Option<String>,
|
41
|
-
pub status: TaskStatus,
|
42
|
-
pub wish_id: String,
|
43
|
-
pub parent_task_attempt: Option<Uuid>,
|
44
|
-
pub created_at: DateTime<Utc>,
|
45
|
-
pub updated_at: DateTime<Utc>,
|
46
|
-
pub has_in_progress_attempt: bool,
|
47
|
-
pub has_merged_attempt: bool,
|
48
|
-
pub last_attempt_failed: bool,
|
49
|
-
pub latest_attempt_executor: Option<String>,
|
50
|
-
}
|
51
|
-
|
52
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
53
|
-
#[ts(export)]
|
54
|
-
pub struct CreateTask {
|
55
|
-
pub project_id: Uuid,
|
56
|
-
pub title: String,
|
57
|
-
pub description: Option<String>,
|
58
|
-
pub wish_id: String, // Required: Wish grouping identifier
|
59
|
-
pub parent_task_attempt: Option<Uuid>,
|
60
|
-
}
|
61
|
-
|
62
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
63
|
-
#[ts(export)]
|
64
|
-
pub struct CreateTaskAndStart {
|
65
|
-
pub project_id: Uuid,
|
66
|
-
pub title: String,
|
67
|
-
pub description: Option<String>,
|
68
|
-
pub wish_id: String, // Required: Wish grouping identifier
|
69
|
-
pub parent_task_attempt: Option<Uuid>,
|
70
|
-
pub executor: Option<crate::executor::ExecutorConfig>,
|
71
|
-
}
|
72
|
-
|
73
|
-
#[derive(Debug, Deserialize, TS, ToSchema)]
|
74
|
-
#[ts(export)]
|
75
|
-
pub struct UpdateTask {
|
76
|
-
pub title: Option<String>,
|
77
|
-
pub description: Option<String>,
|
78
|
-
pub status: Option<TaskStatus>,
|
79
|
-
pub wish_id: Option<String>, // Optional: Can reassign wish
|
80
|
-
pub parent_task_attempt: Option<Uuid>,
|
81
|
-
}
|
82
|
-
|
83
|
-
impl Task {
|
84
|
-
pub async fn find_by_project_id_with_attempt_status(
|
85
|
-
pool: &SqlitePool,
|
86
|
-
project_id: Uuid,
|
87
|
-
) -> Result<Vec<TaskWithAttemptStatus>, sqlx::Error> {
|
88
|
-
let records = sqlx::query!(
|
89
|
-
r#"SELECT
|
90
|
-
t.id AS "id!: Uuid",
|
91
|
-
t.project_id AS "project_id!: Uuid",
|
92
|
-
t.title,
|
93
|
-
t.description,
|
94
|
-
t.status AS "status!: TaskStatus",
|
95
|
-
t.wish_id,
|
96
|
-
t.parent_task_attempt AS "parent_task_attempt: Uuid",
|
97
|
-
t.created_at AS "created_at!: DateTime<Utc>",
|
98
|
-
t.updated_at AS "updated_at!: DateTime<Utc>",
|
99
|
-
|
100
|
-
CASE WHEN EXISTS (
|
101
|
-
SELECT 1
|
102
|
-
FROM task_attempts ta
|
103
|
-
JOIN execution_processes ep
|
104
|
-
ON ep.task_attempt_id = ta.id
|
105
|
-
WHERE ta.task_id = t.id
|
106
|
-
AND ep.status = 'running'
|
107
|
-
AND ep.process_type IN ('setupscript','cleanupscript','codingagent')
|
108
|
-
LIMIT 1
|
109
|
-
) THEN 1 ELSE 0 END AS "has_in_progress_attempt!: i64",
|
110
|
-
|
111
|
-
CASE WHEN EXISTS (
|
112
|
-
SELECT 1
|
113
|
-
FROM task_attempts ta
|
114
|
-
WHERE ta.task_id = t.id
|
115
|
-
AND ta.merge_commit IS NOT NULL
|
116
|
-
LIMIT 1
|
117
|
-
) THEN 1 ELSE 0 END AS "has_merged_attempt!: i64",
|
118
|
-
|
119
|
-
CASE WHEN (
|
120
|
-
SELECT ep.status
|
121
|
-
FROM task_attempts ta
|
122
|
-
JOIN execution_processes ep
|
123
|
-
ON ep.task_attempt_id = ta.id
|
124
|
-
WHERE ta.task_id = t.id
|
125
|
-
AND ep.process_type IN ('setupscript','cleanupscript','codingagent')
|
126
|
-
ORDER BY ep.created_at DESC
|
127
|
-
LIMIT 1
|
128
|
-
) IN ('failed','killed') THEN 1 ELSE 0 END
|
129
|
-
AS "last_attempt_failed!: i64",
|
130
|
-
|
131
|
-
( SELECT ta.executor
|
132
|
-
FROM task_attempts ta
|
133
|
-
WHERE ta.task_id = t.id
|
134
|
-
ORDER BY ta.created_at DESC
|
135
|
-
LIMIT 1
|
136
|
-
) AS "latest_attempt_executor"
|
137
|
-
|
138
|
-
FROM tasks t
|
139
|
-
WHERE t.project_id = $1
|
140
|
-
ORDER BY t.created_at DESC"#,
|
141
|
-
project_id
|
142
|
-
)
|
143
|
-
.fetch_all(pool)
|
144
|
-
.await?;
|
145
|
-
|
146
|
-
let tasks = records
|
147
|
-
.into_iter()
|
148
|
-
.map(|rec| TaskWithAttemptStatus {
|
149
|
-
id: rec.id,
|
150
|
-
project_id: rec.project_id,
|
151
|
-
title: rec.title,
|
152
|
-
description: rec.description,
|
153
|
-
status: rec.status,
|
154
|
-
wish_id: rec.wish_id,
|
155
|
-
parent_task_attempt: rec.parent_task_attempt,
|
156
|
-
created_at: rec.created_at,
|
157
|
-
updated_at: rec.updated_at,
|
158
|
-
has_in_progress_attempt: rec.has_in_progress_attempt != 0,
|
159
|
-
has_merged_attempt: rec.has_merged_attempt != 0,
|
160
|
-
last_attempt_failed: rec.last_attempt_failed != 0,
|
161
|
-
latest_attempt_executor: rec.latest_attempt_executor,
|
162
|
-
})
|
163
|
-
.collect();
|
164
|
-
|
165
|
-
Ok(tasks)
|
166
|
-
}
|
167
|
-
|
168
|
-
pub async fn find_by_id(pool: &SqlitePool, id: Uuid) -> Result<Option<Self>, sqlx::Error> {
|
169
|
-
sqlx::query_as!(
|
170
|
-
Task,
|
171
|
-
r#"SELECT id as "id!: Uuid", project_id as "project_id!: Uuid", title, description, status as "status!: TaskStatus", wish_id, parent_task_attempt as "parent_task_attempt: Uuid", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>"
|
172
|
-
FROM tasks
|
173
|
-
WHERE id = $1"#,
|
174
|
-
id
|
175
|
-
)
|
176
|
-
.fetch_optional(pool)
|
177
|
-
.await
|
178
|
-
}
|
179
|
-
|
180
|
-
pub async fn find_by_id_and_project_id(
|
181
|
-
pool: &SqlitePool,
|
182
|
-
id: Uuid,
|
183
|
-
project_id: Uuid,
|
184
|
-
) -> Result<Option<Self>, sqlx::Error> {
|
185
|
-
sqlx::query_as!(
|
186
|
-
Task,
|
187
|
-
r#"SELECT id as "id!: Uuid", project_id as "project_id!: Uuid", title, description, status as "status!: TaskStatus", wish_id, parent_task_attempt as "parent_task_attempt: Uuid", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>"
|
188
|
-
FROM tasks
|
189
|
-
WHERE id = $1 AND project_id = $2"#,
|
190
|
-
id,
|
191
|
-
project_id
|
192
|
-
)
|
193
|
-
.fetch_optional(pool)
|
194
|
-
.await
|
195
|
-
}
|
196
|
-
|
197
|
-
pub async fn create(
|
198
|
-
pool: &SqlitePool,
|
199
|
-
data: &CreateTask,
|
200
|
-
task_id: Uuid,
|
201
|
-
) -> Result<Self, sqlx::Error> {
|
202
|
-
sqlx::query_as!(
|
203
|
-
Task,
|
204
|
-
r#"INSERT INTO tasks (id, project_id, title, description, status, wish_id, parent_task_attempt)
|
205
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
206
|
-
RETURNING id as "id!: Uuid", project_id as "project_id!: Uuid", title, description, status as "status!: TaskStatus", wish_id, parent_task_attempt as "parent_task_attempt: Uuid", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
|
207
|
-
task_id,
|
208
|
-
data.project_id,
|
209
|
-
data.title,
|
210
|
-
data.description,
|
211
|
-
TaskStatus::Todo as TaskStatus,
|
212
|
-
data.wish_id,
|
213
|
-
data.parent_task_attempt
|
214
|
-
)
|
215
|
-
.fetch_one(pool)
|
216
|
-
.await
|
217
|
-
}
|
218
|
-
|
219
|
-
pub async fn update(
|
220
|
-
pool: &SqlitePool,
|
221
|
-
id: Uuid,
|
222
|
-
project_id: Uuid,
|
223
|
-
title: String,
|
224
|
-
description: Option<String>,
|
225
|
-
status: TaskStatus,
|
226
|
-
wish_id: String,
|
227
|
-
parent_task_attempt: Option<Uuid>,
|
228
|
-
) -> Result<Self, sqlx::Error> {
|
229
|
-
let status_value = status as TaskStatus;
|
230
|
-
sqlx::query_as!(
|
231
|
-
Task,
|
232
|
-
r#"UPDATE tasks
|
233
|
-
SET title = $3, description = $4, status = $5, wish_id = $6, parent_task_attempt = $7
|
234
|
-
WHERE id = $1 AND project_id = $2
|
235
|
-
RETURNING id as "id!: Uuid", project_id as "project_id!: Uuid", title, description, status as "status!: TaskStatus", wish_id, parent_task_attempt as "parent_task_attempt: Uuid", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
|
236
|
-
id,
|
237
|
-
project_id,
|
238
|
-
title,
|
239
|
-
description,
|
240
|
-
status_value,
|
241
|
-
wish_id,
|
242
|
-
parent_task_attempt
|
243
|
-
)
|
244
|
-
.fetch_one(pool)
|
245
|
-
.await
|
246
|
-
}
|
247
|
-
|
248
|
-
pub async fn update_status(
|
249
|
-
pool: &SqlitePool,
|
250
|
-
id: Uuid,
|
251
|
-
project_id: Uuid,
|
252
|
-
status: TaskStatus,
|
253
|
-
) -> Result<(), sqlx::Error> {
|
254
|
-
let status_value = status as TaskStatus;
|
255
|
-
sqlx::query!(
|
256
|
-
"UPDATE tasks SET status = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $1 AND project_id = $2",
|
257
|
-
id,
|
258
|
-
project_id,
|
259
|
-
status_value
|
260
|
-
)
|
261
|
-
.execute(pool)
|
262
|
-
.await?;
|
263
|
-
Ok(())
|
264
|
-
}
|
265
|
-
|
266
|
-
pub async fn delete(pool: &SqlitePool, id: Uuid, project_id: Uuid) -> Result<u64, sqlx::Error> {
|
267
|
-
let result = sqlx::query!(
|
268
|
-
"DELETE FROM tasks WHERE id = $1 AND project_id = $2",
|
269
|
-
id,
|
270
|
-
project_id
|
271
|
-
)
|
272
|
-
.execute(pool)
|
273
|
-
.await?;
|
274
|
-
Ok(result.rows_affected())
|
275
|
-
}
|
276
|
-
|
277
|
-
pub async fn exists(
|
278
|
-
pool: &SqlitePool,
|
279
|
-
id: Uuid,
|
280
|
-
project_id: Uuid,
|
281
|
-
) -> Result<bool, sqlx::Error> {
|
282
|
-
let result = sqlx::query!(
|
283
|
-
"SELECT id as \"id!: Uuid\" FROM tasks WHERE id = $1 AND project_id = $2",
|
284
|
-
id,
|
285
|
-
project_id
|
286
|
-
)
|
287
|
-
.fetch_optional(pool)
|
288
|
-
.await?;
|
289
|
-
Ok(result.is_some())
|
290
|
-
}
|
291
|
-
|
292
|
-
pub async fn find_related_tasks_by_attempt_id(
|
293
|
-
pool: &SqlitePool,
|
294
|
-
attempt_id: Uuid,
|
295
|
-
project_id: Uuid,
|
296
|
-
) -> Result<Vec<Self>, sqlx::Error> {
|
297
|
-
// Find both children and parent for this attempt
|
298
|
-
sqlx::query_as!(
|
299
|
-
Task,
|
300
|
-
r#"SELECT DISTINCT t.id as "id!: Uuid", t.project_id as "project_id!: Uuid", t.title, t.description, t.status as "status!: TaskStatus", t.wish_id, t.parent_task_attempt as "parent_task_attempt: Uuid", t.created_at as "created_at!: DateTime<Utc>", t.updated_at as "updated_at!: DateTime<Utc>"
|
301
|
-
FROM tasks t
|
302
|
-
WHERE (
|
303
|
-
-- Find children: tasks that have this attempt as parent
|
304
|
-
t.parent_task_attempt = $1 AND t.project_id = $2
|
305
|
-
) OR (
|
306
|
-
-- Find parent: task that owns the parent attempt of current task
|
307
|
-
EXISTS (
|
308
|
-
SELECT 1 FROM tasks current_task
|
309
|
-
JOIN task_attempts parent_attempt ON current_task.parent_task_attempt = parent_attempt.id
|
310
|
-
WHERE parent_attempt.task_id = t.id
|
311
|
-
AND parent_attempt.id = $1
|
312
|
-
AND current_task.project_id = $2
|
313
|
-
)
|
314
|
-
)
|
315
|
-
-- Exclude the current task itself to prevent circular references
|
316
|
-
AND t.id != (SELECT task_id FROM task_attempts WHERE id = $1)
|
317
|
-
ORDER BY t.created_at DESC"#,
|
318
|
-
attempt_id,
|
319
|
-
project_id
|
320
|
-
)
|
321
|
-
.fetch_all(pool)
|
322
|
-
.await
|
323
|
-
}
|
324
|
-
|
325
|
-
pub async fn link_existing_tasks(
|
326
|
-
pool: &SqlitePool,
|
327
|
-
child_task_id: Uuid,
|
328
|
-
parent_task_attempt: Uuid,
|
329
|
-
project_id: Uuid,
|
330
|
-
) -> Result<Self, sqlx::Error> {
|
331
|
-
sqlx::query_as!(
|
332
|
-
Task,
|
333
|
-
r#"UPDATE tasks
|
334
|
-
SET parent_task_attempt = $1, updated_at = CURRENT_TIMESTAMP
|
335
|
-
WHERE id = $2 AND project_id = $3
|
336
|
-
RETURNING id as "id!: Uuid", project_id as "project_id!: Uuid", title, description, status as "status!: TaskStatus", wish_id, parent_task_attempt as "parent_task_attempt: Uuid", created_at as "created_at!: DateTime<Utc>", updated_at as "updated_at!: DateTime<Utc>""#,
|
337
|
-
parent_task_attempt,
|
338
|
-
child_task_id,
|
339
|
-
project_id
|
340
|
-
)
|
341
|
-
.fetch_one(pool)
|
342
|
-
.await
|
343
|
-
}
|
344
|
-
|
345
|
-
}
|