From 04bc0d27f4414a5e3d9224e606203b6ce600be3d Mon Sep 17 00:00:00 2001 From: Kynan Ware <47394200+BagToad@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:58:57 -0700 Subject: [PATCH] Show friendly Copilot (AI) name in gh pr view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CopilotDisplayName helper that translates known Copilot bot logins (copilot-pull-request-reviewer, copilot-swe-agent) to the friendly 'Copilot (AI)' display name. Applied to: - PullRequestReview.AuthorLogin() — review comment author - Comment.AuthorLogin() — PR/issue comment author - parseReviewers() in pr view — reviewer list display This ensures gh pr view shows 'Copilot (AI)' instead of the raw 'copilot-pull-request-reviewer' login for both the reviewer status line and any review comments left by Copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- api/queries_comments.go | 2 +- api/queries_pr_review.go | 12 +++++------- api/queries_repo.go | 15 +++++++++++---- api/queries_repo_test.go | 20 ++++++++++++++++++++ pkg/cmd/pr/view/view.go | 4 ++-- 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/api/queries_comments.go b/api/queries_comments.go index 8af17fd2a..b0450c068 100644 --- a/api/queries_comments.go +++ b/api/queries_comments.go @@ -129,7 +129,7 @@ func (c Comment) Identifier() string { } func (c Comment) AuthorLogin() string { - return c.Author.Login + return copilotDisplayName(c.Author.Login) } func (c Comment) Association() string { diff --git a/api/queries_pr_review.go b/api/queries_pr_review.go index a6fa34f9e..7f23f8b5e 100644 --- a/api/queries_pr_review.go +++ b/api/queries_pr_review.go @@ -52,7 +52,7 @@ func (prr PullRequestReview) Identifier() string { } func (prr PullRequestReview) AuthorLogin() string { - return prr.Author.Login + return copilotDisplayName(prr.Author.Login) } func (prr PullRequestReview) Association() string { @@ -158,8 +158,9 @@ func (r RequestedReviewer) DisplayName() string { if r.TypeName == teamTypeName { return fmt.Sprintf("%s/%s", r.Organization.Login, r.Slug) } - if r.TypeName == botTypeName && r.Login == CopilotReviewerLogin { - return "Copilot (AI)" + displayName := copilotDisplayName(r.Login) + if displayName != r.Login { + return displayName } if r.Name != "" { return fmt.Sprintf("%s (%s)", r.Login, r.Name) @@ -221,10 +222,7 @@ func NewReviewerBot(login string) ReviewerBot { } func (b ReviewerBot) DisplayName() string { - if b.login == CopilotReviewerLogin { - return fmt.Sprintf("%s (AI)", CopilotActorName) - } - return b.Login() + return copilotDisplayName(b.login) } func (r ReviewerBot) sealedReviewerCandidate() {} diff --git a/api/queries_repo.go b/api/queries_repo.go index d8ffa191d..31dbf75f1 100644 --- a/api/queries_repo.go +++ b/api/queries_repo.go @@ -1087,6 +1087,16 @@ const CopilotAssigneeLogin = "copilot-swe-agent" const CopilotReviewerLogin = "copilot-pull-request-reviewer" const CopilotActorName = "Copilot" +// copilotDisplayName returns "Copilot (AI)" if the login is a known Copilot bot login, +// otherwise returns the login unchanged. Use this to translate raw bot logins into +// user-friendly display names in command output. +func copilotDisplayName(login string) string { + if login == CopilotReviewerLogin || login == CopilotAssigneeLogin { + return fmt.Sprintf("%s (AI)", CopilotActorName) + } + return login +} + type AssignableActor interface { DisplayName() string ID() string @@ -1145,10 +1155,7 @@ func NewAssignableBot(id, login string) AssignableBot { } func (b AssignableBot) DisplayName() string { - if b.login == CopilotAssigneeLogin { - return fmt.Sprintf("%s (AI)", CopilotActorName) - } - return b.Login() + return copilotDisplayName(b.login) } func (b AssignableBot) ID() string { diff --git a/api/queries_repo_test.go b/api/queries_repo_test.go index ad0b8e857..928a9e885 100644 --- a/api/queries_repo_test.go +++ b/api/queries_repo_test.go @@ -563,6 +563,26 @@ func TestDisplayName(t *testing.T) { } } +func TestCopilotDisplayName(t *testing.T) { + tests := []struct { + login string + want string + }{ + {login: "copilot-pull-request-reviewer", want: "Copilot (AI)"}, + {login: "copilot-swe-agent", want: "Copilot (AI)"}, + {login: "octocat", want: "octocat"}, + {login: "", want: ""}, + } + for _, tt := range tests { + t.Run(tt.login, func(t *testing.T) { + got := copilotDisplayName(tt.login) + if got != tt.want { + t.Errorf("copilotDisplayName(%q) = %q, want %q", tt.login, got, tt.want) + } + }) + } +} + func TestRepoExists(t *testing.T) { tests := []struct { name string diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index 564cce913..e6ae63a10 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -351,7 +351,7 @@ func parseReviewers(pr api.PullRequest) []*reviewerState { for _, review := range pr.Reviews.Nodes { if review.Author.Login != pr.Author.Login { - name := review.Author.Login + name := review.AuthorLogin() if name == "" { name = ghostName } @@ -364,7 +364,7 @@ func parseReviewers(pr api.PullRequest) []*reviewerState { // Overwrite reviewer's state if a review request for the same reviewer exists. for _, reviewRequest := range pr.ReviewRequests.Nodes { - name := reviewRequest.RequestedReviewer.LoginOrSlug() + name := reviewRequest.RequestedReviewer.DisplayName() reviewerStates[name] = &reviewerState{ Name: name, State: requestedReviewState,