The fetch loop already assigns "first" on each iteration, so the
initial assignment in the variables map is dead code. Remove it from
both List and Search.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace responses/checkVarsFns with httpStubs func per test case in
TestList, TestSearch, and TestListCategories, matching the pattern used
in agent-task/capi tests. Expand inline JSON to heredoc, remove flower
box comments, and add "empty list" and "exact fit" test cases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace false-positive filter tests with GraphQLQuery responders that
assert on actual GQL variables (first, after, states, answered, orderBy,
categoryId in List; query string qualifiers in Search)
- Add Bot actor test case (Bot.ID maps to DiscussionActor.ID, Name is empty)
- Add limit>100 test cases for both List and Search to verify the
per-iteration first variable is set correctly (100 on page 1, remainder
on page 2)
- Fix limit>100 bug in client_impl.go: move variables["first"] assignment
inside the loop so each iteration caps at min(remaining, 100)
- Remove intStr helper; use strconv.Itoa directly
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses babakks' review on PR #13252:
- Convert 18 individual test functions to 3 table-driven functions
- Replace ptr helper with new(value) syntax throughout
- Assert all Discussion struct fields in success cases
- Add after-cursor test cases for pagination
- Fold pagination tests into table structure
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add comprehensive httpmock-based unit tests for the client package covering:
- List: success path, discussions disabled, limit/filter validation, pagination
- Search: success path, filter validation, pagination
- ListCategories: success path, discussions disabled
Tests use httpmock.Registry with defer Verify(t) to ensure all stubs
are exercised, following the established testing pattern in this repo.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The GraphQL Discussion type has a `closed` boolean field, not a
`state` string. Updated the API response struct and GQL fragment
to query `closed` instead of `state`, and derive the domain-level
State string ("OPEN"/"CLOSED") from the boolean in mapDiscussion.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When --category is used, ListCategories runs before the List query.
On repos with discussions disabled, it silently returns empty categories,
leading to a confusing "must be one of []" error. Now it checks
hasDiscussionsEnabled and returns the standard "discussions disabled"
error, matching the behavior of List and Search.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Export domain consts (FilterStateOpen/Closed, OrderByCreated/Updated,
OrderDirectionAsc/Desc) in types.go
- Change State fields to *string (nil = all states)
- Add DiscussionListResult type with NextCursor for pagination
- Update List/Search signatures: add after param, return result type
- Add limit <= 0 guard clauses in client methods
- Replace strings.ToUpper/ToLower with switch statements + default errors
- Rename pageLimit to remaining, hoist hasDiscussionsEnabled check
- Use qualifier/keyword terminology in search query building
- Quote author values with %q for whitespace safety
- Add Keywords string field (single string, not []string)
- Split --order into --sort {created|updated} and --order {asc|desc}
- Add --search/-S and --after flags
- Add next field in JSON output envelope
- Extract defaultLimit const
- Add toFilterState helper for CLI-to-domain mapping
- Move matchCategory to shared/categories.go with godoc
- Use single-line error messages with sorted slugs
- Add (preview) annotations to Short docs
- Update Use to "list [flags]"
- Add examples for --answered=false, --state all, multi-label
- Update tests for new signatures and new flags
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the discussion list command with full support for:
- Listing discussions with state, category, answered, and order filters
- Searching discussions by author and labels (uses Search API)
- Category resolution by slug or name (case-insensitive)
- TTY and non-TTY table output with colored IDs and labels
- JSON output with totalCount envelope
- Web mode (--web) to open discussions in browser
- Pagination for large result sets
Implement List, Search, and ListCategories methods on the
DiscussionClient. List and Search use plain text GraphQL for
flexible variable handling. ListCategories uses safely typed
GraphQL via shurcooL/githubv4.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce the pkg/cmd/discussion/ package with:
- DiscussionClient interface and domain types (client/)
- Generated mock via moq (client/)
- Factory function for lazy client creation (shared/)
- JSON field definitions for --json output (shared/)
- Root 'discussion' command registered in the core group
The interface defines all planned operations (list, search, get, create,
update, close, reopen, comment, lock, unlock, mark-answer, unmark-answer)
with stub implementations that will be replaced as each subcommand is
added in subsequent PRs.
Domain types are intentionally separate from API types per review guidance.
No JSON struct tags are used; serialization is handled by ExportData methods.
Refs: cli/cli#12810, github/gh-cli-and-desktop#115
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The github.com/golang/snappy repository was archived and is no longer
maintained. klauspost/compress provides a drop-in replacement, which
is actively maintained, and the klauspost/compress module is already
an existing (indirect) dependency.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
The gh api command builds its own HTTP client inline without
forwarding InvokingAgent, so the User-Agent header was missing
the Agent/<name> suffix when invoked by AI coding agents.
Thread InvokingAgent through Factory → ApiOptions → HTTPClientOptions,
mirroring the existing AppVersion pattern.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a new Prompter implementation (huhPrompter) that uses the
charmbracelet/huh library in its standard interactive mode, as an
alternative to the survey-based default prompter. The new implementation
is gated behind the GH_EXPERIMENTAL_PROMPTER environment variable,
following the same truthy/falsey pattern as GH_ACCESSIBLE_PROMPTER.
Key differences from the accessible prompter:
- No WithAccessible(true) flag (full interactive TUI)
- Uses EchoModePassword (masked with *) instead of EchoModeNone
- No default value annotations appended to prompt text
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reviewer prompt branch checked reviewerSearchFunc != nil directly
instead of useReviewerSearch, making the fetch and prompt decisions
inconsistent. This mirrors how the assignee path already uses
useAssigneeSearch at both the fetch and prompt gates.
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>
The expression was redundantly re-checking whether assignees or
reviewers were chosen. RepoMetadata already gates on
input.Assignees || input.Reviewers before consulting
ApiActorsSupported, so passing state.ApiActorsSupported directly
is sufficient.
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>
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>
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>
Add comment headers to group examples by concern (title/body, labels,
reviewers, assignees, projects/milestones), matching the style used by
other commands like pr review and repo create. Also adds examples for
--body-file and re-requesting review via --add-reviewer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The --add-reviewer flag on pr edit already supports re-requesting
reviews from reviewers who have already submitted their review,
but this wasn't documented in the help text.
Ref #12489
Ref #2053
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>
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>