Install and Pin a GitHub CLI Extension Safely
Extensions run as your user with your GitHub access, so gh extension install is a trust decision wearing a one-line command. This guide covers installing from a repository, confirming gh can see it, and pinning a repeatable version. The examples use GitHub CLI 2.87.3, installed on this machine on 23 September 2026. Allow about ten minutes, longer if the repository has to build a local executable. The normal commands are unprivileged: do not reach for sudo just to install a user-level extension.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the installed command
Confirm which gh binary will run and read its help. This is read-only and does not contact a repository:
$ command -v gh
/usr/bin/gh
$ gh --version
gh version 2.87.3 (2026-02-23)
$ gh extension install --help
Two options matter here: --pin selects a release tag or commit ref, and --force forces an upgrade or overrides the case where the latest version is already installed.
Checkpoint
If gh is missing, stop and install GitHub CLI through your normal distribution or vendor process. This guide does not need a package-manager command, and adding one risks changing the wrong system.
2. Install a remote extension
For a repository hosted on GitHub, use its owner and name:
$ gh extension install OWNER/REPOSITORY
Replace both placeholders with the exact repository you have reviewed. A repository called gh-foobar gives you a command called gh foobar. Do not infer trust from the gh- prefix: an extension is executable code and can reach anything available to the account running it.
GitHub CLI first looks for release artefacts as a binary extension. Failing that, it clones the repository as a script extension and expects an executable or script in the repository root. Read the project's release notes and installation instructions before accepting either path.
A successful command returns status 0 and normally prints an installation message, but the exact output varies by extension and release. Verify the local registry afterwards:
$ gh extension list
NAME VERSION REPOSITORY
foobar v1.2.3 OWNER/REPOSITORY
That table is illustrative: the version, repository and spacing come from your own installed extension. If the command fails, run gh extension list and check the spelling, authentication and network access before retrying.
3. Pin a version when repeatability matters
Without --pin, you get whatever the extension's release or repository currently calls latest. Pin a release tag instead:
$ gh extension install OWNER/REPOSITORY --pin v1.2.3
$ gh extension list
The pin is a tag or commit ref, not a version range. Script extensions document a commit ref as the useful form; binary extensions use the project's release tag. Check the repository's own release page so the ref matches the extension type and its published artefacts.
Tip
Pinning stops surprise upgrades, but it does not make an untrusted extension safe. Review the source, release provenance and permissions regardless, and if you are installing into a build or deployment account, record the ref beside the rest of that environment's inputs.
4. Install from a full repository URL
The short OWNER/REPOSITORY form only works for GitHub-hosted repositories. Use a full URL for another Git host or a GitHub Enterprise instance:
$ gh extension install https://git.example.com/OWNER/REPOSITORY
Use a URL you have checked yourself, not one copied from an untrusted shell command or issue comment. If the host needs credentials, authenticate with whatever mechanism is already approved for it.
Warning
Do not put access tokens in the URL. They can leak through shell history, process listings or logs.
5. Install a local checkout while developing
Change into the root of the local extension repository and pass a dot as the argument:
$ cd /path/to/gh-foobar
$ gh extension install .
$ gh extension list
For a local install, GitHub CLI manages the extension as a symbolic link (or an equivalent on Windows) to an executable named after the repository, sitting in the repository root. For gh-foobar, that means an executable called gh-foobar at the root; a precompiled extension has to be built and copied there manually before it will run.
Checkpoint
Confirm the file exists before you invoke the extension:
$ test -x ./gh-foobar && echo 'extension executable is ready'
extension executable is ready
$ gh foobar --help
A local link follows the checkout as it changes, which is handy for development but means the command changes whenever the working tree does. Do not treat an unreviewed working tree as a production dependency.
6. Upgrade or remove an extension deliberately
Warning
--force changes the installed extension state. Use it only once you have chosen the repository and version, and record the current one first:
$ gh extension list
$ gh extension install OWNER/REPOSITORY --pin v1.3.0 --force
$ gh extension list
This replaces or updates the existing installation; it is not a dry run. If the new version turns out unsuitable, reinstall the previously recorded tag:
$ gh extension install OWNER/REPOSITORY --pin v1.2.3 --force
Recovery
That assumes the older tag and its release artefact still exist. Before upgrading an extension used by scripts, test the new command in a separate account or checkout and keep the old ref to hand. The install command itself gives you no rollback snapshot.
Common failure checks
- Installs but will not run: the repository may be missing the expected root executable, have the wrong executable name, or need a build step. Check the repository's documented layout rather than renaming files blindly.
- Remote install fails outright: the release may have no compatible artefact, and the repository may not be a valid script extension either.
- Listed but command not found: check the extension name and invoke its help with
gh EXTENSION --help. - Entry is stale or unwanted: use the separate extension-management command shown by
gh extension --help. Do not delete files from GitHub CLI's data directory by hand, or you can leave its registry inconsistent.
Done means
- Version and help checked:
gh --versionandgh extension install --helpconfirmed the installed behaviour. - Repository reviewed first: the URL or
OWNER/REPOSITORYvalue was checked before any code was installed. - Presence confirmed:
gh extension listshows the expected extension and recorded version. - Pinned where it matters: a release tag or commit ref is fixed when repeatable behaviour is needed.
- Local executable correct: a local extension has the correctly named executable in its repository root.
- Forced upgrades reversible: any forced upgrade has a recorded previous ref you can reinstall.