diff --git a/apps/code/src/main/services/agent/service.ts b/apps/code/src/main/services/agent/service.ts index af80ec302..645afd3e1 100644 --- a/apps/code/src/main/services/agent/service.ts +++ b/apps/code/src/main/services/agent/service.ts @@ -1628,6 +1628,29 @@ For git operations while detached: error: err, }); }); + + // The user-initiated PR-creation flow links the current branch to the + // workspace atomically (see GitService.createPr). PRs created via bash — + // e.g. an agent running a `/commit-and-pr` skill — never go through that + // flow, so `workspace.linkedBranch` would otherwise stay unset and + // PR-aware UI (the unified PR badge, branch mismatch warning, diff + // source) would have no anchor. Emit AgentFileActivity here too so + // WorkspaceService.handleAgentFileActivity links the current feature + // branch the moment we observe a PR for it. + getCurrentBranch(session.repoPath) + .then((branchName) => { + this.emit(AgentServiceEvent.AgentFileActivity, { + taskId: session.taskId, + branchName, + }); + }) + .catch((err) => { + log.warn("Failed to resolve branch for PR auto-link", { + taskRunId, + taskId: session.taskId, + error: err, + }); + }); } /** diff --git a/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx b/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx index d8b38d6e1..bc5ec9990 100644 --- a/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx +++ b/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx @@ -59,17 +59,6 @@ interface TaskActionsMenuProps { * list. Cloud tasks without a PR render nothing. */ export function TaskActionsMenu({ taskId, isCloud }: TaskActionsMenuProps) { - // PR URL resolution — pick the right source based on task kind. - const cloudPrUrl = useCloudPrUrl(taskId); - const linkedPrUrl = useLinkedBranchPrUrl(taskId); - const prUrl = isCloud ? cloudPrUrl : linkedPrUrl; - - const { - meta: { state: prState, merged, draft }, - } = usePrDetails(prUrl); - const { execute: executePrAction, isPending: isPrActionPending } = - usePrActions(prUrl); - // Git state (skipped for cloud — useGitInteraction handles undefined repo). const workspace = useWorkspace(taskId); const isFocused = useFocusStore( @@ -84,18 +73,47 @@ export function TaskActionsMenu({ taskId, isCloud }: TaskActionsMenuProps) { actions: gitActions, } = useGitInteraction(taskId, isCloud ? undefined : localRepoPath); + // PR URL resolution — pick the right source based on task kind. + // For local tasks, prefer the explicitly linked-branch lookup but fall back + // to whatever `getPrStatus` found on the current branch. The fallback + // catches PRs created outside the in-app flow (e.g. agents/skills running + // `gh pr create` via bash) where `workspace.linkedBranch` may not be set + // yet. + const cloudPrUrl = useCloudPrUrl(taskId); + const linkedPrUrl = useLinkedBranchPrUrl(taskId); + const prUrl = isCloud ? cloudPrUrl : (linkedPrUrl ?? gitState.prUrl ?? null); + + const { + meta: { state: prState, merged, draft }, + } = usePrDetails(prUrl); + const { execute: executePrAction, isPending: isPrActionPending } = + usePrActions(prUrl); + const pr = prUrl && prState !== null ? { url: prUrl, state: prState } : null; // Cloud tasks only appear when they have a PR. if (isCloud && !pr) return null; - // "view-pr" is redundant when the badge itself links to the PR; - // "create-pr" is redundant once a PR exists. + // When a PR exists the badge handles "view PR" and "create PR" is moot. + // The work-shipping slots (commit and the push/sync/publish trio) only get + // disabled to signal "nothing to do" (no changes, branch up to date, no + // commits to publish) — that's noise next to a PR badge, so drop them. + // Other disabled actions stay so their `disabledReason` tooltip can still + // explain why they're unavailable. + const noWorkSlots = new Set([ + "commit", + "push", + "sync", + "publish", + ]); const gitItems = isCloud ? [] - : gitState.actions.filter( - (a) => !(pr && (a.id === "view-pr" || a.id === "create-pr")), - ); + : gitState.actions.filter((a) => { + if (!pr) return true; + if (a.id === "view-pr" || a.id === "create-pr") return false; + if (!a.enabled && noWorkSlots.has(a.id)) return false; + return true; + }); return ( <>