Extract build*Form() methods from each huhPrompter method, separating
form construction from form.Run(). This enables testing the real form
construction code by driving it with direct model updates, adapted
from huh's own test patterns.
Tests cover Input, Select, MultiSelect, Confirm, Password,
MarkdownEditor, and MultiSelectWithSearch including a persistence
test that verifies selections survive across search query changes.
Also fixes a search cache initialization bug where the first
buildOptions("") call would skip the searchFunc due to
cachedSearchQuery defaulting to "".
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Migrate from github.com/charmbracelet/huh v1 to charm.land/huh/v2,
updating ThemeBase16 to the new ThemeFunc API.
Fix selected options being lost across searches in the huhPrompter's
MultiSelectWithSearch. The root cause was huh's internal Eval cache:
when the user returned to a previously-seen search query, cached
options with stale .selected state overwrote the current selections
via updateValue(). The fix includes selectedValues in the OptionsFunc
binding hash (via searchOptionsBinding) so the cache key changes
whenever selections change, preventing stale cache hits. A local
searchFunc result cache avoids redundant API calls when only the
selection state (not the query) has changed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement a huh-native MultiSelectWithSearch that renders the search
input and multi-select list simultaneously using LayoutStack. The
search input is in Group 0 and the multi-select in Group 1, with
OptionsFunc bound to the search query so results update when the
user presses Enter to advance focus. Users can Shift+Tab back to
refine their search, and selections persist across queries.
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>
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>
GitHub's Artifact API now rejects artifact names like '..' server-side
with a 400 Bad Request, making it impossible to create artifacts with
path traversal names. This means the scenario this test was verifying
(that gh run download catches traversal names) can no longer be
reproduced through normal artifact creation.
The client-side traversal check in gh run download remains in place as a
defense-in-depth measure.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The sandbox overrides HOME so git cannot find the user's global config,
causing 'Author identity unknown' errors when acceptance test scripts
make commits. Write a minimal .gitconfig with user.name and user.email
into the sandbox working directory during sharedSetup.
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>
Expand the ApiActorsSupported doc comment to explain the two API
generations (legacy AssignableUser vs actor-based AssignableActor),
what each returns, and the specific GraphQL schema additions to
check for when evaluating GHES support.
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>
The assigneeSearchFunc previously accumulated actors into
editable.Metadata.AssignableActors so that MembersToIDs could
later resolve logins to node IDs. Now that the edit flow uses
AssigneeLogins + ReplaceActorsForAssignableByLogin on github.com,
this accumulation is no longer needed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>