Commit graph

463 commits

Author SHA1 Message Date
Kynan Ware
628325920d Reject cross-host issue refs in ResolveIssueRef
Relationship mutations (parent, sub-issue, blocked-by, blocking) run
against baseRepo's host with node IDs that must come from that same
host. Catch a different-host ref up front with a clear error instead
of letting the mutation fail with a confusing node-ID error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 18:04:48 -06:00
Kynan Ware
eff9d48f6e Replace em-dashes with hyphens in code comments
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 17:33:29 -06:00
Kynan Ware
0c48bd0981 Cache issue-type name->ID map and use it in issue edit
prShared.FetchOptions already fetches RepoIssueTypes when IssueType is
edited, but only kept the names. editRun then called RepoIssueTypes a
second time via ResolveIssueTypeName.

Have FetchOptions store the name->ID map on Editable, and look the ID
up directly in editRun. The lookup now also lives inside the per-issue
loop, which fixes a bug where the interactive type prompt's chosen
value was set after the upfront resolve, sending an empty issueTypeId
on the mutation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 17:12:29 -06:00
Kynan Ware
e9f102e8be Test interactive issue-type prompt in createRun
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 16:42:36 -06:00
Kynan Ware
85ee04d0fd Exercise loop in blocked-by/blocking createRun test
Two values per relationship verifies each ref is resolved and applied
independently rather than masking a single-iteration bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 16:31:02 -06:00
Kynan Ware
a04438c22c Merge createRun_issuesV2 cases into Test_createRun
Same code path, same scope. Test_createRun's httpStubs signature picks
up *testing.T to match the V2 cases that need it for input assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 16:27:22 -06:00
Kynan Ware
201eb7ec5e Merge issuesV2Flags cases into TestNewCmdCreate
The five Issues 2.0 cases (type, parent by number, parent by URL,
blocked-by, blocking) live as a separate parallel test today. They
exercise the same NewCmdCreate code path as the existing cases, so
fold them into TestNewCmdCreate and extend the assertions to cover
IssueType, Parent, BlockedBy, and Blocking.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 16:22:45 -06:00
Kynan Ware
a78bedb772 Wrap relationship JSON output in {nodes, totalCount}, fetch full pages
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>
2026-05-06 16:05:07 -06:00
Kynan Ware
6e76f11dec fix: correct GraphQL field name in AddBlockedBy/RemoveBlockedBy mutations
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>
2026-04-30 08:49:43 -06:00
Kynan Ware
e3b0d9dec3 fix: restore displaced TODO comment, add GHES edit relationship test
- Restore // TODO projectsV1Deprecation comment above
  TestProjectsV1Deprecation (was displaced when new tests were
  inserted at that location)
- Add 'relationships unsupported on GHES' test case to edit command
  using DisabledDetectorMock (parity with create's GHES tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
2a72a59f76 fix: address round 2 code review findings
- Fix PR edit regression: gate Type and Parent behind Allowed bool
  in FieldsToEditSurvey, matching the Reviewers.Allowed pattern.
  Only issue edit sets Allowed=true; PR edit won't show these fields.
- Add missing RemoveBlocking assertion in flag parsing tests
- Quote issue type names containing spaces in search queries
  (type:"Bug Report" instead of type:Bug Report)
- Remove duplicate TODO comment in view.go
- Avoid double RepoIssueTypes API call in interactive create:
  cache the resolved ID from the picker, skip re-resolution
- Rename applyIssueTypes → applyIssueType (singular)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
b6f29b0376 feat: add --remove-blocking flag to issue edit
Completes the symmetry of relationship flags:
- --add-blocked-by / --remove-blocked-by
- --add-blocking / --remove-blocking

The --remove-blocking flag swaps API args (same as --add-blocking):
calls RemoveBlockedBy(issueId=OTHER, blockingIssueId=THIS).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
3f1df513c5 test: add missing --type flag tests for issue list
Add flag parsing and behavior tests for gh issue list --type:
- TestNewCmdList/type_flag: verifies opts.IssueType is set
- Test_issueList/with_issue_type: verifies search path is forced
  and query includes type:Bug qualifier

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
c00bc6a9b4 fix: address code review findings from multi-model review
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>
2026-04-30 08:49:43 -06:00
Kynan Ware
ddb2fce07b refactor: extract shared helpers for issue ref resolution and type lookup
Address code review findings:

- Extract resolveIssueRef into shared.ResolveIssueRef (was duplicated
  between create.go and edit.go)
- Extract issue type name→ID resolution into shared.ResolveIssueTypeName
  (was duplicated between create applyIssueTypes and edit applyEditIssueType)
- Fix double import of issue/shared in view.go

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
4c12393b88 test: add comprehensive tests for Issues 2.0 features
Create tests (11 new):
- Flag parsing: --type, --parent (number/URL), --blocked-by, --blocking
- Behavior: type resolution + mutation, type not found error,
  parent resolution + addSubIssue, blocked-by/blocking with
  swapped args verification, GHES unsupported error

Edit tests (18 new):
- Flag parsing: --type, --set-parent, --remove-parent, mutual
  exclusivity, --add-sub-issue, --remove-sub-issue,
  --add-blocked-by, --remove-blocked-by, --add-blocking
- Behavior: type edit, set/remove parent, add/remove sub-issues,
  add/remove blocked-by, add-blocking with swapped args,
  batch edit type across multiple issues
- Bug fix: copy SetParent value into Editable.Parent.Value

View tests (5 new):
- TTY: full view with all Issues 2.0 fields, regression test
  with no new fields
- JSON: issueType export, parent/subIssues/subIssuesSummary
  export, blockedBy/blocking export

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
67f63a1096 feat: add --type filter flag to issue list
Filter issues by type using the search API path. The --type flag
appends a type: qualifier to the search query, forcing the search
path (same as --label and --milestone).

Updated FilterOptions with IssueType field, IsDefault(), and
SearchQueryBuild() to include the type qualifier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
b7ee31d791 feat: add Issues 2.0 flags to issue edit
New flags for issue edit:
- --type: set issue type by name
- --set-parent / --remove-parent: set or remove parent issue
  (mutually exclusive via cmdutil.MutuallyExclusive)
- --add-sub-issue / --remove-sub-issue: manage sub-issues
- --add-blocked-by / --remove-blocked-by: manage blocked-by relationships
- --add-blocking: add blocking relationships (swaps API args)

Interactive mode: Type and Parent added to the field picker survey.
FetchOptions loads issue types when Type is selected.

Editable struct: added IssueType and Parent fields with Dirty(),
Clone(), FieldsToEditSurvey, and EditFieldsSurvey support.

GHES: relationships gated behind IssueRelationshipsSupported.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
44e9f4bd50 feat: add --type, --parent, --blocked-by, --blocking to issue create
Post-creation mutations for Issues 2.0 fields:
- --type: resolve type name to ID via RepoIssueTypes, then
  updateIssueIssueType mutation
- --parent: resolve issue ref (number or URL), then addSubIssue
  mutation (supports cross-repo URLs)
- --blocked-by: resolve refs, then addBlockedBy mutations
- --blocking: resolve refs, then addBlockedBy with swapped args

Interactive mode: type picker when repo has issue types configured.

GHES: relationships gated behind IssueRelationshipsSupported feature
detection (3.19+). Types and sub-issues need no detection (GA 3.17+).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
7dae882c9d feat: add Issues 2.0 fields to issue view
Display new issue metadata in TTY view:
- Issue type on state line (gray, before Open/Closed)
- Type, Parent, Blocked by, Blocking metadata rows
- Sub-issues section with completion progress (X/Y, Z%)
- Cross-repo references show full owner/repo#N format

All new fields included in defaultFields and JSON export.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 08:49:43 -06:00
Kynan Ware
8f7d20855e
Merge pull request #13025 from cli/kw/refactor/reviewer-assignee-actor-symmetry
Consolidate actor-mode signals into ApiActorsSupported
2026-03-25 11:43:18 -06:00
William Martin
69d89a6590
Merge pull request #12884 from cli/babakks/use-min-discovery-fields-for-issue-create
fix(issue): avoid fetching unnecessary fields for discovery
2026-03-25 15:39:40 +01:00
Kynan Ware
bff468bafe fix(pr create): wire up @copilot assignee replacement and [bot] suffix
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>
2026-03-24 23:26:28 -06:00
Kynan Ware
ae5e857c2e refactor(featuredetection): rename ActorIsAssignable to ApiActorsSupported
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>
2026-03-24 21:09:04 -06:00
Kynan Ware
3c00ffdade refactor(pr shared): consolidate ActorAssignees and ActorReviewers into ApiActorsSupported
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>
2026-03-24 21:04:41 -06:00
Kynan Ware
4e6bc78e04 refactor(pr shared): extract SpecialAssigneeReplacer for @me and Copilot expansion
The inline replaceSpecialAssigneeNames closures in AssigneeIds and
AssigneeLogins were duplicated. Extract them into an exported
SpecialAssigneeReplacer type that consolidates MeReplacer and
CopilotReplacer into a single ReplaceSlice call, parameterised by
actorAssignees and copilotUseLogin.

Adopt the new type in the issue create flow as well, replacing the
manual MeReplacer + conditional CopilotReplacer sequence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 18:59:21 -06:00
Kynan Ware
11f177a8c3 feat(pr create, issue create): search-based assignee selection in MetadataSurvey
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>
2026-03-23 18:49:28 -06:00
Kynan Ware
33783748f3 review: address code review feedback
- Fix tests: assert logins (not display names) in actorLogins
- Remove dead ReplaceActorsForAssignableByID (no callers)
- Extract shared AssigneeSearchFunc to pkg/cmd/pr/shared/editable.go
- Remove duplicate assigneeSearchFunc from pr/edit and issue/edit

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 17:36:52 -06:00
Kynan Ware
947f8fb1b7 refactor(issue edit): wire up search-based assignee selection
Add AssigneeSearchFunc to gh issue edit interactive flow, matching
the pattern already used in gh pr edit. This eliminates the bulk
RepositoryAssignableActors fetch for interactive assignee selection,
using dynamic SuggestedAssignableActors search instead.

Also clean up pr edit assigneeSearchFunc signature to remove the
unused editable parameter (no longer needed after removing the
actor accumulation hack).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 17:06:38 -06:00
Kynan Ware
b3cfe7454c refactor(pr edit, issue edit): use login-based assignee mutation for flag flows
When ActorAssignees is true (github.com), the --add-assignee and
--remove-assignee flag flows now pass logins directly to
ReplaceActorsForAssignableByLogin instead of bulk fetching all
assignable actors and resolving logins to node IDs.

Changes:
- New AssigneeLogins() method on Editable that computes the final
  login set (defaults + add - remove) without ID resolution
- UpdateIssue: call AssigneeLogins + ByLogin when ActorAssignees is true
- EditableOptionsFetch: skip assignee bulk fetch for flag flows on
  github.com (only fetch on GHES where ID resolution is needed)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 15:33:37 -06:00
Kynan Ware
e6d9019bc9 fix(pr create): use login-based assignee mutation on 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

Fixes cli/cli#13000

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 15:21:20 -06:00
Babak K. Shandiz
9f5dfa80c8
test(issue transfer): update stub for IssueRepositoryInfo query
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-09 11:49:44 +00:00
Babak K. Shandiz
aad0239683
fix(issue transfer): use IssueRepoInfo to fetch minimal fields for issues
Only the destination repo ID is needed for issue transfer. Switch from
GitHubRepo to IssueRepoInfo to use minimal fields appropriate for issues.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-09 11:49:22 +00:00
Babak K. Shandiz
64416e1ea1
test(issue create): update stubs for IssueRepositoryInfo query
Update test mocks to match the renamed GraphQL query used by
IssueRepoInfo, and switch to StubIssueRepoInfoResponse helper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-09 11:35:13 +00:00
Babak K. Shandiz
1d95b633e3
fix(issue create): use IssueRepoInfo to avoid requiring Contents:Read permission
Switch issue create from GitHubRepo to IssueRepoInfo so that
gh issue create works with fine-grained PATs that only have
Issues:Write and Metadata:Read permissions.

Fixes cli/cli#12798

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-09 11:33:12 +00:00
Kynan Ware
3651c289ed Show friendly display names in gh issue view
Apply DisplayName() to author and assignee display in issue view,
consistent with the pr view changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-06 12:01:06 -07:00
Kynan Ware
93c4340f29
Merge pull request #12627 from cli/kw/pr-create-multi-select-with-search-ccr
`gh pr create`: login-based reviewer requests and search-based interactive selection
2026-03-06 10:24:19 -07:00
Kynan Ware
4f2304d4e5 Remove StateReason feature detection for issue close
The stateReason field was added in GHES ~3.4, which is far older than
the earliest supported GHES version (3.14). The feature detection and
conditional inclusion of stateReason is therefore unnecessary.

This removes:
- StateReason field from IssueFeatures struct
- GHES introspection query in IssueFeatures() (only ActorIsAssignable
  remains, which is always false on GHES)
- Conditional stateReason field inclusion in issue list
- Feature detection guard in issue close
- Feature detection guard in FindIssueOrPR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:31:30 -07:00
Kynan Ware
6b56a23970 Remove unnecessary StateReasonDuplicate feature detection
The DUPLICATE enum variant for IssueClosedStateReason was added in
GHES 3.16, which is older than the earliest supported GHES version.
The feature detection check is therefore unnecessary.

Addresses: https://github.com/cli/cli/pull/12811#issuecomment-3997044372

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-04 13:24:05 -07:00
Kynan Ware
d594c5e918
docs: add examples to gh issue close help text (#12830)
Add examples for closing issues, closing with a comment, closing as
duplicate, and closing with a reason.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Babak K. Shandiz <babakks@github.com>
2026-03-04 14:53:27 +00:00
Takeshi
b9c8d8e023
Merge branch 'trunk' into issue-close-duplicate-of 2026-03-03 22:03:25 -05:00
Kynan Ware
4ea9076d81
Merge pull request #12651 from gunadhya/fix-repeating-cmd-gh-issue-develop
`issue develop`: prevent .git/config corruption on repeated `--name` invocation
2026-03-03 18:55:43 -07:00
Kynan Ware
d983ae0469
Merge pull request #12623 from LouisLau-art/fix/issue-list-pr-search-qualifier
fix(issue list): reject pull request-only search qualifiers
2026-03-03 14:52:35 -07:00
Louis Shawn
b601b307a4 test(issue list): cover additional PR search qualifier variants 2026-03-03 10:13:57 +08:00
Kynan Ware
1957a022b6
Merge pull request #12652 from VishnuVV27/fix-issue-view-comments-redundancy
Fix redundant API call in gh issue view --comments (#12606)
2026-03-02 10:58:41 -07:00
Louis Shawn
5194256928 fix(issue list): reject pull request-only search qualifiers 2026-03-02 10:50:15 -07:00
gunadhya
ee8014a4d9 Simplify progress indicators in issue develop 2026-03-02 17:01:57 +05:30
Takeshi
01c83acfe8 Add --duplicate-of flag and duplicate reason to gh issue close
Support closing issues as duplicates via --reason duplicate and
--duplicate-of <issue> flags. The --duplicate-of flag accepts an issue
number or URL, validates it references a different issue (not a PR),
and passes the duplicate issue ID to the closeIssue mutation.

Feature detection checks whether the GHES instance supports the
DUPLICATE enum value in IssueClosedStateReason before using it.
2026-02-28 20:10:07 -05:00
Kynan Ware
9de48154de Add missing TODO comments for featuredetection if-statements
Add greppable TODO identifiers above all if-statements that reference
featuredetection struct fields, as required by the featuredetection
linter. This ensures every feature detection branch is tagged for
future cleanup when GHES gains support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-16 14:24:32 -07:00
gunadhya
b38f6772e5 Fix issue develop repeated invocation with named branches 2026-02-09 23:31:22 +05:30