Skip to main content
Act on GitHub in the official CLI’s vocabulary. 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.

Install

In YAML the same install rides the 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

A write through 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

Every level answers --help, and man gh, man gh repo and man gh api render the same text from the same spec.

repo

view prints what gh prints: a name: line, a description: line, then -- and the README, with the separator omitted when the repository has none. For the repository object as JSON, use gh api repos/OWNER/REPO. 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 GET with no fields and POST once a field is given, unless -X says otherwise.
  • A GET carries its fields in the query string; every other method carries them in a JSON body.
  • -f/--raw-field is always a string. -F/--field reads true, false, null and integers as their JSON types.
  • A call with no fields sends no body at all, so a bare DELETE is a bare DELETE rather 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 -F value, but not in a -f one, which is the split gh’s own --help describes.
  • A read (GET, HEAD, OPTIONS) leaves the mount’s cache alone; only a write expires it.
Output is JSON on stdout, so the rest of the shell composes with it:

Divergences from upstream gh

gh is virtualized, not wrapped, so the table below is the whole of what differs. Everything else matches gh 2.85. The interactive and host-side verbs (auth, gist, codespace, browse, repo clone) are out of scope for a virtualized CLI, the same way they are for the other account CLIs.