Repository: cli/cli

Issue:
"gh codespace ssh" times out waiting for the target Codespace instance to start
### Describe the bug

When an existing Codespace instance is stopped, running `gh codespace ssh`
starts the Codespace instance before attempting to connect. However, the command
times out with an error before connecting.

gh version:

```
$ gh version
gh version 2.11.3 (2022-05-25)
https://github.com/cli/cli/releases/tag/v2.11.3
```

### Steps to reproduce the behavior

1. Run `gh codespace ssh` on a stopped Codespace instance
2. Wait for the Codespace to start
3. Observe timeout error message

### Expected vs actual behavior

Expected behavior: `gh codespace ssh` waits for the Codespace instance to start,
and then successfully connects to it

Actual behavior: `gh codespace ssh` times out before Codespace instance finishes
starting

### Logs

```
$ gh codespace ssh -- -vvv
? Choose codespace: redacted/redacted: master
Starting codespace ⣻failed to connect to Live Share: timed out while waiting for the codespace to start
```


Discussion:
@mislav: Thank you for the report!

/cc @cli/codespaces This happens to me as well with codespaces that got suspended since the last time I've used them.@ctaylor4874: Following this as I'm also experiencing the same issue@nikhilmaddirala: Hi, i'm getting this same error every time I try to run `gh codespace ssh`

> `error connecting to codespace: timed out while waiting for the codespace to start`


Solution patch:
diff --git a/api/client.go b/api/client.go
index e4cd392d4..3a2aa83ca 100644
--- a/api/client.go
+++ b/api/client.go
@@ -299,6 +299,7 @@ func (c Client) GraphQL(hostname string, query string, variables map[string]inte
 	}
 
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
+	req.Header.Set("GraphQL-Features", "merge_queue")
 
 	resp, err := c.http.Do(req)
 	if err != nil {
diff --git a/api/queries_pr.go b/api/queries_pr.go
index 6cda72919..4cbc1ffd1 100644
--- a/api/queries_pr.go
+++ b/api/queries_pr.go
@@ -17,24 +17,26 @@ type PullRequestAndTotalCount struct {
 }
 
 type PullRequest struct {
-	ID               string
-	Number           int
-	Title            string
-	State            string
-	Closed           bool
-	URL              string
-	BaseRefName      string
-	HeadRefName      string
-	Body             string
-	Mergeable        string
-	Additions        int
-	Deletions        int
-	ChangedFiles     int
-	MergeStateStatus string
-	CreatedAt        time.Time
-	UpdatedAt        time.Time
-	ClosedAt         *time.Time
-	MergedAt         *time.Time
+	ID                  string
+	Number              int
+	Title               string
+	State               string
+	Closed              bool
+	URL                 string
+	BaseRefName         string
+	HeadRefName         string
+	Body                string
+	Mergeable           string
+	Additions           int
+	Deletions           int
+	ChangedFiles        int
+	MergeStateStatus    string
+	IsInMergeQueue      bool
+	IsMergeQueueEnabled bool // Indicates whether the pull request's base ref has a merge queue enabled.
+	CreatedAt           time.Time
+	UpdatedAt           time.Time
+	ClosedAt            *time.Time
+	MergedAt            *time.Time
 
 	MergeCommit          *Commit
 	PotentialMergeCommit *Commit
diff --git a/go.mod b/go.mod
index a129b9406..f623df767 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/cli/cli/v2
 go 1.18
 
 require (
-	github.com/AlecAivazis/survey/v2 v2.3.4
+	github.com/AlecAivazis/survey/v2 v2.3.5
 	github.com/MakeNowJust/heredoc v1.0.0
 	github.com/briandowns/spinner v1.18.1
 	github.com/charmbracelet/glamour v0.4.0
@@ -35,7 +35,7 @@ require (
 	github.com/sourcegraph/jsonrpc2 v0.1.0
 	github.com/spf13/cobra v1.4.0
 	github.com/spf13/pflag v1.0.5
-	github.com/stretchr/testify v1.7.1
+	github.com/stretchr/testify v1.7.2
 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
diff --git a/go.sum b/go.sum
index db0c0d15f..481880c76 100644
--- a/go.sum
+++ b/go.sum
@@ -31,8 +31,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/AlecAivazis/survey/v2 v2.3.4 h1:pchTU9rsLUSvWEl2Aq9Pv3k0IE2fkqtGxazskAMd9Ng=
-github.com/AlecAivazis/survey/v2 v2.3.4/go.mod h1:hrV6Y/kQCLhIZXGcriDCUBtB3wnN7156gMXJ3+b23xM=
+github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
+github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -230,8 +230,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -357,6 +357,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/internal/codespaces/codespaces.go b/internal/codespaces/codespaces.go
index 330a6a772..6e65b6449 100644
--- a/internal/codespaces/codespaces.go
+++ b/internal/codespaces/codespaces.go
@@ -38,6 +38,7 @@ type logger interface {
 func ConnectToLiveshare(ctx context.Context, progress progressIndicator, sessionLogger logger, apiClient apiClient, codespace *api.Codespace) (sess *liveshare.Session, err error) {
 	if codespace.State != api.CodespaceStateAvailable {
 		progress.StartProgressIndicatorWithLabel("Starting codespace")
+		defer progress.StopProgressIndicator()
 		if err := apiClient.StartCodespace(ctx, codespace.Name); err != nil {
 			return nil, fmt.Errorf("error starting codespace: %w", err)
 		}
diff --git a/internal/featuredetection/feature_detection.go b/internal/featuredetection/feature_detection.go
index ee96656f1..45800a8d7 100644
--- a/internal/featuredetection/feature_detection.go
+++ b/internal/featuredetection/feature_detection.go
@@ -24,24 +24,30 @@ type PullRequestFeatures struct {
 	ReviewDecision       bool
 	StatusCheckRollup    bool
 	BranchProtectionRule bool
+	MergeQueue           bool
 }
 
 var allPullRequestFeatures = PullRequestFeatures{
 	ReviewDecision:       true,
 	StatusCheckRollup:    true,
 	BranchProtectionRule: true,
+	MergeQueue:           true,
 }
 
 type RepositoryFeatures struct {
 	IssueTemplateMutation    bool
 	IssueTemplateQuery       bool
 	PullRequestTemplateQuery bool
+	VisibilityField          bool
+	AutoMerge                bool
 }
 
 var allRepositoryFeatures = RepositoryFeatures{
 	IssueTemplateMutation:    true,
 	IssueTemplateQuery:       true,
 	PullRequestTemplateQuery: true,
+	VisibilityField:          true,
+	AutoMerge:                true,
 }
 
 type detector struct {
@@ -70,7 +76,34 @@ func (d *detector) PullRequestFeatures() (PullRequestFeatures, error) {
 		return allPullRequestFeatures, nil
 	}
 
-	return allPullRequestFeatures, nil
+	features := PullRequestFeatures{
+		ReviewDecision:       true,
+		StatusCheckRollup:    true,
+		BranchProtectionRule: true,
+	}
+
+	var featureDetection struct {
+		PullRequest struct {
+			Fields []struct {
+				Name string
+			} `graphql:"fields(includeDeprecated: true)"`
+		} `graphql:"PullRequest: __type(name: \"PullRequest\")"`
+	}
+
+	gql := graphql.NewClient(ghinstance.GraphQLEndpoint(d.host), d.httpClient)
+
+	err := gql.QueryNamed(context.Background(), "PullRequest_fields", &featureDetection, nil)
+	if err != nil {
+		return features, err
+	}
+
+	for _, field := range featureDetection.PullRequest.Fields {
+		if field.Name == "isInMergeQueue" {
+			features.MergeQueue = true
+		}
+	}
+
+	return features, nil
 }
 
 func (d *detector) RepositoryFeatures() (RepositoryFeatures, error) {
@@ -102,6 +135,12 @@ func (d *detector) RepositoryFeatures() (RepositoryFeatures, error) {
 		if field.Name == "pullRequestTemplates" {
 			features.PullRequestTemplateQuery = true
 		}
+		if field.Name == "visibility" {
+			features.VisibilityField = true
+		}
+		if field.Name == "autoMergeAllowed" {
+			features.AutoMerge = true
+		}
 	}
 
 	return features, nil
diff --git a/pkg/cmd/auth/logout/logout.go b/pkg/cmd/auth/logout/logout.go
index 60b1ce6ef..04cb2a087 100644
--- a/pkg/cmd/auth/logout/logout.go
+++ b/pkg/cmd/auth/logout/logout.go
@@ -19,8 +19,7 @@ type LogoutOptions struct {
 	HttpClient func() (*http.Client, error)
 	IO         *iostreams.IOStreams
 	Config     func() (config.Config, error)
-
-	Hostname string
+	Hostname   string
 }
 
 func NewCmdLogout(f *cmdutil.Factory, runF func(*LogoutOptions) error) *cobra.Command {
@@ -50,7 +49,6 @@ func NewCmdLogout(f *cmdutil.Factory, runF func(*LogoutOptions) error) *cobra.Co
 			if opts.Hostname == "" && !opts.IO.CanPrompt() {
 				return cmdutil.FlagErrorf("--hostname required when not running interactively")
 			}
-
 			if runF != nil {
 				return runF(opts)
 			}
@@ -135,21 +133,6 @@ func logoutRun(opts *LogoutOptions) error {
 		usernameStr = fmt.Sprintf(" account '%s'", username)
 	}
 
-	if opts.IO.CanPrompt() {
-		var keepGoing bool
-		err := prompt.SurveyAskOne(&survey.Confirm{
-			Message: fmt.Sprintf("Are you sure you want to log out of %s%s?", hostname, usernameStr),
-			Default: true,
-		}, &keepGoing)
-		if err != nil {
-			return fmt.Errorf("could not prompt: %w", err)
-		}
-
-		if !keepGoing {
-			return nil
-		}
-	}
-
 	cfg.UnsetHost(hostname)
 	err = cfg.WriteHosts()
 	if err != nil {
diff --git a/pkg/cmd/codespace/common.go b/pkg/cmd/codespace/common.go
index 5e5bc7f64..402fcd7ea 100644
--- a/pkg/cmd/codespace/common.go
+++ b/pkg/cmd/codespace/common.go
@@ -19,6 +19,7 @@ import (
 	"github.com/cli/cli/v2/pkg/iostreams"
 	"github.com/cli/cli/v2/pkg/liveshare"
 	"github.com/spf13/cobra"
+	"golang.org/x/crypto/ssh"
 	"golang.org/x/term"
 )
 
@@ -60,8 +61,18 @@ func (a *App) StopProgressIndicator() {
 	a.io.StopProgressIndicator()
 }
 
+type liveshareSession interface {
+	Close() error
+	GetSharedServers(context.Context) ([]*liveshare.Port, error)
+	KeepAlive(string)
+	OpenStreamingChannel(context.Context, liveshare.ChannelID) (ssh.Channel, error)
+	StartJupyterServer(context.Context) (int, string, error)
+	StartSharing(context.Context, string, int) (liveshare.ChannelID, error)
+	StartSSHServer(context.Context) (int, string, error)
+}
+
 // Connects to a codespace using Live Share and returns that session
-func startLiveShareSession(ctx context.Context, codespace *api.Codespace, a *App, debug bool, debugFile string) (session *liveshare.Session, err error) {
+func startLiveShareSession(ctx context.Context, codespace *api.Codespace, a *App, debug bool, debugFile string) (session liveshareSession, err error) {
 	// While connecting, ensure in the background that the user has keys installed.
 	// That lets us report a more useful error message if they don't.
 	authkeys := make(chan error, 1)
diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go
index 1c65046d5..f5691a563 100644
--- a/pkg/cmd/pr/create/create.go
+++ b/pkg/cmd/pr/create/create.go
@@ -588,9 +588,6 @@ func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
 			return nil, cmdutil.CancelError
 		} else {
 			// "Create a fork of ..."
-			if baseRepo.IsPrivate {
-				return nil, fmt.Errorf("cannot fork private repository %s", ghrepo.FullName(baseRepo))
-			}
 			headBranchLabel = fmt.Sprintf("%s:%s", currentLogin, headBranch)
 		}
 	}
diff --git a/pkg/cmd/pr/merge/merge.go b/pkg/cmd/pr/merge/merge.go
index 960fd426e..34360e2b1 100644
--- a/pkg/cmd/pr/merge/merge.go
+++ b/pkg/cmd/pr/merge/merge.go
@@ -47,9 +47,12 @@ type MergeOptions struct {
 	UseAdmin                bool
 	IsDeleteBranchIndicated bool
 	CanDeleteLocalBranch    bool
-	InteractiveMode         bool
+	MergeStrategyEmpty      bool
 }
 
+// ErrAlreadyInMergeQueue indicates that the pull request is already in a merge queue
+var ErrAlreadyInMergeQueue = errors.New("already in merge queue")
+
 func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Command {
 	opts := &MergeOptions{
 		IO:         f.IOStreams,
@@ -74,6 +77,11 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
 
 			Without an argument, the pull request that belongs to the current branch
 			is selected.
+
+			When targeting a branch that requires a merge queue, no merge strategy is required.
+			If required checks have not yet passed, AutoMerge will be enabled.
+			If required checks have passed, the pull request will be added to the merge queue.
+			To bypass a merge queue and merge directly, pass the '--admin' flag.
     	`),
 		Args: cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
@@ -101,10 +109,7 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
 				methodFlags++
 			}
 			if methodFlags == 0 {
-				if !opts.IO.CanPrompt() {
-					return cmdutil.FlagErrorf("--merge, --rebase, or --squash required when not running interactively")
-				}
-				opts.InteractiveMode = true
+				opts.MergeStrategyEmpty = true
 			} else if methodFlags > 1 {
 				return cmdutil.FlagErrorf("only one of --merge, --rebase, or --squash can be enabled")
 			}
@@ -151,7 +156,12 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
 			if runF != nil {
 				return runF(opts)
 			}
-			return mergeRun(opts)
+
+			err := mergeRun(opts)
+			if errors.Is(err, ErrAlreadyInMergeQueue) {
+				return nil
+			}
+			return err
 		},
 	}
 
@@ -170,18 +180,19 @@ func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Comm
 
 // mergeContext contains state and dependencies to merge a pull request.
 type mergeContext struct {
-	pr                *api.PullRequest
-	baseRepo          ghrepo.Interface
-	httpClient        *http.Client
-	opts              *MergeOptions
-	cs                *iostreams.ColorScheme
-	isTerminal        bool
-	merged            bool
-	localBranchExists bool
-	autoMerge         bool
-	crossRepoPR       bool
-	deleteBranch      bool
-	switchedToBranch  string
+	pr                 *api.PullRequest
+	baseRepo           ghrepo.Interface
+	httpClient         *http.Client
+	opts               *MergeOptions
+	cs                 *iostreams.ColorScheme
+	isTerminal         bool
+	merged             bool
+	localBranchExists  bool
+	autoMerge          bool
+	crossRepoPR        bool
+	deleteBranch       bool
+	switchedToBranch   string
+	mergeQueueRequired bool
 }
 
 // Attempt to disable auto merge on the pull request.
@@ -192,6 +203,16 @@ func (m *mergeContext) disableAutoMerge() error {
 	return m.infof("%s Auto-merge disabled for pull request #%d\n", m.cs.SuccessIconWithColor(m.cs.Green), m.pr.Number)
 }
 
+// Check if this pull request is in a merge queue
+func (m *mergeContext) inMergeQueue() error {
+	// if the pull request is in a merge queue no further action is possible
+	if m.pr.IsInMergeQueue {
+		_ = m.warnf("%s Pull request #%d is already queued to merge\n", m.cs.WarningIcon(), m.pr.Number)
+		return ErrAlreadyInMergeQueue
+	}
+	return nil
+}
+
 // Warn if the pull request and the remote branch have diverged.
 func (m *mergeContext) warnIfDiverged() {
 	if m.opts.SelectorArg != "" || len(m.pr.Commits.Nodes) == 0 {
@@ -212,6 +233,11 @@ func (m *mergeContext) warnIfDiverged() {
 
 // Check if the current state of the pull request allows for merging
 func (m *mergeContext) canMerge() error {
+	if m.mergeQueueRequired {
+		// a pull request can always be added to the merge queue
+		return nil
+	}
+
 	reason := blockedReason(m.pr.MergeStateStatus, m.opts.UseAdmin)
 
 	if reason == "" || m.autoMerge || m.merged {
@@ -253,37 +279,50 @@ func (m *mergeContext) merge() error {
 		setCommitBody: m.opts.BodySet,
 	}
 
-	// get user input if not already given
-	if m.opts.InteractiveMode {
-		apiClient := api.NewClientFromHTTP(m.httpClient)
-		r, err := api.GitHubRepo(apiClient, m.baseRepo)
-		if err != nil {
-			return err
-		}
-
-		payload.method, err = mergeMethodSurvey(r)
-		if err != nil {
-			return err
+	if m.shouldAddToMergeQueue() {
+		if !m.opts.MergeStrategyEmpty {
+			// only warn for now
+			_ = m.warnf("%s The merge strategy for %s is set by the merge queue\n", m.cs.Yellow("!"), m.pr.BaseRefName)
 		}
+		// auto merge will either enable auto merge or add to the merge queue
+		payload.auto = true
+	} else {
+		// get user input if not already given
+		if m.opts.MergeStrategyEmpty {
+			if !m.opts.IO.CanPrompt() {
+				return cmdutil.FlagErrorf("--merge, --rebase, or --squash required when not running interactively")
+			}
 
-		m.deleteBranch, err = deleteBranchSurvey(m.opts, m.crossRepoPR, m.localBranchExists)
-		if err != nil {
-			return err
-		}
+			apiClient := api.NewClientFromHTTP(m.httpClient)
+			r, err := api.GitHubRepo(apiClient, m.baseRepo)
+			if err != nil {
+				return err
+			}
 
-		allowEditMsg := payload.method != PullRequestMergeMethodRebase
-		for {
-			action, err := confirmSurvey(allowEditMsg)
+			payload.method, err = mergeMethodSurvey(r)
 			if err != nil {
-				return fmt.Errorf("unable to confirm: %w", err)
+				return err
 			}
 
-			submit, err := confirmSubmission(m.httpClient, m.opts, action, &payload)
+			m.deleteBranch, err = deleteBranchSurvey(m.opts, m.crossRepoPR, m.localBranchExists)
 			if err != nil {
 				return err
 			}
-			if submit {
-				break
+
+			allowEditMsg := payload.method != PullRequestMergeMethodRebase
+			for {
+				action, err := confirmSurvey(allowEditMsg)
+				if err != nil {
+					return fmt.Errorf("unable to confirm: %w", err)
+				}
+
+				submit, err := confirmSubmission(m.httpClient, m.opts, action, &payload)
+				if err != nil {
+					return err
+				}
+				if submit {
+					break
+				}
 			}
 		}
 	}
@@ -293,6 +332,11 @@ func (m *mergeContext) merge() error {
 		return err
 	}
 
+	if m.shouldAddToMergeQueue() {
+		_ = m.infof("%s Pull request #%d will be added to the merge queue for %s when ready\n", m.cs.SuccessIconWithColor(m.cs.Green), m.pr.Number, m.pr.BaseRefName)
+		return nil
+	}
+
 	if payload.auto {
 		method := ""
 		switch payload.method {
@@ -322,7 +366,7 @@ func (m *mergeContext) deleteLocalBranch() error {
 
 	if m.merged {
 		// prompt for delete
-		if m.opts.InteractiveMode && !m.opts.IsDeleteBranchIndicated {
+		if m.opts.IO.CanPrompt() && !m.opts.IsDeleteBranchIndicated {
 			err := prompt.SurveyAskOne(&survey.Confirm{
 				Message: fmt.Sprintf("Pull request #%d was already merged. Delete the branch locally?", m.pr.Number),
 				Default: false,
@@ -346,17 +390,6 @@ func (m *mergeContext) deleteLocalBranch() error {
 
 	// branch the command was run on is the same as the pull request branch
 	if currentBranch == m.pr.HeadRefName {
-		// if the target branch of the PR is not known, set the current branch to the
-		// default branch of the repository
-		targetBranch := m.pr.BaseRefName
-		if targetBranch == "" {
-			apiClient := api.NewClientFromHTTP(m.httpClient)
-			targetBranch, err = api.RepoDefaultBranch(apiClient, m.baseRepo)
-			if err != nil {
-				return err
-			}
-		}
-
 		remotes, err := m.opts.Remotes()
 		if err != nil {
 			return err
@@ -367,6 +400,7 @@ func (m *mergeContext) deleteLocalBranch() error {
 			return err
 		}
 
+		targetBranch := m.pr.BaseRefName
 		if git.HasLocalBranch(targetBranch) {
 			if err := git.CheckoutBranch(targetBranch); err != nil {
 				return err
@@ -415,6 +449,12 @@ func (m *mergeContext) deleteRemoteBranch() error {
 	return m.infof("%s Deleted branch %s%s\n", m.cs.SuccessIconWithColor(m.cs.Red), m.cs.Cyan(m.pr.HeadRefName), branch)
 }
 
+// Add the Pull Request to a merge queue
+// Admins can bypass the queue and merge directly
+func (m *mergeContext) shouldAddToMergeQueue() bool {
+	return m.mergeQueueRequired && !m.opts.UseAdmin
+}
+
 func (m *mergeContext) warnf(format string, args ...interface{}) error {
 	_, err := fmt.Fprintf(m.opts.IO.ErrOut, format, args...)
 	return err
@@ -432,7 +472,7 @@ func (m *mergeContext) infof(format string, args ...interface{}) error {
 func NewMergeContext(opts *MergeOptions) (*mergeContext, error) {
 	findOptions := shared.FindOptions{
 		Selector: opts.SelectorArg,
-		Fields:   []string{"id", "number", "state", "title", "lastCommit", "mergeStateStatus", "headRepositoryOwner", "headRefName"},
+		Fields:   []string{"id", "number", "state", "title", "lastCommit", "mergeStateStatus", "headRepositoryOwner", "headRefName", "baseRefName", "isInMergeQueue", "isMergeQueueEnabled"},
 	}
 	pr, baseRepo, err := opts.Finder.Find(findOptions)
 	if err != nil {
@@ -445,17 +485,18 @@ func NewMergeContext(opts *MergeOptions) (*mergeContext, error) {
 	}
 
 	return &mergeContext{
-		opts:              opts,
-		pr:                pr,
-		cs:                opts.IO.ColorScheme(),
-		baseRepo:          baseRepo,
-		isTerminal:        opts.IO.IsStdoutTTY(),
-		httpClient:        httpClient,
-		merged:            pr.State == MergeStateStatusMerged,
-		deleteBranch:      opts.DeleteBranch,
-		crossRepoPR:       pr.HeadRepositoryOwner.Login != baseRepo.RepoOwner(),
-		autoMerge:         opts.AutoMergeEnable && !isImmediatelyMergeable(pr.MergeStateStatus),
-		localBranchExists: opts.CanDeleteLocalBranch && git.HasLocalBranch(pr.HeadRefName),
+		opts:               opts,
+		pr:                 pr,
+		cs:                 opts.IO.ColorScheme(),
+		baseRepo:           baseRepo,
+		isTerminal:         opts.IO.IsStdoutTTY(),
+		httpClient:         httpClient,
+		merged:             pr.State == MergeStateStatusMerged,
+		deleteBranch:       opts.DeleteBranch,
+		crossRepoPR:        pr.HeadRepositoryOwner.Login != baseRepo.RepoOwner(),
+		autoMerge:          opts.AutoMergeEnable && !isImmediatelyMergeable(pr.MergeStateStatus),
+		localBranchExists:  opts.CanDeleteLocalBranch && git.HasLocalBranch(pr.HeadRefName),
+		mergeQueueRequired: pr.IsMergeQueueEnabled,
 	}, nil
 }
 
@@ -466,6 +507,10 @@ func mergeRun(opts *MergeOptions) error {
 		return err
 	}
 
+	if err := ctx.inMergeQueue(); err != nil {
+		return err
+	}
+
 	// no further action is possible when disabling auto merge
 	if opts.AutoMergeDisable {
 		return ctx.disableAutoMerge()
diff --git a/pkg/cmd/pr/shared/finder.go b/pkg/cmd/pr/shared/finder.go
index 79d001027..a3c6fa721 100644
--- a/pkg/cmd/pr/shared/finder.go
+++ b/pkg/cmd/pr/shared/finder.go
@@ -14,6 +14,7 @@ import (
 	"github.com/cli/cli/v2/api"
 	remotes "github.com/cli/cli/v2/context"
 	"github.com/cli/cli/v2/git"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghinstance"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/pkg/cmdutil"
@@ -139,6 +140,18 @@ func (f *finder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, err
 	numberFieldOnly := fields.Len() == 1 && fields.Contains("number")
 	fields.Add("id") // for additional preload queries below
 
+	if fields.Contains("isInMergeQueue") || fields.Contains("isMergeQueueEnabled") {
+		detector := fd.NewDetector(httpClient, f.repo.RepoHost())
+		prFeatures, err := detector.PullRequestFeatures()
+		if err != nil {
+			return nil, nil, err
+		}
+		if !prFeatures.MergeQueue {
+			fields.Remove("isInMergeQueue")
+			fields.Remove("isMergeQueueEnabled")
+		}
+	}
+
 	var pr *api.PullRequest
 	if f.prNumber > 0 {
 		if numberFieldOnly {
diff --git a/pkg/cmd/repo/edit/edit.go b/pkg/cmd/repo/edit/edit.go
index 953b69dbb..176de1b39 100644
--- a/pkg/cmd/repo/edit/edit.go
+++ b/pkg/cmd/repo/edit/edit.go
@@ -12,6 +12,7 @@ import (
 	"github.com/AlecAivazis/survey/v2"
 	"github.com/MakeNowJust/heredoc"
 	"github.com/cli/cli/v2/api"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghinstance"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/pkg/cmdutil"
@@ -48,6 +49,7 @@ type EditOptions struct {
 	AddTopics       []string
 	RemoveTopics    []string
 	InteractiveMode bool
+	Detector        fd.Detector
 	// Cache of current repo topics to avoid retrieving them
 	// in multiple flows.
 	topicsCache []string
@@ -158,9 +160,17 @@ func editRun(ctx context.Context, opts *EditOptions) error {
 	repo := opts.Repository
 
 	if opts.InteractiveMode {
+		detector := opts.Detector
+		if detector == nil {
+			detector = fd.NewDetector(opts.HTTPClient, repo.RepoHost())
+		}
+		repoFeatures, err := detector.RepositoryFeatures()
+		if err != nil {
+			return err
+		}
+
 		apiClient := api.NewClientFromHTTP(opts.HTTPClient)
 		fieldsToRetrieve := []string{
-			"autoMergeAllowed",
 			"defaultBranchRef",
 			"deleteBranchOnMerge",
 			"description",
@@ -174,8 +184,14 @@ func editRun(ctx context.Context, opts *EditOptions) error {
 			"rebaseMergeAllowed",
 			"repositoryTopics",
 			"squashMergeAllowed",
-			"visibility",
 		}
+		if repoFeatures.VisibilityField {
+			fieldsToRetrieve = append(fieldsToRetrieve, "visibility")
+		}
+		if repoFeatures.AutoMerge {
+			fieldsToRetrieve = append(fieldsToRetrieve, "autoMergeAllowed")
+		}
+
 		opts.IO.StartProgressIndicator()
 		fetchedRepo, err := api.FetchRepository(apiClient, opts.Repository, fieldsToRetrieve)
 		opts.IO.StopProgressIndicator()
diff --git a/pkg/cmd/repo/list/list.go b/pkg/cmd/repo/list/list.go
index 321d753e8..a526594bd 100644
--- a/pkg/cmd/repo/list/list.go
+++ b/pkg/cmd/repo/list/list.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/cli/cli/v2/api"
 	"github.com/cli/cli/v2/internal/config"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/cli/cli/v2/pkg/iostreams"
 	"github.com/cli/cli/v2/pkg/text"
@@ -21,6 +22,7 @@ type ListOptions struct {
 	Config     func() (config.Config, error)
 	IO         *iostreams.IOStreams
 	Exporter   cmdutil.Exporter
+	Detector   fd.Detector
 
 	Limit int
 	Owner string
@@ -104,7 +106,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
 	return cmd
 }
 
-var defaultFields = []string{"nameWithOwner", "description", "isPrivate", "isFork", "isArchived", "createdAt", "pushedAt", "visibility"}
+var defaultFields = []string{"nameWithOwner", "description", "isPrivate", "isFork", "isArchived", "createdAt", "pushedAt"}
 
 func listRun(opts *ListOptions) error {
 	httpClient, err := opts.HttpClient()
@@ -112,6 +114,29 @@ func listRun(opts *ListOptions) error {
 		return err
 	}
 
+	cfg, err := opts.Config()
+	if err != nil {
+		return err
+	}
+
+	host, err := cfg.DefaultHost()
+	if err != nil {
+		return err
+	}
+
+	if opts.Detector == nil {
+		opts.Detector = fd.NewDetector(httpClient, host)
+	}
+	features, err := opts.Detector.RepositoryFeatures()
+	if err != nil {
+		return err
+	}
+
+	fields := defaultFields
+	if features.VisibilityField {
+		fields = append(defaultFields, "visibility")
+	}
+
 	filter := FilterOptions{
 		Visibility:  opts.Visibility,
 		Fork:        opts.Fork,
@@ -120,22 +145,12 @@ func listRun(opts *ListOptions) error {
 		Topic:       opts.Topic,
 		Archived:    opts.Archived,
 		NonArchived: opts.NonArchived,
-		Fields:      defaultFields,
+		Fields:      fields,
 	}
 	if opts.Exporter != nil {
 		filter.Fields = opts.Exporter.Fields()
 	}
 
-	cfg, err := opts.Config()
-	if err != nil {
-		return err
-	}
-
-	host, err := cfg.DefaultHost()
-	if err != nil {
-		return err
-	}
-
 	listResult, err := listRepos(httpClient, host, opts.Limit, opts.Owner, filter)
 	if err != nil {
 		return err
@@ -158,7 +173,7 @@ func listRun(opts *ListOptions) error {
 		info := repoInfo(repo)
 		infoColor := cs.Gray
 
-		if repo.Visibility != "PUBLIC" {
+		if repo.IsPrivate {
 			infoColor = cs.Yellow
 		}
 
@@ -208,9 +223,7 @@ func listHeader(owner string, matchCount, totalMatchCount int, hasFilters bool)
 }
 
 func repoInfo(r api.Repository) string {
-	tags := []string{
-		strings.ToLower(r.Visibility),
-	}
+	tags := []string{visibilityLabel(r)}
 
 	if r.IsFork {
 		tags = append(tags, "fork")
@@ -221,3 +234,12 @@ func repoInfo(r api.Repository) string {
 
 	return strings.Join(tags, ", ")
 }
+
+func visibilityLabel(repo api.Repository) string {
+	if repo.Visibility != "" {
+		return strings.ToLower(repo.Visibility)
+	} else if repo.IsPrivate {
+		return "private"
+	}
+	return "public"
+}
diff --git a/pkg/cmd/search/repos/repos.go b/pkg/cmd/search/repos/repos.go
index 3c60ef1eb..57f376672 100644
--- a/pkg/cmd/search/repos/repos.go
+++ b/pkg/cmd/search/repos/repos.go
@@ -154,7 +154,7 @@ func displayResults(io *iostreams.IOStreams, results search.RepositoriesResult)
 	cs := io.ColorScheme()
 	tp := utils.NewTablePrinter(io)
 	for _, repo := range results.Items {
-		tags := []string{repo.Visibility}
+		tags := []string{visibilityLabel(repo)}
 		if repo.IsFork {
 			tags = append(tags, "fork")
 		}
@@ -184,3 +184,12 @@ func displayResults(io *iostreams.IOStreams, results search.RepositoriesResult)
 	}
 	return tp.Render()
 }
+
+func visibilityLabel(repo search.Repository) string {
+	if repo.Visibility != "" {
+		return repo.Visibility
+	} else if repo.IsPrivate {
+		return "private"
+	}
+	return "public"
+}
diff --git a/pkg/liveshare/client.go b/pkg/liveshare/client.go
index b67e1b1cf..33a8e7691 100644
--- a/pkg/liveshare/client.go
+++ b/pkg/liveshare/client.go
@@ -20,7 +20,6 @@ import (
 	"time"
 
 	"github.com/opentracing/opentracing-go"
-	"golang.org/x/crypto/ssh"
 )
 
 type logger interface {
@@ -136,41 +135,3 @@ type joinWorkspaceArgs struct {
 type joinWorkspaceResult struct {
 	SessionNumber int `json:"sessionNumber"`
 }
-
-// A channelID is an identifier for an exposed port on a remote
-// container that may be used to open an SSH channel to it.
-type channelID struct {
-	name, condition string
-}
-
-func (s *Session) openStreamingChannel(ctx context.Context, id channelID) (ssh.Channel, error) {
-	type getStreamArgs struct {
-		StreamName string `json:"streamName"`
-		Condition  string `json:"condition"`
-	}
-	args := getStreamArgs{
-		StreamName: id.name,
-		Condition:  id.condition,
-	}
-	var streamID string
-	if err := s.rpc.do(ctx, "streamManager.getStream", args, &streamID); err != nil {
-		return nil, fmt.Errorf("error getting stream id: %w", err)
-	}
-
-	span, ctx := opentracing.StartSpanFromContext(ctx, "Session.OpenChannel+SendRequest")
-	defer span.Finish()
-	_ = ctx // ctx is not currently used
-
-	channel, reqs, err := s.ssh.conn.OpenChannel("session", nil)
-	if err != nil {
-		return nil, fmt.Errorf("error opening ssh channel for transport: %w", err)
-	}
-	go ssh.DiscardRequests(reqs)
-
-	requestType := fmt.Sprintf("stream-transport-%s", streamID)
-	if _, err = channel.SendRequest(requestType, true, nil); err != nil {
-		return nil, fmt.Errorf("error sending channel request: %w", err)
-	}
-
-	return channel, nil
-}
diff --git a/pkg/liveshare/port_forwarder.go b/pkg/liveshare/port_forwarder.go
index ba2c7ff40..f042eeaea 100644
--- a/pkg/liveshare/port_forwarder.go
+++ b/pkg/liveshare/port_forwarder.go
@@ -7,12 +7,19 @@ import (
 	"net"
 
 	"github.com/opentracing/opentracing-go"
+	"golang.org/x/crypto/ssh"
 )
 
+type portForwardingSession interface {
+	StartSharing(context.Context, string, int) (ChannelID, error)
+	OpenStreamingChannel(context.Context, ChannelID) (ssh.Channel, error)
+	KeepAlive(string)
+}
+
 // A PortForwarder forwards TCP traffic over a Live Share session from a port on a remote
 // container to a local destination such as a network port or Go reader/writer.
 type PortForwarder struct {
-	session    *Session
+	session    portForwardingSession
 	name       string
 	remotePort int
 	keepAlive  bool
@@ -22,7 +29,7 @@ type PortForwarder struct {
 // remote port and Live Share session. The name describes the purpose
 // of the remote port or service. The keepAlive flag indicates whether
 // the session should be kept alive with port forwarding traffic.
-func NewPortForwarder(session *Session, name string, remotePort int, keepAlive bool) *PortForwarder {
+func NewPortForwarder(session portForwardingSession, name string, remotePort int, keepAlive bool) *PortForwarder {
 	return &PortForwarder{
 		session:    session,
 		name:       name,
@@ -92,8 +99,8 @@ func (fwd *PortForwarder) Forward(ctx context.Context, conn io.ReadWriteCloser)
 	return awaitError(ctx, errc)
 }
 
-func (fwd *PortForwarder) shareRemotePort(ctx context.Context) (channelID, error) {
-	id, err := fwd.session.startSharing(ctx, fwd.name, fwd.remotePort)
+func (fwd *PortForwarder) shareRemotePort(ctx context.Context) (ChannelID, error) {
+	id, err := fwd.session.StartSharing(ctx, fwd.name, fwd.remotePort)
 	if err != nil {
 		err = fmt.Errorf("failed to share remote port %d: %w", fwd.remotePort, err)
 	}
@@ -110,35 +117,39 @@ func awaitError(ctx context.Context, errc <-chan error) error {
 	}
 }
 
+type trafficMonitorSession interface {
+	KeepAlive(string)
+}
+
 // trafficMonitor implements io.Reader. It keeps the session alive by notifying
 // it of the traffic type during Read operations.
 type trafficMonitor struct {
 	reader io.Reader
 
-	session     *Session
+	session     trafficMonitorSession
 	trafficType string
 }
 
 // newTrafficMonitor returns a new trafficMonitor for the specified
 // session and traffic type. It wraps the provided io.Reader with its own
 // Read method.
-func newTrafficMonitor(reader io.Reader, session *Session, trafficType string) *trafficMonitor {
+func newTrafficMonitor(reader io.Reader, session trafficMonitorSession, trafficType string) *trafficMonitor {
 	return &trafficMonitor{reader, session, trafficType}
 }
 
 func (t *trafficMonitor) Read(p []byte) (n int, err error) {
-	t.session.keepAlive(t.trafficType)
+	t.session.KeepAlive(t.trafficType)
 	return t.reader.Read(p)
 }
 
 // handleConnection handles forwarding for a single accepted connection, then closes it.
-func (fwd *PortForwarder) handleConnection(ctx context.Context, id channelID, conn io.ReadWriteCloser) (err error) {
+func (fwd *PortForwarder) handleConnection(ctx context.Context, id ChannelID, conn io.ReadWriteCloser) (err error) {
 	span, ctx := opentracing.StartSpanFromContext(ctx, "PortForwarder.handleConnection")
 	defer span.Finish()
 
 	defer safeClose(conn, &err)
 
-	channel, err := fwd.session.openStreamingChannel(ctx, id)
+	channel, err := fwd.session.OpenStreamingChannel(ctx, id)
 	if err != nil {
 		return fmt.Errorf("error opening streaming channel for new connection: %w", err)
 	}
diff --git a/pkg/liveshare/ports.go b/pkg/liveshare/ports.go
index 851fe3190..6cef1584b 100644
--- a/pkg/liveshare/ports.go
+++ b/pkg/liveshare/ports.go
@@ -6,7 +6,6 @@ import (
 	"fmt"
 
 	"github.com/sourcegraph/jsonrpc2"
-	"golang.org/x/sync/errgroup"
 )
 
 // Port describes a port exposed by the container.
@@ -30,37 +29,6 @@ const (
 	PortChangeKindUpdate PortChangeKind = "update"
 )
 
-// startSharing tells the Live Share host to start sharing the specified port from the container.
-// The sessionName describes the purpose of the remote port or service.
-// It returns an identifier that can be used to open an SSH channel to the remote port.
-func (s *Session) startSharing(ctx context.Context, sessionName string, port int) (channelID, error) {
-	args := []interface{}{port, sessionName, fmt.Sprintf("http://localhost:%d", port)}
-	g, ctx := errgroup.WithContext(ctx)
-
-	g.Go(func() error {
-		startNotification, err := s.WaitForPortNotification(ctx, port, PortChangeKindStart)
-		if err != nil {
-			return fmt.Errorf("error while waiting for port notification: %w", err)
-
-		}
-		if !startNotification.Success {
-			return fmt.Errorf("error while starting port sharing: %s", startNotification.ErrorDetail)
-		}
-		return nil // success
-	})
-
-	var response Port
-	g.Go(func() error {
-		return s.rpc.do(ctx, "serverSharing.startSharing", args, &response)
-	})
-
-	if err := g.Wait(); err != nil {
-		return channelID{}, err
-	}
-
-	return channelID{response.StreamName, response.StreamCondition}, nil
-}
-
 type PortNotification struct {
 	Success bool // Helps us disambiguate between the SharingSucceeded/SharingFailed events
 	// The following are properties included in the SharingSucceeded/SharingFailed events sent by the server sharing service in the Codespace
diff --git a/pkg/liveshare/session.go b/pkg/liveshare/session.go
index e2648c1c8..33531bffd 100644
--- a/pkg/liveshare/session.go
+++ b/pkg/liveshare/session.go
@@ -5,8 +5,18 @@ import (
 	"fmt"
 	"strconv"
 	"time"
+
+	"github.com/opentracing/opentracing-go"
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/sync/errgroup"
 )
 
+// A ChannelID is an identifier for an exposed port on a remote
+// container that may be used to open an SSH channel to it.
+type ChannelID struct {
+	name, condition string
+}
+
 // A Session represents the session between a connected Live Share client and server.
 type Session struct {
 	ssh *sshSession
@@ -91,7 +101,7 @@ func (s *Session) StartJupyterServer(ctx context.Context) (int, string, error) {
 // heartbeat runs until context cancellation, periodically checking whether there is a
 // reason to keep the connection alive, and if so, notifying the Live Share host to do so.
 // Heartbeat ensures it does not send more than one request every "interval" to ratelimit
-// how many keepAlives we send at a time.
+// how many KeepAlives we send at a time.
 func (s *Session) heartbeat(ctx context.Context, interval time.Duration) {
 	ticker := time.NewTicker(interval)
 	defer ticker.Stop()
@@ -118,9 +128,9 @@ func (s *Session) notifyHostOfActivity(ctx context.Context, activity string) err
 	return s.rpc.do(ctx, "ICodespaceHostService.notifyCodespaceOfClientActivity", params, nil)
 }
 
-// keepAlive accepts a reason that is retained if there is no active reason
+// KeepAlive accepts a reason that is retained if there is no active reason
 // to send to the server.
-func (s *Session) keepAlive(reason string) {
+func (s *Session) KeepAlive(reason string) {
 	select {
 	case s.keepAliveReason <- reason:
 	default:
@@ -128,3 +138,66 @@ func (s *Session) keepAlive(reason string) {
 		// so we can ignore this one
 	}
 }
+
+// StartSharing tells the Live Share host to start sharing the specified port from the container.
+// The sessionName describes the purpose of the remote port or service.
+// It returns an identifier that can be used to open an SSH channel to the remote port.
+func (s *Session) StartSharing(ctx context.Context, sessionName string, port int) (ChannelID, error) {
+	args := []interface{}{port, sessionName, fmt.Sprintf("http://localhost:%d", port)}
+	g, ctx := errgroup.WithContext(ctx)
+
+	g.Go(func() error {
+		startNotification, err := s.WaitForPortNotification(ctx, port, PortChangeKindStart)
+		if err != nil {
+			return fmt.Errorf("error while waiting for port notification: %w", err)
+
+		}
+		if !startNotification.Success {
+			return fmt.Errorf("error while starting port sharing: %s", startNotification.ErrorDetail)
+		}
+		return nil // success
+	})
+
+	var response Port
+	g.Go(func() error {
+		return s.rpc.do(ctx, "serverSharing.startSharing", args, &response)
+	})
+
+	if err := g.Wait(); err != nil {
+		return ChannelID{}, err
+	}
+
+	return ChannelID{response.StreamName, response.StreamCondition}, nil
+}
+
+func (s *Session) OpenStreamingChannel(ctx context.Context, id ChannelID) (ssh.Channel, error) {
+	type getStreamArgs struct {
+		StreamName string `json:"streamName"`
+		Condition  string `json:"condition"`
+	}
+	args := getStreamArgs{
+		StreamName: id.name,
+		Condition:  id.condition,
+	}
+	var streamID string
+	if err := s.rpc.do(ctx, "streamManager.getStream", args, &streamID); err != nil {
+		return nil, fmt.Errorf("error getting stream id: %w", err)
+	}
+
+	span, ctx := opentracing.StartSpanFromContext(ctx, "Session.OpenChannel+SendRequest")
+	defer span.Finish()
+	_ = ctx // ctx is not currently used
+
+	channel, reqs, err := s.ssh.conn.OpenChannel("session", nil)
+	if err != nil {
+		return nil, fmt.Errorf("error opening ssh channel for transport: %w", err)
+	}
+	go ssh.DiscardRequests(reqs)
+
+	requestType := fmt.Sprintf("stream-transport-%s", streamID)
+	if _, err = channel.SendRequest(requestType, true, nil); err != nil {
+		return nil, fmt.Errorf("error sending channel request: %w", err)
+	}
+
+	return channel, nil
+}



Test patch:
diff --git a/internal/featuredetection/feature_detection_test.go b/internal/featuredetection/feature_detection_test.go
index 1b6c26273..ae8695482 100644
--- a/internal/featuredetection/feature_detection_test.go
+++ b/internal/featuredetection/feature_detection_test.go
@@ -24,16 +24,26 @@ func TestPullRequestFeatures(t *testing.T) {
 				ReviewDecision:       true,
 				StatusCheckRollup:    true,
 				BranchProtectionRule: true,
+				MergeQueue:           true,
 			},
 			wantErr: false,
 		},
 		{
 			name:     "GHE",
 			hostname: "git.my.org",
+			queryResponse: map[string]string{
+				`query PullRequest_fields\b`: heredoc.Doc(`
+					{ "data": { "PullRequest": { "fields": [
+						{"name": "isInMergeQueue"},
+						{"name": "isMergeQueueEnabled"}
+					] } } }
+				`),
+			},
 			wantFeatures: PullRequestFeatures{
 				ReviewDecision:       true,
 				StatusCheckRollup:    true,
 				BranchProtectionRule: true,
+				MergeQueue:           true,
 			},
 			wantErr: false,
 		},
@@ -72,6 +82,8 @@ func TestRepositoryFeatures(t *testing.T) {
 				IssueTemplateMutation:    true,
 				IssueTemplateQuery:       true,
 				PullRequestTemplateQuery: true,
+				VisibilityField:          true,
+				AutoMerge:                true,
 			},
 			wantErr: false,
 		},
@@ -105,6 +117,40 @@ func TestRepositoryFeatures(t *testing.T) {
 			},
 			wantErr: false,
 		},
+		{
+			name:     "GHE has visibility field",
+			hostname: "git.my.org",
+			queryResponse: map[string]string{
+				`query Repository_fields\b`: heredoc.Doc(`
+					{ "data": { "Repository": { "fields": [
+						{"name": "visibility"}
+					] } } }
+				`),
+			},
+			wantFeatures: RepositoryFeatures{
+				IssueTemplateMutation: true,
+				IssueTemplateQuery:    true,
+				VisibilityField:       true,
+			},
+			wantErr: false,
+		},
+		{
+			name:     "GHE has automerge field",
+			hostname: "git.my.org",
+			queryResponse: map[string]string{
+				`query Repository_fields\b`: heredoc.Doc(`
+					{ "data": { "Repository": { "fields": [
+						{"name": "autoMergeAllowed"}
+					] } } }
+				`),
+			},
+			wantFeatures: RepositoryFeatures{
+				IssueTemplateMutation: true,
+				IssueTemplateQuery:    true,
+				AutoMerge:             true,
+			},
+			wantErr: false,
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/cmd/auth/logout/logout_test.go b/pkg/cmd/auth/logout/logout_test.go
index eaa13cde0..73fd50d40 100644
--- a/pkg/cmd/auth/logout/logout_test.go
+++ b/pkg/cmd/auth/logout/logout_test.go
@@ -24,12 +24,9 @@ func Test_NewCmdLogout(t *testing.T) {
 		tty      bool
 	}{
 		{
-			name: "tty with hostname",
-			tty:  true,
-			cli:  "--hostname harry.mason",
-			wants: LogoutOptions{
-				Hostname: "harry.mason",
-			},
+			name:     "nontty no arguments",
+			cli:      "",
+			wantsErr: true,
 		},
 		{
 			name: "tty no arguments",
@@ -40,16 +37,19 @@ func Test_NewCmdLogout(t *testing.T) {
 			},
 		},
 		{
-			name: "nontty with hostname",
+			name: "tty with hostname",
+			tty:  true,
 			cli:  "--hostname harry.mason",
 			wants: LogoutOptions{
 				Hostname: "harry.mason",
 			},
 		},
 		{
-			name:     "nontty no arguments",
-			cli:      "",
-			wantsErr: true,
+			name: "nontty with hostname",
+			cli:  "--hostname harry.mason",
+			wants: LogoutOptions{
+				Hostname: "harry.mason",
+			},
 		},
 	}
 	for _, tt := range tests {
@@ -107,17 +107,13 @@ func Test_logoutRun_tty(t *testing.T) {
 			wantHosts: "cheryl.mason:\n    oauth_token: abc123\n",
 			askStubs: func(as *prompt.AskStubber) {
 				as.StubPrompt("What account do you want to log out of?").AnswerWith("github.com")
-				as.StubPrompt("Are you sure you want to log out of github.com account 'cybilb'?").AnswerWith(true)
 			},
 			wantErrOut: regexp.MustCompile(`Logged out of github.com account 'cybilb'`),
 		},
 		{
-			name:     "no arguments, one host",
-			opts:     &LogoutOptions{},
-			cfgHosts: []string{"github.com"},
-			askStubs: func(as *prompt.AskStubber) {
-				as.StubPrompt("Are you sure you want to log out of github.com account 'cybilb'?").AnswerWith(true)
-			},
+			name:       "no arguments, one host",
+			opts:       &LogoutOptions{},
+			cfgHosts:   []string{"github.com"},
 			wantErrOut: regexp.MustCompile(`Logged out of github.com account 'cybilb'`),
 		},
 		{
@@ -130,11 +126,8 @@ func Test_logoutRun_tty(t *testing.T) {
 			opts: &LogoutOptions{
 				Hostname: "cheryl.mason",
 			},
-			cfgHosts:  []string{"cheryl.mason", "github.com"},
-			wantHosts: "github.com:\n    oauth_token: abc123\n",
-			askStubs: func(as *prompt.AskStubber) {
-				as.StubPrompt("Are you sure you want to log out of cheryl.mason account 'cybilb'?").AnswerWith(true)
-			},
+			cfgHosts:   []string{"cheryl.mason", "github.com"},
+			wantHosts:  "github.com:\n    oauth_token: abc123\n",
 			wantErrOut: regexp.MustCompile(`Logged out of cheryl.mason account 'cybilb'`),
 		},
 	}
diff --git a/pkg/cmd/pr/merge/merge_test.go b/pkg/cmd/pr/merge/merge_test.go
index b32695be1..035954fb9 100644
--- a/pkg/cmd/pr/merge/merge_test.go
+++ b/pkg/cmd/pr/merge/merge_test.go
@@ -52,7 +52,7 @@ func Test_NewCmdMerge(t *testing.T) {
 				IsDeleteBranchIndicated: false,
 				CanDeleteLocalBranch:    true,
 				MergeMethod:             PullRequestMergeMethodMerge,
-				InteractiveMode:         true,
+				MergeStrategyEmpty:      true,
 				Body:                    "",
 				BodySet:                 false,
 			},
@@ -67,7 +67,7 @@ func Test_NewCmdMerge(t *testing.T) {
 				IsDeleteBranchIndicated: true,
 				CanDeleteLocalBranch:    true,
 				MergeMethod:             PullRequestMergeMethodMerge,
-				InteractiveMode:         true,
+				MergeStrategyEmpty:      true,
 				Body:                    "",
 				BodySet:                 false,
 			},
@@ -82,7 +82,7 @@ func Test_NewCmdMerge(t *testing.T) {
 				IsDeleteBranchIndicated: false,
 				CanDeleteLocalBranch:    true,
 				MergeMethod:             PullRequestMergeMethodMerge,
-				InteractiveMode:         true,
+				MergeStrategyEmpty:      true,
 				Body:                    "a body from file",
 				BodySet:                 true,
 			},
@@ -98,7 +98,7 @@ func Test_NewCmdMerge(t *testing.T) {
 				IsDeleteBranchIndicated: false,
 				CanDeleteLocalBranch:    true,
 				MergeMethod:             PullRequestMergeMethodMerge,
-				InteractiveMode:         true,
+				MergeStrategyEmpty:      true,
 				Body:                    "this is on standard input",
 				BodySet:                 true,
 			},
@@ -113,7 +113,7 @@ func Test_NewCmdMerge(t *testing.T) {
 				IsDeleteBranchIndicated: false,
 				CanDeleteLocalBranch:    true,
 				MergeMethod:             PullRequestMergeMethodMerge,
-				InteractiveMode:         true,
+				MergeStrategyEmpty:      true,
 				Body:                    "cool",
 				BodySet:                 true,
 			},
@@ -130,12 +130,6 @@ func Test_NewCmdMerge(t *testing.T) {
 			isTTY:   true,
 			wantErr: "argument required when using the --repo flag",
 		},
-		{
-			name:    "insufficient flags in non-interactive mode",
-			args:    "123",
-			isTTY:   false,
-			wantErr: "--merge, --rebase, or --squash required when not running interactively",
-		},
 		{
 			name:    "multiple merge methods",
 			args:    "123 --merge --rebase",
@@ -191,7 +185,7 @@ func Test_NewCmdMerge(t *testing.T) {
 			assert.Equal(t, tt.want.DeleteBranch, opts.DeleteBranch)
 			assert.Equal(t, tt.want.CanDeleteLocalBranch, opts.CanDeleteLocalBranch)
 			assert.Equal(t, tt.want.MergeMethod, opts.MergeMethod)
-			assert.Equal(t, tt.want.InteractiveMode, opts.InteractiveMode)
+			assert.Equal(t, tt.want.MergeStrategyEmpty, opts.MergeStrategyEmpty)
 			assert.Equal(t, tt.want.Body, opts.Body)
 			assert.Equal(t, tt.want.BodySet, opts.BodySet)
 		})
@@ -276,7 +270,7 @@ func TestPrMerge(t *testing.T) {
 			Title:            "The title of the PR",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -285,14 +279,14 @@ func TestPrMerge(t *testing.T) {
 			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
 			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
 			assert.NotContains(t, input, "commitHeadline")
-		}))
+		}),
+	)
 
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
-
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", true, "pr merge 1 --merge")
+	output, err := runCommand(http, "main", true, "pr merge 1 --merge")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -317,14 +311,14 @@ func TestPrMerge_blocked(t *testing.T) {
 			Title:            "The title of the PR",
 			MergeStateStatus: "BLOCKED",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", true, "pr merge 1 --merge")
+	output, err := runCommand(http, "main", true, "pr merge 1 --merge")
 	assert.EqualError(t, err, "SilentError")
 
 	assert.Equal(t, "", output.String())
@@ -350,14 +344,14 @@ func TestPrMerge_dirty(t *testing.T) {
 			BaseRefName:      "trunk",
 			HeadRefName:      "feature",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", true, "pr merge 1 --merge")
+	output, err := runCommand(http, "main", true, "pr merge 1 --merge")
 	assert.EqualError(t, err, "SilentError")
 
 	assert.Equal(t, "", output.String())
@@ -382,7 +376,7 @@ func TestPrMerge_nontty(t *testing.T) {
 			Title:            "The title of the PR",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -398,7 +392,7 @@ func TestPrMerge_nontty(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", false, "pr merge 1 --merge")
+	output, err := runCommand(http, "main", false, "pr merge 1 --merge")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -420,7 +414,7 @@ func TestPrMerge_editMessage_nontty(t *testing.T) {
 			Title:            "The title of the PR",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -437,7 +431,7 @@ func TestPrMerge_editMessage_nontty(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", false, "pr merge 1 --merge -t mytitle -b mybody")
+	output, err := runCommand(http, "main", false, "pr merge 1 --merge -t mytitle -b mybody")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -459,7 +453,7 @@ func TestPrMerge_withRepoFlag(t *testing.T) {
 			Title:            "The title of the PR",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -473,7 +467,7 @@ func TestPrMerge_withRepoFlag(t *testing.T) {
 	_, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 
-	output, err := runCommand(http, "master", true, "pr merge 1 --merge -R OWNER/REPO")
+	output, err := runCommand(http, "main", true, "pr merge 1 --merge -R OWNER/REPO")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -497,9 +491,10 @@ func TestPrMerge_deleteBranch(t *testing.T) {
 			State:            "OPEN",
 			Title:            "Blueberries are a good fruit",
 			HeadRefName:      "blueberries",
+			BaseRefName:      "main",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -516,8 +511,8 @@ func TestPrMerge_deleteBranch(t *testing.T) {
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 
-	cs.Register(`git rev-parse --verify refs/heads/master`, 0, "")
-	cs.Register(`git checkout master`, 0, "")
+	cs.Register(`git rev-parse --verify refs/heads/main`, 0, "")
+	cs.Register(`git checkout main`, 0, "")
 	cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
 	cs.Register(`git branch -D blueberries`, 0, "")
 	cs.Register(`git pull --ff-only`, 0, "")
@@ -530,7 +525,7 @@ func TestPrMerge_deleteBranch(t *testing.T) {
 	assert.Equal(t, "", output.String())
 	assert.Equal(t, heredoc.Doc(`
 		✓ Merged pull request #10 (Blueberries are a good fruit)
-		✓ Deleted branch blueberries and switched to branch master
+		✓ Deleted branch blueberries and switched to branch main
 	`), output.Stderr())
 }
 
@@ -549,7 +544,7 @@ func TestPrMerge_deleteBranch_nonDefault(t *testing.T) {
 			MergeStateStatus: "CLEAN",
 			BaseRefName:      "fruit",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -599,7 +594,7 @@ func TestPrMerge_deleteBranch_checkoutNewBranch(t *testing.T) {
 			MergeStateStatus: "CLEAN",
 			BaseRefName:      "fruit",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -648,7 +643,7 @@ func TestPrMerge_deleteNonCurrentBranch(t *testing.T) {
 			HeadRefName:      "blueberries",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -668,7 +663,7 @@ func TestPrMerge_deleteNonCurrentBranch(t *testing.T) {
 	cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
 	cs.Register(`git branch -D blueberries`, 0, "")
 
-	output, err := runCommand(http, "master", true, `pr merge --merge --delete-branch blueberries`)
+	output, err := runCommand(http, "main", true, `pr merge --merge --delete-branch blueberries`)
 	if err != nil {
 		t.Fatalf("Got unexpected error running `pr merge` %s", err)
 	}
@@ -690,11 +685,11 @@ func Test_nonDivergingPullRequest(t *testing.T) {
 		Title:            "Blueberries are a good fruit",
 		State:            "OPEN",
 		MergeStateStatus: "CLEAN",
+		BaseRefName:      "main",
 	}
 	stubCommit(pr, "COMMITSHA1")
 
-	prFinder := shared.RunCommandFinder("", pr, baseRepo("OWNER", "REPO", "master"))
-	prFinder.ExpectFields([]string{"id", "number", "state", "title", "lastCommit", "mergeStateStatus", "headRepositoryOwner", "headRefName"})
+	shared.RunCommandFinder("", pr, baseRepo("OWNER", "REPO", "main"))
 
 	http.Register(
 		httpmock.GraphQL(`mutation PullRequestMerge\b`),
@@ -730,11 +725,11 @@ func Test_divergingPullRequestWarning(t *testing.T) {
 		Title:            "Blueberries are a good fruit",
 		State:            "OPEN",
 		MergeStateStatus: "CLEAN",
+		BaseRefName:      "main",
 	}
 	stubCommit(pr, "COMMITSHA1")
 
-	prFinder := shared.RunCommandFinder("", pr, baseRepo("OWNER", "REPO", "master"))
-	prFinder.ExpectFields([]string{"id", "number", "state", "title", "lastCommit", "mergeStateStatus", "headRepositoryOwner", "headRefName"})
+	shared.RunCommandFinder("", pr, baseRepo("OWNER", "REPO", "main"))
 
 	http.Register(
 		httpmock.GraphQL(`mutation PullRequestMerge\b`),
@@ -774,7 +769,7 @@ func Test_pullRequestWithoutCommits(t *testing.T) {
 			State:            "OPEN",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -813,7 +808,7 @@ func TestPrMerge_rebase(t *testing.T) {
 			State:            "OPEN",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -829,7 +824,7 @@ func TestPrMerge_rebase(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", true, "pr merge 2 --rebase")
+	output, err := runCommand(http, "main", true, "pr merge 2 --rebase")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -854,7 +849,7 @@ func TestPrMerge_squash(t *testing.T) {
 			State:            "OPEN",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -870,7 +865,7 @@ func TestPrMerge_squash(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "master", true, "pr merge 3 --squash")
+	output, err := runCommand(http, "main", true, "pr merge 3 --squash")
 	if err != nil {
 		t.Fatalf("error running command `pr merge`: %v", err)
 	}
@@ -892,34 +887,31 @@ func TestPrMerge_alreadyMerged(t *testing.T) {
 			Number:           4,
 			State:            "MERGED",
 			HeadRefName:      "blueberries",
-			BaseRefName:      "master",
+			BaseRefName:      "main",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 
-	cs.Register(`git rev-parse --verify refs/heads/master`, 0, "")
-	cs.Register(`git checkout master`, 0, "")
+	cs.Register(`git rev-parse --verify refs/heads/main`, 0, "")
+	cs.Register(`git checkout main`, 0, "")
 	cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
 	cs.Register(`git branch -D blueberries`, 0, "")
 	cs.Register(`git pull --ff-only`, 0, "")
 
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(true)
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("Pull request #4 was already merged. Delete the branch locally?").AnswerWith(true)
 
 	output, err := runCommand(http, "blueberries", true, "pr merge 4")
 	assert.NoError(t, err)
 	assert.Equal(t, "", output.String())
-	assert.Equal(t, "✓ Deleted branch blueberries and switched to branch master\n", output.Stderr())
+	assert.Equal(t, "✓ Deleted branch blueberries and switched to branch main\n", output.Stderr())
 }
 
-func TestPrMerge_alreadyMerged_nonInteractive(t *testing.T) {
+func TestPrMerge_alreadyMerged_withMergeStrategy(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
 
@@ -932,7 +924,7 @@ func TestPrMerge_alreadyMerged_nonInteractive(t *testing.T) {
 			HeadRepositoryOwner: api.Owner{Login: "OWNER"},
 			MergeStateStatus:    "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	cs, cmdTeardown := run.Stub()
@@ -940,7 +932,7 @@ func TestPrMerge_alreadyMerged_nonInteractive(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	output, err := runCommand(http, "blueberries", true, "pr merge 4 --merge")
+	output, err := runCommand(http, "blueberries", false, "pr merge 4 --merge")
 	if err != nil {
 		t.Fatalf("Got unexpected error running `pr merge` %s", err)
 	}
@@ -949,7 +941,41 @@ func TestPrMerge_alreadyMerged_nonInteractive(t *testing.T) {
 	assert.Equal(t, "! Pull request #4 was already merged\n", output.Stderr())
 }
 
-func TestPrMerge_alreadyMerged_nonInteractive_crossRepo(t *testing.T) {
+func TestPrMerge_alreadyMerged_withMergeStrategy_TTY(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"4",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              4,
+			State:               "MERGED",
+			HeadRepositoryOwner: api.Owner{Login: "OWNER"},
+			MergeStateStatus:    "CLEAN",
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+	cs.Register(`git branch -D `, 0, "")
+
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("Pull request #4 was already merged. Delete the branch locally?").AnswerWith(true)
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 4 --merge")
+	if err != nil {
+		t.Fatalf("Got unexpected error running `pr merge` %s", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "✓ Deleted branch \n", output.Stderr())
+}
+
+func TestPrMerge_alreadyMerged_withMergeStrategy_crossRepo(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
 
@@ -962,7 +988,7 @@ func TestPrMerge_alreadyMerged_nonInteractive_crossRepo(t *testing.T) {
 			HeadRepositoryOwner: api.Owner{Login: "monalisa"},
 			MergeStateStatus:    "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	cs, cmdTeardown := run.Stub()
@@ -978,7 +1004,7 @@ func TestPrMerge_alreadyMerged_nonInteractive_crossRepo(t *testing.T) {
 	assert.Equal(t, "", output.String())
 	assert.Equal(t, "", output.Stderr())
 }
-func TestPRMerge_interactive(t *testing.T) {
+func TestPRMergeTTY(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
 
@@ -991,7 +1017,7 @@ func TestPRMerge_interactive(t *testing.T) {
 			HeadRefName:      "blueberries",
 			MergeStateStatus: "CLEAN",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -1002,6 +1028,7 @@ func TestPRMerge_interactive(t *testing.T) {
 			"rebaseMergeAllowed": true,
 			"squashMergeAllowed": true
 		} } }`))
+
 	http.Register(
 		httpmock.GraphQL(`mutation PullRequestMerge\b`),
 		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
@@ -1015,27 +1042,20 @@ func TestPRMerge_interactive(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
 
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(0) // Merge method survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(false) // Delete branch survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Submit") // Confirm submit survey
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerDefault()
+	as.StubPrompt("Delete the branch locally and on GitHub?").AnswerDefault()
+	as.StubPrompt("What's next?").AnswerWith("Submit")
 
 	output, err := runCommand(http, "blueberries", true, "")
 	if err != nil {
 		t.Fatalf("Got unexpected error running `pr merge` %s", err)
 	}
 
-	//nolint:staticcheck // prefer exact matchers over ExpectLines
-	test.ExpectLines(t, output.Stderr(), "Merged pull request #3")
+	assert.Equal(t, "✓ Merged pull request #3 (It was the best of times)\n", output.Stderr())
 }
 
-func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
+func TestPRMergeTTY_withDeleteBranch(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
 
@@ -1047,8 +1067,9 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
 			Title:            "It was the best of times",
 			HeadRefName:      "blueberries",
 			MergeStateStatus: "CLEAN",
+			BaseRefName:      "main",
 		},
-		baseRepo("OWNER", "REPO", "master"),
+		baseRepo("OWNER", "REPO", "main"),
 	)
 
 	http.Register(
@@ -1057,7 +1078,10 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
 		{ "data": { "repository": {
 			"mergeCommitAllowed": true,
 			"rebaseMergeAllowed": true,
-			"squashMergeAllowed": true
+			"squashMergeAllowed": true,
+			"mergeQueue": {
+				"mergeMethod": ""
+			}
 		} } }`))
 	http.Register(
 		httpmock.GraphQL(`mutation PullRequestMerge\b`),
@@ -1073,20 +1097,15 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
 	cs, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 
-	cs.Register(`git rev-parse --verify refs/heads/master`, 0, "")
-	cs.Register(`git checkout master`, 0, "")
+	cs.Register(`git rev-parse --verify refs/heads/main`, 0, "")
+	cs.Register(`git checkout main`, 0, "")
 	cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "")
 	cs.Register(`git branch -D blueberries`, 0, "")
 	cs.Register(`git pull --ff-only`, 0, "")
 
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(0) // Merge method survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Submit") // Confirm submit survey
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerDefault()
+	as.StubPrompt("What's next?").AnswerWith("Submit")
 
 	output, err := runCommand(http, "blueberries", true, "-d")
 	if err != nil {
@@ -1096,17 +1115,19 @@ func TestPRMerge_interactiveWithDeleteBranch(t *testing.T) {
 	assert.Equal(t, "", output.String())
 	assert.Equal(t, heredoc.Doc(`
 		✓ Merged pull request #3 (It was the best of times)
-		✓ Deleted branch blueberries and switched to branch master
+		✓ Deleted branch blueberries and switched to branch main
 	`), output.Stderr())
 }
 
-func TestPRMerge_interactiveSquashEditCommitMsgAndSubject(t *testing.T) {
+func TestPRMergeTTY_squashEditCommitMsgAndSubject(t *testing.T) {
 	ios, _, stdout, stderr := iostreams.Test()
+	ios.SetStdinTTY(true)
 	ios.SetStdoutTTY(true)
 	ios.SetStderrTTY(true)
 
 	tr := initFakeHTTP()
 	defer tr.Verify(t)
+
 	tr.Register(
 		httpmock.GraphQL(`query RepositoryInfo\b`),
 		httpmock.StringResponse(`
@@ -1141,20 +1162,12 @@ func TestPRMerge_interactiveSquashEditCommitMsgAndSubject(t *testing.T) {
 	_, cmdTeardown := run.Stub()
 	defer cmdTeardown(t)
 
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(2) // Merge method survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(false) // Delete branch survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Edit commit subject") // Confirm submit survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Edit commit message") // Confirm submit survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Submit") // Confirm submit survey
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerWith("Squash and merge")
+	as.StubPrompt("Delete the branch on GitHub?").AnswerDefault()
+	as.StubPrompt("What's next?").AnswerWith("Edit commit message")
+	as.StubPrompt("What's next?").AnswerWith("Edit commit subject")
+	as.StubPrompt("What's next?").AnswerWith("Submit")
 
 	err := mergeRun(&MergeOptions{
 		IO:     ios,
@@ -1162,8 +1175,8 @@ func TestPRMerge_interactiveSquashEditCommitMsgAndSubject(t *testing.T) {
 		HttpClient: func() (*http.Client, error) {
 			return &http.Client{Transport: tr}, nil
 		},
-		SelectorArg:     "https://github.com/OWNER/REPO/pull/123",
-		InteractiveMode: true,
+		SelectorArg:        "https://github.com/OWNER/REPO/pull/123",
+		MergeStrategyEmpty: true,
 		Finder: shared.NewMockFinder(
 			"https://github.com/OWNER/REPO/pull/123",
 			&api.PullRequest{ID: "THE-ID", Number: 123, Title: "title", MergeStateStatus: "CLEAN"},
@@ -1176,7 +1189,34 @@ func TestPRMerge_interactiveSquashEditCommitMsgAndSubject(t *testing.T) {
 	assert.Equal(t, "✓ Squashed and merged pull request #123 (title)\n", stderr.String())
 }
 
-func TestPRMerge_interactiveCancelled(t *testing.T) {
+func TestPRMergeEmptyStrategyNonTTY(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:               "THE-ID",
+			Number:           1,
+			State:            "OPEN",
+			Title:            "The title of the PR",
+			MergeStateStatus: "CLEAN",
+			BaseRefName:      "main",
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", false, "pr merge 1")
+	assert.EqualError(t, err, "--merge, --rebase, or --squash required when not running interactively")
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "", output.Stderr())
+}
+
+func TestPRTTY_cancelled(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
 
@@ -1200,16 +1240,10 @@ func TestPRMerge_interactiveCancelled(t *testing.T) {
 
 	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
 
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(0) // Merge method survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(true) // Delete branch survey
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne("Cancel") // Confirm submit survey
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerDefault()
+	as.StubPrompt("Delete the branch locally and on GitHub?").AnswerDefault()
+	as.StubPrompt("What's next?").AnswerWith("Cancel")
 
 	output, err := runCommand(http, "blueberries", true, "")
 	if !errors.Is(err, cmdutil.CancelError) {
@@ -1225,11 +1259,9 @@ func Test_mergeMethodSurvey(t *testing.T) {
 		RebaseMergeAllowed: true,
 		SquashMergeAllowed: true,
 	}
-	//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
-	as, surveyTeardown := prompt.InitAskStubber()
-	defer surveyTeardown()
-	//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
-	as.StubOne(0) // Select first option which is rebase merge
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerWith("Rebase and merge")
+
 	method, err := mergeMethodSurvey(repo)
 	assert.Nil(t, err)
 	assert.Equal(t, PullRequestMergeMethodRebase, method)
@@ -1345,6 +1377,248 @@ func TestMergeRun_disableAutoMerge(t *testing.T) {
 	assert.Equal(t, "✓ Auto-merge disabled for pull request #123\n", stderr.String())
 }
 
+func TestPrInMergeQueue(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              1,
+			State:               "OPEN",
+			Title:               "The title of the PR",
+			MergeStateStatus:    "CLEAN",
+			IsInMergeQueue:      true,
+			IsMergeQueueEnabled: true,
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "! Pull request #1 is already queued to merge\n", output.Stderr())
+}
+
+func TestPrAddToMergeQueueWithMergeMethod(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              1,
+			State:               "OPEN",
+			Title:               "The title of the PR",
+			MergeStateStatus:    "CLEAN",
+			IsInMergeQueue:      false,
+			IsMergeQueueEnabled: true,
+			BaseRefName:         "main",
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+	http.Register(
+		httpmock.GraphQL(`mutation PullRequestAutoMerge\b`),
+		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
+			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
+			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
+		}),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1 --merge")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "! The merge strategy for main is set by the merge queue\n✓ Pull request #1 will be added to the merge queue for main when ready\n", output.Stderr())
+}
+
+func TestPrAddToMergeQueueClean(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              1,
+			State:               "OPEN",
+			Title:               "The title of the PR",
+			MergeStateStatus:    "CLEAN",
+			IsInMergeQueue:      false,
+			IsMergeQueueEnabled: true,
+			BaseRefName:         "main",
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	http.Register(
+		httpmock.GraphQL(`mutation PullRequestAutoMerge\b`),
+		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
+			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
+			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
+		}),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "✓ Pull request #1 will be added to the merge queue for main when ready\n", output.Stderr())
+}
+
+func TestPrAddToMergeQueueBlocked(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              1,
+			State:               "OPEN",
+			Title:               "The title of the PR",
+			MergeStateStatus:    "BLOCKED",
+			IsInMergeQueue:      false,
+			IsMergeQueueEnabled: true,
+			BaseRefName:         "main",
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	http.Register(
+		httpmock.GraphQL(`mutation PullRequestAutoMerge\b`),
+		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
+			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
+			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
+		}),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "✓ Pull request #1 will be added to the merge queue for main when ready\n", output.Stderr())
+}
+
+func TestPrAddToMergeQueueAdmin(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:                  "THE-ID",
+			Number:              1,
+			State:               "OPEN",
+			Title:               "The title of the PR",
+			MergeStateStatus:    "CLEAN",
+			IsInMergeQueue:      false,
+			IsMergeQueueEnabled: true,
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	http.Register(
+		httpmock.GraphQL(`query RepositoryInfo\b`),
+		httpmock.StringResponse(`
+		{ "data": { "repository": {
+			"mergeCommitAllowed": true,
+			"rebaseMergeAllowed": true,
+			"squashMergeAllowed": true
+		} } }`))
+
+	http.Register(
+		httpmock.GraphQL(`mutation PullRequestMerge\b`),
+		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
+			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
+			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
+			assert.NotContains(t, input, "commitHeadline")
+		}),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	as := prompt.NewAskStubber(t)
+	as.StubPrompt("What merge method would you like to use?").AnswerDefault()
+	as.StubPrompt("Delete the branch locally and on GitHub?").AnswerDefault()
+	as.StubPrompt("What's next?").AnswerDefault()
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1 --admin")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "✓ Merged pull request #1 (The title of the PR)\n", output.Stderr())
+}
+
+func TestPrAddToMergeQueueAdminWithMergeStrategy(t *testing.T) {
+	http := initFakeHTTP()
+	defer http.Verify(t)
+
+	shared.RunCommandFinder(
+		"1",
+		&api.PullRequest{
+			ID:               "THE-ID",
+			Number:           1,
+			State:            "OPEN",
+			Title:            "The title of the PR",
+			MergeStateStatus: "CLEAN",
+			IsInMergeQueue:   false,
+		},
+		baseRepo("OWNER", "REPO", "main"),
+	)
+
+	http.Register(
+		httpmock.GraphQL(`mutation PullRequestMerge\b`),
+		httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
+			assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
+			assert.Equal(t, "MERGE", input["mergeMethod"].(string))
+			assert.NotContains(t, input, "commitHeadline")
+		}),
+	)
+
+	cs, cmdTeardown := run.Stub()
+	defer cmdTeardown(t)
+	cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
+
+	output, err := runCommand(http, "blueberries", true, "pr merge 1 --admin --merge")
+	if err != nil {
+		t.Fatalf("error running command `pr merge`: %v", err)
+	}
+
+	assert.Equal(t, "", output.String())
+	assert.Equal(t, "✓ Merged pull request #1 (The title of the PR)\n", output.Stderr())
+}
+
 type testEditor struct{}
 
 func (e testEditor) Edit(filename, text string) (string, error) {
diff --git a/pkg/cmd/repo/list/list_test.go b/pkg/cmd/repo/list/list_test.go
index 262ff6ffb..8d0bf72b2 100644
--- a/pkg/cmd/repo/list/list_test.go
+++ b/pkg/cmd/repo/list/list_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"io"
 	"net/http"
+	"strings"
 	"testing"
 	"time"
 
@@ -13,6 +14,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/cli/cli/v2/internal/config"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/cli/cli/v2/pkg/httpmock"
 	"github.com/cli/cli/v2/pkg/iostreams"
@@ -394,3 +396,44 @@ func TestRepoList_filtering(t *testing.T) {
 	assert.Equal(t, "", output.Stderr())
 	assert.Equal(t, "\nNo results match your search\n\n", output.String())
 }
+
+func TestRepoList_noVisibilityField(t *testing.T) {
+	ios, _, stdout, stderr := iostreams.Test()
+	ios.SetStdoutTTY(false)
+	ios.SetStdinTTY(false)
+	ios.SetStderrTTY(false)
+
+	reg := &httpmock.Registry{}
+	defer reg.Verify(t)
+
+	reg.Register(
+		httpmock.GraphQL(`query RepositoryList\b`),
+		httpmock.GraphQLQuery(`{"data":{"repositoryOwner":{"login":"octocat","repositories":{"totalCount":0}}}}`,
+			func(query string, params map[string]interface{}) {
+				assert.False(t, strings.Contains(query, "visibility"))
+			},
+		),
+	)
+
+	opts := ListOptions{
+		IO: ios,
+		HttpClient: func() (*http.Client, error) {
+			return &http.Client{Transport: reg}, nil
+		},
+		Config: func() (config.Config, error) {
+			return config.InheritEnv(config.NewBlankConfig()), nil
+		},
+		Now: func() time.Time {
+			t, _ := time.Parse(time.RFC822, "19 Feb 21 15:00 UTC")
+			return t
+		},
+		Limit:    30,
+		Detector: &fd.DisabledDetectorMock{},
+	}
+
+	err := listRun(&opts)
+
+	assert.NoError(t, err)
+	assert.Equal(t, "", stderr.String())
+	assert.Equal(t, "", stdout.String())
+}
diff --git a/pkg/liveshare/session_test.go b/pkg/liveshare/session_test.go
index c06a4f06a..cfe8ccd11 100644
--- a/pkg/liveshare/session_test.go
+++ b/pkg/liveshare/session_test.go
@@ -103,7 +103,7 @@ func TestServerStartSharing(t *testing.T) {
 
 	done := make(chan error)
 	go func() {
-		streamID, err := session.startSharing(ctx, serverProtocol, serverPort)
+		streamID, err := session.StartSharing(ctx, serverProtocol, serverPort)
 		if err != nil {
 			done <- fmt.Errorf("error sharing server: %w", err)
 		}
@@ -247,10 +247,10 @@ func TestInvalidHostKey(t *testing.T) {
 func TestKeepAliveNonBlocking(t *testing.T) {
 	session := &Session{keepAliveReason: make(chan string, 1)}
 	for i := 0; i < 2; i++ {
-		session.keepAlive("io")
+		session.KeepAlive("io")
 	}
 
-	// if keepAlive blocks, we'll never reach this and timeout the test
+	// if KeepAlive blocks, we'll never reach this and timeout the test
 	// timing out
 }
 
@@ -367,10 +367,10 @@ func TestSessionHeartbeat(t *testing.T) {
 
 	go session.heartbeat(ctx, 50*time.Millisecond)
 	go func() {
-		session.keepAlive("input")
+		session.KeepAlive("input")
 		wg.Wait()
 		wg.Add(1)
-		session.keepAlive("input")
+		session.KeepAlive("input")
 		wg.Wait()
 		done <- struct{}{}
 	}()
@@ -380,7 +380,7 @@ func TestSessionHeartbeat(t *testing.T) {
 		t.Errorf("error from server: %v", err)
 	case <-done:
 		activityCount := strings.Count(logger.String(), "input")
-		// by design keepAlive can drop requests, and therefore there is zero guarantee
+		// by design KeepAlive can drop requests, and therefore there is zero guarantee
 		// that we actually get two requests if the network happened to be slow (rarely)
 		// during testing.
 		if activityCount != 1 && activityCount != 2 {