automagik-forge 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cargo/config.toml +13 -0
- package/.claude/commands/commit.md +376 -0
- package/.claude/commands/prompt.md +871 -0
- package/.env.example +20 -0
- package/.github/actions/setup-node/action.yml +29 -0
- package/.github/images/automagik-logo.png +0 -0
- package/.github/workflows/pre-release.yml +470 -0
- package/.github/workflows/publish.yml +145 -0
- package/.github/workflows/test.yml +63 -0
- package/.mcp.json +57 -0
- package/AGENT.md +40 -0
- package/CLAUDE.md +40 -0
- package/CODE-OF-CONDUCT.md +89 -0
- package/Cargo.toml +19 -0
- package/Dockerfile +43 -0
- package/LICENSE +201 -0
- package/Makefile +97 -0
- package/README.md +447 -143
- package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +12 -0
- package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +12 -0
- package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +12 -0
- package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +68 -0
- package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +12 -0
- package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +12 -0
- package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +12 -0
- package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +104 -0
- package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +12 -0
- package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +32 -0
- package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +56 -0
- package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +68 -0
- package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +92 -0
- package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +12 -0
- package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +56 -0
- package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +62 -0
- package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +62 -0
- package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +56 -0
- package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +32 -0
- package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +104 -0
- package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +62 -0
- package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +56 -0
- package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +92 -0
- package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +62 -0
- package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +12 -0
- package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +104 -0
- package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +104 -0
- package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +68 -0
- package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +62 -0
- package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +62 -0
- package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +62 -0
- package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +50 -0
- package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +68 -0
- package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +12 -0
- package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +68 -0
- package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +12 -0
- package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +12 -0
- package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +62 -0
- package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +104 -0
- package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +20 -0
- package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +104 -0
- package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +56 -0
- package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +104 -0
- package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +26 -0
- package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +62 -0
- package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +12 -0
- package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +104 -0
- package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +104 -0
- package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +12 -0
- package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +68 -0
- package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +62 -0
- package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +20 -0
- package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +12 -0
- package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +12 -0
- package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +12 -0
- package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +20 -0
- package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +12 -0
- package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +12 -0
- package/backend/Cargo.toml +71 -0
- package/backend/build.rs +32 -0
- package/backend/migrations/20250617183714_init.sql +44 -0
- package/backend/migrations/20250620212427_execution_processes.sql +25 -0
- package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +28 -0
- package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +23 -0
- package/backend/migrations/20250623120000_executor_sessions.sql +17 -0
- package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +4 -0
- package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +4 -0
- package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +2 -0
- package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +5 -0
- package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +2 -0
- package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +2 -0
- package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +2 -0
- package/backend/migrations/20250710000000_add_setup_completion.sql +3 -0
- package/backend/migrations/20250715154859_add_task_templates.sql +25 -0
- package/backend/migrations/20250716143725_add_default_templates.sql +174 -0
- package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +20 -0
- package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +7 -0
- package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +9 -0
- package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +2 -0
- package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +25 -0
- package/backend/migrations/20250723000000_add_wish_to_tasks.sql +7 -0
- package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +5 -0
- package/backend/scripts/toast-notification.ps1 +23 -0
- 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 +218 -0
- package/backend/src/bin/generate_types.rs +189 -0
- package/backend/src/bin/mcp_task_server.rs +191 -0
- package/backend/src/execution_monitor.rs +1193 -0
- package/backend/src/executor.rs +1053 -0
- package/backend/src/executors/amp.rs +697 -0
- package/backend/src/executors/ccr.rs +91 -0
- package/backend/src/executors/charm_opencode.rs +113 -0
- package/backend/src/executors/claude.rs +887 -0
- package/backend/src/executors/cleanup_script.rs +124 -0
- package/backend/src/executors/dev_server.rs +53 -0
- package/backend/src/executors/echo.rs +79 -0
- package/backend/src/executors/gemini/config.rs +67 -0
- package/backend/src/executors/gemini/streaming.rs +363 -0
- package/backend/src/executors/gemini.rs +765 -0
- package/backend/src/executors/mod.rs +23 -0
- package/backend/src/executors/opencode_ai.rs +113 -0
- package/backend/src/executors/setup_script.rs +130 -0
- package/backend/src/executors/sst_opencode/filter.rs +184 -0
- package/backend/src/executors/sst_opencode/tools.rs +139 -0
- package/backend/src/executors/sst_opencode.rs +756 -0
- package/backend/src/lib.rs +45 -0
- package/backend/src/main.rs +324 -0
- package/backend/src/mcp/mod.rs +1 -0
- package/backend/src/mcp/task_server.rs +850 -0
- package/backend/src/middleware/mod.rs +3 -0
- package/backend/src/middleware/model_loaders.rs +242 -0
- package/backend/src/models/api_response.rs +36 -0
- package/backend/src/models/config.rs +375 -0
- package/backend/src/models/execution_process.rs +430 -0
- package/backend/src/models/executor_session.rs +225 -0
- package/backend/src/models/mod.rs +12 -0
- package/backend/src/models/project.rs +356 -0
- package/backend/src/models/task.rs +345 -0
- package/backend/src/models/task_attempt.rs +1214 -0
- package/backend/src/models/task_template.rs +146 -0
- package/backend/src/openapi.rs +93 -0
- package/backend/src/routes/auth.rs +297 -0
- package/backend/src/routes/config.rs +385 -0
- package/backend/src/routes/filesystem.rs +228 -0
- package/backend/src/routes/health.rs +16 -0
- package/backend/src/routes/mod.rs +9 -0
- package/backend/src/routes/projects.rs +562 -0
- package/backend/src/routes/stream.rs +244 -0
- package/backend/src/routes/task_attempts.rs +1172 -0
- package/backend/src/routes/task_templates.rs +229 -0
- package/backend/src/routes/tasks.rs +353 -0
- package/backend/src/services/analytics.rs +216 -0
- package/backend/src/services/git_service.rs +1321 -0
- package/backend/src/services/github_service.rs +307 -0
- package/backend/src/services/mod.rs +13 -0
- package/backend/src/services/notification_service.rs +263 -0
- package/backend/src/services/pr_monitor.rs +214 -0
- package/backend/src/services/process_service.rs +940 -0
- package/backend/src/utils/path.rs +96 -0
- package/backend/src/utils/shell.rs +19 -0
- package/backend/src/utils/text.rs +24 -0
- package/backend/src/utils/worktree_manager.rs +578 -0
- package/backend/src/utils.rs +125 -0
- package/backend/test.db +0 -0
- package/build-npm-package.sh +61 -0
- package/dev_assets_seed/config.json +19 -0
- package/frontend/.eslintrc.json +25 -0
- package/frontend/.prettierrc.json +8 -0
- package/frontend/components.json +17 -0
- package/frontend/index.html +19 -0
- package/frontend/package-lock.json +7321 -0
- package/frontend/package.json +61 -0
- package/frontend/postcss.config.js +6 -0
- 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 +3 -0
- package/frontend/public/automagik-forge-logo.svg +3 -0
- 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 +1 -0
- package/frontend/public/viba-kanban-favicon.png +0 -0
- package/frontend/src/App.tsx +157 -0
- package/frontend/src/components/DisclaimerDialog.tsx +106 -0
- package/frontend/src/components/GitHubLoginDialog.tsx +314 -0
- package/frontend/src/components/OnboardingDialog.tsx +185 -0
- package/frontend/src/components/PrivacyOptInDialog.tsx +130 -0
- package/frontend/src/components/ProvidePatDialog.tsx +98 -0
- package/frontend/src/components/TaskTemplateManager.tsx +336 -0
- package/frontend/src/components/config-provider.tsx +119 -0
- package/frontend/src/components/context/TaskDetailsContextProvider.tsx +470 -0
- package/frontend/src/components/context/taskDetailsContext.ts +125 -0
- package/frontend/src/components/keyboard-shortcuts-demo.tsx +35 -0
- package/frontend/src/components/layout/navbar.tsx +86 -0
- package/frontend/src/components/logo.tsx +44 -0
- package/frontend/src/components/projects/ProjectCard.tsx +155 -0
- package/frontend/src/components/projects/project-detail.tsx +251 -0
- package/frontend/src/components/projects/project-form-fields.tsx +238 -0
- package/frontend/src/components/projects/project-form.tsx +301 -0
- package/frontend/src/components/projects/project-list.tsx +200 -0
- package/frontend/src/components/projects/projects-page.tsx +20 -0
- package/frontend/src/components/tasks/BranchSelector.tsx +169 -0
- package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +94 -0
- package/frontend/src/components/tasks/EditorSelectionDialog.tsx +119 -0
- package/frontend/src/components/tasks/TaskCard.tsx +154 -0
- package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +33 -0
- package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +109 -0
- package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +135 -0
- package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +296 -0
- package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +32 -0
- package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +392 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +256 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +56 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +92 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +22 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +49 -0
- package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +186 -0
- package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +288 -0
- package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +216 -0
- package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +93 -0
- package/frontend/src/components/tasks/TaskDetailsHeader.tsx +169 -0
- package/frontend/src/components/tasks/TaskDetailsPanel.tsx +126 -0
- package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +302 -0
- package/frontend/src/components/tasks/TaskFollowUpSection.tsx +130 -0
- package/frontend/src/components/tasks/TaskFormDialog.tsx +400 -0
- package/frontend/src/components/tasks/TaskKanbanBoard.tsx +180 -0
- package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +259 -0
- package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +243 -0
- package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +899 -0
- package/frontend/src/components/tasks/index.ts +2 -0
- package/frontend/src/components/theme-provider.tsx +82 -0
- package/frontend/src/components/theme-toggle.tsx +36 -0
- package/frontend/src/components/ui/alert.tsx +59 -0
- package/frontend/src/components/ui/auto-expanding-textarea.tsx +70 -0
- package/frontend/src/components/ui/badge.tsx +36 -0
- package/frontend/src/components/ui/button.tsx +56 -0
- package/frontend/src/components/ui/card.tsx +86 -0
- package/frontend/src/components/ui/checkbox.tsx +44 -0
- package/frontend/src/components/ui/chip.tsx +25 -0
- package/frontend/src/components/ui/dialog.tsx +124 -0
- package/frontend/src/components/ui/dropdown-menu.tsx +198 -0
- package/frontend/src/components/ui/file-search-textarea.tsx +292 -0
- package/frontend/src/components/ui/folder-picker.tsx +279 -0
- package/frontend/src/components/ui/input.tsx +25 -0
- package/frontend/src/components/ui/label.tsx +24 -0
- package/frontend/src/components/ui/loader.tsx +26 -0
- package/frontend/src/components/ui/markdown-renderer.tsx +75 -0
- package/frontend/src/components/ui/select.tsx +160 -0
- package/frontend/src/components/ui/separator.tsx +31 -0
- package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +185 -0
- package/frontend/src/components/ui/table.tsx +117 -0
- package/frontend/src/components/ui/tabs.tsx +53 -0
- package/frontend/src/components/ui/textarea.tsx +22 -0
- package/frontend/src/components/ui/tooltip.tsx +28 -0
- package/frontend/src/hooks/useNormalizedConversation.ts +440 -0
- package/frontend/src/index.css +225 -0
- package/frontend/src/lib/api.ts +630 -0
- package/frontend/src/lib/keyboard-shortcuts.ts +266 -0
- package/frontend/src/lib/responsive-config.ts +70 -0
- package/frontend/src/lib/types.ts +39 -0
- package/frontend/src/lib/utils.ts +10 -0
- package/frontend/src/main.tsx +50 -0
- package/frontend/src/pages/McpServers.tsx +418 -0
- package/frontend/src/pages/Settings.tsx +610 -0
- package/frontend/src/pages/project-tasks.tsx +575 -0
- package/frontend/src/pages/projects.tsx +18 -0
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/tailwind.config.js +125 -0
- package/frontend/tsconfig.json +26 -0
- package/frontend/tsconfig.node.json +10 -0
- package/frontend/vite.config.ts +33 -0
- package/npx-cli/README.md +159 -0
- package/npx-cli/automagik-forge-0.0.55.tgz +0 -0
- package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
- package/{dist/linux-x64/automagik-forge.zip → npx-cli/automagik-forge-0.1.10.tgz} +0 -0
- package/npx-cli/package.json +17 -0
- package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
- package/package.json +23 -13
- package/pnpm-workspace.yaml +2 -0
- package/rust-toolchain.toml +11 -0
- package/rustfmt.toml +3 -0
- package/scripts/load-env.js +43 -0
- package/scripts/mcp_test.js +374 -0
- package/scripts/prepare-db.js +45 -0
- package/scripts/setup-dev-environment.js +274 -0
- package/scripts/start-mcp-sse.js +70 -0
- package/scripts/test-debug.js +32 -0
- package/scripts/test-mcp-sse.js +138 -0
- package/scripts/test-simple.js +44 -0
- package/scripts/test-wish-final.js +179 -0
- package/scripts/test-wish-system.js +221 -0
- package/shared/types.ts +182 -0
- package/test-npm-package.sh +42 -0
- package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
- /package/{bin → npx-cli/bin}/cli.js +0 -0
@@ -0,0 +1,899 @@
|
|
1
|
+
import {
|
2
|
+
Check,
|
3
|
+
ExternalLink,
|
4
|
+
GitBranch as GitBranchIcon,
|
5
|
+
GitPullRequest,
|
6
|
+
History,
|
7
|
+
Play,
|
8
|
+
Plus,
|
9
|
+
RefreshCw,
|
10
|
+
Settings,
|
11
|
+
StopCircle,
|
12
|
+
} from 'lucide-react';
|
13
|
+
import { is_planning_executor_type } from '@/lib/utils';
|
14
|
+
import {
|
15
|
+
Tooltip,
|
16
|
+
TooltipContent,
|
17
|
+
TooltipProvider,
|
18
|
+
TooltipTrigger,
|
19
|
+
} from '@/components/ui/tooltip.tsx';
|
20
|
+
import { Button } from '@/components/ui/button.tsx';
|
21
|
+
import {
|
22
|
+
DropdownMenu,
|
23
|
+
DropdownMenuContent,
|
24
|
+
DropdownMenuItem,
|
25
|
+
DropdownMenuTrigger,
|
26
|
+
} from '@/components/ui/dropdown-menu.tsx';
|
27
|
+
import {
|
28
|
+
Dialog,
|
29
|
+
DialogContent,
|
30
|
+
DialogDescription,
|
31
|
+
DialogFooter,
|
32
|
+
DialogHeader,
|
33
|
+
DialogTitle,
|
34
|
+
} from '@/components/ui/dialog.tsx';
|
35
|
+
import BranchSelector from '@/components/tasks/BranchSelector.tsx';
|
36
|
+
import {
|
37
|
+
attemptsApi,
|
38
|
+
executionProcessesApi,
|
39
|
+
makeRequest,
|
40
|
+
FollowUpResponse,
|
41
|
+
ApiResponse,
|
42
|
+
} from '@/lib/api.ts';
|
43
|
+
import {
|
44
|
+
Dispatch,
|
45
|
+
SetStateAction,
|
46
|
+
useCallback,
|
47
|
+
useContext,
|
48
|
+
useEffect,
|
49
|
+
useMemo,
|
50
|
+
useState,
|
51
|
+
} from 'react';
|
52
|
+
import type {
|
53
|
+
BranchStatus,
|
54
|
+
ExecutionProcess,
|
55
|
+
GitBranch,
|
56
|
+
TaskAttempt,
|
57
|
+
} from 'shared/types.ts';
|
58
|
+
import {
|
59
|
+
TaskAttemptDataContext,
|
60
|
+
TaskAttemptStoppingContext,
|
61
|
+
TaskDetailsContext,
|
62
|
+
TaskExecutionStateContext,
|
63
|
+
TaskRelatedTasksContext,
|
64
|
+
TaskSelectedAttemptContext,
|
65
|
+
} from '@/components/context/taskDetailsContext.ts';
|
66
|
+
import { useConfig } from '@/components/config-provider.tsx';
|
67
|
+
import { useKeyboardShortcuts } from '@/lib/keyboard-shortcuts.ts';
|
68
|
+
import { useNavigate } from 'react-router-dom';
|
69
|
+
|
70
|
+
// Helper function to get the display name for different editor types
|
71
|
+
function getEditorDisplayName(editorType: string): string {
|
72
|
+
switch (editorType) {
|
73
|
+
case 'vscode':
|
74
|
+
return 'Visual Studio Code';
|
75
|
+
case 'cursor':
|
76
|
+
return 'Cursor';
|
77
|
+
case 'windsurf':
|
78
|
+
return 'Windsurf';
|
79
|
+
case 'intellij':
|
80
|
+
return 'IntelliJ IDEA';
|
81
|
+
case 'zed':
|
82
|
+
return 'Zed';
|
83
|
+
case 'custom':
|
84
|
+
return 'Custom Editor';
|
85
|
+
default:
|
86
|
+
return 'Editor';
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
type Props = {
|
91
|
+
setError: Dispatch<SetStateAction<string | null>>;
|
92
|
+
setShowCreatePRDialog: Dispatch<SetStateAction<boolean>>;
|
93
|
+
selectedBranch: string | null;
|
94
|
+
selectedAttempt: TaskAttempt;
|
95
|
+
taskAttempts: TaskAttempt[];
|
96
|
+
creatingPR: boolean;
|
97
|
+
handleEnterCreateAttemptMode: () => void;
|
98
|
+
availableExecutors: {
|
99
|
+
id: string;
|
100
|
+
name: string;
|
101
|
+
}[];
|
102
|
+
branches: GitBranch[];
|
103
|
+
};
|
104
|
+
|
105
|
+
function CurrentAttempt({
|
106
|
+
setError,
|
107
|
+
setShowCreatePRDialog,
|
108
|
+
selectedBranch,
|
109
|
+
selectedAttempt,
|
110
|
+
taskAttempts,
|
111
|
+
creatingPR,
|
112
|
+
handleEnterCreateAttemptMode,
|
113
|
+
availableExecutors,
|
114
|
+
branches,
|
115
|
+
}: Props) {
|
116
|
+
const { task, projectId, handleOpenInEditor, projectHasDevScript } =
|
117
|
+
useContext(TaskDetailsContext);
|
118
|
+
const { config } = useConfig();
|
119
|
+
const { setSelectedAttempt } = useContext(TaskSelectedAttemptContext);
|
120
|
+
const navigate = useNavigate();
|
121
|
+
const { isStopping, setIsStopping } = useContext(TaskAttemptStoppingContext);
|
122
|
+
const { attemptData, fetchAttemptData, isAttemptRunning } = useContext(
|
123
|
+
TaskAttemptDataContext
|
124
|
+
);
|
125
|
+
const { relatedTasks } = useContext(TaskRelatedTasksContext);
|
126
|
+
const { executionState, fetchExecutionState } = useContext(
|
127
|
+
TaskExecutionStateContext
|
128
|
+
);
|
129
|
+
|
130
|
+
const [isStartingDevServer, setIsStartingDevServer] = useState(false);
|
131
|
+
const [merging, setMerging] = useState(false);
|
132
|
+
const [rebasing, setRebasing] = useState(false);
|
133
|
+
const [devServerDetails, setDevServerDetails] =
|
134
|
+
useState<ExecutionProcess | null>(null);
|
135
|
+
const [isHoveringDevServer, setIsHoveringDevServer] = useState(false);
|
136
|
+
const [branchStatus, setBranchStatus] = useState<BranchStatus | null>(null);
|
137
|
+
const [branchStatusLoading, setBranchStatusLoading] = useState(false);
|
138
|
+
const [showRebaseDialog, setShowRebaseDialog] = useState(false);
|
139
|
+
const [selectedRebaseBranch, setSelectedRebaseBranch] = useState<string>('');
|
140
|
+
const [showStopConfirmation, setShowStopConfirmation] = useState(false);
|
141
|
+
const [isApprovingPlan, setIsApprovingPlan] = useState(false);
|
142
|
+
const [copied, setCopied] = useState(false);
|
143
|
+
|
144
|
+
const processedDevServerLogs = useMemo(() => {
|
145
|
+
if (!devServerDetails) return 'No output yet...';
|
146
|
+
|
147
|
+
const stdout = devServerDetails.stdout || '';
|
148
|
+
const stderr = devServerDetails.stderr || '';
|
149
|
+
const allOutput = stdout + (stderr ? '\n' + stderr : '');
|
150
|
+
const lines = allOutput.split('\n').filter((line) => line.trim());
|
151
|
+
const lastLines = lines.slice(-10);
|
152
|
+
return lastLines.length > 0 ? lastLines.join('\n') : 'No output yet...';
|
153
|
+
}, [devServerDetails]);
|
154
|
+
|
155
|
+
// Find running dev server in current project
|
156
|
+
const runningDevServer = useMemo(() => {
|
157
|
+
return attemptData.processes.find(
|
158
|
+
(process) =>
|
159
|
+
process.process_type === 'devserver' && process.status === 'running'
|
160
|
+
);
|
161
|
+
}, [attemptData.processes]);
|
162
|
+
|
163
|
+
// Check if plan approval is needed
|
164
|
+
const isPlanTask = useMemo(() => {
|
165
|
+
return !!(
|
166
|
+
selectedAttempt.executor &&
|
167
|
+
is_planning_executor_type(selectedAttempt.executor)
|
168
|
+
);
|
169
|
+
}, [selectedAttempt.executor]);
|
170
|
+
|
171
|
+
const fetchDevServerDetails = useCallback(async () => {
|
172
|
+
if (!runningDevServer || !task || !selectedAttempt) return;
|
173
|
+
|
174
|
+
try {
|
175
|
+
const result = await executionProcessesApi.getDetails(
|
176
|
+
runningDevServer.id
|
177
|
+
);
|
178
|
+
setDevServerDetails(result);
|
179
|
+
} catch (err) {
|
180
|
+
console.error('Failed to fetch dev server details:', err);
|
181
|
+
}
|
182
|
+
}, [runningDevServer, task, selectedAttempt, projectId]);
|
183
|
+
|
184
|
+
useEffect(() => {
|
185
|
+
if (!isHoveringDevServer || !runningDevServer) {
|
186
|
+
setDevServerDetails(null);
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
|
190
|
+
fetchDevServerDetails();
|
191
|
+
const interval = setInterval(fetchDevServerDetails, 2000);
|
192
|
+
return () => clearInterval(interval);
|
193
|
+
}, [isHoveringDevServer, runningDevServer, fetchDevServerDetails]);
|
194
|
+
|
195
|
+
const startDevServer = async () => {
|
196
|
+
if (!task || !selectedAttempt) return;
|
197
|
+
|
198
|
+
setIsStartingDevServer(true);
|
199
|
+
|
200
|
+
try {
|
201
|
+
await attemptsApi.startDevServer(
|
202
|
+
projectId,
|
203
|
+
selectedAttempt.task_id,
|
204
|
+
selectedAttempt.id
|
205
|
+
);
|
206
|
+
fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
|
207
|
+
} catch (err) {
|
208
|
+
console.error('Failed to start dev server:', err);
|
209
|
+
} finally {
|
210
|
+
setIsStartingDevServer(false);
|
211
|
+
}
|
212
|
+
};
|
213
|
+
|
214
|
+
const stopDevServer = async () => {
|
215
|
+
if (!task || !selectedAttempt || !runningDevServer) return;
|
216
|
+
|
217
|
+
setIsStartingDevServer(true);
|
218
|
+
|
219
|
+
try {
|
220
|
+
await attemptsApi.stopExecutionProcess(
|
221
|
+
projectId,
|
222
|
+
selectedAttempt.task_id,
|
223
|
+
selectedAttempt.id,
|
224
|
+
runningDevServer.id
|
225
|
+
);
|
226
|
+
fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
|
227
|
+
} catch (err) {
|
228
|
+
console.error('Failed to stop dev server:', err);
|
229
|
+
} finally {
|
230
|
+
setIsStartingDevServer(false);
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
const stopAllExecutions = useCallback(async () => {
|
235
|
+
if (!task || !selectedAttempt || !isAttemptRunning) return;
|
236
|
+
|
237
|
+
try {
|
238
|
+
setIsStopping(true);
|
239
|
+
await attemptsApi.stop(
|
240
|
+
projectId,
|
241
|
+
selectedAttempt.task_id,
|
242
|
+
selectedAttempt.id
|
243
|
+
);
|
244
|
+
await fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
|
245
|
+
setTimeout(() => {
|
246
|
+
fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
|
247
|
+
}, 1000);
|
248
|
+
} catch (err) {
|
249
|
+
console.error('Failed to stop executions:', err);
|
250
|
+
} finally {
|
251
|
+
setIsStopping(false);
|
252
|
+
}
|
253
|
+
}, [
|
254
|
+
task,
|
255
|
+
selectedAttempt,
|
256
|
+
projectId,
|
257
|
+
fetchAttemptData,
|
258
|
+
setIsStopping,
|
259
|
+
isAttemptRunning,
|
260
|
+
]);
|
261
|
+
|
262
|
+
useKeyboardShortcuts({
|
263
|
+
stopExecution: () => setShowStopConfirmation(true),
|
264
|
+
newAttempt: !isAttemptRunning ? handleEnterCreateAttemptMode : () => {},
|
265
|
+
hasOpenDialog: showStopConfirmation,
|
266
|
+
closeDialog: () => setShowStopConfirmation(false),
|
267
|
+
onEnter: () => {
|
268
|
+
setShowStopConfirmation(false);
|
269
|
+
stopAllExecutions();
|
270
|
+
},
|
271
|
+
});
|
272
|
+
|
273
|
+
const handleAttemptChange = useCallback(
|
274
|
+
(attempt: TaskAttempt) => {
|
275
|
+
setSelectedAttempt(attempt);
|
276
|
+
fetchAttemptData(attempt.id, attempt.task_id);
|
277
|
+
fetchExecutionState(attempt.id, attempt.task_id);
|
278
|
+
},
|
279
|
+
[fetchAttemptData, fetchExecutionState, setSelectedAttempt]
|
280
|
+
);
|
281
|
+
|
282
|
+
const handleMergeClick = async () => {
|
283
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
284
|
+
|
285
|
+
// Directly perform merge without checking branch status
|
286
|
+
await performMerge();
|
287
|
+
};
|
288
|
+
|
289
|
+
const fetchBranchStatus = useCallback(async () => {
|
290
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
291
|
+
|
292
|
+
try {
|
293
|
+
setBranchStatusLoading(true);
|
294
|
+
const result = await attemptsApi.getBranchStatus(
|
295
|
+
projectId,
|
296
|
+
selectedAttempt.task_id,
|
297
|
+
selectedAttempt.id
|
298
|
+
);
|
299
|
+
setBranchStatus((prev) => {
|
300
|
+
if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
|
301
|
+
return result;
|
302
|
+
});
|
303
|
+
} catch (err) {
|
304
|
+
setError('Failed to load branch status');
|
305
|
+
} finally {
|
306
|
+
setBranchStatusLoading(false);
|
307
|
+
}
|
308
|
+
}, [projectId, selectedAttempt?.id, selectedAttempt?.task_id, setError]);
|
309
|
+
|
310
|
+
// Fetch branch status when selected attempt changes
|
311
|
+
useEffect(() => {
|
312
|
+
if (selectedAttempt) {
|
313
|
+
fetchBranchStatus();
|
314
|
+
}
|
315
|
+
}, [selectedAttempt, fetchBranchStatus]);
|
316
|
+
|
317
|
+
const performMerge = async () => {
|
318
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
319
|
+
|
320
|
+
try {
|
321
|
+
setMerging(true);
|
322
|
+
await attemptsApi.merge(
|
323
|
+
projectId,
|
324
|
+
selectedAttempt.task_id,
|
325
|
+
selectedAttempt.id
|
326
|
+
);
|
327
|
+
// Refetch branch status to show updated state
|
328
|
+
fetchBranchStatus();
|
329
|
+
} catch (error) {
|
330
|
+
console.error('Failed to merge changes:', error);
|
331
|
+
// @ts-expect-error it is type ApiError
|
332
|
+
setError(error.message || 'Failed to merge changes');
|
333
|
+
} finally {
|
334
|
+
setMerging(false);
|
335
|
+
}
|
336
|
+
};
|
337
|
+
|
338
|
+
const handleRebaseClick = async () => {
|
339
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
340
|
+
|
341
|
+
try {
|
342
|
+
setRebasing(true);
|
343
|
+
await attemptsApi.rebase(
|
344
|
+
projectId,
|
345
|
+
selectedAttempt.task_id,
|
346
|
+
selectedAttempt.id
|
347
|
+
);
|
348
|
+
// Refresh branch status after rebase
|
349
|
+
fetchBranchStatus();
|
350
|
+
} catch (err) {
|
351
|
+
setError(err instanceof Error ? err.message : 'Failed to rebase branch');
|
352
|
+
} finally {
|
353
|
+
setRebasing(false);
|
354
|
+
}
|
355
|
+
};
|
356
|
+
|
357
|
+
const handleRebaseWithNewBranch = async (newBaseBranch: string) => {
|
358
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
359
|
+
|
360
|
+
try {
|
361
|
+
setRebasing(true);
|
362
|
+
await attemptsApi.rebase(
|
363
|
+
projectId,
|
364
|
+
selectedAttempt.task_id,
|
365
|
+
selectedAttempt.id,
|
366
|
+
newBaseBranch
|
367
|
+
);
|
368
|
+
// Refresh branch status after rebase
|
369
|
+
fetchBranchStatus();
|
370
|
+
setShowRebaseDialog(false);
|
371
|
+
} catch (err) {
|
372
|
+
setError(err instanceof Error ? err.message : 'Failed to rebase branch');
|
373
|
+
} finally {
|
374
|
+
setRebasing(false);
|
375
|
+
}
|
376
|
+
};
|
377
|
+
|
378
|
+
const handleRebaseDialogConfirm = () => {
|
379
|
+
if (selectedRebaseBranch) {
|
380
|
+
handleRebaseWithNewBranch(selectedRebaseBranch);
|
381
|
+
}
|
382
|
+
};
|
383
|
+
|
384
|
+
const handleRebaseDialogOpen = () => {
|
385
|
+
setSelectedRebaseBranch('');
|
386
|
+
setShowRebaseDialog(true);
|
387
|
+
};
|
388
|
+
|
389
|
+
const handleCreatePRClick = async () => {
|
390
|
+
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) return;
|
391
|
+
|
392
|
+
// If PR already exists, open it
|
393
|
+
if (selectedAttempt.pr_url) {
|
394
|
+
window.open(selectedAttempt.pr_url, '_blank');
|
395
|
+
return;
|
396
|
+
}
|
397
|
+
|
398
|
+
setShowCreatePRDialog(true);
|
399
|
+
};
|
400
|
+
|
401
|
+
const handlePlanApproval = async () => {
|
402
|
+
if (!task || !selectedAttempt || !isPlanTask) return;
|
403
|
+
|
404
|
+
setIsApprovingPlan(true);
|
405
|
+
try {
|
406
|
+
const response = await makeRequest(
|
407
|
+
`/api/projects/${projectId}/tasks/${task.id}/attempts/${selectedAttempt.id}/approve-plan`,
|
408
|
+
{
|
409
|
+
method: 'POST',
|
410
|
+
// No body needed - endpoint only handles approval now
|
411
|
+
}
|
412
|
+
);
|
413
|
+
|
414
|
+
if (response.ok) {
|
415
|
+
const result: ApiResponse<FollowUpResponse> = await response.json();
|
416
|
+
if (result.success && result.data) {
|
417
|
+
console.log('Plan approved successfully:', result.message);
|
418
|
+
|
419
|
+
// If a new task was created, navigate to it
|
420
|
+
if (result.data.created_new_attempt) {
|
421
|
+
const newTaskId = result.data.actual_attempt_id;
|
422
|
+
console.log('Navigating to new task:', newTaskId);
|
423
|
+
navigate(`/projects/${projectId}/tasks/${newTaskId}`);
|
424
|
+
} else {
|
425
|
+
// Otherwise, just refresh the current task data
|
426
|
+
fetchAttemptData(selectedAttempt.id, selectedAttempt.task_id);
|
427
|
+
}
|
428
|
+
} else {
|
429
|
+
setError(`Failed to approve plan: ${result.message}`);
|
430
|
+
}
|
431
|
+
} else {
|
432
|
+
setError('Failed to approve plan');
|
433
|
+
}
|
434
|
+
} catch (error) {
|
435
|
+
setError(
|
436
|
+
`Error approving plan: ${error instanceof Error ? error.message : 'Unknown error'}`
|
437
|
+
);
|
438
|
+
} finally {
|
439
|
+
setIsApprovingPlan(false);
|
440
|
+
}
|
441
|
+
};
|
442
|
+
|
443
|
+
// Get display name for selected branch
|
444
|
+
const selectedBranchDisplayName = useMemo(() => {
|
445
|
+
if (!selectedBranch) return 'current';
|
446
|
+
|
447
|
+
// For remote branches, show just the branch name without the remote prefix
|
448
|
+
if (selectedBranch.includes('/')) {
|
449
|
+
const parts = selectedBranch.split('/');
|
450
|
+
return parts[parts.length - 1];
|
451
|
+
}
|
452
|
+
return selectedBranch;
|
453
|
+
}, [selectedBranch]);
|
454
|
+
|
455
|
+
// Get display name for the configured editor
|
456
|
+
const editorDisplayName = useMemo(() => {
|
457
|
+
if (!config?.editor?.editor_type) return 'Editor';
|
458
|
+
return getEditorDisplayName(config.editor.editor_type);
|
459
|
+
}, [config?.editor?.editor_type]);
|
460
|
+
|
461
|
+
const handleCopyWorktreePath = useCallback(async () => {
|
462
|
+
try {
|
463
|
+
await navigator.clipboard.writeText(selectedAttempt.worktree_path);
|
464
|
+
setCopied(true);
|
465
|
+
setTimeout(() => setCopied(false), 2000);
|
466
|
+
} catch (err) {
|
467
|
+
console.error('Failed to copy worktree path:', err);
|
468
|
+
}
|
469
|
+
}, [selectedAttempt.worktree_path]);
|
470
|
+
|
471
|
+
return (
|
472
|
+
<div className="space-y-2">
|
473
|
+
<div className="grid grid-cols-4 gap-3 items-start">
|
474
|
+
<div>
|
475
|
+
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
|
476
|
+
Started
|
477
|
+
</div>
|
478
|
+
<div className="text-sm font-medium">
|
479
|
+
{new Date(selectedAttempt.created_at).toLocaleDateString()}{' '}
|
480
|
+
{new Date(selectedAttempt.created_at).toLocaleTimeString([], {
|
481
|
+
hour: '2-digit',
|
482
|
+
minute: '2-digit',
|
483
|
+
})}
|
484
|
+
</div>
|
485
|
+
</div>
|
486
|
+
|
487
|
+
<div>
|
488
|
+
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
|
489
|
+
Agent
|
490
|
+
</div>
|
491
|
+
<div className="text-sm font-medium">
|
492
|
+
{availableExecutors.find((e) => e.id === selectedAttempt.executor)
|
493
|
+
?.name ||
|
494
|
+
selectedAttempt.executor ||
|
495
|
+
'Unknown'}
|
496
|
+
</div>
|
497
|
+
</div>
|
498
|
+
|
499
|
+
<div>
|
500
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
|
501
|
+
<span>Base Branch</span>
|
502
|
+
<TooltipProvider>
|
503
|
+
<Tooltip>
|
504
|
+
<TooltipTrigger asChild>
|
505
|
+
<Button
|
506
|
+
variant="ghost"
|
507
|
+
size="sm"
|
508
|
+
onClick={handleRebaseDialogOpen}
|
509
|
+
disabled={
|
510
|
+
rebasing ||
|
511
|
+
branchStatusLoading ||
|
512
|
+
isAttemptRunning ||
|
513
|
+
isPlanTask
|
514
|
+
}
|
515
|
+
className="h-4 w-4 p-0 hover:bg-muted"
|
516
|
+
>
|
517
|
+
<Settings className="h-3 w-3" />
|
518
|
+
</Button>
|
519
|
+
</TooltipTrigger>
|
520
|
+
<TooltipContent>
|
521
|
+
<p>Change base branch</p>
|
522
|
+
</TooltipContent>
|
523
|
+
</Tooltip>
|
524
|
+
</TooltipProvider>
|
525
|
+
</div>
|
526
|
+
<div className="flex items-center gap-1.5">
|
527
|
+
<GitBranchIcon className="h-3 w-3 text-muted-foreground" />
|
528
|
+
<span className="text-sm font-medium">
|
529
|
+
{branchStatus?.base_branch_name || selectedBranchDisplayName}
|
530
|
+
</span>
|
531
|
+
</div>
|
532
|
+
</div>
|
533
|
+
|
534
|
+
<div>
|
535
|
+
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
|
536
|
+
{isPlanTask ? 'Plan Status' : 'Merge Status'}
|
537
|
+
</div>
|
538
|
+
<div className="flex items-center gap-1.5">
|
539
|
+
{isPlanTask ? (
|
540
|
+
// Plan status for planning tasks
|
541
|
+
relatedTasks && relatedTasks.length > 0 ? (
|
542
|
+
<div className="flex items-center gap-1.5">
|
543
|
+
<div className="h-2 w-2 bg-green-500 rounded-full" />
|
544
|
+
<span className="text-sm font-medium text-green-700">
|
545
|
+
Task Created
|
546
|
+
</span>
|
547
|
+
</div>
|
548
|
+
) : (
|
549
|
+
<div className="flex items-center gap-1.5">
|
550
|
+
<div className="h-2 w-2 bg-gray-500 rounded-full" />
|
551
|
+
<span className="text-sm font-medium text-gray-700">
|
552
|
+
Draft
|
553
|
+
</span>
|
554
|
+
</div>
|
555
|
+
)
|
556
|
+
) : // Merge status for regular tasks
|
557
|
+
selectedAttempt.merge_commit ? (
|
558
|
+
<div className="flex items-center gap-1.5">
|
559
|
+
<div className="h-2 w-2 bg-green-500 rounded-full" />
|
560
|
+
<span className="text-sm font-medium text-green-700">
|
561
|
+
Merged
|
562
|
+
</span>
|
563
|
+
<span className="text-xs font-mono text-muted-foreground">
|
564
|
+
({selectedAttempt.merge_commit.slice(0, 8)})
|
565
|
+
</span>
|
566
|
+
</div>
|
567
|
+
) : (
|
568
|
+
<div className="flex items-center gap-1.5">
|
569
|
+
<div className="h-2 w-2 bg-yellow-500 rounded-full" />
|
570
|
+
<span className="text-sm font-medium text-yellow-700">
|
571
|
+
Not merged
|
572
|
+
</span>
|
573
|
+
</div>
|
574
|
+
)}
|
575
|
+
</div>
|
576
|
+
</div>
|
577
|
+
</div>
|
578
|
+
|
579
|
+
<div className="col-span-4">
|
580
|
+
<div className="flex items-center gap-1.5 mb-1">
|
581
|
+
<div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
|
582
|
+
Worktree Path
|
583
|
+
</div>
|
584
|
+
<Button
|
585
|
+
variant="ghost"
|
586
|
+
size="sm"
|
587
|
+
onClick={() => handleOpenInEditor()}
|
588
|
+
className="h-6 px-2 text-xs hover:bg-muted gap-1"
|
589
|
+
>
|
590
|
+
<ExternalLink className="h-3 w-3" />
|
591
|
+
Open in {editorDisplayName}
|
592
|
+
</Button>
|
593
|
+
</div>
|
594
|
+
<div
|
595
|
+
className={`text-xs font-mono px-2 py-1 rounded break-all cursor-pointer transition-all duration-300 flex items-center gap-2 ${
|
596
|
+
copied
|
597
|
+
? 'bg-green-100 text-green-800 border border-green-300'
|
598
|
+
: 'text-muted-foreground bg-muted hover:bg-muted/80'
|
599
|
+
}`}
|
600
|
+
onClick={handleCopyWorktreePath}
|
601
|
+
title={copied ? 'Copied!' : 'Click to copy worktree path'}
|
602
|
+
>
|
603
|
+
{copied && <Check className="h-3 w-3 text-green-600" />}
|
604
|
+
<span className={copied ? 'text-green-800' : ''}>
|
605
|
+
{selectedAttempt.worktree_path}
|
606
|
+
</span>
|
607
|
+
{copied && (
|
608
|
+
<span className="text-green-700 font-medium">Copied!</span>
|
609
|
+
)}
|
610
|
+
</div>
|
611
|
+
</div>
|
612
|
+
|
613
|
+
<div className="col-span-4 flex flex-wrap items-center justify-between gap-2">
|
614
|
+
<div className="flex items-center gap-2 flex-wrap">
|
615
|
+
<TooltipProvider>
|
616
|
+
<Tooltip>
|
617
|
+
<TooltipTrigger asChild>
|
618
|
+
<div
|
619
|
+
className={!projectHasDevScript ? 'cursor-not-allowed' : ''}
|
620
|
+
onMouseEnter={() => setIsHoveringDevServer(true)}
|
621
|
+
onMouseLeave={() => setIsHoveringDevServer(false)}
|
622
|
+
>
|
623
|
+
<Button
|
624
|
+
variant={runningDevServer ? 'destructive' : 'outline'}
|
625
|
+
size="sm"
|
626
|
+
onClick={runningDevServer ? stopDevServer : startDevServer}
|
627
|
+
disabled={isStartingDevServer || !projectHasDevScript}
|
628
|
+
className="gap-1"
|
629
|
+
>
|
630
|
+
{runningDevServer ? (
|
631
|
+
<>
|
632
|
+
<StopCircle className="h-3 w-3" />
|
633
|
+
Stop Dev
|
634
|
+
</>
|
635
|
+
) : (
|
636
|
+
<>
|
637
|
+
<Play className="h-3 w-3" />
|
638
|
+
Dev Server
|
639
|
+
</>
|
640
|
+
)}
|
641
|
+
</Button>
|
642
|
+
</div>
|
643
|
+
</TooltipTrigger>
|
644
|
+
<TooltipContent
|
645
|
+
className={runningDevServer ? 'max-w-2xl p-4' : ''}
|
646
|
+
side="top"
|
647
|
+
align="center"
|
648
|
+
avoidCollisions={true}
|
649
|
+
>
|
650
|
+
{!projectHasDevScript ? (
|
651
|
+
<p>
|
652
|
+
Add a dev server script in project settings to enable this
|
653
|
+
feature
|
654
|
+
</p>
|
655
|
+
) : runningDevServer && devServerDetails ? (
|
656
|
+
<div className="space-y-2">
|
657
|
+
<p className="text-sm font-medium">
|
658
|
+
Dev Server Logs (Last 10 lines):
|
659
|
+
</p>
|
660
|
+
<pre className="text-xs bg-muted p-2 rounded max-h-64 overflow-y-auto whitespace-pre-wrap">
|
661
|
+
{processedDevServerLogs}
|
662
|
+
</pre>
|
663
|
+
</div>
|
664
|
+
) : runningDevServer ? (
|
665
|
+
<p>Stop the running dev server</p>
|
666
|
+
) : (
|
667
|
+
<p>Start the dev server</p>
|
668
|
+
)}
|
669
|
+
</TooltipContent>
|
670
|
+
</Tooltip>
|
671
|
+
</TooltipProvider>
|
672
|
+
</div>
|
673
|
+
|
674
|
+
<div className="flex items-center gap-2 flex-wrap">
|
675
|
+
{taskAttempts.length > 1 && (
|
676
|
+
<DropdownMenu>
|
677
|
+
<TooltipProvider>
|
678
|
+
<Tooltip>
|
679
|
+
<TooltipTrigger asChild>
|
680
|
+
<DropdownMenuTrigger asChild>
|
681
|
+
<Button variant="outline" size="sm" className="gap-2">
|
682
|
+
<History className="h-4 w-4" />
|
683
|
+
History
|
684
|
+
</Button>
|
685
|
+
</DropdownMenuTrigger>
|
686
|
+
</TooltipTrigger>
|
687
|
+
<TooltipContent>
|
688
|
+
<p>View attempt history</p>
|
689
|
+
</TooltipContent>
|
690
|
+
</Tooltip>
|
691
|
+
</TooltipProvider>
|
692
|
+
<DropdownMenuContent align="start" className="w-64">
|
693
|
+
{taskAttempts.map((attempt) => (
|
694
|
+
<DropdownMenuItem
|
695
|
+
key={attempt.id}
|
696
|
+
onClick={() => handleAttemptChange(attempt)}
|
697
|
+
className={
|
698
|
+
selectedAttempt?.id === attempt.id ? 'bg-accent' : ''
|
699
|
+
}
|
700
|
+
>
|
701
|
+
<div className="flex flex-col w-full">
|
702
|
+
<span className="font-medium text-sm">
|
703
|
+
{new Date(attempt.created_at).toLocaleDateString()}{' '}
|
704
|
+
{new Date(attempt.created_at).toLocaleTimeString()}
|
705
|
+
</span>
|
706
|
+
<span className="text-xs text-muted-foreground">
|
707
|
+
{attempt.executor || 'executor'}
|
708
|
+
</span>
|
709
|
+
</div>
|
710
|
+
</DropdownMenuItem>
|
711
|
+
))}
|
712
|
+
</DropdownMenuContent>
|
713
|
+
</DropdownMenu>
|
714
|
+
)}
|
715
|
+
|
716
|
+
{/* Git Operations */}
|
717
|
+
{selectedAttempt && branchStatus && (
|
718
|
+
<>
|
719
|
+
{branchStatus.is_behind &&
|
720
|
+
!branchStatus.merged &&
|
721
|
+
!isPlanTask && (
|
722
|
+
<Button
|
723
|
+
onClick={handleRebaseClick}
|
724
|
+
disabled={
|
725
|
+
rebasing || branchStatusLoading || isAttemptRunning
|
726
|
+
}
|
727
|
+
variant="outline"
|
728
|
+
size="sm"
|
729
|
+
className="border-orange-300 text-orange-700 hover:bg-orange-50 gap-1"
|
730
|
+
>
|
731
|
+
<RefreshCw
|
732
|
+
className={`h-3 w-3 ${rebasing ? 'animate-spin' : ''}`}
|
733
|
+
/>
|
734
|
+
{rebasing ? 'Rebasing...' : `Rebase`}
|
735
|
+
</Button>
|
736
|
+
)}
|
737
|
+
{isPlanTask ? (
|
738
|
+
// Plan tasks: show approval button
|
739
|
+
<Button
|
740
|
+
onClick={handlePlanApproval}
|
741
|
+
disabled={
|
742
|
+
isAttemptRunning ||
|
743
|
+
executionState?.execution_state === 'CodingAgentFailed' ||
|
744
|
+
executionState?.execution_state === 'SetupFailed'
|
745
|
+
}
|
746
|
+
size="sm"
|
747
|
+
className="bg-green-600 hover:bg-green-700 disabled:bg-gray-400 gap-1"
|
748
|
+
>
|
749
|
+
<GitBranchIcon className="h-3 w-3" />
|
750
|
+
{isApprovingPlan ? 'Approving...' : 'Create Task'}
|
751
|
+
</Button>
|
752
|
+
) : (
|
753
|
+
// Normal merge and PR buttons for regular tasks
|
754
|
+
!branchStatus.merged && (
|
755
|
+
<>
|
756
|
+
<Button
|
757
|
+
onClick={handleCreatePRClick}
|
758
|
+
disabled={
|
759
|
+
creatingPR ||
|
760
|
+
Boolean(branchStatus.is_behind) ||
|
761
|
+
isAttemptRunning
|
762
|
+
}
|
763
|
+
variant="outline"
|
764
|
+
size="sm"
|
765
|
+
className="border-blue-300 text-blue-700 hover:bg-blue-50 gap-1"
|
766
|
+
>
|
767
|
+
<GitPullRequest className="h-3 w-3" />
|
768
|
+
{selectedAttempt.pr_url
|
769
|
+
? 'Open PR'
|
770
|
+
: creatingPR
|
771
|
+
? 'Creating...'
|
772
|
+
: 'Create PR'}
|
773
|
+
</Button>
|
774
|
+
<Button
|
775
|
+
onClick={handleMergeClick}
|
776
|
+
disabled={
|
777
|
+
merging ||
|
778
|
+
Boolean(branchStatus.is_behind) ||
|
779
|
+
isAttemptRunning
|
780
|
+
}
|
781
|
+
size="sm"
|
782
|
+
className="bg-green-600 hover:bg-green-700 disabled:bg-gray-400 gap-1"
|
783
|
+
>
|
784
|
+
<GitBranchIcon className="h-3 w-3" />
|
785
|
+
{merging ? 'Merging...' : 'Merge'}
|
786
|
+
</Button>
|
787
|
+
</>
|
788
|
+
)
|
789
|
+
)}
|
790
|
+
</>
|
791
|
+
)}
|
792
|
+
|
793
|
+
{isStopping || isAttemptRunning ? (
|
794
|
+
<Button
|
795
|
+
variant="destructive"
|
796
|
+
size="sm"
|
797
|
+
onClick={stopAllExecutions}
|
798
|
+
disabled={isStopping}
|
799
|
+
className="gap-2"
|
800
|
+
>
|
801
|
+
<StopCircle className="h-4 w-4" />
|
802
|
+
{isStopping ? 'Stopping...' : 'Stop Attempt'}
|
803
|
+
</Button>
|
804
|
+
) : (
|
805
|
+
<Button
|
806
|
+
variant="outline"
|
807
|
+
size="sm"
|
808
|
+
onClick={handleEnterCreateAttemptMode}
|
809
|
+
className="gap-2"
|
810
|
+
>
|
811
|
+
<Plus className="h-4 w-4" />
|
812
|
+
New Attempt
|
813
|
+
</Button>
|
814
|
+
)}
|
815
|
+
</div>
|
816
|
+
</div>
|
817
|
+
|
818
|
+
{/* Rebase Dialog */}
|
819
|
+
<Dialog open={showRebaseDialog} onOpenChange={setShowRebaseDialog}>
|
820
|
+
<DialogContent className="sm:max-w-md">
|
821
|
+
<DialogHeader>
|
822
|
+
<DialogTitle>Rebase Task Attempt</DialogTitle>
|
823
|
+
<DialogDescription>
|
824
|
+
Choose a new base branch to rebase this task attempt onto.
|
825
|
+
</DialogDescription>
|
826
|
+
</DialogHeader>
|
827
|
+
|
828
|
+
<div className="space-y-4">
|
829
|
+
<div className="space-y-2">
|
830
|
+
<label htmlFor="base-branch" className="text-sm font-medium">
|
831
|
+
Base Branch
|
832
|
+
</label>
|
833
|
+
<BranchSelector
|
834
|
+
branches={branches}
|
835
|
+
selectedBranch={selectedRebaseBranch}
|
836
|
+
onBranchSelect={setSelectedRebaseBranch}
|
837
|
+
placeholder="Select a base branch"
|
838
|
+
excludeCurrentBranch={false}
|
839
|
+
/>
|
840
|
+
</div>
|
841
|
+
</div>
|
842
|
+
|
843
|
+
<DialogFooter>
|
844
|
+
<Button
|
845
|
+
variant="outline"
|
846
|
+
onClick={() => setShowRebaseDialog(false)}
|
847
|
+
disabled={rebasing}
|
848
|
+
>
|
849
|
+
Cancel
|
850
|
+
</Button>
|
851
|
+
<Button
|
852
|
+
onClick={handleRebaseDialogConfirm}
|
853
|
+
disabled={rebasing || !selectedRebaseBranch}
|
854
|
+
>
|
855
|
+
{rebasing ? 'Rebasing...' : 'Rebase'}
|
856
|
+
</Button>
|
857
|
+
</DialogFooter>
|
858
|
+
</DialogContent>
|
859
|
+
</Dialog>
|
860
|
+
|
861
|
+
{/* Stop Execution Confirmation Dialog */}
|
862
|
+
<Dialog
|
863
|
+
open={showStopConfirmation}
|
864
|
+
onOpenChange={setShowStopConfirmation}
|
865
|
+
>
|
866
|
+
<DialogContent className="sm:max-w-md">
|
867
|
+
<DialogHeader>
|
868
|
+
<DialogTitle>Stop Current Attempt?</DialogTitle>
|
869
|
+
<DialogDescription>
|
870
|
+
Are you sure you want to stop the current execution? This action
|
871
|
+
cannot be undone.
|
872
|
+
</DialogDescription>
|
873
|
+
</DialogHeader>
|
874
|
+
<DialogFooter>
|
875
|
+
<Button
|
876
|
+
variant="outline"
|
877
|
+
onClick={() => setShowStopConfirmation(false)}
|
878
|
+
disabled={isStopping}
|
879
|
+
>
|
880
|
+
Cancel
|
881
|
+
</Button>
|
882
|
+
<Button
|
883
|
+
variant="destructive"
|
884
|
+
onClick={async () => {
|
885
|
+
setShowStopConfirmation(false);
|
886
|
+
await stopAllExecutions();
|
887
|
+
}}
|
888
|
+
disabled={isStopping}
|
889
|
+
>
|
890
|
+
{isStopping ? 'Stopping...' : 'Stop'}
|
891
|
+
</Button>
|
892
|
+
</DialogFooter>
|
893
|
+
</DialogContent>
|
894
|
+
</Dialog>
|
895
|
+
</div>
|
896
|
+
);
|
897
|
+
}
|
898
|
+
|
899
|
+
export default CurrentAttempt;
|