gh implementation uses the vocabulary of the official
cli/cli project. A repository is a tree, so
mirage already reads one as files: the
github mount is the read half, and ls,
cat and grep are how an agent explores it. gh is the write half,
plus the account-level operations a filesystem has no shape for.
Licenses: The upstream cli/cli project uses the
MIT License; Mirage’s
independent implementation uses
Apache-2.0.
Install
clis: section; see the
CLI overview.
repo and branch are what real gh reads off the current directory’s git
remote and checkout. A workspace has neither, so the install carries them:
repo answers a line that names no repository, and both feed the
{owner}/{repo}/{branch} placeholders. Two installs under different head words are two
accounts.
Reading is the mount, acting is the CLI
gh lands on the same repository the mount reads, but it
lands by repository name rather than by any vfs path, so the mount has
nothing to aim a per-path invalidation at. The spec declares which
resource it serves, and the executor expires that mount’s index after the
line, so the next cat or ls refetches instead of serving the pre-write
bytes. Nothing is required of the caller.
Verbs
--help; man gh, man gh issue, and man gh pr create render the same text from the same spec. list also accepts ls,
and issue and pull request create also accept new.
Typed workflows
Typed read commands accept-R/--repo [HOST/]OWNER/REPO where applicable.
Most also accept --json field1,field2 and -q/--jq EXPRESSION; --jq
requires --json, so the selected field set stays explicit and bounded.
List commands use -L/--limit and fetch as many REST pages as needed.
-:
repo
view prints a name: line, a description: line, then --
and the README, with the separator omitted when the repository has none.
Use --json for a stable subset of repository fields or gh api for the
complete REST object.
rename takes the new name as the operand and the repository to
rename on -R, which is the reverse of what the shape of the line
suggests; that is upstream’s grammar, not a mirage choice.
[HOST/]OWNER/REPO is parsed from the right, so the owner and the
repository are the last two segments and a leading host is dropped. The
host is accepted for compatibility with lines copied from real gh but
does not route: every call goes to the install’s baseUrl. A second
host means a second install.
api
gh api reaches every endpoint that has no typed verb, which is most of
them.
{owner}, {repo} and {branch} expand from the install, the way real
gh expands them from the current repository. Any other brace pair is left
exactly as typed and reaches the wire, which is gh’s behavior too. Quote
the endpoint so the shell does not eat the braces.
The rules are gh’s own:
- The method is
GETwith no fields andPOSTonce a field is given, unless-Xsays otherwise. - A
GETcarries its fields in the query string; every other method carries them in a JSON body. -f/--raw-fieldis always a string.-F/--fieldreadstrue,false,nulland integers as their JSON types. A typed value beginning with@reads that workspace file, and@-reads stdin.- Object keys such as
config[enabled]and arrays such aslabels[]build nested JSON rather than flat keys. --input FILEsends that JSON document as the body and moves any fields to the query string.--input -reads stdin.-H/--headeradds or replaces request headers.--paginatefollows RESTLinkheaders. Responses stream page by page;--slurpwraps all pages in one array.--silentsuppresses response output without suppressing the request.- A call with no fields sends no body at all, so a bare
DELETEis a bareDELETErather than an empty JSON object with a content type. Some endpoints read those differently. - The leading slash is optional.
- A placeholder expands in an endpoint and in a
-Fvalue, but not in a-fone, which is the split gh’s own--helpdescribes. - A read (
GET,HEAD,OPTIONS) leaves the mount’s cache alone; only a write expires it.
-q/--jq evaluates in-process
and renders strings raw, or the rest of the shell can consume stdout:
Divergences from upstream gh
gh is virtualized, not wrapped. The intentional boundaries are: