feat: add native copilot command to shim copilot cli#12444
feat: add native copilot command to shim copilot cli#12444
Conversation
andyfeller
left a comment
There was a problem hiding this comment.
Congrats on joining the ranks of gh contribution, @devm33!
Not meant to be a full review but wanted to offer some feedback to help shape this 🙇
There was a problem hiding this comment.
Pull request overview
This PR introduces a native gh copilot command that shims the Copilot CLI, addressing issues where the deprecated gh copilot extension no longer works and LLMs incorrectly suggest using gh copilot to invoke the Copilot CLI. The implementation automatically downloads and manages the copilot binary if it's not already in the user's PATH.
Key changes:
- Adds a new native
copilotcommand that checks for the copilot binary in PATH, then in gh's data directory, and downloads it if not found - Implements archive extraction for both tar.gz (Linux/macOS) and zip (Windows) formats with path traversal protection
- Provides a
--removeflag to uninstall the downloaded binary
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 15 comments.
| File | Description |
|---|---|
| pkg/cmd/root/root.go | Registers the new copilot command in the root command structure |
| pkg/cmd/copilot/copilot.go | Implements the copilot command with download, extraction, and execution logic |
| pkg/cmd/copilot/copilot_test.go | Adds basic tests for command structure and removal functionality |
Comments suppressed due to low confidence (2)
pkg/cmd/copilot/copilot.go:169
- The function does not have a doc comment. Public or exported helper functions should include documentation describing their purpose, parameters, and return values.
func extractTarGz(r io.Reader, destDir string) error {
pkg/cmd/copilot/copilot.go:282
- Using file mode directly from the zip archive could preserve dangerous permissions or special bits. Consider masking the mode to only preserve standard file permissions (e.g.,
mode & 0777) to prevent potential security issues.
if err := extractZipFile(target, f.Mode(), rc); err != nil {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
babakks
left a comment
There was a problem hiding this comment.
Thanks for the PR again, @devm33! 🙏
I haven't yet gone through the tests, but noticed some points that will improve the impl and help with future maintenance.
Regarding the comment on structuring the code to be like other gh commands, I can push a commit to fix that, but I won't do it now in case you have uncommitted/unpushed changes. Let me know please.
pkg/cmd/copilot/copilot.go
Outdated
| func removeCopilotFromDir(io *iostreams.IOStreams, installDir string) error { | ||
| if _, err := os.Stat(installDir); os.IsNotExist(err) { | ||
| fmt.Fprintln(io.ErrOut, "Copilot CLI is not installed") | ||
| return nil | ||
| } | ||
| if err := os.RemoveAll(installDir); err != nil { | ||
| return fmt.Errorf("failed to remove Copilot CLI: %w", err) | ||
| } | ||
| fmt.Fprintln(io.ErrOut, "Copilot CLI removed successfully") | ||
| return nil | ||
| } |
There was a problem hiding this comment.
I think it's better to handle messaging on stdio at the command level. This way, we won't need to pass IOStreams down.
|
I just fixed conflicts between the branches and pushed. When the CI passes, I'll rebase the branch and rewrite the commits as there are lots of repeated/overlapping changes. After that, I'll push a few more to fix issues I've discovered while fixing the conflicts. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
babakks
left a comment
There was a problem hiding this comment.
LGTM! 🙏 Just a few quick ones.
| if opts.IO.IsStdoutTTY() { | ||
| fmt.Fprintln(opts.IO.ErrOut, "Copilot CLI removed successfully") | ||
| } |
There was a problem hiding this comment.
thought: I wonder if this conditional should be checking IsStderrTTY since the output is writing to stderr instead of stdout 🤔
This isn't blocking by any means but something I noticed here and in other places of the code base:
Lines 98 to 100 in 559e729
cli/pkg/cmd/extension/command.go
Lines 173 to 175 in 559e729
Lines 132 to 134 in 559e729
There was a problem hiding this comment.
I think it might be nice to unify some of these conditionals to something shared anyway 🤔
…ackage Signed-off-by: Babak K. Shandiz <babakks@github.com> Co-authored-by: Kynan Ware <bagtoad@github.com> Co-authored-by: Devraj Mehta <devm33@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com> Co-authored-by: Kynan Ware <bagtoad@github.com> Co-authored-by: Devraj Mehta <devm33@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com> Co-authored-by: Kynan Ware <bagtoad@github.com> Co-authored-by: Devraj Mehta <devm33@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com> Co-authored-by: Kynan Ware <bagtoad@github.com> Co-authored-by: Devraj Mehta <devm33@github.com>
Signed-off-by: Babak K. Shandiz <babakks@github.com>
|
@BagToad and I verified the changes work on Windows. For that to work we had to fix the download URL. |
Replaces runtime.GOOS with 'win32' in the test archive filename within TestDownloadCopilot. This ensures the test uses the expected archive name for Windows.
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [cli/cli](https://github.com/cli/cli) | minor | `v2.85.0` → `v2.86.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>cli/cli (cli/cli)</summary> ### [`v2.86.0`](https://github.com/cli/cli/releases/tag/v2.86.0): GitHub CLI 2.86.0 [Compare Source](cli/cli@v2.85.0...v2.86.0) ####Install and run GitHub Copilot CLI directly from `gh` Since we deprecated the [GitHub Copilot in the CLI](https://github.blog/changelog/2025-09-25-upcoming-deprecation-of-gh-copilot-cli-extension/) extension in favor of the new agentic [GitHub Copilot CLI](https://github.com/github/copilot-cli), we want to give developers using `gh` a simple way to get started using our most powerful terminal assistant. - `gh copilot` will prompt to install, then run Copilot CLI - `gh copilot <args>` will execute the Copilot CLI, forwarding any arguments and flags For more information and usage options, run `gh copilot --help`. #### What's Changed ##### ✨ Features - `gh copilot`: add native `copilot` command to execute/install copilot cli by [@​devm33](https://github.com/devm33) in [#​12444](cli/cli#12444) - `gh cache delete`: allow for delete all caches for a ref by [@​davidspek](https://github.com/davidspek) in [#​12101](cli/cli#12101) - `gh pr create`: error when head and base refs are identical in pr create by [@​majiayu000](https://github.com/majiayu000) in [#​12376](cli/cli#12376) ##### 📚 Docs & Chores - Fix Windows asset URL in `copilot` command tests by [@​babakks](https://github.com/babakks) in [#​12500](cli/cli#12500) - Update contributing guidelines for clarity by [@​BagToad](https://github.com/BagToad) in [#​12505](cli/cli#12505) #### New Contributors - [@​devm33](https://github.com/devm33) made their first contribution in [#​12444](cli/cli#12444) - [@​davidspek](https://github.com/davidspek) made their first contribution in [#​12101](cli/cli#12101) **Full Changelog**: <cli/cli@v2.85.0...v2.86.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi44Ni4xIiwidXBkYXRlZEluVmVyIjoiNDIuODYuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
Fixes #12427
Introduces
gh copilotas a core command.Key behavior
gh copilotwill prompt to install Copilot CLI when run for the first time.gh copilot <args>will execute the Copilot CLI, forwarding any args and flags provided except those thatghsupports.--) character for bypassing flag conflicts betweenghand Copilot CLI, e.g.gh copilot -- --helpgh copilotis run in GitHub Actions, install without confirmation prompt.