GitHub CLI Cheatsheet

Aliases and Extensions

Use this GitHub CLI reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Setting Aliases

gh alias set <name> <expansion> stores a shortcut in gh's own config, so it works in every shell without touching a dotfile.

gh alias set pv 'pr view'
gh pv 123 -w                       # gh pr view 123 -w

gh alias set bugs 'issue list --label=bug'
gh alias set homework 'issue list --assignee @me'
gh alias set co 'pr checkout'
gh alias set 'issue mine' 'issue list --mention @me'
gh issue mine                      # multi-word aliases work
gh alias set pv 'pr view --comments' --clobber
FlagEffect
-s, --shellRun the expansion through a shell, so pipes and && work
--clobberOverwrite an alias of the same name instead of erroring

Extra arguments are appended, so gh pv 123 -w becomes gh pr view 123 -w. That is enough for most aliases and needs no placeholders.

Positional Arguments

$1, $2, and so on interpolate arguments at a specific position instead of appending them.

gh alias set epicsBy 'issue list --author="$1" --label="epic"'
gh epicsBy vilmibm     # gh issue list --author="vilmibm" --label="epic"

gh alias set prsFor 'pr list --author="$1" --state all'
gh alias set land 'pr merge "$1" --squash --delete-branch'

An alias using $1 requires that argument. Calling it with none fails rather than silently expanding to an empty string.

Shell Aliases

--shell hands the expansion to sh, which is the only way to use a pipe, a redirect, &&, or another program. Note that a shell alias must spell out gh itself.

gh alias set --shell igrep 'gh issue list --label="$1" | grep "$2"'
gh igrep epic foo

gh alias set --shell prc 'gh pr checks --watch --fail-fast && \
  gh pr merge --squash --delete-branch'

gh alias set --shell latest \
  'gh release view --json tagName --jq .tagName'

gh alias set --shell openpr \
  'gh pr view --json url --jq .url | xargs open'

gh alias set --shell wip \
  'gh pr list --search "draft:true author:@me" --json number,title \
   --template "{{range .}}#{{.number}} {{.title}}{{\"\n\"}}{{end}}"'

Quote $1 inside the expansion. Without quotes, a value containing a space splits into two arguments.

Managing Aliases

gh alias list                      # every alias and its expansion
gh alias delete pv
gh alias delete --all
gh alias import aliases.yml        # bulk set from YAML
gh alias import - < aliases.yml
gh alias import aliases.yml --clobber
# aliases.yml
co: pr checkout
bugs: issue list --label=bug
land: pr merge "$1" --squash --delete-branch
igrep: '!gh issue list --label="$1" | grep "$2"'

In the YAML form, a leading ! marks a shell alias, which is the same convention gh alias list prints. Aliases live in ~/.config/gh/config.yml under an aliases: key, so they are easy to sync between machines by committing that file somewhere private.

An alias cannot shadow a real gh command. gh alias set pr '...' is rejected, which is why the useful names are two letters or a distinct word.

Installing Extensions

An extension is a repository whose name starts with gh-. Installing owner/gh-foo gives you gh foo.

gh extension search
gh extension search dashboard
gh extension search --owner cli --sort stars
gh extension search --license MIT --limit 50
gh extension install dlvhdr/gh-dash
gh extension install github/gh-copilot
gh extension install owner/gh-tool --pin v1.2.0
gh extension install .                 # install the cwd for local dev
gh extension list                      # gh ext ls also works
gh extension upgrade gh-dash
gh extension upgrade --all
gh extension upgrade --all --dry-run   # show what would change
gh extension remove gh-dash
gh extension browse                    # interactive TUI browser
gh extension exec dash --help          # run without the top-level name
CommandPurpose
searchSearch the extension registry, with --owner, --license, --sort
install <owner>/<repo>Install, --pin <tag|sha> freezes a version
listInstalled extensions, their repos, and versions
upgrade <name> / --allUpdate, --dry-run previews, --force forces
remove <name>Uninstall
browseInteractive add and remove UI
exec <name>Invoke an extension explicitly, useful in scripts

--pin is worth using in a team setup. Without it, upgrade --all can pull a breaking change into everyone's workflow at once.

Writing an Extension

gh extension create my-tool                    # bash script scaffold
gh extension create my-tool --precompiled=go   # Go project scaffold
gh extension create my-tool --precompiled=other

The rules are short:

  • The repository must be named gh-<name>, and gh <name> is what invokes it.
  • The executable is a file named gh-<name> at the repo root, marked executable.
  • It receives the remaining arguments as $1, $2, and inherits the environment, so gh api inside it is already authenticated.
  • Anything executable works, so a bash script, a Python file with a shebang, or a compiled binary are all valid.
#!/usr/bin/env bash
set -euo pipefail
gh api repos/{owner}/{repo}/issues --jq '.[] | "\(.number) \(.title)"'
gh extension install .    # test the local checkout
gh my-tool                # run it

A precompiled extension publishes per-platform binaries as release assets, and gh downloads the right one on install. The cli/gh-extension-precompile action wires that up, and gh extension create --precompiled=go scaffolds the workflow for it.