Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .agents/commands/pr/create-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
description: Create a pull request for the current branch (agent-driven, one-click)
argumentHint: "[--draft]"
---

# Goal

Create a pull request for the current branch in one pass. The user
clicked the Create PR button in the diff-editor sidebar — they expect
the PR to be created without further prompting.

An attachment named `pr-context.md` is included with this turn. It
contains:

- Current branch and base branch
- Whether the branch is published (has upstream)
- Commits ahead/behind upstream
- Whether there are uncommitted changes
- Required preconditions the user's branch must satisfy before
`gh pr create` will succeed

Read `pr-context.md` first. Use it as ground truth instead of re-deriving
the state yourself.

# Arguments

- `--draft` — create the PR as a draft. Pass `--draft` through to the
`gh pr create` call.

Parse the arguments from the user's prompt (everything after the skill
name). Do not ask the user to confirm the draft flag — it came from
their button click.

# Workflow

## 1. Satisfy preconditions

In the order listed in `pr-context.md` under "Required preconditions":

- **Uncommitted changes**: generate a commit message from the staged
diff (use `git diff --cached` and `git status`). If nothing is
staged, `git add -A`. Then `git commit -m "<message>"`. Keep the
message short and specific — do not write a PR-body-style
description here.
- **Unpublished branch**: `git push -u origin -- "<branch>"` — quote `<branch>`
to avoid shell injection on names with metacharacters.
- **Unpushed commits on a published branch**: `git push`.
- **Behind upstream**: stop. Report to the user that they should sync
first. Do not force-push. Do not rebase without asking.

If any push fails non-fast-forward, stop and report — never
force-push.

## 2. Draft the PR body

Use `git log "<base>..HEAD"` to read the commits, `git diff "<base>...HEAD"`
for the scope of changes. Produce:

- **Title**: short, imperative, derived from the most recent commit
message or the scope of the diff.
- **Body**: concise. Summary + a short Test Plan checklist. Skip
sections that have nothing meaningful to say — do not pad.

## 3. Create the PR

```
gh pr create \
--base <defaultBranch> \
--title "<title>" \
--body "<body>"
```

If `--draft` was passed, add `--draft`.

## 4. Report back

Print the PR URL as a plain link on its own line. One short sentence
above it summarizing what you did (e.g. "Published `feature-x` and
opened draft PR."). Do not paste the full body back.

# Guardrails

- Never force-push.
- Never skip pre-commit hooks (`--no-verify`) or signing.
- If a hook fails, report the failure; do not retry with `--no-verify`.
- Do not open a browser — the caller handles that.
- Do not run a full `AGENTS.md` standards review in this skill. The
button is a fast path; use `/create-pr` (the general-purpose skill)
for the gated review flow.
521 changes: 521 additions & 0 deletions apps/desktop/plans/20260420-1045-agent-driven-pr-flow.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface V2UserPreferencesApi {
setUrlLinks: (next: LinkTierMap) => void;
setRightSidebarOpen: (next: boolean | ((prev: boolean) => boolean)) => void;
setRightSidebarTab: (next: RightSidebarTab) => void;
setRightSidebarWidth: (next: number) => void;
setDeleteLocalBranch: (next: boolean) => void;
}

Expand Down Expand Up @@ -104,6 +105,25 @@ export function useV2UserPreferences(): V2UserPreferencesApi {
[collections],
);

const setRightSidebarWidth = useCallback(
(next: number) => {
const existing = collections.v2UserPreferences.get(
V2_USER_PREFERENCES_ID,
);
if (!existing) {
collections.v2UserPreferences.insert({
...DEFAULT_V2_USER_PREFERENCES,
rightSidebarWidth: next,
});
return;
}
collections.v2UserPreferences.update(V2_USER_PREFERENCES_ID, (draft) => {
draft.rightSidebarWidth = next;
});
},
[collections],
);

const setDeleteLocalBranch = useCallback(
(next: boolean) => {
const existing = collections.v2UserPreferences.get(
Expand All @@ -129,6 +149,7 @@ export function useV2UserPreferences(): V2UserPreferencesApi {
setUrlLinks,
setRightSidebarOpen,
setRightSidebarTab,
setRightSidebarWidth,
setDeleteLocalBranch,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ function DashboardLayout() {
);

return (
// FORK NOTE: keep `bg-tertiary` outer wrapper, `isTearoff` /
// `isScratchRoute` conditional rendering and `<KeepAliveWorkspaces />`.
// upstream #3777 introduces a `workspace-right-sidebar-slot` portal
// at the dashboard level for the new PR action header — that slot is
// kept further down (outside this conflict block).
<div className="flex flex-col h-full w-full bg-tertiary">
{!isTearoff && <TopBar />}
<div className="flex flex-1 min-h-0 min-w-0 overflow-hidden">
Expand Down Expand Up @@ -145,19 +150,20 @@ function DashboardLayout() {
<div className="flex flex-1 min-h-0 min-w-0">
<KeepAliveWorkspaces />
</div>
<AddRepositoryModals />
{deleteTarget && (
<DeleteWorkspaceDialog
workspaceId={deleteTarget.workspaceId}
workspaceName={deleteTarget.workspaceName}
workspaceType={deleteTarget.workspaceType}
open={true}
onOpenChange={(open) => {
if (!open) setDeleteTarget(null);
}}
/>
)}
</div>
<div id="workspace-right-sidebar-slot" className="flex h-full shrink-0" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep right-sidebar slot in the main workspace row

The new workspace-right-sidebar-slot is rendered as a sibling of the main flex-1 content inside a flex-col container, and it has h-full shrink-0. In this layout, that makes the slot consume full parent height on the vertical axis, which can force the actual workspace row to collapse or overflow; the portal target should live inside the horizontal workspace row (or not use h-full here) so it doesn’t steal main-axis space.

Useful? React with 👍 / 👎.

<AddRepositoryModals />
{deleteTarget && (
<DeleteWorkspaceDialog
workspaceId={deleteTarget.workspaceId}
workspaceName={deleteTarget.workspaceName}
workspaceType={deleteTarget.workspaceType}
open={true}
onOpenChange={(open) => {
if (!open) setDeleteTarget(null);
}}
/>
)}
</div>
);
}
Loading
Loading