77} from "@features/git-interaction/hooks/useGitQueries" ;
88import { computeDiffStats } from "@features/git-interaction/utils/diffStats" ;
99import { useDraftStore } from "@features/message-editor/stores/draftStore" ;
10+ import type { EditorContent } from "@features/message-editor/utils/content" ;
1011import { ProvisioningView } from "@features/provisioning/components/ProvisioningView" ;
1112import { useProvisioningStore } from "@features/provisioning/stores/provisioningStore" ;
1213import { SessionView } from "@features/sessions/components/SessionView" ;
@@ -15,15 +16,19 @@ import { useSessionConnection } from "@features/sessions/hooks/useSessionConnect
1516import { useSessionViewState } from "@features/sessions/hooks/useSessionViewState" ;
1617import { useRestoreTask } from "@features/suspension/hooks/useRestoreTask" ;
1718import { useSuspendedTaskIds } from "@features/suspension/hooks/useSuspendedTaskIds" ;
19+ import { BranchMismatchDialog } from "@features/task-detail/components/BranchMismatchDialog" ;
1820import { WorkspaceSetupPrompt } from "@features/task-detail/components/WorkspaceSetupPrompt" ;
21+ import { useBranchMismatchGuard } from "@features/workspace/hooks/useBranchMismatch" ;
1922import {
2023 useCreateWorkspace ,
2124 useWorkspaceLoaded ,
2225} from "@features/workspace/hooks/useWorkspace" ;
2326import { Box , Flex } from "@radix-ui/themes" ;
27+ import { trpcClient } from "@renderer/trpc/client" ;
2428import type { Task } from "@shared/types" ;
29+ import { logger } from "@utils/logger" ;
2530import { getTaskRepository } from "@utils/repository" ;
26- import { useCallback , useEffect , useMemo } from "react" ;
31+ import { useCallback , useEffect , useMemo , useState } from "react" ;
2732
2833interface TaskLogsPanelProps {
2934 taskId : string ;
@@ -47,7 +52,7 @@ export function TaskLogsPanel({ taskId, task, hideInput }: TaskLogsPanelProps) {
4752
4853 const isProvisioning = useProvisioningStore ( ( s ) => s . activeTasks . has ( taskId ) ) ;
4954
50- const { requestFocus } = useDraftStore ( ( s ) => s . actions ) ;
55+ const { requestFocus, setPendingContent } = useDraftStore ( ( s ) => s . actions ) ;
5156
5257 const {
5358 session,
@@ -74,13 +79,68 @@ export function TaskLogsPanel({ taskId, task, hideInput }: TaskLogsPanelProps) {
7479 } ) ;
7580
7681 const {
77- handleSendPrompt,
82+ handleSendPrompt : rawSendPrompt ,
7883 handleCancelPrompt,
7984 handleRetry,
8085 handleNewSession,
8186 handleBashCommand,
8287 } = useSessionCallbacks ( { taskId, task, session, repoPath } ) ;
8388
89+ // Branch mismatch guard
90+ const { shouldWarn, linkedBranch, currentBranch, dismissWarning } =
91+ useBranchMismatchGuard ( taskId ) ;
92+ const [ pendingMessage , setPendingMessage ] = useState < string | null > ( null ) ;
93+ const [ isSwitchingBranch , setIsSwitchingBranch ] = useState ( false ) ;
94+
95+ const handleSendPrompt = useCallback (
96+ ( text : string ) => {
97+ if ( shouldWarn ) {
98+ setPendingMessage ( text ) ;
99+ return ;
100+ }
101+ rawSendPrompt ( text ) ;
102+ } ,
103+ [ shouldWarn , rawSendPrompt ] ,
104+ ) ;
105+
106+ const handleMismatchSwitch = useCallback ( async ( ) => {
107+ if ( ! linkedBranch || ! repoPath ) return ;
108+ setIsSwitchingBranch ( true ) ;
109+ try {
110+ await trpcClient . git . checkoutBranch . mutate ( {
111+ directoryPath : repoPath ,
112+ branchName : linkedBranch ,
113+ } ) ;
114+ dismissWarning ( ) ;
115+ if ( pendingMessage ) {
116+ rawSendPrompt ( pendingMessage ) ;
117+ }
118+ } catch ( error ) {
119+ logger . scope ( "task-logs-panel" ) . error ( "Failed to switch branch" , error ) ;
120+ } finally {
121+ setIsSwitchingBranch ( false ) ;
122+ setPendingMessage ( null ) ;
123+ }
124+ } , [ linkedBranch , repoPath , dismissWarning , pendingMessage , rawSendPrompt ] ) ;
125+
126+ const handleMismatchContinue = useCallback ( ( ) => {
127+ dismissWarning ( ) ;
128+ if ( pendingMessage ) {
129+ rawSendPrompt ( pendingMessage ) ;
130+ }
131+ setPendingMessage ( null ) ;
132+ } , [ dismissWarning , pendingMessage , rawSendPrompt ] ) ;
133+
134+ const handleMismatchCancel = useCallback ( ( ) => {
135+ if ( pendingMessage ) {
136+ const content : EditorContent = {
137+ segments : [ { type : "text" , text : pendingMessage } ] ,
138+ } ;
139+ setPendingContent ( taskId , content ) ;
140+ }
141+ setPendingMessage ( null ) ;
142+ } , [ pendingMessage , setPendingContent , taskId ] ) ;
143+
84144 const cloudOutput = session ?. cloudOutput ?? null ;
85145 const prUrl =
86146 isCloud && cloudOutput ?. pr_url ? ( cloudOutput . pr_url as string ) : null ;
@@ -165,6 +225,18 @@ export function TaskLogsPanel({ taskId, task, hideInput }: TaskLogsPanelProps) {
165225 </ ErrorBoundary >
166226 </ Box >
167227 </ Flex >
228+
229+ { linkedBranch && currentBranch && (
230+ < BranchMismatchDialog
231+ open = { pendingMessage !== null }
232+ linkedBranch = { linkedBranch }
233+ currentBranch = { currentBranch }
234+ onSwitch = { handleMismatchSwitch }
235+ onContinue = { handleMismatchContinue }
236+ onCancel = { handleMismatchCancel }
237+ isSwitching = { isSwitchingBranch }
238+ />
239+ ) }
168240 </ BackgroundWrapper >
169241 ) ;
170242}
0 commit comments