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
| Flag | Effect |
|---|---|
-s, --shell | Run the expansion through a shell, so pipes and && work |
--clobber | Overwrite 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
| Command | Purpose |
|---|---|
search | Search the extension registry, with --owner, --license, --sort |
install <owner>/<repo> | Install, --pin <tag|sha> freezes a version |
list | Installed extensions, their repos, and versions |
upgrade <name> / --all | Update, --dry-run previews, --force forces |
remove <name> | Uninstall |
browse | Interactive 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>, andgh <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, sogh apiinside 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.