From b5496140a5ab18a69c370c97290fac3162b6716a Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Mon, 4 May 2026 14:52:57 +0300 Subject: [PATCH 1/2] fix(code): show unified PR badge for externally-created PRs The unified badge depended on `workspace.linkedBranch`, which only got set when the in-app create-PR flow ran. Skills that run `gh pr create` via bash never went through that flow, so the badge fell back to the old "View PR" button (and a dropdown of disabled commit/push items) even though `getPrStatus` had already discovered the PR on the current branch. - TaskActionsMenu: fall back to `gitState.prUrl` when no `linkedBranch` is recorded so the badge appears for any worktree whose current branch has a PR. - TaskActionsMenu: when a PR exists, drop disabled commit/push entries from the adjacent dropdown so it only surfaces actionable items. - AgentService: when a PR URL is detected in bash output, emit `AgentFileActivity` so `WorkspaceService` records the current branch as `linkedBranch`. Keeps PR-aware UI (branch mismatch, diff source) in sync without waiting for the next file edit. Generated-By: PostHog Code Task-Id: 66a40920-2b25-40b6-983e-1a489353431d --- apps/code/src/main/services/agent/service.ts | 23 +++++++++++ .../components/TaskActionsMenu.tsx | 41 +++++++++++-------- 2 files changed, 48 insertions(+), 16 deletions(-) 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..4450f8fde 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,38 @@ 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. + // Disabled commit/push entries (no changes, branch up to date) just clutter + // the dropdown next to the badge — drop them so the menu only surfaces + // actions the user can actually take. 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; + return a.enabled; + }); return ( <> From b4d4a9a40ecf4a2078d43f3752491a323f8aa31e Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Mon, 4 May 2026 14:57:20 +0300 Subject: [PATCH 2/2] fix(code): preserve disabledReason tooltips for non-work-shipping actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrow the dropdown filter to only drop disabled commit / push / sync / publish entries — these slots flip to disabled solely to signal "no work to do," so they're noise next to a PR badge. Other disabled actions stay visible so their disabledReason tooltip can still explain why they're unavailable (network issues, diverged branch, etc). Generated-By: PostHog Code Task-Id: 66a40920-2b25-40b6-983e-1a489353431d --- .../components/TaskActionsMenu.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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 4450f8fde..bc5ec9990 100644 --- a/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx +++ b/apps/code/src/renderer/features/git-interaction/components/TaskActionsMenu.tsx @@ -95,15 +95,24 @@ export function TaskActionsMenu({ taskId, isCloud }: TaskActionsMenuProps) { if (isCloud && !pr) return null; // When a PR exists the badge handles "view PR" and "create PR" is moot. - // Disabled commit/push entries (no changes, branch up to date) just clutter - // the dropdown next to the badge — drop them so the menu only surfaces - // actions the user can actually take. + // 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) => { if (!pr) return true; if (a.id === "view-pr" || a.id === "create-pr") return false; - return a.enabled; + if (!a.enabled && noWorkSlots.has(a.id)) return false; + return true; }); return (