From 4dac9bdd71c027f33a0c92127ebfc8e4fa4c2a7a Mon Sep 17 00:00:00 2001 From: imabdulazeez Date: Tue, 5 May 2026 16:34:05 +0530 Subject: [PATCH 1/4] Add setting to disable auto-create PR on push - Add client setting `autoCreatePrOnPush` (default on) - Downgrade feature-branch quick actions to commit/push only when off - Expose toggle in General settings --- .../GitActionsControl.logic.test.ts | 76 +++++++++++++++++++ .../src/components/GitActionsControl.logic.ts | 7 +- apps/web/src/components/GitActionsControl.tsx | 12 ++- .../components/settings/SettingsPanels.tsx | 26 +++++++ packages/contracts/src/settings.ts | 1 + 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/GitActionsControl.logic.test.ts b/apps/web/src/components/GitActionsControl.logic.test.ts index 7950753330e..55511ceb3f1 100644 --- a/apps/web/src/components/GitActionsControl.logic.test.ts +++ b/apps/web/src/components/GitActionsControl.logic.test.ts @@ -1131,3 +1131,79 @@ describe("resolveAutoFeatureBranchName", () => { assert.equal(ref, "feature/update"); }); }); + +describe("when: autoCreatePr is disabled", () => { + it("downgrades feature-branch commit+push from PR to plain commit & push", () => { + const quick = resolveQuickAction( + status({ hasWorkingTreeChanges: true }), + false, + false, + true, + false, + ); + assert.deepInclude(quick, { + kind: "run_action", + action: "commit_push", + label: "Commit & push", + disabled: false, + }); + }); + + it("downgrades feature-branch ahead-with-upstream from create PR to plain push", () => { + const quick = resolveQuickAction( + status({ aheadCount: 2 }), + false, + false, + true, + false, + ); + assert.deepInclude(quick, { + kind: "run_action", + action: "push", + label: "Push", + disabled: false, + }); + }); + + it("downgrades feature-branch ahead-without-upstream from create PR to plain push", () => { + const quick = resolveQuickAction( + status({ aheadCount: 1, hasUpstream: false }), + false, + false, + true, + false, + ); + assert.deepInclude(quick, { + kind: "run_action", + action: "push", + label: "Push", + disabled: false, + }); + }); + + it("preserves Commit, push & PR when autoCreatePr is true (default)", () => { + const quick = resolveQuickAction( + status({ hasWorkingTreeChanges: true }), + false, + false, + true, + true, + ); + assert.deepInclude(quick, { + kind: "run_action", + action: "commit_push_pr", + label: "Commit, push & PR", + disabled: false, + }); + }); + + it("preserves Push & create PR when autoCreatePr is true (default)", () => { + const quick = resolveQuickAction(status({ aheadCount: 2 }), false, false, true, true); + assert.deepInclude(quick, { + kind: "run_action", + action: "create_pr", + label: "Push & create PR", + disabled: false, + }); + }); +}); diff --git a/apps/web/src/components/GitActionsControl.logic.ts b/apps/web/src/components/GitActionsControl.logic.ts index 3f6bae61cdd..9e75697c788 100644 --- a/apps/web/src/components/GitActionsControl.logic.ts +++ b/apps/web/src/components/GitActionsControl.logic.ts @@ -169,6 +169,7 @@ export function resolveQuickAction( isBusy: boolean, isDefaultRef = false, hasPrimaryRemote = true, + autoCreatePr = true, ): GitQuickAction { if (isBusy) { return { label: "Commit", disabled: true, kind: "show_hint", hint: "Git action in progress." }; @@ -205,7 +206,7 @@ export function resolveQuickAction( if (!gitStatus.hasUpstream && !hasPrimaryRemote) { return { label: "Commit", disabled: false, kind: "run_action", action: "commit" }; } - if (hasOpenPr || isDefaultRef) { + if (hasOpenPr || isDefaultRef || !autoCreatePr) { return { label: "Commit & push", disabled: false, kind: "run_action", action: "commit_push" }; } return { @@ -238,7 +239,7 @@ export function resolveQuickAction( hint: "No local commits to push.", }; } - if (hasOpenPr || isDefaultRef) { + if (hasOpenPr || isDefaultRef || !autoCreatePr) { return { label: "Push", disabled: false, @@ -272,7 +273,7 @@ export function resolveQuickAction( } if (isAhead) { - if (hasOpenPr || isDefaultRef) { + if (hasOpenPr || isDefaultRef || !autoCreatePr) { return { label: "Push", disabled: false, diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx index 01b84bd94a5..55c22357170 100644 --- a/apps/web/src/components/GitActionsControl.tsx +++ b/apps/web/src/components/GitActionsControl.tsx @@ -44,6 +44,7 @@ import { resolveThreadBranchUpdate, } from "./GitActionsControl.logic"; import { AnimatedHeight } from "./AnimatedHeight"; +import { useSettings } from "../hooks/useSettings"; import { Button } from "~/components/ui/button"; import { Checkbox } from "~/components/ui/checkbox"; import { @@ -1136,10 +1137,17 @@ export default function GitActionsControl({ () => buildMenuItems(gitStatusForActions, isGitActionRunning, hasPrimaryRemote), [gitStatusForActions, hasPrimaryRemote, isGitActionRunning], ); + const autoCreatePrOnPush = useSettings((s) => s.autoCreatePrOnPush); const quickAction = useMemo( () => - resolveQuickAction(gitStatusForActions, isGitActionRunning, isDefaultRef, hasPrimaryRemote), - [gitStatusForActions, hasPrimaryRemote, isDefaultRef, isGitActionRunning], + resolveQuickAction( + gitStatusForActions, + isGitActionRunning, + isDefaultRef, + hasPrimaryRemote, + autoCreatePrOnPush, + ), + [autoCreatePrOnPush, gitStatusForActions, hasPrimaryRemote, isDefaultRef, isGitActionRunning], ); const quickActionDisabledReason = quickAction.disabled ? (quickAction.hint ?? "This action is currently unavailable.") diff --git a/apps/web/src/components/settings/SettingsPanels.tsx b/apps/web/src/components/settings/SettingsPanels.tsx index 8fc36d4a32b..632929e9029 100644 --- a/apps/web/src/components/settings/SettingsPanels.tsx +++ b/apps/web/src/components/settings/SettingsPanels.tsx @@ -972,6 +972,32 @@ export function GeneralSettingsPanel() { } /> + + updateSettings({ + autoCreatePrOnPush: DEFAULT_UNIFIED_SETTINGS.autoCreatePrOnPush, + }) + } + /> + ) : null + } + control={ + + updateSettings({ autoCreatePrOnPush: Boolean(checked) }) + } + aria-label="Auto-create PR on push" + /> + } + /> + Date: Wed, 6 May 2026 10:14:08 +0530 Subject: [PATCH 2/4] Format resolveQuickAction call in GitActionsControl test --- apps/web/src/components/GitActionsControl.logic.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/web/src/components/GitActionsControl.logic.test.ts b/apps/web/src/components/GitActionsControl.logic.test.ts index 55511ceb3f1..2cc8913cb60 100644 --- a/apps/web/src/components/GitActionsControl.logic.test.ts +++ b/apps/web/src/components/GitActionsControl.logic.test.ts @@ -1150,13 +1150,7 @@ describe("when: autoCreatePr is disabled", () => { }); it("downgrades feature-branch ahead-with-upstream from create PR to plain push", () => { - const quick = resolveQuickAction( - status({ aheadCount: 2 }), - false, - false, - true, - false, - ); + const quick = resolveQuickAction(status({ aheadCount: 2 }), false, false, true, false); assert.deepInclude(quick, { kind: "run_action", action: "push", From 957a62607404607fdadcb4fc27f985b6a272e761 Mon Sep 17 00:00:00 2001 From: imabdulazeez Date: Wed, 6 May 2026 11:21:03 +0530 Subject: [PATCH 3/4] Add autoCreatePrOnPush to test clientSettings fixture Adds default value for new setting to keep tests passing --- apps/desktop/src/clientPersistence.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/src/clientPersistence.test.ts b/apps/desktop/src/clientPersistence.test.ts index d4c4768d2c8..6c41085c3f6 100644 --- a/apps/desktop/src/clientPersistence.test.ts +++ b/apps/desktop/src/clientPersistence.test.ts @@ -49,6 +49,7 @@ function makeSecretStorage(available: boolean): DesktopSecretStorage { } const clientSettings: ClientSettings = { + autoCreatePrOnPush: true, autoOpenPlanSidebar: false, confirmThreadArchive: true, confirmThreadDelete: false, From cd3500b26671965d282f5389f8259863d5a1299b Mon Sep 17 00:00:00 2001 From: imabdulazeez Date: Wed, 6 May 2026 11:32:34 +0530 Subject: [PATCH 4/4] Add autoCreatePrOnPush to test clientSettings fixtures --- apps/web/src/localApi.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/src/localApi.test.ts b/apps/web/src/localApi.test.ts index 895163cf109..1d25de6a727 100644 --- a/apps/web/src/localApi.test.ts +++ b/apps/web/src/localApi.test.ts @@ -570,6 +570,7 @@ describe("wsApi", () => { it("reads and writes persistence through the desktop bridge when available", async () => { const clientSettings = { + autoCreatePrOnPush: true, autoOpenPlanSidebar: false, confirmThreadArchive: true, confirmThreadDelete: false, @@ -631,6 +632,7 @@ describe("wsApi", () => { const { createLocalApi } = await import("./localApi"); const api = createLocalApi(rpcClientMock as never); const clientSettings = { + autoCreatePrOnPush: true, autoOpenPlanSidebar: false, confirmThreadArchive: true, confirmThreadDelete: false,