Pair --type with --remove-type so callers can clear an issue's type
without going through the interactive editor, mirroring the
--milestone / --remove-milestone and --parent / --remove-parent
patterns. The two type flags are mutually exclusive.
UpdateIssueIssueType now sends a null issueTypeId when the caller
passes an empty string, which is what the API requires to clear the
field. The orchestrator fires the mutation when either IssueTypeID is
non-empty or RemoveIssueType is set.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Issues 2.0 mutations (issue type, parent set/remove, sub-issues,
blocked-by, blocking) are deferred until after the main UpdateIssue
because they target IDs that the standard mutation does not handle.
Move them behind a shared api.DeferredUpdateIssue orchestrator that
fans them out in parallel and joins all errors so a single failure
does not abort the rest.
editRun no longer carries its own applyEditParent / applyEditSubIssues
/ applyEditRelationships helpers; the per-issue goroutine resolves
refs to node IDs via a small deferredUpdateIssueOptions builder, then
hands the populated DeferredUpdateIssueOptions to api.DeferredUpdateIssue.
Also moves ResolveIssueRef and ResolveIssueTypeName from the deleted
resolve.go into lookup.go.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pinned githubv4 version pre-dates these Issues 2.0 input types, so
the input structs are defined locally to match the pattern in
ReplaceActorsForAssignableByLogin.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the JSON shape of issueType, parent, subIssues, subIssuesSummary,
blockedBy, and blocking, including the null cases for issueType and
parent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The subIssues, blockedBy, and blocking JSON output is currently shaped
as a flat array, which silently truncates when there are more entries
than the GraphQL fragment fetches. That's misleading once a user
crosses the page size, so this switches each connection to a
{nodes, totalCount} object so consumers can see when there's more.
While confirming page sizes, the GitHub limits turn out to be:
- sub-issues: up to 100 per parent
https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues
- blocked-by / blocking: up to 50 per relationship type
https://github.blog/changelog/2025-08-21-dependencies-on-issues/
So subIssues moves to first:100 to fetch the full set; blockedBy and
blocking stay at first:50, which already covers their cap.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add id to the GraphQL fragment for subIssues, blockedBy, and blocking
nodes so the canonical node identifier is fetched, and surface it in
the JSON output alongside number/title/url/state for parent, subIssues,
blockedBy, and blocking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GraphQL already returns an error when the repository or issue does not
exist, making the empty-ID check unreachable.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The AddBlockedByPayload and RemoveBlockedByPayload types expose
the result as 'issue', not 'blockedIssue'. Found during live spec
testing against github.com — the mutations returned empty responses.
Updated mutation queries and corresponding test fixtures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Critical fixes:
- GHES data-flow regression: blockedBy/blocking fields now
conditionally added to view lookupFields only when
IssueRelationshipsSupported is true (GHES 3.19+). Previously
would break gh issue view on GHES 3.17-3.18.
- State line separator: restore original bullet (•) to avoid
breaking downstream parsers. Issue type prefix uses middle dot (·).
Optimizations:
- Batch edit --type: resolve issueTypeID once before the loop
instead of per-goroutine (eliminates N-1 redundant API calls)
- Parent removal: include id in parent GraphQL fragment, use
issue.Parent.ID directly instead of extra IssueNodeID lookup
Nit fixes:
- Fix formatLinkedIssueRef godoc to match actual behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add API infrastructure for issue types, sub-issues, and issue
relationships (blocked-by/blocking):
- New types: IssueType, LinkedIssue, SubIssues, SubIssuesSummary,
LinkedIssueConnection
- New Issue struct fields for all Issues 2.0 data
- GraphQL query builder cases for new fields
- ExportData cases for JSON output
- Mutation functions: UpdateIssueIssueType, AddSubIssue,
RemoveSubIssue, AddBlockedBy, RemoveBlockedBy
- Helper functions: RepoIssueTypes, IssueNodeID
- Feature detection: IssueRelationshipsSupported for GHES 3.19+
(issue types and sub-issues are GA on GHES 3.17+, no detection needed)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a token (GitHub App, fine-grained PAT, or GITHUB_TOKEN) lacks the
project permission, querying projectItems on a PR or issue fails with
"Resource not accessible by integration" or "Resource not accessible by
personal access token". ProjectsV2IgnorableError did not match these
errors, causing commands like pr view, pr edit, and issue view to fail
entirely instead of gracefully omitting project data.
Add "Resource not accessible by" as an ignorable error prefix. This is
safe because ProjectsV2IgnorableError is only called in project-specific
code paths.
Closes#13280
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Commit dd424d85f added NameWithOwner to PRRepository for agent-task
listings but didn't update the headRepository GraphQL query to fetch it.
This caused gh pr view --json headRepository to emit an empty
"nameWithOwner":"" field, breaking the pr-create-respects-simple-
pushdefault acceptance test.
Fetch nameWithOwner in the query and update the test assertion to expect
it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two bugs introduced in #13009 found during acceptance testing:
1. `pr create --assignee @copilot` sent the literal `@copilot` to the
API because NewIssueState only ran MeReplacer on assignees, not
CopilotReplacer. Fixed by switching to SpecialAssigneeReplacer (which
handles both @me and @copilot) like issue create already does.
2. The replaceActorsForAssignable mutation requires the [bot] suffix on
bot actor logins (e.g. `copilot-swe-agent[bot]`), unlike
requestReviewsByLogin which has a separate botLogins field. Added the
suffix in ReplaceActorsForAssignableByLogin for CopilotAssigneeLogin.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Aligns the feature detector field name with the downstream
ApiActorsSupported flag introduced in the previous commit, so the
signal has one consistent name from detection through to consumption.
Also consolidates leftover TODO tags (actorIsAssignableCleanup,
requestReviewsByLoginCleanup) under the single // TODO ApiActorsSupported
tag so there's exactly one thing to grep for.
Pure rename with no logic changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CLI had two per-entity flags (ActorAssignees on EditableAssignees and
IssueMetadataState, ActorReviewers on IssueMetadataState) threaded through
different layers of the stack to distinguish github.com from GHES. Both
flags were always set from the same source (issueFeatures.ActorIsAssignable)
and never had different values, but they were carried independently on
different structs. This led to a confusing asymmetry where:
- EditableAssignees had ActorAssignees but EditableReviewers had nothing
- The PR edit flow piggybacked on editable.Assignees.ActorAssignees to
make reviewer mutation decisions, which was misleading
- RepoMetadataInput only had ActorAssignees with no reviewer equivalent
This commit replaces all per-entity flags with a single ApiActorsSupported
bool hoisted to the shared level on Editable, IssueMetadataState, and
RepoMetadataInput. Both assignees and reviewers now key off the same signal.
Every branch site is marked with // TODO ApiActorsSupported so we can grep
for cleanup sites when GHES eventually supports the actor-based mutations
(replaceActorsForAssignable, requestReviewsByLogin).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Detect which AI coding agent is invoking gh by checking well-known
environment variables and include the agent name in the User-Agent
header sent to GitHub APIs.
Supported agents: Codex, Gemini CLI, Copilot CLI, OpenCode,
Claude Code, and Amp. Generic AI_AGENT env var is also supported
with validation to prevent header injection.
Fixesgithub/cli#1111
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wire up MultiSelectWithSearch for assignees in MetadataSurvey, replacing
the static MultiSelect that required bulk fetching all assignable actors.
This applies to both gh pr create and gh issue create interactive flows
when selecting assignees via the 'Add metadata' prompt.
Changes:
- Add assigneeSearchFunc parameter to MetadataSurvey
- Skip assignee bulk fetch when search func is available
- New SearchRepoAssignableActors API function for repo-level search
(create flows have no issue/PR node ID yet)
- New RepoAssigneeSearchFunc in shared editable.go
- Refactor actorsToSearchResult helper shared by both search functions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When ActorAssignees is true (github.com), pass assignee logins directly
to the ReplaceActorsForAssignable mutation instead of resolving logins
to node IDs. This eliminates the need to bulk fetch all assignable
users/actors and fixes a bug where providing assignees via CLI flag
and then interactively adding metadata would fail with 'not found'
because the cached MetadataResult had no assignee data.
Changes:
- Set state.ActorAssignees = true in pr create (was missing)
- AddMetadataToIssueParams: pass assigneeLogins when ActorAssignees
is true, skip fetch and ID resolution entirely
- CreatePullRequest/IssueCreate: call ReplaceActorsForAssignableByLogin
after creation to assign via logins
- Consolidate replaceActorsForAssignable mutation into api/ package
(ReplaceActorsForAssignableByLogin + ReplaceActorsForAssignableByID)
- Remove duplicate replaceActorAssigneesForEditable from editable_http.go
- Add TODO replaceActorsByLoginCleanup markers on edit paths
Fixescli/cli#13000
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add success, not-found, and edge case tests for both GitHubRepo and
IssueRepoInfo, covering field population, parent repo handling,
viewer permission checks, and issues-disabled scenarios.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a new IssueRepoInfo function that fetches only the fields needed
for issue creation (id, name, owner, hasIssuesEnabled, viewerPermission),
avoiding defaultBranchRef and other fields that require Contents:Read.
Also add StubIssueRepoInfoResponse helper to httpmock for testing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add actorDisplayName call in CommentAuthor.DisplayName for consistency
- Use require.Equal in TestActorDisplayName instead of manual comparisons
- Simplify user type name constant usage
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace copilotDisplayName with actorDisplayName(typeName, login, name)
which handles all actor types: known bots get friendly names (e.g.
Copilot → 'Copilot (AI)'), regular bots return login, users with
names return 'login (Name)', others return login.
All DisplayName() methods on Author, CommentAuthor, GitHubUser,
AssignableUser, AssignableBot, RequestedReviewer, and ReviewerBot
now delegate to actorDisplayName with their available fields.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Replace the top-level organization(login: $owner) query with
repository.owner { ... on Organization { teams } }. This uses
GraphQL inline fragments to conditionally fetch team data only
when the repo owner is an Organization, eliminating the need to
handle 'Could not resolve to an Organization' errors for
personal repos.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add author { login } to the SuggestedReviewerActors GraphQL query
and pre-seed the seen map with the author login so they are excluded
from all sources (suggestions, collaborators, teams). Previously the
author was only skipped via the isAuthor flag in the suggestions loop
but could still appear as a collaborator.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Query the viewer login in SuggestedReviewerActorsForRepo and
pre-seed the seen map so the current user is filtered out of
collaborator results. You cannot review your own PR.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark the piggyback-on-open-PR technique for detecting Copilot
reviewer availability as a HACK, since there is no repo-level API
to check Copilot eligibility without a PR context.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a cleanup TODO comment above the GHES feature detection branch
in CreatePullRequest so we can track removing the ID-based reviewer
request path once GHES supports requestReviewsByLogin.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate duplicated INSUFFICIENT_SCOPES error handling into a single
implementation. The project queries package now calls
api.GenerateScopeErrorForGQL instead of reimplementing the same logic.
Removes duplicated requiredScopesFromServerMessage, scopesRE, and the
associated test (already covered by api/client_test.go).
Fixes#12823
Add the changeType field from the PullRequestChangedFile GraphQL type
to the PullRequestFile struct. This exposes the file status (added,
modified, deleted, renamed, copied, changed) in gh pr list --json files
and gh pr view --json files output.
Closes#11385
The assignees query fragment only requested id, login, and name but the
GitHubUser struct includes a DatabaseID field. Since the field was never
requested from the API, it always defaulted to Go's zero value (0) in
JSON output. This adds databaseId to the fragment so the actual value is
returned.
Also adds ExportData test cases for assignees on both Issue and
PullRequest to verify databaseId round-trips correctly through JSON
serialization.
Consolidate all review-related types, methods, and functions into
queries_pr_review.go for better code organization. The file is now
ordered by logical sections: review data types, review status, review
requests, reviewer candidates, API operations, and helpers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>