From 2fb3a9987ab3dbb3f5de540aca9499ef75522de6 Mon Sep 17 00:00:00 2001 From: Sameen Karim Date: Tue, 5 May 2026 01:02:20 -0400 Subject: [PATCH 1/2] open prs as draft by default --- README.md | 10 +- cmd/link.go | 17 ++- cmd/link_test.go | 113 ++++++++++++++++++- cmd/submit.go | 17 ++- cmd/submit_test.go | 146 +++++++++++++++++++++++++ docs/src/content/docs/faq.md | 6 +- docs/src/content/docs/reference/cli.md | 10 +- internal/github/client_interface.go | 1 + internal/github/github.go | 27 +++++ internal/github/mock_client.go | 8 ++ skills/gh-stack/SKILL.md | 20 ++-- 11 files changed, 343 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 38e00b3..d1eb364 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ When creating new PRs, you will be prompted to enter a title for each one. Press | Flag | Description | |------|-------------| | `--auto` | Use auto-generated PR titles without prompting | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (defaults to auto-detected remote) | **Examples:** @@ -366,7 +366,7 @@ When creating new PRs, you will be prompted to enter a title for each one. Press ```sh gh stack submit gh stack submit --auto -gh stack submit --draft +gh stack submit --open ``` ### `gh stack link` @@ -386,7 +386,7 @@ If the PRs are not yet in a stack, a new stack is created. If some of the PRs ar | Flag | Description | |------|-------------| | `--base ` | Base branch for the bottom of the stack (default: `main`) | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (defaults to auto-detected remote) | **Examples:** @@ -401,8 +401,8 @@ gh stack link 10 20 30 # Add branches to an existing stack of PRs gh stack link 42 43 feature-auth feature-ui -# Use a different base branch and create PRs as drafts -gh stack link --base develop --draft feat-a feat-b feat-c +# Use a different base branch and mark PRs as ready for review +gh stack link --base develop --open feat-a feat-b feat-c ``` ### `gh stack view` diff --git a/cmd/link.go b/cmd/link.go index 289d40c..2e0e3ab 100644 --- a/cmd/link.go +++ b/cmd/link.go @@ -14,7 +14,7 @@ import ( type linkOptions struct { base string - draft bool + open bool remote string } @@ -51,7 +51,7 @@ the new PRs (existing PRs are never removed).`, } cmd.Flags().StringVar(&opts.base, "base", "main", "Base branch for the bottom of the stack") - cmd.Flags().BoolVar(&opts.draft, "draft", false, "Create new PRs as drafts") + cmd.Flags().BoolVar(&opts.open, "open", false, "Mark new and existing PRs as ready for review") cmd.Flags().StringVar(&opts.remote, "remote", "", "Remote to push to (defaults to auto-detected remote)") return cmd @@ -321,7 +321,7 @@ func createMissingPRs(cfg *config.Config, client github.ClientOps, opts *linkOpt title := humanize(arg) body := generatePRBody("") - newPR, err := client.CreatePR(baseBranch, arg, title, body, opts.draft) + newPR, err := client.CreatePR(baseBranch, arg, title, body, !opts.open) if err != nil { cfg.Errorf("failed to create PR for branch %s: %v", arg, err) return nil, ErrAPIFailure @@ -375,6 +375,17 @@ func fixBaseBranches(cfg *config.Config, client github.ClientOps, opts *linkOpti cfg.PRLink(r.prNumber, r.prURL), expectedBase) } } + + // Convert draft PR to ready for review when --open is set. + if opts.open && pr.IsDraft { + if err := client.MarkPRReadyForReview(pr.ID); err != nil { + cfg.Warningf("failed to mark PR %s as ready for review: %v", + cfg.PRLink(r.prNumber, r.prURL), err) + } else { + cfg.Successf("Marked PR %s as ready for review", + cfg.PRLink(r.prNumber, r.prURL)) + } + } } } diff --git a/cmd/link_test.go b/cmd/link_test.go index 48aaef9..e70b20e 100644 --- a/cmd/link_test.go +++ b/cmd/link_test.go @@ -477,7 +477,7 @@ func TestLink_BranchNames_AllNeedPRs(t *testing.T) { assert.Contains(t, output, "Created stack with 3 PRs") } -func TestLink_BranchNames_DraftFlag(t *testing.T) { +func TestLink_BranchNames_DefaultDraft(t *testing.T) { restore := git.SetOps(newLinkGitMock("feat-a", "feat-b")) defer restore() @@ -512,13 +512,120 @@ func TestLink_BranchNames_DraftFlag(t *testing.T) { } cmd := LinkCmd(cfg) - cmd.SetArgs([]string{"--draft", "feat-a", "feat-b"}) + cmd.SetArgs([]string{"feat-a", "feat-b"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + err := cmd.Execute() + + assert.NoError(t, err) + assert.True(t, createdDraft, "PRs should be created as drafts by default") +} + +func TestLink_BranchNames_OpenFlag(t *testing.T) { + restore := git.SetOps(newLinkGitMock("feat-a", "feat-b")) + defer restore() + + var createdDraft bool + prCounter := 0 + + cfg, _, _ := config.NewTestConfig() + cfg.GitHubClientOverride = &github.MockClient{ + FindPRForBranchFn: func(branch string) (*github.PullRequest, error) { + return nil, nil + }, + FindPRByNumberFn: func(n int) (*github.PullRequest, error) { + heads := map[int]string{1: "feat-a", 2: "feat-b"} + bases := map[int]string{1: "main", 2: "feat-a"} + if h, ok := heads[n]; ok { + return &github.PullRequest{Number: n, HeadRefName: h, BaseRefName: bases[n]}, nil + } + return nil, nil + }, + CreatePRFn: func(base, head, title, body string, draft bool) (*github.PullRequest, error) { + createdDraft = draft + prCounter++ + return &github.PullRequest{ + Number: prCounter, HeadRefName: head, BaseRefName: base, + URL: fmt.Sprintf("https://github.com/o/r/pull/%d", prCounter), + }, nil + }, + ListStacksFn: func() ([]github.RemoteStack, error) { + return []github.RemoteStack{}, nil + }, + CreateStackFn: func([]int) (int, error) { return 1, nil }, + } + + cmd := LinkCmd(cfg) + cmd.SetArgs([]string{"--open", "feat-a", "feat-b"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + err := cmd.Execute() + + assert.NoError(t, err) + assert.False(t, createdDraft, "PRs should not be created as drafts when --open is set") +} + +func TestLink_OpenFlag_ConvertsDraftPRs(t *testing.T) { + restore := git.SetOps(newLinkGitMock("feat-a", "feat-b")) + defer restore() + + var markedReady []string + + cfg, _, errR := config.NewTestConfig() + cfg.GitHubClientOverride = &github.MockClient{ + FindPRForBranchFn: func(branch string) (*github.PullRequest, error) { + switch branch { + case "feat-a": + return &github.PullRequest{ + Number: 1, ID: "PR_1", HeadRefName: "feat-a", BaseRefName: "main", + IsDraft: true, URL: "https://github.com/o/r/pull/1", + }, nil + case "feat-b": + return &github.PullRequest{ + Number: 2, ID: "PR_2", HeadRefName: "feat-b", BaseRefName: "feat-a", + IsDraft: true, URL: "https://github.com/o/r/pull/2", + }, nil + } + return nil, nil + }, + FindPRByNumberFn: func(n int) (*github.PullRequest, error) { + switch n { + case 1: + return &github.PullRequest{ + Number: 1, ID: "PR_1", HeadRefName: "feat-a", BaseRefName: "main", + IsDraft: true, URL: "https://github.com/o/r/pull/1", + }, nil + case 2: + return &github.PullRequest{ + Number: 2, ID: "PR_2", HeadRefName: "feat-b", BaseRefName: "feat-a", + IsDraft: true, URL: "https://github.com/o/r/pull/2", + }, nil + } + return nil, nil + }, + MarkPRReadyForReviewFn: func(prID string) error { + markedReady = append(markedReady, prID) + return nil + }, + ListStacksFn: func() ([]github.RemoteStack, error) { + return []github.RemoteStack{}, nil + }, + CreateStackFn: func([]int) (int, error) { return 1, nil }, + } + + cmd := LinkCmd(cfg) + cmd.SetArgs([]string{"--open", "feat-a", "feat-b"}) cmd.SetOut(io.Discard) cmd.SetErr(io.Discard) err := cmd.Execute() + cfg.Err.Close() + errOut, _ := io.ReadAll(errR) + output := string(errOut) + assert.NoError(t, err) - assert.True(t, createdDraft, "PRs should be created as drafts when --draft is set") + assert.Equal(t, []string{"PR_1", "PR_2"}, markedReady, "both draft PRs should be marked ready") + assert.Contains(t, output, "Marked PR") } func TestLink_MixedArgs_PRNumberAndBranch(t *testing.T) { diff --git a/cmd/submit.go b/cmd/submit.go index 5c4c0a3..16db9ea 100644 --- a/cmd/submit.go +++ b/cmd/submit.go @@ -18,7 +18,7 @@ import ( type submitOptions struct { auto bool - draft bool + open bool remote string } @@ -34,7 +34,7 @@ func SubmitCmd(cfg *config.Config) *cobra.Command { } cmd.Flags().BoolVar(&opts.auto, "auto", false, "Use auto-generated PR titles without prompting") - cmd.Flags().BoolVar(&opts.draft, "draft", false, "Create PRs as drafts") + cmd.Flags().BoolVar(&opts.open, "open", false, "Mark new and existing PRs as ready for review") cmd.Flags().StringVar(&opts.remote, "remote", "", "Remote to push to (defaults to auto-detected remote)") return cmd @@ -235,6 +235,17 @@ func ensurePR(cfg *config.Config, client github.ClientOps, s *stack.Stack, i int cfg.Printf("PR %s for %s is up to date", cfg.PRLink(pr.Number, pr.URL), b.Branch) } + // Convert draft PR to ready for review when --open is set. + if opts.open && pr.IsDraft { + if err := client.MarkPRReadyForReview(pr.ID); err != nil { + cfg.Warningf("failed to mark PR %s as ready for review: %v", + cfg.PRLink(pr.Number, pr.URL), err) + } else { + cfg.Successf("Marked PR %s as ready for review", + cfg.PRLink(pr.Number, pr.URL)) + } + } + return nil } @@ -263,7 +274,7 @@ func createPR(cfg *config.Config, client github.ClientOps, s *stack.Stack, i int } body := generatePRBody(prBody) - newPR, createErr := client.CreatePR(baseBranch, b.Branch, title, body, opts.draft) + newPR, createErr := client.CreatePR(baseBranch, b.Branch, title, body, !opts.open) if createErr != nil { cfg.Warningf("failed to create PR for %s: %v", b.Branch, createErr) return nil diff --git a/cmd/submit_test.go b/cmd/submit_test.go index 2ffe5d3..c4ded3d 100644 --- a/cmd/submit_test.go +++ b/cmd/submit_test.go @@ -137,6 +137,152 @@ func TestSubmit_CreatesPRsAndStack(t *testing.T) { assert.Contains(t, output, "Pushed and synced 2 branches") } +func TestSubmit_DefaultDraft(t *testing.T) { + s := stack.Stack{ + Trunk: stack.BranchRef{Branch: "main"}, + Branches: []stack.BranchRef{ + {Branch: "b1"}, + }, + } + + tmpDir := t.TempDir() + writeStackFile(t, tmpDir, s) + + var createdDraft bool + + mock := newSubmitMock(tmpDir, "b1") + mock.PushFn = func(string, []string, bool, bool) error { return nil } + mock.LogRangeFn = func(base, head string) ([]git.CommitInfo, error) { + return []git.CommitInfo{{Subject: "commit for " + head}}, nil + } + restore := git.SetOps(mock) + defer restore() + + cfg, _, _ := config.NewTestConfig() + cfg.GitHubClientOverride = &github.MockClient{ + ListStacksFn: func() ([]github.RemoteStack, error) { return nil, nil }, + FindPRForBranchFn: func(string) (*github.PullRequest, error) { return nil, nil }, + CreatePRFn: func(base, head, title, body string, draft bool) (*github.PullRequest, error) { + createdDraft = draft + return &github.PullRequest{Number: 1, ID: "PR_1", URL: "https://github.com/o/r/pull/1"}, nil + }, + CreateStackFn: func([]int) (int, error) { return 1, nil }, + } + + cmd := SubmitCmd(cfg) + cmd.SetArgs([]string{"--auto"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + err := cmd.Execute() + + assert.NoError(t, err) + assert.True(t, createdDraft, "PRs should be created as drafts by default") +} + +func TestSubmit_OpenFlag(t *testing.T) { + s := stack.Stack{ + Trunk: stack.BranchRef{Branch: "main"}, + Branches: []stack.BranchRef{ + {Branch: "b1"}, + }, + } + + tmpDir := t.TempDir() + writeStackFile(t, tmpDir, s) + + var createdDraft bool + + mock := newSubmitMock(tmpDir, "b1") + mock.PushFn = func(string, []string, bool, bool) error { return nil } + mock.LogRangeFn = func(base, head string) ([]git.CommitInfo, error) { + return []git.CommitInfo{{Subject: "commit for " + head}}, nil + } + restore := git.SetOps(mock) + defer restore() + + cfg, _, _ := config.NewTestConfig() + cfg.GitHubClientOverride = &github.MockClient{ + ListStacksFn: func() ([]github.RemoteStack, error) { return nil, nil }, + FindPRForBranchFn: func(string) (*github.PullRequest, error) { return nil, nil }, + CreatePRFn: func(base, head, title, body string, draft bool) (*github.PullRequest, error) { + createdDraft = draft + return &github.PullRequest{Number: 1, ID: "PR_1", URL: "https://github.com/o/r/pull/1"}, nil + }, + CreateStackFn: func([]int) (int, error) { return 1, nil }, + } + + cmd := SubmitCmd(cfg) + cmd.SetArgs([]string{"--auto", "--open"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + err := cmd.Execute() + + assert.NoError(t, err) + assert.False(t, createdDraft, "PRs should not be created as drafts when --open is set") +} + +func TestSubmit_OpenFlag_ConvertsDraftPRs(t *testing.T) { + s := stack.Stack{ + Trunk: stack.BranchRef{Branch: "main"}, + Branches: []stack.BranchRef{ + {Branch: "b1", PullRequest: &stack.PullRequestRef{Number: 10, ID: "PR_10"}}, + {Branch: "b2"}, + }, + } + + tmpDir := t.TempDir() + writeStackFile(t, tmpDir, s) + + var markedReady []string + + mock := newSubmitMock(tmpDir, "b1") + mock.PushFn = func(string, []string, bool, bool) error { return nil } + mock.LogRangeFn = func(base, head string) ([]git.CommitInfo, error) { + return []git.CommitInfo{{Subject: "commit for " + head}}, nil + } + restore := git.SetOps(mock) + defer restore() + + cfg, _, errR := config.NewTestConfig() + cfg.GitHubClientOverride = &github.MockClient{ + ListStacksFn: func() ([]github.RemoteStack, error) { return nil, nil }, + FindPRForBranchFn: func(branch string) (*github.PullRequest, error) { + switch branch { + case "b1": + return &github.PullRequest{ + Number: 10, ID: "PR_10", HeadRefName: "b1", BaseRefName: "main", + IsDraft: true, URL: "https://github.com/o/r/pull/10", + }, nil + } + return nil, nil + }, + CreatePRFn: func(base, head, title, body string, draft bool) (*github.PullRequest, error) { + return &github.PullRequest{ + Number: 11, ID: "PR_11", URL: "https://github.com/o/r/pull/11", + }, nil + }, + MarkPRReadyForReviewFn: func(prID string) error { + markedReady = append(markedReady, prID) + return nil + }, + CreateStackFn: func([]int) (int, error) { return 1, nil }, + } + + cmd := SubmitCmd(cfg) + cmd.SetArgs([]string{"--auto", "--open"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + err := cmd.Execute() + + cfg.Err.Close() + errOut, _ := io.ReadAll(errR) + output := string(errOut) + + assert.NoError(t, err) + assert.Equal(t, []string{"PR_10"}, markedReady, "existing draft PR should be marked ready") + assert.Contains(t, output, "Marked PR") +} + func TestSubmit_PushFailure(t *testing.T) { s := stack.Stack{ Trunk: stack.BranchRef{Branch: "main"}, diff --git a/docs/src/content/docs/faq.md b/docs/src/content/docs/faq.md index d8c60cf..d3bf22a 100644 --- a/docs/src/content/docs/faq.md +++ b/docs/src/content/docs/faq.md @@ -245,7 +245,7 @@ gh stack link change1 change2 change3 This doesn't create any local tracking and only hits the APIs to create Stacked PRs. -If the provided branches already have open PRs, `link` will use them. If not, it creates PRs with the correct base branch chaining. +If the provided branches already have open PRs, `link` will use them. If not, it creates draft PRs with the correct base branch chaining. To add more to the stack, run `link` again, but be sure to include the full list of PRs/branches in the stack: @@ -253,10 +253,10 @@ To add more to the stack, run `link` again, but be sure to include the full list gh stack link 123 124 125 change4 change5 ``` -You can also use `--base` to specify a different trunk branch and `--draft` to create PRs as drafts: +You can also use `--base` to specify a different trunk branch and `--open` to mark PRs as ready for review: ```bash -gh stack link --base develop --draft change1 change2 change3 +gh stack link --base develop --open change1 change2 change3 ``` Alternatively, if you want full local stack tracking (for commands like `rebase`, `sync`, and navigation), you can adopt existing branches to local tracking with `gh stack`: diff --git a/docs/src/content/docs/reference/cli.md b/docs/src/content/docs/reference/cli.md index e346def..93c11e9 100644 --- a/docs/src/content/docs/reference/cli.md +++ b/docs/src/content/docs/reference/cli.md @@ -238,7 +238,7 @@ When creating new PRs, you will be prompted to enter a title for each one. Press | Flag | Description | |------|-------------| | `--auto` | Use auto-generated PR titles without prompting | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (defaults to auto-detected remote) | **Examples:** @@ -246,7 +246,7 @@ When creating new PRs, you will be prompted to enter a title for each one. Press ```sh gh stack submit gh stack submit --auto -gh stack submit --draft +gh stack submit --open ``` ### `gh stack sync` @@ -386,7 +386,7 @@ If the PRs are not yet in a stack, a new stack is created. If some of the PRs ar | Flag | Description | |------|-------------| | `--base ` | Base branch for the bottom of the stack (default: `main`) | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (defaults to auto-detected remote) | **Examples:** @@ -401,8 +401,8 @@ gh stack link 10 20 30 # Add branches to an existing stack of PRs gh stack link 42 43 feature-auth feature-ui -# Use a different base branch and create PRs as drafts -gh stack link --base develop --draft feat-a feat-b feat-c +# Use a different base branch and mark PRs as ready for review +gh stack link --base develop --open feat-a feat-b feat-c ``` --- diff --git a/internal/github/client_interface.go b/internal/github/client_interface.go index 8b8ce37..153ae3d 100644 --- a/internal/github/client_interface.go +++ b/internal/github/client_interface.go @@ -10,6 +10,7 @@ type ClientOps interface { FindPRDetailsForBranch(branch string) (*PRDetails, error) CreatePR(base, head, title, body string, draft bool) (*PullRequest, error) UpdatePRBase(number int, base string) error + MarkPRReadyForReview(prID string) error ListStacks() ([]RemoteStack, error) CreateStack(prNumbers []int) (int, error) UpdateStack(stackID string, prNumbers []int) error diff --git a/internal/github/github.go b/internal/github/github.go index 21b3b70..efb89d5 100644 --- a/internal/github/github.go +++ b/internal/github/github.go @@ -234,6 +234,33 @@ func (c *Client) UpdatePRBase(number int, base string) error { return c.rest.Patch(path, bytes.NewReader(body), nil) } +// MarkPRReadyForReview converts a draft pull request to ready for review. +func (c *Client) MarkPRReadyForReview(prID string) error { + var mutation struct { + MarkPullRequestReadyForReview struct { + PullRequest struct { + ID string + } + } `graphql:"markPullRequestReadyForReview(input: $input)"` + } + + type MarkPullRequestReadyForReviewInput struct { + PullRequestID string `json:"pullRequestId"` + } + + variables := map[string]interface{}{ + "input": MarkPullRequestReadyForReviewInput{ + PullRequestID: prID, + }, + } + + if err := c.gql.Mutate("MarkPullRequestReadyForReview", &mutation, variables); err != nil { + return fmt.Errorf("marking PR ready for review: %w", err) + } + + return nil +} + func (c *Client) repositoryID() (string, error) { var query struct { Repository struct { diff --git a/internal/github/mock_client.go b/internal/github/mock_client.go index eadab01..7f21125 100644 --- a/internal/github/mock_client.go +++ b/internal/github/mock_client.go @@ -10,6 +10,7 @@ type MockClient struct { FindPRDetailsForBranchFn func(string) (*PRDetails, error) CreatePRFn func(string, string, string, string, bool) (*PullRequest, error) UpdatePRBaseFn func(int, string) error + MarkPRReadyForReviewFn func(string) error ListStacksFn func() ([]RemoteStack, error) CreateStackFn func([]int) (int, error) UpdateStackFn func(string, []int) error @@ -61,6 +62,13 @@ func (m *MockClient) UpdatePRBase(number int, base string) error { return nil } +func (m *MockClient) MarkPRReadyForReview(prID string) error { + if m.MarkPRReadyForReviewFn != nil { + return m.MarkPRReadyForReviewFn(prID) + } + return nil +} + func (m *MockClient) ListStacks() ([]RemoteStack, error) { if m.ListStacksFn != nil { return m.ListStacksFn() diff --git a/skills/gh-stack/SKILL.md b/skills/gh-stack/SKILL.md index 7ec397a..18d703a 100644 --- a/skills/gh-stack/SKILL.md +++ b/skills/gh-stack/SKILL.md @@ -147,8 +147,8 @@ Small, incidental fixes (e.g., fixing a typo you noticed) can go in the current | Add branch + stage all + commit | `gh stack add -Am "message" api-routes` | | Push branches to remote | `gh stack push` | | Push to specific remote | `gh stack push --remote origin` | -| Push branches + create PRs | `gh stack submit --auto` | -| Create PRs as drafts | `gh stack submit --auto --draft` | +| Push branches + create draft PRs | `gh stack submit --auto` | +| Create PRs as ready for review | `gh stack submit --auto --open` | | Sync (fetch, rebase, push) | `gh stack sync` | | Sync with specific remote | `gh stack sync --remote origin` | | Rebase entire stack | `gh stack rebase` | @@ -231,8 +231,8 @@ git commit -m "Add frontend dashboard" # ── Stack complete: feat/auth → feat/api-routes → feat/frontend ── -# 7. Push everything and create draft PRs -gh stack submit --auto --draft +# 7. Push everything and create PRs (drafts by default) +gh stack submit --auto # 8. Verify the stack gh stack view --json @@ -525,14 +525,14 @@ Push all stack branches and create PRs on GitHub. **Always pass `--auto`** — w # Submit and auto-title new PRs (required for non-interactive use) gh stack submit --auto -# Submit and create PRs as drafts -gh stack submit --auto --draft +# Submit and create PRs as ready for review (not drafts) +gh stack submit --auto --open ``` | Flag | Description | |------|-------------| | `--auto` | Auto-generate PR titles without prompting (**required** for non-interactive use) | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (use if multiple remotes exist) | **Behavior:** @@ -568,8 +568,8 @@ gh stack link [flags] [...] # Link branches into a stack (pushes, creates PRs, creates stack) gh stack link branch-a branch-b branch-c -# Use a different base branch and create PRs as drafts -gh stack link --base develop --draft branch-a branch-b branch-c +# Use a different base branch and mark PRs as ready for review +gh stack link --base develop --open branch-a branch-b branch-c # Link existing PRs by number gh stack link 10 20 30 @@ -581,7 +581,7 @@ gh stack link 42 43 feature-auth feature-ui | Flag | Description | |------|---------| | `--base ` | Base branch for the bottom of the stack (default: `main`) | -| `--draft` | Create new PRs as drafts | +| `--open` | Mark new and existing PRs as ready for review | | `--remote ` | Remote to push to (use if multiple remotes exist) | **Behavior:** From 2b824027296596ed49b2672b2a16a91fc2db03ef Mon Sep 17 00:00:00 2001 From: Sameen Karim Date: Tue, 5 May 2026 15:17:13 -0400 Subject: [PATCH 2/2] apply suggested docs updates from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/src/content/docs/faq.md | 2 +- docs/src/content/docs/reference/cli.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/content/docs/faq.md b/docs/src/content/docs/faq.md index d3bf22a..ab50f28 100644 --- a/docs/src/content/docs/faq.md +++ b/docs/src/content/docs/faq.md @@ -245,7 +245,7 @@ gh stack link change1 change2 change3 This doesn't create any local tracking and only hits the APIs to create Stacked PRs. -If the provided branches already have open PRs, `link` will use them. If not, it creates draft PRs with the correct base branch chaining. +If the provided branches already have open PRs, `link` will use them. If not, it creates draft PRs by default with the correct base branch chaining. To add more to the stack, run `link` again, but be sure to include the full list of PRs/branches in the stack: diff --git a/docs/src/content/docs/reference/cli.md b/docs/src/content/docs/reference/cli.md index 93c11e9..a23143a 100644 --- a/docs/src/content/docs/reference/cli.md +++ b/docs/src/content/docs/reference/cli.md @@ -233,12 +233,12 @@ gh stack submit [flags] Creates a Stacked PR for every branch in the stack, pushing branches to the remote. After creating PRs, `submit` automatically creates a **Stack** on GitHub to link the PRs together. If the stack already exists on GitHub (e.g., from a previous submit), new PRs are added to the existing stack. -When creating new PRs, you will be prompted to enter a title for each one. Press Enter to accept the default (branch name), or use `--auto` to skip prompting entirely. +When creating new PRs, you will be prompted to enter a title for each one. Press Enter to accept the default (branch name), or use `--auto` to skip prompting entirely. New PRs are created as **drafts by default**; use `--open` to create new PRs as ready for review and to mark existing PRs as ready for review. | Flag | Description | |------|-------------| | `--auto` | Use auto-generated PR titles without prompting | -| `--open` | Mark new and existing PRs as ready for review | +| `--open` | Create new PRs as ready for review instead of drafts, and mark existing PRs as ready for review | | `--remote ` | Remote to push to (defaults to auto-detected remote) | **Examples:**