Repository: cli/cli
Pull request #7496 (2023-05-26 23:12:31)

Add `gh cs view` command
Fixes: https://github.com/cli/cli/issues/7479

Adds the `gh cs view` command to give more details about a specific codespace. By default, we only show a few of the fields that most users will care about (similar to what VS Code does, shown below) but a wider array of options are available via the `--json` flag. Also, if the user is in a codespace and runs the command it will show details about that codespace.

What VS Code shows:
<img width="300" alt="image" src="https://github.com/cli/cli/assets/22037769/5bdd5f5e-f530-475c-ab02-0dc11ee85507">

`gh cs view` (no flags) shows the codespace picker:
<img width="418" alt="image" src="https://github.com/cli/cli/assets/22037769/eba906f9-c864-4e28-b969-4c8ec96c0153">

And once you select a codespace or provide the name using the `-c` flag, it will show this:
<img width="491" alt="image" src="https://github.com/cli/cli/assets/22037769/d0face2b-ccda-412a-8334-6de4077faa31">

All of the data available with the `--json` flag: 
<img width="500" alt="image" src="https://github.com/cli/cli/assets/22037769/7a110e73-3ef5-42a5-b32d-a24fcf04e9b7">


Tests are WIP pending some design review.


Patch:
diff --git a/api/queries_pr.go b/api/queries_pr.go
index eb41c4c14..6f121caab 100644
--- a/api/queries_pr.go
+++ b/api/queries_pr.go
@@ -97,84 +97,7 @@ type CommitStatusCheckRollup struct {
 	Contexts CheckContexts
 }
 
-// https://docs.github.com/en/graphql/reference/enums#checkrunstate
-type CheckRunState string
-
-const (
-	CheckRunStateActionRequired CheckRunState = "ACTION_REQUIRED"
-	CheckRunStateCancelled      CheckRunState = "CANCELLED"
-	CheckRunStateCompleted      CheckRunState = "COMPLETED"
-	CheckRunStateFailure        CheckRunState = "FAILURE"
-	CheckRunStateInProgress     CheckRunState = "IN_PROGRESS"
-	CheckRunStateNeutral        CheckRunState = "NEUTRAL"
-	CheckRunStatePending        CheckRunState = "PENDING"
-	CheckRunStateQueued         CheckRunState = "QUEUED"
-	CheckRunStateSkipped        CheckRunState = "SKIPPED"
-	CheckRunStateStale          CheckRunState = "STALE"
-	CheckRunStateStartupFailure CheckRunState = "STARTUP_FAILURE"
-	CheckRunStateSuccess        CheckRunState = "SUCCESS"
-	CheckRunStateTimedOut       CheckRunState = "TIMED_OUT"
-	CheckRunStateWaiting        CheckRunState = "WAITING"
-)
-
-type CheckRunCountByState struct {
-	State CheckRunState
-	Count int
-}
-
-// https://docs.github.com/en/graphql/reference/enums#statusstate
-type StatusState string
-
-const (
-	StatusStateError    StatusState = "ERROR"
-	StatusStateExpected StatusState = "EXPECTED"
-	StatusStateFailure  StatusState = "FAILURE"
-	StatusStatePending  StatusState = "PENDING"
-	StatusStateSuccess  StatusState = "SUCCESS"
-)
-
-type StatusContextCountByState struct {
-	State StatusState
-	Count int
-}
-
-// https://docs.github.com/en/graphql/reference/enums#checkstatusstate
-type CheckStatusState string
-
-const (
-	CheckStatusStateCompleted  CheckStatusState = "COMPLETED"
-	CheckStatusStateInProgress CheckStatusState = "IN_PROGRESS"
-	CheckStatusStatePending    CheckStatusState = "PENDING"
-	CheckStatusStateQueued     CheckStatusState = "QUEUED"
-	CheckStatusStateRequested  CheckStatusState = "REQUESTED"
-	CheckStatusStateWaiting    CheckStatusState = "WAITING"
-)
-
-// https://docs.github.com/en/graphql/reference/enums#checkconclusionstate
-type CheckConclusionState string
-
-const (
-	CheckConclusionStateActionRequired CheckConclusionState = "ACTION_REQUIRED"
-	CheckConclusionStateCancelled      CheckConclusionState = "CANCELLED"
-	CheckConclusionStateFailure        CheckConclusionState = "FAILURE"
-	CheckConclusionStateNeutral        CheckConclusionState = "NEUTRAL"
-	CheckConclusionStateSkipped        CheckConclusionState = "SKIPPED"
-	CheckConclusionStateStale          CheckConclusionState = "STALE"
-	CheckConclusionStateStartupFailure CheckConclusionState = "STARTUP_FAILURE"
-	CheckConclusionStateSuccess        CheckConclusionState = "SUCCESS"
-	CheckConclusionStateTimedOut       CheckConclusionState = "TIMED_OUT"
-)
-
 type CheckContexts struct {
-	// These fields are available on newer versions of the GraphQL API
-	// to support summary counts by state
-	CheckRunCount              int
-	CheckRunCountsByState      []CheckRunCountByState
-	StatusContextCount         int
-	StatusContextCountsByState []StatusContextCountByState
-
-	// These are available on older versions and provide more details
-	// required for checks
 	Nodes    []CheckContext
 	PageInfo struct {
 		HasNextPage bool
@@ -196,18 +119,18 @@ type CheckContext struct {
 	// QUEUED IN_PROGRESS COMPLETED WAITING PENDING REQUESTED
 	Status string `json:"status"`
 	// ACTION_REQUIRED TIMED_OUT CANCELLED FAILURE SUCCESS NEUTRAL SKIPPED STARTUP_FAILURE STALE
-	Conclusion  CheckConclusionState `json:"conclusion"`
-	StartedAt   time.Time            `json:"startedAt"`
-	CompletedAt time.Time            `json:"completedAt"`
-	DetailsURL  string               `json:"detailsUrl"`
+	Conclusion  string    `json:"conclusion"`
+	StartedAt   time.Time `json:"startedAt"`
+	CompletedAt time.Time `json:"completedAt"`
+	DetailsURL  string    `json:"detailsUrl"`
 
 	/* StatusContext fields */
 
 	Context string `json:"context"`
 	// EXPECTED ERROR FAILURE PENDING SUCCESS
-	State     StatusState `json:"state"`
-	TargetURL string      `json:"targetUrl"`
-	CreatedAt time.Time   `json:"createdAt"`
+	State     string    `json:"state"`
+	TargetURL string    `json:"targetUrl"`
+	CreatedAt time.Time `json:"createdAt"`
 }
 
 type PRRepository struct {
@@ -345,131 +268,38 @@ func (pr *PullRequest) ChecksStatus() PullRequestChecksStatus {
 		return summary
 	}
 
-	contexts := pr.StatusCheckRollup.Nodes[0].Commit.StatusCheckRollup.Contexts
-
-	// If this commit has counts by state then we can summarise check status from those
-	if len(contexts.CheckRunCountsByState) != 0 && len(contexts.StatusContextCountsByState) != 0 {
-		summary.Total = contexts.CheckRunCount + contexts.StatusContextCount
-		for _, countByState := range contexts.CheckRunCountsByState {
-			switch parseCheckStatusFromCheckRunState(countByState.State) {
-			case passing:
-				summary.Passing += countByState.Count
-			case failing:
-				summary.Failing += countByState.Count
-			default:
-				summary.Pending += countByState.Count
-			}
-		}
-
-		for _, countByState := range contexts.StatusContextCountsByState {
-			switch parseCheckStatusFromStatusState(countByState.State) {
-			case passing:
-				summary.Passing += countByState.Count
-			case failing:
-				summary.Failing += countByState.Count
-			default:
-				summary.Pending += countByState.Count
-			}
-		}
-
-		return summary
-	}
-
-	// If we don't have the counts by state, then we'll need to summarise by looking at the more detailed contexts
-	for _, c := range contexts.Nodes {
-		// Nodes are a discriminated union of CheckRun or StatusContext and we can match on
-		// the TypeName to narrow the type.
-		if c.TypeName == "CheckRun" {
-			// https://docs.github.com/en/graphql/reference/enums#checkstatusstate
-			// If the status is completed then we can check the conclusion field
+	commit := pr.StatusCheckRollup.Nodes[0].Commit
+	for _, c := range commit.StatusCheckRollup.Contexts.Nodes {
+		// Nodes are a discriminated union of CheckRun or StatusContext,
+		// but we can use the State field to disambiguate, rather than using the TypeName.
+		// First we try to get the State, as if we have a StatusContext
+		state := c.State
+		// But if the State is empty, then we assume we actually have a CheckRun
+		if state == "" {
+			// If the Status of a CheckRun is COMPLETED, then we want to look more closely
+			// at the Conclusion, as that contains the relevant information.
 			if c.Status == "COMPLETED" {
-				switch parseCheckStatusFromCheckConclusionState(c.Conclusion) {
-				case passing:
-					summary.Passing++
-				case failing:
-					summary.Failing++
-				default:
-					summary.Pending++
-				}
-				// otherwise we're in some form of pending state:
-				// "COMPLETED", "IN_PROGRESS", "PENDING", "QUEUED", "REQUESTED", "WAITING" or otherwise unknown
+				state = c.Conclusion
 			} else {
-				summary.Pending++
-			}
-
-		} else { // c.TypeName == StatusContext
-			switch parseCheckStatusFromStatusState(c.State) {
-			case passing:
-				summary.Passing++
-			case failing:
-				summary.Failing++
-			default:
-				summary.Pending++
+				state = c.Status
 			}
 		}
+		switch state {
+		case "SUCCESS", "NEUTRAL", "SKIPPED":
+			summary.Passing++
+		case "ERROR", "FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED":
+			summary.Failing++
+		// We treat anything that isn't Passing or Failing as Pending, which included any future unknown states
+		// we might get back from the API.
+		default: // "EXPECTED", "REQUESTED", "WAITING", "QUEUED", "PENDING", "IN_PROGRESS", "STALE", "STARTUP_FAILURE"
+			summary.Pending++
+		}
 		summary.Total++
 	}
 
 	return summary
 }
 
-type checkStatus int
-
-const (
-	passing checkStatus = iota
-	failing
-	pending
-)
-
-func parseCheckStatusFromStatusState(state StatusState) checkStatus {
-	switch state {
-	case StatusStateSuccess:
-		return passing
-	case StatusStateFailure, StatusStateError:
-		return failing
-	case StatusStateExpected, StatusStatePending:
-		return pending
-	// Currently, we treat anything unknown as pending, which includes any future unknown
-	// states we might get back from the API. It might be interesting to do some work to add an additional
-	// unknown state.
-	default:
-		return pending
-	}
-}
-
-func parseCheckStatusFromCheckRunState(state CheckRunState) checkStatus {
-	switch state {
-	case CheckRunStateNeutral, CheckRunStateSkipped, CheckRunStateSuccess:
-		return passing
-	case CheckRunStateActionRequired, CheckRunStateCancelled, CheckRunStateFailure, CheckRunStateTimedOut:
-		return failing
-	case CheckRunStateCompleted, CheckRunStateInProgress, CheckRunStatePending, CheckRunStateQueued,
-		CheckRunStateStale, CheckRunStateStartupFailure, CheckRunStateWaiting:
-		return pending
-	// Currently, we treat anything unknown as pending, which includes any future unknown
-	// states we might get back from the API. It might be interesting to do some work to add an additional
-	// unknown state.
-	default:
-		return pending
-	}
-}
-
-func parseCheckStatusFromCheckConclusionState(state CheckConclusionState) checkStatus {
-	switch state {
-	case CheckConclusionStateNeutral, CheckConclusionStateSkipped, CheckConclusionStateSuccess:
-		return passing
-	case CheckConclusionStateActionRequired, CheckConclusionStateCancelled, CheckConclusionStateFailure, CheckConclusionStateTimedOut:
-		return failing
-	case CheckConclusionStateStale, CheckConclusionStateStartupFailure:
-		return pending
-	// Currently, we treat anything unknown as pending, which includes any future unknown
-	// states we might get back from the API. It might be interesting to do some work to add an additional
-	// unknown state.
-	default:
-		return pending
-	}
-}
-
 func (pr *PullRequest) DisplayableReviews() PullRequestReviews {
 	published := []PullRequestReview{}
 	for _, prr := range pr.Reviews.Nodes {
diff --git a/api/query_builder.go b/api/query_builder.go
index 76eb5294a..3e54f4c60 100644
--- a/api/query_builder.go
+++ b/api/query_builder.go
@@ -143,31 +143,7 @@ var autoMergeRequest = shortenQuery(`
 	}
 `)
 
-func StatusCheckRollupGraphQLWithCountByState() string {
-	return shortenQuery(`
-	statusCheckRollup: commits(last: 1) {
-		nodes {
-			commit {
-				statusCheckRollup {
-					contexts {
-						checkRunCount,
-						checkRunCountsByState {
-						  state,
-						  count
-						},
-						statusContextCount,
-						statusContextCountsByState {
-						  state,
-						  count
-						}
-					}
-				}
-			}
-		}
-	}`)
-}
-
-func StatusCheckRollupGraphQLWithoutCountByState(after string) string {
+func StatusCheckRollupGraphQL(after string) string {
 	var afterClause string
 	if after != "" {
 		afterClause = ",after:" + after
@@ -344,9 +320,7 @@ func IssueGraphQL(fields []string) string {
 		case "requiresStrictStatusChecks": // pseudo-field
 			q = append(q, `baseRef{branchProtectionRule{requiresStrictStatusChecks}}`)
 		case "statusCheckRollup":
-			q = append(q, StatusCheckRollupGraphQLWithoutCountByState(""))
-		case "statusCheckRollupWithCountByState": // pseudo-field
-			q = append(q, StatusCheckRollupGraphQLWithCountByState())
+			q = append(q, StatusCheckRollupGraphQL(""))
 		default:
 			q = append(q, field)
 		}
diff --git a/cmd/gen-docs/main.go b/cmd/gen-docs/main.go
index f3a12ef76..7c5078df9 100644
--- a/cmd/gen-docs/main.go
+++ b/cmd/gen-docs/main.go
@@ -2,17 +2,13 @@ package main
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"path/filepath"
 	"strings"
 
-	"github.com/cli/cli/v2/internal/config"
 	"github.com/cli/cli/v2/internal/docs"
-	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/pkg/cmd/root"
 	"github.com/cli/cli/v2/pkg/cmdutil"
-	"github.com/cli/cli/v2/pkg/extensions"
 	"github.com/cli/cli/v2/pkg/iostreams"
 	"github.com/spf13/pflag"
 )
@@ -45,13 +41,9 @@ func run(args []string) error {
 	}
 
 	ios, _, _, _ := iostreams.Test()
-	rootCmd, _ := root.NewCmdRoot(&cmdutil.Factory{
+	rootCmd := root.NewCmdRoot(&cmdutil.Factory{
 		IOStreams: ios,
 		Browser:   &browser{},
-		Config: func() (config.Config, error) {
-			return config.NewBlankConfig(), nil
-		},
-		ExtensionManager: &em{},
 	}, "", "")
 	rootCmd.InitDefaultHelpCmd()
 
@@ -87,42 +79,8 @@ func linkHandler(name string) string {
 	return fmt.Sprintf("./%s", strings.TrimSuffix(name, ".md"))
 }
 
-// Implements browser.Browser interface.
 type browser struct{}
 
-func (b *browser) Browse(_ string) error {
+func (b *browser) Browse(url string) error {
 	return nil
 }
-
-// Implements extensions.ExtensionManager interface.
-type em struct{}
-
-func (e *em) List() []extensions.Extension {
-	return nil
-}
-
-func (e *em) Install(_ ghrepo.Interface, _ string) error {
-	return nil
-}
-
-func (e *em) InstallLocal(_ string) error {
-	return nil
-}
-
-func (e *em) Upgrade(_ string, _ bool) error {
-	return nil
-}
-
-func (e *em) Remove(_ string) error {
-	return nil
-}
-
-func (e *em) Dispatch(_ []string, _ io.Reader, _, _ io.Writer) (bool, error) {
-	return false, nil
-}
-
-func (e *em) Create(_ string, _ extensions.ExtTemplateType) error {
-	return nil
-}
-
-func (e *em) EnableDryRunMode() {}
diff --git a/cmd/gh/main.go b/cmd/gh/main.go
index c36a4e6e6..f231d7d2c 100644
--- a/cmd/gh/main.go
+++ b/cmd/gh/main.go
@@ -14,10 +14,16 @@ import (
 
 	surveyCore "github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
+	"github.com/MakeNowJust/heredoc"
 	"github.com/cli/cli/v2/api"
+	"github.com/cli/cli/v2/git"
 	"github.com/cli/cli/v2/internal/build"
 	"github.com/cli/cli/v2/internal/config"
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/internal/run"
+	"github.com/cli/cli/v2/internal/text"
 	"github.com/cli/cli/v2/internal/update"
+	"github.com/cli/cli/v2/pkg/cmd/alias/expand"
 	"github.com/cli/cli/v2/pkg/cmd/factory"
 	"github.com/cli/cli/v2/pkg/cmd/root"
 	"github.com/cli/cli/v2/pkg/cmdutil"
@@ -87,9 +93,11 @@ func mainRun() exitCode {
 		cobra.MousetrapHelpText = ""
 	}
 
-	rootCmd, err := root.NewCmdRoot(cmdFactory, buildVersion, buildDate)
+	rootCmd := root.NewCmdRoot(cmdFactory, buildVersion, buildDate)
+
+	cfg, err := cmdFactory.Config()
 	if err != nil {
-		fmt.Fprintf(stderr, "failed to create root command: %s\n", err)
+		fmt.Fprintf(stderr, "failed to read configuration:  %s\n", err)
 		return exitError
 	}
 
@@ -98,10 +106,110 @@ func mainRun() exitCode {
 		expandedArgs = os.Args[1:]
 	}
 
-	// translate `gh help <command>` to `gh <command> --help` for extensions.
-	if len(expandedArgs) >= 2 && expandedArgs[0] == "help" && isExtensionCommand(rootCmd, expandedArgs[1:]) {
-		expandedArgs = expandedArgs[1:]
-		expandedArgs = append(expandedArgs, "--help")
+	// translate `gh help <command>` to `gh <command> --help` for extensions
+	if len(expandedArgs) == 2 && expandedArgs[0] == "help" && !hasCommand(rootCmd, expandedArgs[1:]) {
+		expandedArgs = []string{expandedArgs[1], "--help"}
+	}
+
+	if !hasCommand(rootCmd, expandedArgs) {
+		originalArgs := expandedArgs
+		isShell := false
+
+		argsForExpansion := append([]string{"gh"}, expandedArgs...)
+		expandedArgs, isShell, err = expand.ExpandAlias(cfg, argsForExpansion, nil)
+		if err != nil {
+			fmt.Fprintf(stderr, "failed to process aliases:  %s\n", err)
+			return exitError
+		}
+
+		if hasDebug {
+			fmt.Fprintf(stderr, "%v -> %v\n", originalArgs, expandedArgs)
+		}
+
+		if isShell {
+			exe, err := safeexec.LookPath(expandedArgs[0])
+			if err != nil {
+				fmt.Fprintf(stderr, "failed to run external command: %s", err)
+				return exitError
+			}
+
+			externalCmd := exec.Command(exe, expandedArgs[1:]...)
+			externalCmd.Stderr = os.Stderr
+			externalCmd.Stdout = os.Stdout
+			externalCmd.Stdin = os.Stdin
+			preparedCmd := run.PrepareCmd(externalCmd)
+
+			err = preparedCmd.Run()
+			if err != nil {
+				var execError *exec.ExitError
+				if errors.As(err, &execError) {
+					return exitCode(execError.ExitCode())
+				}
+				fmt.Fprintf(stderr, "failed to run external command: %s\n", err)
+				return exitError
+			}
+
+			return exitOK
+		} else if len(expandedArgs) > 0 && !hasCommand(rootCmd, expandedArgs) {
+			extensionManager := cmdFactory.ExtensionManager
+			if found, err := extensionManager.Dispatch(expandedArgs, os.Stdin, os.Stdout, os.Stderr); err != nil {
+				var execError *exec.ExitError
+				if errors.As(err, &execError) {
+					return exitCode(execError.ExitCode())
+				}
+				fmt.Fprintf(stderr, "failed to run extension: %s\n", err)
+				return exitError
+			} else if found {
+				return exitOK
+			}
+		}
+	}
+
+	// provide completions for aliases and extensions
+	rootCmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+		var results []string
+		aliases := cfg.Aliases()
+		for aliasName, aliasValue := range aliases.All() {
+			if strings.HasPrefix(aliasName, toComplete) {
+				var s string
+				if strings.HasPrefix(aliasValue, "!") {
+					s = fmt.Sprintf("%s\tShell alias", aliasName)
+				} else {
+					aliasValue = text.Truncate(80, aliasValue)
+					s = fmt.Sprintf("%s\tAlias for %s", aliasName, aliasValue)
+				}
+				results = append(results, s)
+			}
+		}
+		for _, ext := range cmdFactory.ExtensionManager.List() {
+			if strings.HasPrefix(ext.Name(), toComplete) {
+				var s string
+				if ext.IsLocal() {
+					s = fmt.Sprintf("%s\tLocal extension gh-%s", ext.Name(), ext.Name())
+				} else {
+					path := ext.URL()
+					if u, err := git.ParseURL(ext.URL()); err == nil {
+						if r, err := ghrepo.FromURL(u); err == nil {
+							path = ghrepo.FullName(r)
+						}
+					}
+					s = fmt.Sprintf("%s\tExtension %s", ext.Name(), path)
+				}
+				results = append(results, s)
+			}
+		}
+		return results, cobra.ShellCompDirectiveNoFileComp
+	}
+
+	authError := errors.New("authError")
+	rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
+		// require that the user is authenticated before running most commands
+		if cmdutil.IsAuthCheckEnabled(cmd) && !cmdutil.CheckAuth(cfg) {
+			fmt.Fprint(stderr, authHelp())
+			return authError
+		}
+
+		return nil
 	}
 
 	rootCmd.SetArgs(expandedArgs)
@@ -109,8 +217,6 @@ func mainRun() exitCode {
 	if cmd, err := rootCmd.ExecuteContextC(ctx); err != nil {
 		var pagerPipeError *iostreams.ErrClosedPagerPipe
 		var noResultsError cmdutil.NoResultsError
-		var execError *exec.ExitError
-		var authError *root.AuthError
 		if err == cmdutil.SilentError {
 			return exitError
 		} else if cmdutil.IsUserCancellation(err) {
@@ -119,7 +225,7 @@ func mainRun() exitCode {
 				fmt.Fprint(stderr, "\n")
 			}
 			return exitCancel
-		} else if errors.As(err, &authError) {
+		} else if errors.Is(err, authError) {
 			return exitAuth
 		} else if errors.As(err, &pagerPipeError) {
 			// ignore the error raised when piping to a closed pager
@@ -130,8 +236,6 @@ func mainRun() exitCode {
 			}
 			// no results is not a command failure
 			return exitOK
-		} else if errors.As(err, &execError) {
-			return exitCode(execError.ExitCode())
 		}
 
 		printError(stderr, err, cmd, hasDebug)
@@ -180,10 +284,10 @@ func mainRun() exitCode {
 	return exitOK
 }
 
-// isExtensionCommand returns true if args resolve to an extension command.
-func isExtensionCommand(rootCmd *cobra.Command, args []string) bool {
-	c, _, err := rootCmd.Find(args)
-	return err == nil && c != nil && c.GroupID == "extension"
+// hasCommand returns true if args resolve to a built-in command
+func hasCommand(rootCmd *cobra.Command, args []string) bool {
+	c, _, err := rootCmd.Traverse(args)
+	return err == nil && c != rootCmd
 }
 
 func printError(out io.Writer, err error, cmd *cobra.Command, debug bool) {
@@ -208,6 +312,27 @@ func printError(out io.Writer, err error, cmd *cobra.Command, debug bool) {
 	}
 }
 
+func authHelp() string {
+	if os.Getenv("GITHUB_ACTIONS") == "true" {
+		return heredoc.Doc(`
+			gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable. Example:
+			  env:
+			    GH_TOKEN: ${{ github.token }}
+		`)
+	}
+
+	if os.Getenv("CI") != "" {
+		return heredoc.Doc(`
+			gh: To use GitHub CLI in automation, set the GH_TOKEN environment variable.
+		`)
+	}
+
+	return heredoc.Doc(`
+		To get started with GitHub CLI, please run:  gh auth login
+		Alternatively, populate the GH_TOKEN environment variable with a GitHub API authentication token.
+	`)
+}
+
 func shouldCheckForUpdate() bool {
 	if os.Getenv("GH_NO_UPDATE_NOTIFIER") != "" {
 		return false
diff --git a/internal/build/build.go b/internal/build/build.go
index 267315ad2..8c3e78a7e 100644
--- a/internal/build/build.go
+++ b/internal/build/build.go
@@ -1,7 +1,6 @@
 package build
 
 import (
-	"os"
 	"runtime/debug"
 )
 
@@ -17,11 +16,4 @@ func init() {
 			Version = info.Main.Version
 		}
 	}
-
-	// Signal the tcell library to skip its expensive `init` block. This saves 30-40ms in startup
-	// time for the gh process. The downside is that some Unicode glyphs from user-generated
-	// content might cause mis-alignment in tcell-enabled views.
-	//
-	// https://github.com/gdamore/tcell/commit/2f889d79bd61b1fd2f43372529975a65b792a7ae
-	_ = os.Setenv("TCELL_MINIMIZE", "1")
 }
diff --git a/internal/codespaces/api/api.go b/internal/codespaces/api/api.go
index c45eafb87..d66aff065 100644
--- a/internal/codespaces/api/api.go
+++ b/internal/codespaces/api/api.go
@@ -192,6 +192,15 @@ type Codespace struct {
 	PendingOperationDisabledReason string              `json:"pending_operation_disabled_reason"`
 	IdleTimeoutNotice              string              `json:"idle_timeout_notice"`
 	WebURL                         string              `json:"web_url"`
+	DevContainerPath               string              `json:"devcontainer_path"`
+	Prebuild                       bool                `json:"prebuild"`
+	Location                       string              `json:"location"`
+	IdleTimeoutMinutes             int                 `json:"idle_timeout_minutes"`
+	RetentionPeriodMinutes         int                 `json:"retention_period_minutes"`
+	RetentionExpiresAt             string              `json:"retention_expires_at"`
+	RecentFolders                  []string            `json:"recent_folders"`
+	BillableOwner                  User                `json:"billable_owner"`
+	EnvironmentId                  string              `json:"environment_id"`
 }
 
 type CodespaceGitStatus struct {
@@ -230,8 +239,8 @@ type CodespaceConnection struct {
 	HostPublicKeys []string `json:"hostPublicKeys"`
 }
 
-// CodespaceFields is the list of exportable fields for a codespace.
-var CodespaceFields = []string{
+// ListCodespaceFields is the list of exportable fields for a codespace when using the `gh cs list` command.
+var ListCodespaceFields = []string{
 	"displayName",
 	"name",
 	"owner",
@@ -244,6 +253,30 @@ var CodespaceFields = []string{
 	"vscsTarget",
 }
 
+// ViewCodespaceFields is the list of exportable fields for a codespace when using the `gh cs view` command.
+var ViewCodespaceFields = []string{
+	"name",
+	"displayName",
+	"state",
+	"owner",
+	"billableOwner",
+	"location",
+	"repository",
+	"gitStatus",
+	"devcontainerPath",
+	"machineName",
+	"machineDisplayName",
+	"prebuild",
+	"createdAt",
+	"lastUsedAt",
+	"idleTimeoutMinutes",
+	"retentionPeriodDays",
+	"retentionExpiresAt",
+	"recentFolders",
+	"vscsTarget",
+	"environmentId",
+}
+
 func (c *Codespace) ExportData(fields []string) map[string]interface{} {
 	v := reflect.ValueOf(c).Elem()
 	data := map[string]interface{}{}
@@ -256,11 +289,17 @@ func (c *Codespace) ExportData(fields []string) map[string]interface{} {
 			data[f] = c.Repository.FullName
 		case "machineName":
 			data[f] = c.Machine.Name
+		case "machineDisplayName":
+			data[f] = c.Machine.DisplayName
+		case "retentionPeriodDays":
+			data[f] = c.RetentionPeriodMinutes / 1440
 		case "gitStatus":
 			data[f] = map[string]interface{}{
 				"ref":                   c.GitStatus.Ref,
 				"hasUnpushedChanges":    c.GitStatus.HasUnpushedChanges,
 				"hasUncommittedChanges": c.GitStatus.HasUncommittedChanges,
+				"ahead":                 c.GitStatus.Ahead,
+				"behind":                c.GitStatus.Behind,
 			}
 		case "vscsTarget":
 			if c.VSCSTarget != "" && c.VSCSTarget != VSCSTargetProduction {
diff --git a/internal/featuredetection/feature_detection.go b/internal/featuredetection/feature_detection.go
index 983784804..7cd9fa2ff 100644
--- a/internal/featuredetection/feature_detection.go
+++ b/internal/featuredetection/feature_detection.go
@@ -23,15 +23,10 @@ var allIssueFeatures = IssueFeatures{
 
 type PullRequestFeatures struct {
 	MergeQueue bool
-	// CheckRunAndStatusContextCounts indicates whether the API supports
-	// the checkRunCount, checkRunCountsByState, statusContextCount and stausContextCountsByState
-	// fields on the StatusCheckRollupContextConnection
-	CheckRunAndStatusContextCounts bool
 }
 
 var allPullRequestFeatures = PullRequestFeatures{
-	MergeQueue:                     true,
-	CheckRunAndStatusContextCounts: true,
+	MergeQueue: true,
 }
 
 type RepositoryFeatures struct {
@@ -98,40 +93,28 @@ func (d *detector) PullRequestFeatures() (PullRequestFeatures, error) {
 	// 	return allPullRequestFeatures, nil
 	// }
 
-	var pullRequestFeatureDetection struct {
+	features := PullRequestFeatures{}
+
+	var featureDetection struct {
 		PullRequest struct {
 			Fields []struct {
 				Name string
 			} `graphql:"fields(includeDeprecated: true)"`
 		} `graphql:"PullRequest: __type(name: \"PullRequest\")"`
-		StatusCheckRollupContextConnection struct {
-			Fields []struct {
-				Name string
-			} `graphql:"fields(includeDeprecated: true)"`
-		} `graphql:"StatusCheckRollupContextConnection: __type(name: \"StatusCheckRollupContextConnection\")"`
 	}
 
 	gql := api.NewClientFromHTTP(d.httpClient)
-	if err := gql.Query(d.host, "PullRequest_fields", &pullRequestFeatureDetection, nil); err != nil {
-		return PullRequestFeatures{}, err
+	err := gql.Query(d.host, "PullRequest_fields", &featureDetection, nil)
+	if err != nil {
+		return features, err
 	}
 
-	features := PullRequestFeatures{}
-
-	for _, field := range pullRequestFeatureDetection.PullRequest.Fields {
+	for _, field := range featureDetection.PullRequest.Fields {
 		if field.Name == "isInMergeQueue" {
 			features.MergeQueue = true
 		}
 	}
 
-	for _, field := range pullRequestFeatureDetection.StatusCheckRollupContextConnection.Fields {
-		// We only check for checkRunCount here but it, checkRunCountsByState, statusContextCount and statusContextCountsByState
-		// were all introduced in the same version of the API.
-		if field.Name == "checkRunCount" {
-			features.CheckRunAndStatusContextCounts = true
-		}
-	}
-
 	return features, nil
 }
 
diff --git a/pkg/cmd/alias/expand/expand.go b/pkg/cmd/alias/expand/expand.go
new file mode 100644
index 000000000..d0e2627fb
--- /dev/null
+++ b/pkg/cmd/alias/expand/expand.go
@@ -0,0 +1,90 @@
+package expand
+
+import (
+	"errors"
+	"fmt"
+	"os/exec"
+	"regexp"
+	"runtime"
+	"strings"
+
+	"github.com/cli/cli/v2/internal/config"
+	"github.com/cli/cli/v2/pkg/findsh"
+	"github.com/google/shlex"
+)
+
+// ExpandAlias processes argv to see if it should be rewritten according to a user's aliases. The
+// second return value indicates whether the alias should be executed in a new shell process instead
+// of running gh itself.
+func ExpandAlias(cfg config.Config, args []string, findShFunc func() (string, error)) (expanded []string, isShell bool, err error) {
+	if len(args) < 2 {
+		// the command is lacking a subcommand
+		return
+	}
+	expanded = args[1:]
+
+	aliases := cfg.Aliases()
+
+	expansion, getErr := aliases.Get(args[1])
+	if getErr != nil {
+		return
+	}
+
+	if strings.HasPrefix(expansion, "!") {
+		isShell = true
+		if findShFunc == nil {
+			findShFunc = findSh
+		}
+		shPath, shErr := findShFunc()
+		if shErr != nil {
+			err = shErr
+			return
+		}
+
+		expanded = []string{shPath, "-c", expansion[1:]}
+
+		if len(args[2:]) > 0 {
+			expanded = append(expanded, "--")
+			expanded = append(expanded, args[2:]...)
+		}
+
+		return
+	}
+
+	extraArgs := []string{}
+	for i, a := range args[2:] {
+		if !strings.Contains(expansion, "$") {
+			extraArgs = append(extraArgs, a)
+		} else {
+			expansion = strings.ReplaceAll(expansion, fmt.Sprintf("$%d", i+1), a)
+		}
+	}
+	lingeringRE := regexp.MustCompile(`\$\d`)
+	if lingeringRE.MatchString(expansion) {
+		err = fmt.Errorf("not enough arguments for alias: %s", expansion)
+		return
+	}
+
+	var newArgs []string
+	newArgs, err = shlex.Split(expansion)
+	if err != nil {
+		return
+	}
+
+	expanded = append(newArgs, extraArgs...)
+	return
+}
+
+func findSh() (string, error) {
+	shPath, err := findsh.Find()
+	if err != nil {
+		if errors.Is(err, exec.ErrNotFound) {
+			if runtime.GOOS == "windows" {
+				return "", errors.New("unable to locate sh to execute the shell alias with. The sh.exe interpreter is typically distributed with Git for Windows.")
+			}
+			return "", errors.New("unable to locate sh to execute shell alias with")
+		}
+		return "", err
+	}
+	return shPath, nil
+}
diff --git a/pkg/cmd/alias/imports/import.go b/pkg/cmd/alias/imports/import.go
index f7de0bb78..f724e2699 100644
--- a/pkg/cmd/alias/imports/import.go
+++ b/pkg/cmd/alias/imports/import.go
@@ -21,8 +21,7 @@ type ImportOptions struct {
 	Filename          string
 	OverwriteExisting bool
 
-	validAliasName      func(string) bool
-	validAliasExpansion func(string) bool
+	existingCommand func(string) bool
 }
 
 func NewCmdImport(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Command {
@@ -75,8 +74,7 @@ func NewCmdImport(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co
 				opts.Filename = args[0]
 			}
 
-			opts.validAliasName = shared.ValidAliasNameFunc(cmd)
-			opts.validAliasExpansion = shared.ValidAliasExpansionFunc(cmd)
+			opts.existingCommand = shared.ExistingCommandFunc(f, cmd)
 
 			if runF != nil {
 				return runF(opts)
@@ -122,9 +120,9 @@ func importRun(opts *ImportOptions) error {
 	var msg strings.Builder
 
 	for _, alias := range getSortedKeys(aliasMap) {
-		if !opts.validAliasName(alias) {
+		if opts.existingCommand(alias) {
 			msg.WriteString(
-				fmt.Sprintf("%s Could not import alias %s: already a gh command, extension, or alias\n",
+				fmt.Sprintf("%s Could not import alias %s: already a gh command\n",
 					cs.FailureIcon(),
 					cs.Bold(alias),
 				),
@@ -135,9 +133,9 @@ func importRun(opts *ImportOptions) error {
 
 		expansion := aliasMap[alias]
 
-		if !opts.validAliasExpansion(expansion) {
+		if !(strings.HasPrefix(expansion, "!") || opts.existingCommand(expansion)) {
 			msg.WriteString(
-				fmt.Sprintf("%s Could not import alias %s: expansion does not correspond to a gh command, extension, or alias\n",
+				fmt.Sprintf("%s Could not import alias %s: expansion does not correspond to a gh command\n",
 					cs.FailureIcon(),
 					cs.Bold(alias),
 				),
diff --git a/pkg/cmd/alias/set/set.go b/pkg/cmd/alias/set/set.go
index 20c86c473..871737881 100644
--- a/pkg/cmd/alias/set/set.go
+++ b/pkg/cmd/alias/set/set.go
@@ -21,8 +21,7 @@ type SetOptions struct {
 	Expansion string
 	IsShell   bool
 
-	validAliasName      func(string) bool
-	validAliasExpansion func(string) bool
+	existingCommand func(string) bool
 }
 
 func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command {
@@ -60,9 +59,6 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
 			$ gh alias set homework 'issue list --assignee @me'
 			$ gh homework
 
-			$ gh alias set 'issue mine' 'issue list --mention @me'
-			$ gh issue mine
-
 			$ gh alias set epicsBy 'issue list --author="$1" --label="epic"'
 			$ gh epicsBy vilmibm  #=> gh issue list --author="vilmibm" --label="epic"
 
@@ -74,8 +70,7 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
 			opts.Name = args[0]
 			opts.Expansion = args[1]
 
-			opts.validAliasName = shared.ValidAliasNameFunc(cmd)
-			opts.validAliasExpansion = shared.ValidAliasExpansionFunc(cmd)
+			opts.existingCommand = shared.ExistingCommandFunc(f, cmd)
 
 			if runF != nil {
 				return runF(opts)
@@ -109,16 +104,18 @@ func setRun(opts *SetOptions) error {
 		fmt.Fprintf(opts.IO.ErrOut, "- Adding alias for %s: %s\n", cs.Bold(opts.Name), cs.Bold(expansion))
 	}
 
-	if opts.IsShell && !strings.HasPrefix(expansion, "!") {
+	isShell := opts.IsShell
+	if isShell && !strings.HasPrefix(expansion, "!") {
 		expansion = "!" + expansion
 	}
+	isShell = strings.HasPrefix(expansion, "!")
 
-	if !opts.validAliasName(opts.Name) {
-		return fmt.Errorf("could not create alias: %q is already a gh command, extension, or alias", opts.Name)
+	if opts.existingCommand(opts.Name) {
+		return fmt.Errorf("could not create alias: %q is already a gh command", opts.Name)
 	}
 
-	if !opts.validAliasExpansion(expansion) {
-		return fmt.Errorf("could not create alias: %s does not correspond to a gh command, extension, or alias", expansion)
+	if !isShell && !opts.existingCommand(expansion) {
+		return fmt.Errorf("could not create alias: %s does not correspond to a gh command", expansion)
 	}
 
 	successMsg := fmt.Sprintf("%s Added alias.", cs.SuccessIcon())
diff --git a/pkg/cmd/alias/shared/validations.go b/pkg/cmd/alias/shared/validations.go
index 9a213ab98..6557f66ca 100644
--- a/pkg/cmd/alias/shared/validations.go
+++ b/pkg/cmd/alias/shared/validations.go
@@ -1,18 +1,14 @@
 package shared
 
 import (
-	"strings"
-
+	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/google/shlex"
 	"github.com/spf13/cobra"
 )
 
-// ValidAliasNameFunc returns a function that will check if the given string
-// is a valid alias name. A name is valid if:
-//   - it does not shadow an existing command,
-//   - it is not nested under a command that is runnable,
-//   - it is not nested under a command that does not exist.
-func ValidAliasNameFunc(cmd *cobra.Command) func(string) bool {
+// ExistingCommandFunc returns a function that will check if the given string
+// corresponds to an existing command.
+func ExistingCommandFunc(f *cmdutil.Factory, cmd *cobra.Command) func(string) bool {
 	return func(args string) bool {
 		split, err := shlex.Split(args)
 		if err != nil || len(split) == 0 {
@@ -20,32 +16,17 @@ func ValidAliasNameFunc(cmd *cobra.Command) func(string) bool {
 		}
 
 		rootCmd := cmd.Root()
-		foundCmd, foundArgs, _ := rootCmd.Find(split)
-		if foundCmd != nil && !foundCmd.Runnable() && len(foundArgs) == 1 {
-			return true
-		}
-
-		return false
-	}
-}
-
-// ValidAliasExpansionFunc returns a function that will check if the given string
-// is a valid alias expansion. An expansion is valid if:
-//   - it is a shell expansion,
-//   - it is a non-shell expansion that corresponds to an existing command, extension, or alias.
-func ValidAliasExpansionFunc(cmd *cobra.Command) func(string) bool {
-	return func(expansion string) bool {
-		if strings.HasPrefix(expansion, "!") {
+		cmd, _, err = rootCmd.Traverse(split)
+		if err == nil && cmd != rootCmd {
 			return true
 		}
 
-		split, err := shlex.Split(expansion)
-		if err != nil || len(split) == 0 {
-			return false
+		for _, ext := range f.ExtensionManager.List() {
+			if ext.Name() == split[0] {
+				return true
+			}
 		}
 
-		rootCmd := cmd.Root()
-		cmd, _, _ = rootCmd.Find(split)
-		return cmd != rootCmd
+		return false
 	}
 }
diff --git a/pkg/cmd/codespace/list.go b/pkg/cmd/codespace/list.go
index 6b1cd1cc0..bba34ff45 100644
--- a/pkg/cmd/codespace/list.go
+++ b/pkg/cmd/codespace/list.go
@@ -70,7 +70,7 @@ func newListCmd(app *App) *cobra.Command {
 
 	listCmd.Flags().StringVarP(&opts.orgName, "org", "o", "", "The `login` handle of the organization to list codespaces for (admin-only)")
 	listCmd.Flags().StringVarP(&opts.userName, "user", "u", "", "The `username` to list codespaces for (used with --org)")
-	cmdutil.AddJSONFlags(listCmd, &exporter, api.CodespaceFields)
+	cmdutil.AddJSONFlags(listCmd, &exporter, api.ListCodespaceFields)
 
 	listCmd.Flags().BoolVarP(&opts.useWeb, "web", "w", false, "List codespaces in the web browser, cannot be used with --user or --org")
 
diff --git a/pkg/cmd/codespace/root.go b/pkg/cmd/codespace/root.go
index 8439430aa..28a80edae 100644
--- a/pkg/cmd/codespace/root.go
+++ b/pkg/cmd/codespace/root.go
@@ -16,6 +16,7 @@ func NewRootCmd(app *App) *cobra.Command {
 	root.AddCommand(newDeleteCmd(app))
 	root.AddCommand(newJupyterCmd(app))
 	root.AddCommand(newListCmd(app))
+	root.AddCommand(newViewCmd(app))
 	root.AddCommand(newLogsCmd(app))
 	root.AddCommand(newPortsCmd(app))
 	root.AddCommand(newSSHCmd(app))
diff --git a/pkg/cmd/codespace/view.go b/pkg/cmd/codespace/view.go
new file mode 100644
index 000000000..11f772b71
--- /dev/null
+++ b/pkg/cmd/codespace/view.go
@@ -0,0 +1,143 @@
+package codespace
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/MakeNowJust/heredoc"
+	"github.com/cli/cli/v2/internal/codespaces/api"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/utils"
+	"github.com/spf13/cobra"
+)
+
+const (
+	minutesInDay = 1440
+)
+
+type viewOptions struct {
+	selector *CodespaceSelector
+	exporter cmdutil.Exporter
+}
+
+func newViewCmd(app *App) *cobra.Command {
+	opts := &viewOptions{}
+
+	viewCmd := &cobra.Command{
+		Use:   "view",
+		Short: "View details about a codespace",
+		Long: heredoc.Doc(`
+			View details about a codespace.
+
+			For more fine-grained details, you can add the "--json" flag to view the full list of fields available.
+
+			If this command doesn't provide enough information, you can use the "gh api" command to view the full JSON response:
+			$ gh api /user/codespaces/<codespace-name>
+		`),
+		Example: heredoc.Doc(`
+			# select a codespace from a list of all codespaces you own
+			$ gh cs view	
+
+			# view the details of a specific codespace
+			$ gh cs view -c <codespace-name>
+			
+			# select a codespace from a list of codespaces in a repository
+			$ gh cs view --repo <owner>/<repo>
+
+			# select a codespace from a list of codespaces with a specific repository owner
+			$ gh cs view --repo-owner <org/username>
+
+			# view the list of all available fields for a codespace
+			$ gh cs view --json
+
+			# view specific fields for a codespace
+			$ gh cs view --json displayName,machineDisplayName,state
+		`),
+		Args: noArgsConstraint,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return app.ViewCodespace(cmd.Context(), opts)
+		},
+	}
+	opts.selector = AddCodespaceSelector(viewCmd, app.apiClient)
+	cmdutil.AddJSONFlags(viewCmd, &opts.exporter, api.ViewCodespaceFields)
+
+	return viewCmd
+}
+
+func (a *App) ViewCodespace(ctx context.Context, opts *viewOptions) error {
+	// If we are in a codespace and a codespace name wasn't provided, show the details for the codespace we are connected to
+	if (os.Getenv("CODESPACES") == "true") && opts.selector.codespaceName == "" {
+		codespaceName := os.Getenv("CODESPACE_NAME")
+		opts.selector.codespaceName = codespaceName
+	}
+
+	selectedCodespace, err := opts.selector.Select(ctx)
+	if err != nil {
+		return err
+	}
+
+	if err := a.io.StartPager(); err != nil {
+		a.errLogger.Printf("error starting pager: %v", err)
+	}
+	defer a.io.StopPager()
+
+	if opts.exporter != nil {
+		return opts.exporter.Write(a.io, selectedCodespace)
+	}
+
+	//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
+	tp := utils.NewTablePrinter(a.io)
+	c := codespace{selectedCodespace}
+	formattedName := formatNameForVSCSTarget(c.Name, c.VSCSTarget)
+
+	// Create an array of fields to display in the table with their values
+	fields := []struct {
+		name  string
+		value string
+	}{
+		{"Name", formattedName},
+		{"State", c.State},
+		{"Repository", c.Repository.FullName},
+		{"Git Status", formatGitStatus(c)},
+		{"Devcontainer Path", c.DevContainerPath},
+		{"Machine Display Name", c.Machine.DisplayName},
+		{"Idle Timeout", fmt.Sprintf("%d minutes", c.IdleTimeoutMinutes)},
+		{"Created At", c.CreatedAt},
+		{"Retention Period", formatRetentionPeriodDays(c)},
+	}
+
+	for _, field := range fields {
+		// Only display the field if it has a value.
+		if field.value != "" {
+			tp.AddField(field.name, nil, nil)
+			tp.AddField(field.value, nil, nil)
+			tp.EndRow()
+		}
+	}
+
+	err = tp.Render()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func formatGitStatus(codespace codespace) string {
+	branchWithGitStatus := codespace.branchWithGitStatus()
+	// u2193 and u2191 are the unicode arrows for down and up
+	return fmt.Sprintf("%s - %d\u2193 %d\u2191", branchWithGitStatus, codespace.GitStatus.Behind, codespace.GitStatus.Ahead)
+}
+
+func formatRetentionPeriodDays(codespace codespace) string {
+	days := codespace.RetentionPeriodMinutes / minutesInDay
+	// Don't display the retention period if it is 0 days
+	if days == 0 {
+		return ""
+	} else if days == 1 {
+		return "1 day"
+	}
+
+	return fmt.Sprintf("%d days", days)
+}
diff --git a/pkg/cmd/extension/command.go b/pkg/cmd/extension/command.go
index 6629e599d..0f2add8aa 100644
--- a/pkg/cmd/extension/command.go
+++ b/pkg/cmd/extension/command.go
@@ -643,8 +643,10 @@ func checkValidExtension(rootCmd *cobra.Command, m extensions.ExtensionManager,
 	}
 
 	commandName := strings.TrimPrefix(extName, "gh-")
-	if c, _, _ := rootCmd.Find([]string{commandName}); c != rootCmd && c.GroupID != "extension" {
-		return nil, fmt.Errorf("%q matches the name of a built-in command or alias", commandName)
+	if c, _, err := rootCmd.Traverse([]string{commandName}); err != nil {
+		return nil, err
+	} else if c != rootCmd {
+		return nil, fmt.Errorf("%q matches the name of a built-in command", commandName)
 	}
 
 	for _, ext := range m.List() {
diff --git a/pkg/cmd/pr/checks/aggregate.go b/pkg/cmd/pr/checks/aggregate.go
index aa139323f..53f2bab5f 100644
--- a/pkg/cmd/pr/checks/aggregate.go
+++ b/pkg/cmd/pr/checks/aggregate.go
@@ -30,10 +30,10 @@ func aggregateChecks(checkContexts []api.CheckContext, requiredChecks bool) (che
 			continue
 		}
 
-		state := string(c.State)
+		state := c.State
 		if state == "" {
 			if c.Status == "COMPLETED" {
-				state = string(c.Conclusion)
+				state = c.Conclusion
 			} else {
 				state = c.Status
 			}
diff --git a/pkg/cmd/pr/shared/finder.go b/pkg/cmd/pr/shared/finder.go
index 4bb79ac8b..f1886b3fe 100644
--- a/pkg/cmd/pr/shared/finder.go
+++ b/pkg/cmd/pr/shared/finder.go
@@ -465,7 +465,7 @@ func preloadPrChecks(client *http.Client, repo ghrepo.Interface, pr *api.PullReq
 				%s
 			}
 		}
-	}`, api.StatusCheckRollupGraphQLWithoutCountByState("$endCursor"))
+	}`, api.StatusCheckRollupGraphQL("$endCursor"))
 
 	variables := map[string]interface{}{
 		"id": pr.ID,
diff --git a/pkg/cmd/pr/status/fixtures/prStatusChecks.json b/pkg/cmd/pr/status/fixtures/prStatusChecks.json
index 0f5d88902..91455ee2c 100644
--- a/pkg/cmd/pr/status/fixtures/prStatusChecks.json
+++ b/pkg/cmd/pr/status/fixtures/prStatusChecks.json
@@ -26,7 +26,6 @@
                       "contexts": {
                         "nodes": [
                           {
-                            "__typeName": "StatusContext",
                             "state": "SUCCESS"
                           }
                         ]
@@ -81,7 +80,6 @@
                       "contexts": {
                         "nodes": [
                           {
-                            "__typeName": "CheckRun",
                             "status": "IN_PROGRESS",
                             "conclusion": ""
                           }
@@ -111,16 +109,13 @@
                       "contexts": {
                         "nodes": [
                           {
-                            "__typeName": "CheckRun",
                             "status": "IN_PROGRESS",
                             "conclusion": ""
                           },
                           {
-                            "__typeName": "StatusContext",
                             "state": "FAILURE"
                           },
                           {
-                            "__typeName": "CheckRun",
                             "status": "COMPLETED",
                             "conclusion": "NEUTRAL"
                           }
@@ -149,7 +144,6 @@
                       "contexts": {
                         "nodes": [
                           {
-                            "__typeName": "StatusContext",
                             "state": "SUCCESS"
                           }
                         ]
diff --git a/pkg/cmd/pr/status/fixtures/prStatusChecksWithStatesByCount.json b/pkg/cmd/pr/status/fixtures/prStatusChecksWithStatesByCount.json
deleted file mode 100644
index 452cb4e06..000000000
--- a/pkg/cmd/pr/status/fixtures/prStatusChecksWithStatesByCount.json
+++ /dev/null
@@ -1,464 +0,0 @@
-{
-  "data": {
-    "repository": {
-      "pullRequests": {
-        "totalCount": 0,
-        "edges": []
-      }
-    },
-    "viewerCreated": {
-      "totalCount": 4,
-      "edges": [
-        {
-          "node": {
-            "number": 8,
-            "title": "Strawberries are not actually berries",
-            "state": "OPEN",
-            "url": "https://github.com/cli/cli/pull/8",
-            "headRefName": "strawberries",
-            "mergeable": "UNKNOWN",
-            "reviewDecision": "CHANGES_REQUESTED",
-            "statusCheckRollup": {
-              "nodes": [
-                {
-                  "commit": {
-                    "statusCheckRollup": {
-                      "contexts": {
-                        "checkRunCount": 0,
-                        "checkRunCountsByState": [
-                          {
-                            "state": "ACTION_REQUIRED",
-                            "count": 0
-                          },
-                          {
-                            "state": "CANCELLED",
-                            "count": 0
-                          },
-                          {
-                            "state": "COMPLETED",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "IN_PROGRESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "NEUTRAL",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "QUEUED",
-                            "count": 0
-                          },
-                          {
-                            "state": "SKIPPED",
-                            "count": 0
-                          },
-                          {
-                            "state": "STALE",
-                            "count": 0
-                          },
-                          {
-                            "state": "STARTUP_FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "TIMED_OUT",
-                            "count": 0
-                          },
-                          {
-                            "state": "WAITING",
-                            "count": 0
-                          }
-                        ],
-                        "statusContextCount": 1,
-                        "statusContextCountsByState": [
-                          {
-                            "state": "EXPECTED",
-                            "count": 0
-                          },
-                          {
-                            "state": "ERROR",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 1
-                          }
-                        ]
-                      }
-                    }
-                  }
-                }
-              ]
-            }
-          }
-        },
-        {
-          "node": {
-            "number": 7,
-            "title": "Bananas are berries",
-            "state": "OPEN",
-            "url": "https://github.com/cli/cli/pull/7",
-            "headRefName": "banananana",
-            "reviewDecision": "APPROVED",
-            "baseRef": {
-              "branchProtectionRule": {
-                "requiredApprovingReviewCount": 0
-              }
-            },
-            "latestReviews": {
-              "nodes": [
-                {
-                  "author": {
-                    "login": "bob"
-                  },
-                  "state": "APPROVED"
-                },
-                {
-                  "author": {
-                    "login": "stella"
-                  },
-                  "state": "CHANGES_REQUESTED"
-                },
-                {
-                  "author": {
-                    "login": "alice"
-                  },
-                  "state": "APPROVED"
-                }
-              ]
-            },
-            "statusCheckRollup": {
-              "nodes": [
-                {
-                  "commit": {
-                    "statusCheckRollup": {
-                      "contexts": {
-                        "checkRunCount": 1,
-                        "checkRunCountsByState": [
-                          {
-                            "state": "ACTION_REQUIRED",
-                            "count": 0
-                          },
-                          {
-                            "state": "CANCELLED",
-                            "count": 0
-                          },
-                          {
-                            "state": "COMPLETED",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "IN_PROGRESS",
-                            "count": 1
-                          },
-                          {
-                            "state": "NEUTRAL",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "QUEUED",
-                            "count": 0
-                          },
-                          {
-                            "state": "SKIPPED",
-                            "count": 0
-                          },
-                          {
-                            "state": "STALE",
-                            "count": 0
-                          },
-                          {
-                            "state": "STARTUP_FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "TIMED_OUT",
-                            "count": 0
-                          },
-                          {
-                            "state": "WAITING",
-                            "count": 0
-                          }
-                        ],
-                        "statusContextCount": 1,
-                        "statusContextCountsByState": [
-                          {
-                            "state": "EXPECTED",
-                            "count": 0
-                          },
-                          {
-                            "state": "ERROR",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 1
-                          }
-                        ]
-                      }
-                    }
-                  }
-                }
-              ]
-            }
-          }
-        },
-        {
-          "node": {
-            "number": 6,
-            "title": "Avocado is probably not a berry",
-            "state": "OPEN",
-            "url": "https://github.com/cli/cli/pull/6",
-            "headRefName": "avo",
-            "mergeable": "MERGEABLE",
-            "reviewDecision": "REVIEW_REQUIRED",
-            "statusCheckRollup": {
-              "nodes": [
-                {
-                  "commit": {
-                    "statusCheckRollup": {
-                      "contexts": {
-                        "checkRunCount": 2,
-                        "checkRunCountsByState": [
-                          {
-                            "state": "ACTION_REQUIRED",
-                            "count": 0
-                          },
-                          {
-                            "state": "CANCELLED",
-                            "count": 0
-                          },
-                          {
-                            "state": "COMPLETED",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "IN_PROGRESS",
-                            "count": 1
-                          },
-                          {
-                            "state": "NEUTRAL",
-                            "count": 1
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "QUEUED",
-                            "count": 0
-                          },
-                          {
-                            "state": "SKIPPED",
-                            "count": 0
-                          },
-                          {
-                            "state": "STALE",
-                            "count": 0
-                          },
-                          {
-                            "state": "STARTUP_FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "TIMED_OUT",
-                            "count": 0
-                          },
-                          {
-                            "state": "WAITING",
-                            "count": 0
-                          }
-                        ],
-                        "statusContextCount": 1,
-                        "statusContextCountsByState": [
-                          {
-                            "state": "EXPECTED",
-                            "count": 0
-                          },
-                          {
-                            "state": "ERROR",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 1
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 0
-                          }
-                        ]
-                      }
-                    }
-                  }
-                }
-              ]
-            }
-          }
-        },
-        {
-          "node": {
-            "number": 5,
-            "title": "Why can't berries get along?",
-            "state": "OPEN",
-            "url": "https://github.com/cli/cli/pull/5",
-            "headRefName": "strawberries",
-            "mergeable": "CONFLICTING",
-            "statusCheckRollup": {
-              "nodes": [
-                {
-                  "commit": {
-                    "statusCheckRollup": {
-                      "contexts": {
-                        "checkRunCount": 0,
-                        "checkRunCountsByState": [
-                          {
-                            "state": "ACTION_REQUIRED",
-                            "count": 0
-                          },
-                          {
-                            "state": "CANCELLED",
-                            "count": 0
-                          },
-                          {
-                            "state": "COMPLETED",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "IN_PROGRESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "NEUTRAL",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "QUEUED",
-                            "count": 0
-                          },
-                          {
-                            "state": "SKIPPED",
-                            "count": 0
-                          },
-                          {
-                            "state": "STALE",
-                            "count": 0
-                          },
-                          {
-                            "state": "STARTUP_FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 0
-                          },
-                          {
-                            "state": "TIMED_OUT",
-                            "count": 0
-                          },
-                          {
-                            "state": "WAITING",
-                            "count": 0
-                          }
-                        ],
-                        "statusContextCount": 1,
-                        "statusContextCountsByState": [
-                          {
-                            "state": "EXPECTED",
-                            "count": 0
-                          },
-                          {
-                            "state": "ERROR",
-                            "count": 0
-                          },
-                          {
-                            "state": "FAILURE",
-                            "count": 0
-                          },
-                          {
-                            "state": "PENDING",
-                            "count": 0
-                          },
-                          {
-                            "state": "SUCCESS",
-                            "count": 1
-                          }
-                        ]
-                      }
-                    }
-                  }
-                }
-              ]
-            }
-          }
-        }
-      ]
-    },
-    "reviewRequested": {
-      "totalCount": 0,
-      "edges": []
-    }
-  }
-}
diff --git a/pkg/cmd/pr/status/http.go b/pkg/cmd/pr/status/http.go
index 755ccf30b..49bdb147c 100644
--- a/pkg/cmd/pr/status/http.go
+++ b/pkg/cmd/pr/status/http.go
@@ -17,8 +17,6 @@ type requestOptions struct {
 	Username       string
 	Fields         []string
 	ConflictStatus bool
-
-	CheckRunAndStatusContextCountsSupported bool
 }
 
 type pullRequestsPayload struct {
@@ -59,7 +57,7 @@ func pullRequestStatus(httpClient *http.Client, repo ghrepo.Interface, options r
 		fragments = fmt.Sprintf("fragment pr on PullRequest{%s}fragment prWithReviews on PullRequest{...pr}", gr)
 	} else {
 		var err error
-		fragments, err = pullRequestFragment(repo.RepoHost(), options.ConflictStatus, options.CheckRunAndStatusContextCountsSupported)
+		fragments, err = pullRequestFragment(repo.RepoHost(), options.ConflictStatus)
 		if err != nil {
 			return nil, err
 		}
@@ -148,7 +146,8 @@ func pullRequestStatus(httpClient *http.Client, repo ghrepo.Interface, options r
 	}
 
 	var resp response
-	if err := apiClient.GraphQL(repo.RepoHost(), query, variables, &resp); err != nil {
+	err := apiClient.GraphQL(repo.RepoHost(), query, variables, &resp)
+	if err != nil {
 		return nil, err
 	}
 
@@ -188,23 +187,16 @@ func pullRequestStatus(httpClient *http.Client, repo ghrepo.Interface, options r
 	return &payload, nil
 }
 
-func pullRequestFragment(hostname string, conflictStatus bool, statusCheckRollupWithCountByState bool) (string, error) {
+func pullRequestFragment(hostname string, conflictStatus bool) (string, error) {
 	fields := []string{
 		"number", "title", "state", "url", "isDraft", "isCrossRepository",
 		"headRefName", "headRepositoryOwner", "mergeStateStatus",
-		"requiresStrictStatusChecks", "autoMergeRequest",
+		"statusCheckRollup", "requiresStrictStatusChecks", "autoMergeRequest",
 	}
 
 	if conflictStatus {
 		fields = append(fields, "mergeable")
 	}
-
-	if statusCheckRollupWithCountByState {
-		fields = append(fields, "statusCheckRollupWithCountByState")
-	} else {
-		fields = append(fields, "statusCheckRollup")
-	}
-
 	reviewFields := []string{"reviewDecision", "latestReviews"}
 	fragments := fmt.Sprintf(`
 	fragment pr on PullRequest {%s}
diff --git a/pkg/cmd/pr/status/status.go b/pkg/cmd/pr/status/status.go
index bb7bb735d..fa517bada 100644
--- a/pkg/cmd/pr/status/status.go
+++ b/pkg/cmd/pr/status/status.go
@@ -8,13 +8,11 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
-	"time"
 
 	"github.com/cli/cli/v2/api"
 	ghContext "github.com/cli/cli/v2/context"
 	"github.com/cli/cli/v2/git"
 	"github.com/cli/cli/v2/internal/config"
-	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/internal/text"
 	"github.com/cli/cli/v2/pkg/cmd/pr/shared"
@@ -35,8 +33,6 @@ type StatusOptions struct {
 	HasRepoOverride bool
 	Exporter        cmdutil.Exporter
 	ConflictStatus  bool
-
-	Detector fd.Detector
 }
 
 func NewCmdStatus(f *cmdutil.Factory, runF func(*StatusOptions) error) *cobra.Command {
@@ -109,16 +105,6 @@ func statusRun(opts *StatusOptions) error {
 		options.Fields = opts.Exporter.Fields()
 	}
 
-	if opts.Detector == nil {
-		cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
-		opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
-	}
-	prFeatures, err := opts.Detector.PullRequestFeatures()
-	if err != nil {
-		return err
-	}
-	options.CheckRunAndStatusContextCountsSupported = prFeatures.CheckRunAndStatusContextCounts
-
 	prPayload, err := pullRequestStatus(httpClient, baseRepo, options)
 	if err != nil {
 		return err
diff --git a/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksFailing.json b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksFailing.json
index 48484f1c4..59c4acd8a 100644
--- a/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksFailing.json
+++ b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksFailing.json
@@ -46,7 +46,6 @@
                   "contexts": {
                     "nodes": [
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "FAILURE",
                         "status": "COMPLETED",
                         "name": "cool tests",
@@ -55,7 +54,6 @@
                         "detailsUrl": "sweet link"
                       },
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "FAILURE",
                         "status": "COMPLETED",
                         "name": "sad tests",
diff --git a/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksPassing.json b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksPassing.json
index 87152d692..d55e460b3 100644
--- a/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksPassing.json
+++ b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAllChecksPassing.json
@@ -46,7 +46,6 @@
                   "contexts": {
                     "nodes": [
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "SUCCESS",
                         "status": "COMPLETED",
                         "name": "cool tests",
@@ -55,7 +54,6 @@
                         "detailsUrl": "sweet link"
                       },
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "SUCCESS",
                         "status": "COMPLETED",
                         "name": "rad tests",
@@ -64,7 +62,6 @@
                         "detailsUrl": "sweet link"
                       },
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "SUCCESS",
                         "status": "COMPLETED",
                         "name": "awesome tests",
diff --git a/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksFailing.json b/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksFailing.json
index adb15e7c3..d817a5da0 100644
--- a/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksFailing.json
+++ b/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksFailing.json
@@ -46,7 +46,6 @@
                   "contexts": {
                     "nodes": [
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "SUCCESS",
                         "status": "COMPLETED",
                         "name": "cool tests",
@@ -55,7 +54,6 @@
                         "detailsUrl": "sweet link"
                       },
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "FAILURE",
                         "status": "COMPLETED",
                         "name": "sad tests",
diff --git a/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksPending.json b/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksPending.json
index 4a011e5c1..019186783 100644
--- a/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksPending.json
+++ b/pkg/cmd/pr/view/fixtures/prViewPreviewWithSomeChecksPending.json
@@ -46,7 +46,6 @@
                   "contexts": {
                     "nodes": [
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "",
                         "status": "WAITING",
                         "name": "cool tests",
@@ -55,7 +54,6 @@
                         "detailsUrl": "sweet link"
                       },
                       {
-                        "__typename": "CheckRun",
                         "conclusion": "SUCCESS",
                         "status": "COMPLETED",
                         "name": "sad tests",
diff --git a/pkg/cmd/root/alias.go b/pkg/cmd/root/alias.go
deleted file mode 100644
index ba0bfe9e2..000000000
--- a/pkg/cmd/root/alias.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package root
-
-import (
-	"errors"
-	"fmt"
-	"os/exec"
-	"regexp"
-	"runtime"
-	"strings"
-
-	"github.com/cli/cli/v2/internal/run"
-	"github.com/cli/cli/v2/internal/text"
-	"github.com/cli/cli/v2/pkg/findsh"
-	"github.com/cli/cli/v2/pkg/iostreams"
-	"github.com/google/shlex"
-	"github.com/spf13/cobra"
-)
-
-func NewCmdShellAlias(io *iostreams.IOStreams, aliasName, aliasValue string) *cobra.Command {
-	return &cobra.Command{
-		Use:   aliasName,
-		Short: fmt.Sprintf("Shell alias for %q", text.Truncate(80, aliasValue)),
-		RunE: func(c *cobra.Command, args []string) error {
-			expandedArgs, err := expandShellAlias(aliasValue, args, nil)
-			if err != nil {
-				return err
-			}
-			externalCmd := exec.Command(expandedArgs[0], expandedArgs[1:]...)
-			externalCmd.Stderr = io.ErrOut
-			externalCmd.Stdout = io.Out
-			externalCmd.Stdin = io.In
-			preparedCmd := run.PrepareCmd(externalCmd)
-			if err = preparedCmd.Run(); err != nil {
-				return fmt.Errorf("failed to run external command: %w\n", err)
-			}
-			return nil
-		},
-		GroupID: "alias",
-		Annotations: map[string]string{
-			"skipAuthCheck": "true",
-		},
-		DisableFlagParsing: true,
-	}
-}
-
-func NewCmdAlias(io *iostreams.IOStreams, aliasName, aliasValue string) *cobra.Command {
-	return &cobra.Command{
-		Use:   aliasName,
-		Short: fmt.Sprintf("Alias for %q", text.Truncate(80, aliasValue)),
-		RunE: func(c *cobra.Command, args []string) error {
-			expandedArgs, err := expandAlias(aliasValue, args)
-			if err != nil {
-				return err
-			}
-			root := c.Root()
-			root.SetArgs(expandedArgs)
-			return root.Execute()
-		},
-		GroupID: "alias",
-		Annotations: map[string]string{
-			"skipAuthCheck": "true",
-		},
-		DisableFlagParsing: true,
-	}
-}
-
-// ExpandAlias processes argv to see if it should be rewritten according to a user's aliases.
-func expandAlias(expansion string, args []string) ([]string, error) {
-	extraArgs := []string{}
-	for i, a := range args {
-		if !strings.Contains(expansion, "$") {
-			extraArgs = append(extraArgs, a)
-		} else {
-			expansion = strings.ReplaceAll(expansion, fmt.Sprintf("$%d", i+1), a)
-		}
-	}
-
-	lingeringRE := regexp.MustCompile(`\$\d`)
-	if lingeringRE.MatchString(expansion) {
-		return nil, fmt.Errorf("not enough arguments for alias: %s", expansion)
-	}
-
-	newArgs, err := shlex.Split(expansion)
-	if err != nil {
-		return nil, err
-	}
-
-	expanded := append(newArgs, extraArgs...)
-
-	return expanded, nil
-}
-
-// ExpandShellAlias processes argv to see if it should be rewritten according to a user's aliases.
-func expandShellAlias(expansion string, args []string, findShFunc func() (string, error)) ([]string, error) {
-	if findShFunc == nil {
-		findShFunc = findSh
-	}
-
-	shPath, shErr := findShFunc()
-	if shErr != nil {
-		return nil, shErr
-	}
-
-	expanded := []string{shPath, "-c", expansion[1:]}
-
-	if len(args) > 0 {
-		expanded = append(expanded, "--")
-		expanded = append(expanded, args...)
-	}
-
-	return expanded, nil
-}
-
-func findSh() (string, error) {
-	shPath, err := findsh.Find()
-	if err != nil {
-		if errors.Is(err, exec.ErrNotFound) {
-			if runtime.GOOS == "windows" {
-				return "", errors.New("unable to locate sh to execute the shell alias with. The sh.exe interpreter is typically distributed with Git for Windows.")
-			}
-			return "", errors.New("unable to locate sh to execute shell alias with")
-		}
-		return "", err
-	}
-	return shPath, nil
-}
diff --git a/pkg/cmd/root/extension.go b/pkg/cmd/root/extension.go
deleted file mode 100644
index 52ef6b0a4..000000000
--- a/pkg/cmd/root/extension.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package root
-
-import (
-	"fmt"
-
-	"github.com/cli/cli/v2/git"
-	"github.com/cli/cli/v2/internal/ghrepo"
-	"github.com/cli/cli/v2/pkg/extensions"
-	"github.com/cli/cli/v2/pkg/iostreams"
-	"github.com/spf13/cobra"
-)
-
-func NewCmdExtension(io *iostreams.IOStreams, em extensions.ExtensionManager, ext extensions.Extension) *cobra.Command {
-	var short string
-	if ext.IsLocal() {
-		short = fmt.Sprintf("Local extension gh-%s", ext.Name())
-	} else {
-		path := ext.URL()
-		if u, err := git.ParseURL(ext.URL()); err == nil {
-			if r, err := ghrepo.FromURL(u); err == nil {
-				path = ghrepo.FullName(r)
-			}
-		}
-		short = fmt.Sprintf("Extension %s", path)
-	}
-	return &cobra.Command{
-		Use:   ext.Name(),
-		Short: short,
-		RunE: func(c *cobra.Command, args []string) error {
-			args = append([]string{ext.Name()}, args...)
-			if _, err := em.Dispatch(args, io.In, io.Out, io.ErrOut); err != nil {
-				return fmt.Errorf("failed to run extension: %w\n", err)
-			}
-			return nil
-		},
-		GroupID: "extension",
-		Annotations: map[string]string{
-			"skipAuthCheck": "true",
-		},
-		DisableFlagParsing: true,
-	}
-}
diff --git a/pkg/cmd/root/help.go b/pkg/cmd/root/help.go
index c33095c1f..9da57d6e9 100644
--- a/pkg/cmd/root/help.go
+++ b/pkg/cmd/root/help.go
@@ -4,11 +4,9 @@ import (
 	"bytes"
 	"fmt"
 	"io"
-	"os"
 	"sort"
 	"strings"
 
-	"github.com/MakeNowJust/heredoc"
 	"github.com/cli/cli/v2/internal/text"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/spf13/cobra"
@@ -18,17 +16,13 @@ import (
 func rootUsageFunc(w io.Writer, command *cobra.Command) error {
 	fmt.Fprintf(w, "Usage:  %s", command.UseLine())
 
-	var subcommands []*cobra.Command
-	for _, c := range command.Commands() {
-		if !c.IsAvailableCommand() {
-			continue
-		}
-		subcommands = append(subcommands, c)
-	}
-
+	subcommands := command.Commands()
 	if len(subcommands) > 0 {
 		fmt.Fprint(w, "\n\nAvailable commands:\n")
 		for _, c := range subcommands {
+			if c.Hidden {
+				continue
+			}
 			fmt.Fprintf(w, "  %s\n", c.Name())
 		}
 		return nil
@@ -88,10 +82,8 @@ func isRootCmd(command *cobra.Command) bool {
 }
 
 func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) {
-	flags := command.Flags()
-
 	if isRootCmd(command) {
-		if versionVal, err := flags.GetBool("version"); err == nil && versionVal {
+		if versionVal, err := command.Flags().GetBool("version"); err == nil && versionVal {
 			fmt.Fprint(f.IOStreams.Out, command.Annotations["versionInfo"])
 			return
 		} else if err != nil {
@@ -103,8 +95,8 @@ func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) {
 
 	cs := f.IOStreams.ColorScheme()
 
-	if help, _ := flags.GetBool("help"); !help && !command.Runnable() && len(flags.Args()) > 0 {
-		nestedSuggestFunc(f.IOStreams.ErrOut, command, strings.Join(flags.Args(), " "))
+	if isRootCmd(command.Parent()) && len(args) >= 2 && args[1] != "--help" && args[1] != "-h" {
+		nestedSuggestFunc(f.IOStreams.ErrOut, command, args[1])
 		hasFailed = true
 		return
 	}
@@ -152,6 +144,14 @@ func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) {
 		}
 		sort.Strings(helpTopics)
 		helpEntries = append(helpEntries, helpEntry{"HELP TOPICS", strings.Join(helpTopics, "\n")})
+
+		if exts := f.ExtensionManager.List(); len(exts) > 0 {
+			var names []string
+			for _, ext := range exts {
+				names = append(names, ext.Name())
+			}
+			helpEntries = append(helpEntries, helpEntry{"EXTENSION COMMANDS", strings.Join(names, "\n")})
+		}
 	}
 
 	flagUsages := command.LocalFlags().FlagUsages()
@@ -189,27 +189,6 @@ Read the manual at https://cli.github.com/manual`})
 	}
 }
 
-func authHelp() string {
-	if os.Getenv("GITHUB_ACTIONS") == "true" {
-		return heredoc.Doc(`
-			gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable. Example:
-			  env:
-			    GH_TOKEN: ${{ github.token }}
-		`)
-	}
-
-	if os.Getenv("CI") != "" {
-		return heredoc.Doc(`
-			gh: To use GitHub CLI in automation, set the GH_TOKEN environment variable.
-		`)
-	}
-
-	return heredoc.Doc(`
-		To get started with GitHub CLI, please run:  gh auth login
-		Alternatively, populate the GH_TOKEN environment variable with a GitHub API authentication token.
-	`)
-}
-
 func findCommand(cmd *cobra.Command, name string) *cobra.Command {
 	for _, c := range cmd.Commands() {
 		if c.Name() == name {
diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go
index 5a79e8aeb..8d18a4db1 100644
--- a/pkg/cmd/root/root.go
+++ b/pkg/cmd/root/root.go
@@ -1,10 +1,8 @@
 package root
 
 import (
-	"fmt"
 	"net/http"
 	"os"
-	"strings"
 	"sync"
 
 	"github.com/MakeNowJust/heredoc"
@@ -12,7 +10,6 @@ import (
 	codespacesAPI "github.com/cli/cli/v2/internal/codespaces/api"
 	actionsCmd "github.com/cli/cli/v2/pkg/cmd/actions"
 	aliasCmd "github.com/cli/cli/v2/pkg/cmd/alias"
-	"github.com/cli/cli/v2/pkg/cmd/alias/shared"
 	apiCmd "github.com/cli/cli/v2/pkg/cmd/api"
 	authCmd "github.com/cli/cli/v2/pkg/cmd/auth"
 	browseCmd "github.com/cli/cli/v2/pkg/cmd/browse"
@@ -39,29 +36,15 @@ import (
 	versionCmd "github.com/cli/cli/v2/pkg/cmd/version"
 	workflowCmd "github.com/cli/cli/v2/pkg/cmd/workflow"
 	"github.com/cli/cli/v2/pkg/cmdutil"
-	"github.com/google/shlex"
 	"github.com/spf13/cobra"
 )
 
-type AuthError struct {
-	err error
-}
-
-func (ae *AuthError) Error() string {
-	return ae.err.Error()
-}
-
-func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command, error) {
-	io := f.IOStreams
-	cfg, err := f.Config()
-	if err != nil {
-		return nil, fmt.Errorf("failed to read configuration: %s\n", err)
-	}
-
+func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "gh <command> <subcommand> [flags]",
 		Short: "GitHub CLI",
 		Long:  `Work seamlessly with GitHub from the command line.`,
+
 		Example: heredoc.Doc(`
 			$ gh issue create
 			$ gh repo clone cli/cli
@@ -70,14 +53,6 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command,
 		Annotations: map[string]string{
 			"versionInfo": versionCmd.Format(version, buildDate),
 		},
-		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-			// require that the user is authenticated before running most commands
-			if cmdutil.IsAuthCheckEnabled(cmd) && !cmdutil.CheckAuth(cfg) {
-				fmt.Fprint(io.ErrOut, authHelp())
-				return &AuthError{}
-			}
-			return nil
-		},
 	}
 
 	// cmd.SetOut(f.IOStreams.Out)    // can't use due to https://github.com/spf13/cobra/issues/1708
@@ -110,10 +85,6 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command,
 		ID:    "actions",
 		Title: "GitHub Actions commands",
 	})
-	cmd.AddGroup(&cobra.Group{
-		ID:    "extension",
-		Title: "Extension commands",
-	})
 
 	// Child commands
 	cmd.AddCommand(versionCmd.NewCmdVersion(f, version, buildDate))
@@ -165,51 +136,6 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command,
 		}
 	}
 
-	// Aliases
-	aliases := cfg.Aliases()
-	validAliasName := shared.ValidAliasNameFunc(cmd)
-	validAliasExpansion := shared.ValidAliasExpansionFunc(cmd)
-	for k, v := range aliases.All() {
-		aliasName := k
-		aliasValue := v
-		if validAliasName(aliasName) && validAliasExpansion(aliasValue) {
-			split, _ := shlex.Split(aliasName)
-			parentCmd, parentArgs, _ := cmd.Find(split)
-			if !parentCmd.ContainsGroup("alias") {
-				parentCmd.AddGroup(&cobra.Group{
-					ID:    "alias",
-					Title: "Alias commands",
-				})
-			}
-			if strings.HasPrefix(aliasValue, "!") {
-				shellAliasCmd := NewCmdShellAlias(io, parentArgs[0], aliasValue)
-				parentCmd.AddCommand(shellAliasCmd)
-				parentCmd.ValidArgs = append(parentCmd.ValidArgs, fmt.Sprintf("%s\tShell alias", aliasName))
-			} else {
-				aliasCmd := NewCmdAlias(io, parentArgs[0], aliasValue)
-				split, _ := shlex.Split(aliasValue)
-				child, _, _ := cmd.Find(split)
-				aliasCmd.SetUsageFunc(func(_ *cobra.Command) error {
-					return rootUsageFunc(f.IOStreams.ErrOut, child)
-				})
-				aliasCmd.SetHelpFunc(func(_ *cobra.Command, args []string) {
-					rootHelpFunc(f, child, args)
-				})
-				parentCmd.AddCommand(aliasCmd)
-				parentCmd.ValidArgs = append(parentCmd.ValidArgs, fmt.Sprintf("%s\tAlias for %s", aliasName, aliasValue))
-			}
-		}
-	}
-
-	// Extensions
-	em := f.ExtensionManager
-	for _, e := range em.List() {
-		extension := e
-		extensionCmd := NewCmdExtension(io, em, e)
-		cmd.AddCommand(extensionCmd)
-		cmd.ValidArgs = append(cmd.ValidArgs, fmt.Sprintf("%s\t%s", extension.Name(), extensionCmd.Short))
-	}
-
 	cmdutil.DisableAuthCheck(cmd)
 
 	// The reference command produces paged output that displays information on every other command.
@@ -219,7 +145,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command,
 	// is special.
 	referenceCmd.Long = stringifyReference(cmd)
 	referenceCmd.SetHelpFunc(longPager(f.IOStreams))
-	return cmd, nil
+	return cmd
 }
 
 func bareHTTPClient(f *cmdutil.Factory, version string) func() (*http.Client, error) {


Test patch:
diff --git a/api/pull_request_test.go b/api/pull_request_test.go
index 6c15cdddd..67ea20cb5 100644
--- a/api/pull_request_test.go
+++ b/api/pull_request_test.go
@@ -59,11 +59,6 @@ func TestChecksStatus_SummarisingCheckRuns(t *testing.T) {
 			payload:              singleCheckRunWithStatus("REQUESTED"),
 			expectedChecksStatus: PullRequestChecksStatus{Pending: 1, Total: 1},
 		},
-		{
-			name:                 "COMPLETED with no conclusion is treated as Pending",
-			payload:              singleCheckRunWithStatus("COMPLETED"),
-			expectedChecksStatus: PullRequestChecksStatus{Pending: 1, Total: 1},
-		},
 		{
 			name:                 "COMPLETED / STARTUP_FAILURE is treated as Pending",
 			payload:              singleCompletedCheckRunWithConclusion("STARTUP_FAILURE"),
@@ -227,120 +222,6 @@ func TestChecksStatus_SummarisingCheckRunsAndStatusContexts(t *testing.T) {
 	require.Equal(t, expectedChecksStatus, pr.ChecksStatus())
 }
 
-func TestChecksStatus_SummarisingCheckRunAndStatusContextCountsByState(t *testing.T) {
-	t.Parallel()
-
-	payload := `
-	{ "statusCheckRollup": { "nodes": [{ "commit": {
-		"statusCheckRollup": {
-			"contexts": {
-				"checkRunCount": 14,
-				"checkRunCountsByState": [
-					{
-						"state": "ACTION_REQUIRED",
-						"count": 1
-					},
-					{
-						"state": "CANCELLED",
-						"count": 1
-					},
-					{
-						"state": "COMPLETED",
-						"count": 1
-					},
-					{
-						"state": "FAILURE",
-						"count": 1
-					},
-					{
-						"state": "IN_PROGRESS",
-						"count": 1
-					},
-					{
-						"state": "NEUTRAL",
-						"count": 1
-					},
-					{
-						"state": "PENDING",
-						"count": 1
-					},
-					{
-						"state": "QUEUED",
-						"count": 1
-					},
-					{
-						"state": "SKIPPED",
-						"count": 1
-					},
-					{
-						"state": "STALE",
-						"count": 1
-					},
-					{
-						"state": "STARTUP_FAILURE",
-						"count": 1
-					},
-					{
-						"state": "SUCCESS",
-						"count": 1
-					},
-					{
-						"state": "TIMED_OUT",
-						"count": 1
-					},
-					{
-						"state": "WAITING",
-						"count": 1
-					},
-					{
-						"state": "AnUnrecognizedStateJustForThisTest",
-						"count": 1
-					}
-				],
-				"statusContextCount": 6,
-				"statusContextCountsByState": [
-					{
-						"state": "EXPECTED",
-						"count": 1
-					},
-					{
-						"state": "ERROR",
-						"count": 1
-					},
-					{
-						"state": "FAILURE",
-						"count": 1
-					},
-					{
-						"state": "PENDING",
-						"count": 1
-					},
-					{
-						"state": "SUCCESS",
-						"count": 1
-					},
-					{
-						"state": "AnUnrecognizedStateJustForThisTest",
-						"count": 1
-					}
-				]
-			}
-		}
-	} }] } }
-	`
-
-	var pr PullRequest
-	require.NoError(t, json.Unmarshal([]byte(payload), &pr))
-
-	expectedChecksStatus := PullRequestChecksStatus{
-		Pending: 11,
-		Failing: 6,
-		Passing: 4,
-		Total:   20,
-	}
-	require.Equal(t, expectedChecksStatus, pr.ChecksStatus())
-}
-
 // Note that it would be incorrect to provide a status of COMPLETED here
 // as the conclusion is always set to null. If you want a COMPLETED status,
 // use `singleCompletedCheckRunWithConclusion`.
diff --git a/internal/featuredetection/feature_detection_test.go b/internal/featuredetection/feature_detection_test.go
index dda6fe6a5..c4944d0de 100644
--- a/internal/featuredetection/feature_detection_test.go
+++ b/internal/featuredetection/feature_detection_test.go
@@ -82,32 +82,18 @@ func TestPullRequestFeatures(t *testing.T) {
 		wantErr       bool
 	}{
 		{
-			name:     "github.com with merge queue and status check counts by state",
+			name:     "github.com",
 			hostname: "github.com",
 			queryResponse: map[string]string{
 				`query PullRequest_fields\b`: heredoc.Doc(`
-				{
-					"data": {
-						"PullRequest": {
-							"fields": [
-								{"name": "isInMergeQueue"},
-								{"name": "isMergeQueueEnabled"}
-							]
-						},
-						"StatusCheckRollupContextConnection": {
-							"fields": [
-								{"name": "checkRunCount"},
-								{"name": "checkRunCountsByState"},
-								{"name": "statusContextCount"},
-								{"name": "statusContextCountsByState"}
-							]
-						}
-					}
-				}`),
+					{ "data": { "PullRequest": { "fields": [
+						{"name": "isInMergeQueue"},
+						{"name": "isMergeQueueEnabled"}
+					] } } }
+				`),
 			},
 			wantFeatures: PullRequestFeatures{
-				MergeQueue:                     true,
-				CheckRunAndStatusContextCounts: true,
+				MergeQueue: true,
 			},
 			wantErr: false,
 		},
@@ -116,77 +102,28 @@ func TestPullRequestFeatures(t *testing.T) {
 			hostname: "github.com",
 			queryResponse: map[string]string{
 				`query PullRequest_fields\b`: heredoc.Doc(`
-				{
-					"data": {
-						"PullRequest": {
-							"fields": []
-						},
-						"StatusCheckRollupContextConnection": {
-							"fields": [
-								{"name": "checkRunCount"},
-								{"name": "checkRunCountsByState"},
-								{"name": "statusContextCount"},
-								{"name": "statusContextCountsByState"}
-							]
-						}
-					}
-				}`),
-			},
-			wantFeatures: PullRequestFeatures{
-				MergeQueue:                     false,
-				CheckRunAndStatusContextCounts: true,
-			},
-			wantErr: false,
-		},
-		{
-			name:     "GHE with merge queue and status check counts by state",
-			hostname: "git.my.org",
-			queryResponse: map[string]string{
-				`query PullRequest_fields\b`: heredoc.Doc(`
-				{
-					"data": {
-						"PullRequest": {
-							"fields": [
-								{"name": "isInMergeQueue"},
-								{"name": "isMergeQueueEnabled"}
-							]
-						},
-						"StatusCheckRollupContextConnection": {
-							"fields": [
-								{"name": "checkRunCount"},
-								{"name": "checkRunCountsByState"},
-								{"name": "statusContextCount"},
-								{"name": "statusContextCountsByState"}
-							]
-						}
-					}
-				}`),
+					{ "data": { "PullRequest": { "fields": [
+					] } } }
+				`),
 			},
 			wantFeatures: PullRequestFeatures{
-				MergeQueue:                     true,
-				CheckRunAndStatusContextCounts: true,
+				MergeQueue: false,
 			},
 			wantErr: false,
 		},
 		{
-			name:     "GHE without merge queue and status check counts by state",
+			name:     "GHE",
 			hostname: "git.my.org",
 			queryResponse: map[string]string{
 				`query PullRequest_fields\b`: heredoc.Doc(`
-				{
-					"data": {
-						"PullRequest": {
-							"fields": []
-						},
-						"StatusCheckRollupContextConnection": {
-							"fields": []
-						}
-					}
-				}`),
+					{ "data": { "PullRequest": { "fields": [
+						{"name": "isInMergeQueue"},
+						{"name": "isMergeQueueEnabled"}
+					] } } }
+				`),
 			},
 			wantFeatures: PullRequestFeatures{
-				MergeQueue:                     false,
-				CheckRunAndStatusContextCounts: false,
+				MergeQueue: true,
 			},
 			wantErr: false,
 		},
diff --git a/pkg/cmd/alias/expand/expand_test.go b/pkg/cmd/alias/expand/expand_test.go
new file mode 100644
index 000000000..33af4b073
--- /dev/null
+++ b/pkg/cmd/alias/expand/expand_test.go
@@ -0,0 +1,185 @@
+package expand
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/MakeNowJust/heredoc"
+	"github.com/cli/cli/v2/internal/config"
+)
+
+func TestExpandAlias(t *testing.T) {
+	findShFunc := func() (string, error) {
+		return "/usr/bin/sh", nil
+	}
+
+	cfg := config.NewFromString(heredoc.Doc(`
+		aliases:
+		  co: pr checkout
+		  il: issue list --author="$1" --label="$2"
+		  ia: issue list --author="$1" --assignee="$1"
+	`))
+
+	type args struct {
+		config config.Config
+		argv   []string
+	}
+	tests := []struct {
+		name         string
+		args         args
+		wantExpanded []string
+		wantIsShell  bool
+		wantErr      error
+	}{
+		{
+			name: "no arguments",
+			args: args{
+				config: cfg,
+				argv:   []string{},
+			},
+			wantExpanded: []string(nil),
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "too few arguments",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh"},
+			},
+			wantExpanded: []string(nil),
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "no expansion",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "pr", "status"},
+			},
+			wantExpanded: []string{"pr", "status"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "simple expansion",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "co"},
+			},
+			wantExpanded: []string{"pr", "checkout"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "adding arguments after expansion",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "co", "123"},
+			},
+			wantExpanded: []string{"pr", "checkout", "123"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "not enough arguments for expansion",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "il"},
+			},
+			wantExpanded: []string{},
+			wantIsShell:  false,
+			wantErr:      errors.New(`not enough arguments for alias: issue list --author="$1" --label="$2"`),
+		},
+		{
+			name: "not enough arguments for expansion 2",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "il", "vilmibm"},
+			},
+			wantExpanded: []string{},
+			wantIsShell:  false,
+			wantErr:      errors.New(`not enough arguments for alias: issue list --author="vilmibm" --label="$2"`),
+		},
+		{
+			name: "satisfy expansion arguments",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "il", "vilmibm", "help wanted"},
+			},
+			wantExpanded: []string{"issue", "list", "--author=vilmibm", "--label=help wanted"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "mixed positional and non-positional arguments",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "il", "vilmibm", "epic", "-R", "monalisa/testing"},
+			},
+			wantExpanded: []string{"issue", "list", "--author=vilmibm", "--label=epic", "-R", "monalisa/testing"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+		{
+			name: "dollar in expansion",
+			args: args{
+				config: cfg,
+				argv:   []string{"gh", "ia", "$coolmoney$"},
+			},
+			wantExpanded: []string{"issue", "list", "--author=$coolmoney$", "--assignee=$coolmoney$"},
+			wantIsShell:  false,
+			wantErr:      nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotExpanded, gotIsShell, err := ExpandAlias(tt.args.config, tt.args.argv, findShFunc)
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Fatal("expected error")
+				}
+				if tt.wantErr.Error() != err.Error() {
+					t.Fatalf("expected error %q, got %q", tt.wantErr, err)
+				}
+				return
+			}
+			if err != nil {
+				t.Fatalf("got error: %v", err)
+			}
+			if !reflect.DeepEqual(gotExpanded, tt.wantExpanded) {
+				t.Errorf("ExpandAlias() gotExpanded = %v, want %v", gotExpanded, tt.wantExpanded)
+			}
+			if gotIsShell != tt.wantIsShell {
+				t.Errorf("ExpandAlias() gotIsShell = %v, want %v", gotIsShell, tt.wantIsShell)
+			}
+		})
+	}
+}
+
+// cfg := `---
+// aliases:
+//   co: pr checkout
+//   il: issue list --author="$1" --label="$2"
+//   ia: issue list --author="$1" --assignee="$1"
+// `
+// 	initBlankContext(cfg, "OWNER/REPO", "trunk")
+// 	for _, c := range []struct {
+// 		Args         string
+// 		ExpectedArgs []string
+// 		Err          string
+// 	}{
+// 		{"gh co", []string{"pr", "checkout"}, ""},
+// 		{"gh il", nil, `not enough arguments for alias: issue list --author="$1" --label="$2"`},
+// 		{"gh il vilmibm", nil, `not enough arguments for alias: issue list --author="vilmibm" --label="$2"`},
+// 		{"gh co 123", []string{"pr", "checkout", "123"}, ""},
+// 		{"gh il vilmibm epic", []string{"issue", "list", `--author=vilmibm`, `--label=epic`}, ""},
+// 		{"gh ia vilmibm", []string{"issue", "list", `--author=vilmibm`, `--assignee=vilmibm`}, ""},
+// 		{"gh ia $coolmoney$", []string{"issue", "list", `--author=$coolmoney$`, `--assignee=$coolmoney$`}, ""},
+// 		{"gh pr status", []string{"pr", "status"}, ""},
+// 		{"gh il vilmibm epic -R vilmibm/testing", []string{"issue", "list", "--author=vilmibm", "--label=epic", "-R", "vilmibm/testing"}, ""},
+// 		{"gh dne", []string{"dne"}, ""},
+// 		{"gh", []string{}, ""},
+// 		{"", []string{}, ""},
+// 	} {
diff --git a/pkg/cmd/alias/imports/import_test.go b/pkg/cmd/alias/imports/import_test.go
index 0013429f6..8837a52db 100644
--- a/pkg/cmd/alias/imports/import_test.go
+++ b/pkg/cmd/alias/imports/import_test.go
@@ -13,6 +13,7 @@ import (
 	"github.com/cli/cli/v2/internal/config"
 	"github.com/cli/cli/v2/pkg/cmd/alias/shared"
 	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/extensions"
 	"github.com/cli/cli/v2/pkg/iostreams"
 	"github.com/google/shlex"
 	"github.com/spf13/cobra"
@@ -256,9 +257,9 @@ func TestImportRun(t *testing.T) {
 			wantStderr: strings.Join(
 				[]string{
 					importFileMsg,
-					"X Could not import alias api: already a gh command, extension, or alias",
-					"X Could not import alias issue: already a gh command, extension, or alias",
-					"X Could not import alias pr: already a gh command, extension, or alias\n\n",
+					"X Could not import alias api: already a gh command",
+					"X Could not import alias issue: already a gh command",
+					"X Could not import alias pr: already a gh command\n\n",
 				},
 				"\n",
 			),
@@ -276,8 +277,8 @@ func TestImportRun(t *testing.T) {
 			wantStderr: strings.Join(
 				[]string{
 					importFileMsg,
-					"X Could not import alias alias1: expansion does not correspond to a gh command, extension, or alias",
-					"X Could not import alias alias2: expansion does not correspond to a gh command, extension, or alias\n\n",
+					"X Could not import alias alias1: expansion does not correspond to a gh command",
+					"X Could not import alias alias2: expansion does not correspond to a gh command\n\n",
 				},
 				"\n",
 			),
@@ -303,6 +304,16 @@ func TestImportRun(t *testing.T) {
 				return cfg, nil
 			}
 
+			// Create fake command factory for testing.
+			f := &cmdutil.Factory{
+				IOStreams: ios,
+				ExtensionManager: &extensions.ExtensionManagerMock{
+					ListFunc: func() []extensions.Extension {
+						return []extensions.Extension{}
+					},
+				},
+			}
+
 			// Create fake command structure for testing.
 			rootCmd := &cobra.Command{}
 			prCmd := &cobra.Command{Use: "pr"}
@@ -316,8 +327,7 @@ func TestImportRun(t *testing.T) {
 			apiCmd.AddCommand(&cobra.Command{Use: "graphql"})
 			rootCmd.AddCommand(apiCmd)
 
-			tt.opts.validAliasName = shared.ValidAliasNameFunc(rootCmd)
-			tt.opts.validAliasExpansion = shared.ValidAliasExpansionFunc(rootCmd)
+			tt.opts.existingCommand = shared.ExistingCommandFunc(f, rootCmd)
 
 			if tt.stdin != "" {
 				stdin.WriteString(tt.stdin)
diff --git a/pkg/cmd/alias/set/set_test.go b/pkg/cmd/alias/set/set_test.go
index 54fcabf5b..bec03cba4 100644
--- a/pkg/cmd/alias/set/set_test.go
+++ b/pkg/cmd/alias/set/set_test.go
@@ -73,7 +73,7 @@ func TestAliasSet_gh_command(t *testing.T) {
 	cfg := config.NewFromString(``)
 
 	_, err := runCommand(cfg, true, "pr 'pr status'", "")
-	assert.EqualError(t, err, `could not create alias: "pr" is already a gh command, extension, or alias`)
+	assert.EqualError(t, err, `could not create alias: "pr" is already a gh command`)
 }
 
 func TestAliasSet_empty_aliases(t *testing.T) {
@@ -231,7 +231,7 @@ func TestAliasSet_invalid_command(t *testing.T) {
 	cfg := config.NewFromString(``)
 
 	_, err := runCommand(cfg, true, "co 'pe checkout'", "")
-	assert.EqualError(t, err, "could not create alias: pe checkout does not correspond to a gh command, extension, or alias")
+	assert.EqualError(t, err, "could not create alias: pe checkout does not correspond to a gh command")
 }
 
 func TestShellAlias_flag(t *testing.T) {
diff --git a/pkg/cmd/alias/shared/validations_test.go b/pkg/cmd/alias/shared/validations_test.go
index 72270a608..faa0e497d 100644
--- a/pkg/cmd/alias/shared/validations_test.go
+++ b/pkg/cmd/alias/shared/validations_test.go
@@ -3,35 +3,22 @@ package shared
 import (
 	"testing"
 
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/extensions"
 	"github.com/spf13/cobra"
 	"github.com/stretchr/testify/assert"
 )
 
-func TestValidAliasNameFunc(t *testing.T) {
-	// Create fake command structure for testing.
-	issueCmd := &cobra.Command{Use: "issue"}
-	prCmd := &cobra.Command{Use: "pr"}
-	prCmd.AddCommand(&cobra.Command{Use: "checkout"})
-
-	cmd := &cobra.Command{}
-	cmd.AddCommand(prCmd)
-	cmd.AddCommand(issueCmd)
-
-	f := ValidAliasNameFunc(cmd)
-
-	assert.False(t, f("pr"))
-	assert.False(t, f("pr checkout"))
-	assert.False(t, f("issue"))
-	assert.False(t, f("repo list"))
+func TestExistingCommandFunc(t *testing.T) {
+	// Create fake command factory for testing.
+	factory := &cmdutil.Factory{
+		ExtensionManager: &extensions.ExtensionManagerMock{
+			ListFunc: func() []extensions.Extension {
+				return []extensions.Extension{}
+			},
+		},
+	}
 
-	assert.True(t, f("ps"))
-	assert.True(t, f("checkout"))
-	assert.True(t, f("issue erase"))
-	assert.True(t, f("pr erase"))
-	assert.True(t, f("pr checkout branch"))
-}
-
-func TestValidAliasExpansionFunc(t *testing.T) {
 	// Create fake command structure for testing.
 	issueCmd := &cobra.Command{Use: "issue"}
 	prCmd := &cobra.Command{Use: "pr"}
@@ -41,14 +28,13 @@ func TestValidAliasExpansionFunc(t *testing.T) {
 	cmd.AddCommand(prCmd)
 	cmd.AddCommand(issueCmd)
 
-	f := ValidAliasExpansionFunc(cmd)
-
-	assert.False(t, f("ps"))
-	assert.False(t, f("checkout"))
-	assert.False(t, f("repo list"))
+	f := ExistingCommandFunc(factory, cmd)
 
-	assert.True(t, f("!git branch --show-current"))
 	assert.True(t, f("pr"))
 	assert.True(t, f("pr checkout"))
 	assert.True(t, f("issue"))
+
+	assert.False(t, f("ps"))
+	assert.False(t, f("checkout"))
+	assert.False(t, f("repo list"))
 }
diff --git a/pkg/cmd/codespace/view_test.go b/pkg/cmd/codespace/view_test.go
new file mode 100644
index 000000000..edcfefef6
--- /dev/null
+++ b/pkg/cmd/codespace/view_test.go
@@ -0,0 +1,125 @@
+package codespace
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/cli/cli/v2/internal/codespaces/api"
+	"github.com/cli/cli/v2/pkg/iostreams"
+)
+
+func Test_NewCmdView(t *testing.T) {
+	tests := []struct {
+		tName         string
+		codespaceName string
+		opts          *viewOptions
+		cliArgs       []string
+		wantErr       bool
+		wantStdout    string
+		errMsg        string
+	}{
+		{
+			tName:   "selector throws because no terminal found",
+			opts:    &viewOptions{},
+			wantErr: true,
+			errMsg:  "choosing codespace: error getting answers: no terminal",
+		},
+		{
+			tName:         "command fails because provided codespace doesn't exist",
+			codespaceName: "i-dont-exist",
+			opts:          &viewOptions{},
+			wantErr:       true,
+			errMsg:        "getting full codespace details: codespace not found",
+		},
+		{
+			tName:         "command succeeds because codespace exists (no details)",
+			codespaceName: "monalisa-cli-cli-abcdef",
+			opts:          &viewOptions{},
+			wantErr:       false,
+			wantStdout:    "Name\tmonalisa-cli-cli-abcdef\nGit Status\t - 0↓ 0↑\nIdle Timeout\t0 minutes\n",
+		},
+		{
+			tName:         "command succeeds because codespace exists (with details)",
+			codespaceName: "monalisa-cli-cli-hijklm",
+			opts:          &viewOptions{},
+			wantErr:       false,
+			wantStdout:    "Name\tmonalisa-cli-cli-hijklm\nGit Status\tmain* - 2↓ 1↑\nIdle Timeout\t30 minutes\nRetention Period\t1 day\n",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.tName, func(t *testing.T) {
+			ios, _, stdout, _ := iostreams.Test()
+			a := &App{
+				apiClient: testViewApiMock(),
+				io:        ios,
+			}
+			selector := &CodespaceSelector{api: a.apiClient, codespaceName: tt.codespaceName}
+			tt.opts.selector = selector
+
+			var err error
+			if tt.cliArgs == nil {
+				if tt.opts.selector == nil {
+					t.Fatalf("selector must be set in opts if cliArgs are not provided")
+				}
+
+				err = a.ViewCodespace(context.Background(), tt.opts)
+			} else {
+				cmd := newViewCmd(a)
+				cmd.SilenceUsage = true
+				cmd.SilenceErrors = true
+				cmd.SetOut(ios.ErrOut)
+				cmd.SetErr(ios.ErrOut)
+				cmd.SetArgs(tt.cliArgs)
+				_, err = cmd.ExecuteC()
+			}
+
+			if tt.wantErr {
+				if err == nil {
+					t.Error("Edit() expected error, got nil")
+				} else if err.Error() != tt.errMsg {
+					t.Errorf("Edit() error = %q, want %q", err, tt.errMsg)
+				}
+			} else if err != nil {
+				t.Errorf("Edit() expected no error, got %v", err)
+			}
+
+			if out := stdout.String(); out != tt.wantStdout {
+				t.Errorf("stdout = %q, want %q", out, tt.wantStdout)
+			}
+		})
+	}
+}
+
+func testViewApiMock() *apiClientMock {
+	codespaceWithNoDetails := &api.Codespace{
+		Name: "monalisa-cli-cli-abcdef",
+	}
+	codespaceWithDetails := &api.Codespace{
+		Name: "monalisa-cli-cli-hijklm",
+		GitStatus: api.CodespaceGitStatus{
+			Ahead:                 1,
+			Behind:                2,
+			Ref:                   "main",
+			HasUnpushedChanges:    true,
+			HasUncommittedChanges: true,
+		},
+		IdleTimeoutMinutes:     30,
+		RetentionPeriodMinutes: 1440,
+	}
+	return &apiClientMock{
+		GetCodespaceFunc: func(_ context.Context, name string, _ bool) (*api.Codespace, error) {
+			if name == codespaceWithDetails.Name {
+				return codespaceWithDetails, nil
+			} else if name == codespaceWithNoDetails.Name {
+				return codespaceWithNoDetails, nil
+			}
+
+			return nil, fmt.Errorf("codespace not found")
+		},
+		ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
+			return []*api.Codespace{codespaceWithNoDetails, codespaceWithDetails}, nil
+		},
+	}
+}
diff --git a/pkg/cmd/extension/command_test.go b/pkg/cmd/extension/command_test.go
index 6080e8483..ebc4716de 100644
--- a/pkg/cmd/extension/command_test.go
+++ b/pkg/cmd/extension/command_test.go
@@ -974,7 +974,7 @@ func Test_checkValidExtension(t *testing.T) {
 				manager: m,
 				extName: "gh-auth",
 			},
-			wantError: "\"auth\" matches the name of a built-in command or alias",
+			wantError: "\"auth\" matches the name of a built-in command",
 		},
 		{
 			name: "clashes with an installed extension",
diff --git a/pkg/cmd/pr/status/status_test.go b/pkg/cmd/pr/status/status_test.go
index 3df048503..1b39ddb24 100644
--- a/pkg/cmd/pr/status/status_test.go
+++ b/pkg/cmd/pr/status/status_test.go
@@ -12,7 +12,6 @@ import (
 	"github.com/cli/cli/v2/context"
 	"github.com/cli/cli/v2/git"
 	"github.com/cli/cli/v2/internal/config"
-	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/internal/run"
 	"github.com/cli/cli/v2/pkg/cmdutil"
@@ -23,10 +22,6 @@ import (
 )
 
 func runCommand(rt http.RoundTripper, branch string, isTTY bool, cli string) (*test.CmdOut, error) {
-	return runCommandWithDetector(rt, branch, isTTY, cli, &fd.DisabledDetectorMock{})
-}
-
-func runCommandWithDetector(rt http.RoundTripper, branch string, isTTY bool, cli string, detector fd.Detector) (*test.CmdOut, error) {
 	ios, _, stdout, stderr := iostreams.Test()
 	ios.SetStdoutTTY(isTTY)
 	ios.SetStdinTTY(isTTY)
@@ -60,12 +55,7 @@ func runCommandWithDetector(rt http.RoundTripper, branch string, isTTY bool, cli
 		GitClient: &git.Client{GitPath: "some/path/git"},
 	}
 
-	withProvidedDetector := func(opts *StatusOptions) error {
-		opts.Detector = detector
-		return statusRun(opts)
-	}
-
-	cmd := NewCmdStatus(factory, withProvidedDetector)
+	cmd := NewCmdStatus(factory, nil)
 	cmd.PersistentFlags().StringP("repo", "R", "", "")
 
 	argv, err := shlex.Split(cli)
@@ -116,8 +106,7 @@ func TestPRStatus(t *testing.T) {
 func TestPRStatus_reviewsAndChecks(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
-	// status,conclusion matches the old StatusContextRollup query
-	http.Register(httpmock.GraphQL(`status,conclusion`), httpmock.FileResponse("./fixtures/prStatusChecks.json"))
+	http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("./fixtures/prStatusChecks.json"))
 
 	output, err := runCommand(http, "blueberries", true, "")
 	if err != nil {
@@ -138,31 +127,6 @@ func TestPRStatus_reviewsAndChecks(t *testing.T) {
 	}
 }
 
-func TestPRStatus_reviewsAndChecksWithStatesByCount(t *testing.T) {
-	http := initFakeHTTP()
-	defer http.Verify(t)
-	// checkRunCount,checkRunCountsByState matches the new StatusContextRollup query
-	http.Register(httpmock.GraphQL(`checkRunCount,checkRunCountsByState`), httpmock.FileResponse("./fixtures/prStatusChecksWithStatesByCount.json"))
-
-	output, err := runCommandWithDetector(http, "blueberries", true, "", &fd.EnabledDetectorMock{})
-	if err != nil {
-		t.Errorf("error running command `pr status`: %v", err)
-	}
-
-	expected := []string{
-		"✓ Checks passing + Changes requested ! Merge conflict status unknown",
-		"- Checks pending ✓ 2 Approved",
-		"× 1/3 checks failing - Review required ✓ No merge conflicts",
-		"✓ Checks passing × Merge conflicts",
-	}
-
-	for _, line := range expected {
-		if !strings.Contains(output.String(), line) {
-			t.Errorf("output did not contain %q: %q", line, output.String())
-		}
-	}
-}
-
 func TestPRStatus_currentBranch_showTheMostRecentPR(t *testing.T) {
 	http := initFakeHTTP()
 	defer http.Verify(t)
diff --git a/pkg/cmd/root/alias_test.go b/pkg/cmd/root/alias_test.go
deleted file mode 100644
index 4a2d3c13e..000000000
--- a/pkg/cmd/root/alias_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package root
-
-import (
-	"errors"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestExpandAlias(t *testing.T) {
-	tests := []struct {
-		name         string
-		expansion    string
-		args         []string
-		wantExpanded []string
-		wantErr      string
-	}{
-		{
-			name:         "no expansion",
-			expansion:    "pr status",
-			args:         []string{},
-			wantExpanded: []string{"pr", "status"},
-		},
-		{
-			name:         "adding arguments after expansion",
-			expansion:    "pr checkout",
-			args:         []string{"123"},
-			wantExpanded: []string{"pr", "checkout", "123"},
-		},
-		{
-			name:      "not enough arguments for expansion",
-			expansion: `issue list --author="$1" --label="$2"`,
-			args:      []string{},
-			wantErr:   `not enough arguments for alias: issue list --author="$1" --label="$2"`,
-		},
-		{
-			name:      "not enough arguments for expansion 2",
-			expansion: `issue list --author="$1" --label="$2"`,
-			args:      []string{"vilmibm"},
-			wantErr:   `not enough arguments for alias: issue list --author="vilmibm" --label="$2"`,
-		},
-		{
-			name:         "satisfy expansion arguments",
-			expansion:    `issue list --author="$1" --label="$2"`,
-			args:         []string{"vilmibm", "help wanted"},
-			wantExpanded: []string{"issue", "list", "--author=vilmibm", "--label=help wanted"},
-		},
-		{
-			name:         "mixed positional and non-positional arguments",
-			expansion:    `issue list --author="$1" --label="$2"`,
-			args:         []string{"vilmibm", "epic", "-R", "monalisa/testing"},
-			wantExpanded: []string{"issue", "list", "--author=vilmibm", "--label=epic", "-R", "monalisa/testing"},
-		},
-		{
-			name:         "dollar in expansion",
-			expansion:    `issue list --author="$1" --assignee="$1"`,
-			args:         []string{"$coolmoney$"},
-			wantExpanded: []string{"issue", "list", "--author=$coolmoney$", "--assignee=$coolmoney$"},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			gotExpanded, err := expandAlias(tt.expansion, tt.args)
-			if tt.wantErr != "" {
-				assert.Nil(t, gotExpanded)
-				assert.EqualError(t, err, tt.wantErr)
-				return
-			}
-			assert.NoError(t, err)
-			assert.Equal(t, tt.wantExpanded, gotExpanded)
-		})
-	}
-}
-
-func TestExpandShellAlias(t *testing.T) {
-	findShFunc := func() (string, error) {
-		return "/usr/bin/sh", nil
-	}
-	tests := []struct {
-		name         string
-		expansion    string
-		args         []string
-		findSh       func() (string, error)
-		wantExpanded []string
-		wantErr      string
-	}{
-		{
-			name:         "simple expansion",
-			expansion:    "!git branch --show-current",
-			args:         []string{},
-			findSh:       findShFunc,
-			wantExpanded: []string{"/usr/bin/sh", "-c", "git branch --show-current"},
-		},
-		{
-			name:         "adding arguments after expansion",
-			expansion:    "!git branch checkout",
-			args:         []string{"123"},
-			findSh:       findShFunc,
-			wantExpanded: []string{"/usr/bin/sh", "-c", "git branch checkout", "--", "123"},
-		},
-		{
-			name:      "unable to find sh",
-			expansion: "!git branch --show-current",
-			args:      []string{},
-			findSh: func() (string, error) {
-				return "", errors.New("unable to locate sh")
-			},
-			wantErr: "unable to locate sh",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			gotExpanded, err := expandShellAlias(tt.expansion, tt.args, tt.findSh)
-			if tt.wantErr != "" {
-				assert.Nil(t, gotExpanded)
-				assert.EqualError(t, err, tt.wantErr)
-				return
-			}
-			assert.NoError(t, err)
-			assert.Equal(t, tt.wantExpanded, gotExpanded)
-		})
-	}
-}