GitHub CLI - Remote Analysis & Discovery
Remote repository operations, codebase comparison, and code discovery without cloning.
When to Use
- Analyze repositories without cloning
- Compare codebases side-by-side
- Fetch specific files from any repo
- Find trending repositories and code patterns
- Search code across GitHub
Quick Operations
Fetch a file remotely
gh repo read-file path/file.ts --repo OWNER/REPO
gh repo read-file (preview) is the preferred path: it prints raw content, takes --ref for any branch/tag/commit, and handles large files. --output PATH writes to disk (--clobber to overwrite); --json returns metadata (content, downloadUrl, encoding, gitSHA, gitUrl, htmlUrl, name, path, size, type, url). Fall back to gh api where the command is unavailable:
gh api repos/OWNER/REPO/contents/path/file.ts -H "Accept: application/vnd.github.raw"
There is no base64decode template function - --template '{{.content | base64decode}}' fails with function "base64decode" not defined. To decode the default JSON response, pipe it:
gh api repos/OWNER/REPO/contents/path/file.ts --jq '.content' | base64 -d
The Contents API inlines only files up to 1MB. Past that, .content is an empty string (encoding: "none") and the pipe above silently prints nothing - use read-file or the raw Accept header instead.
Terminal escape sequences (gh 2.97.0+)
gh refuses to print remote content containing terminal escape sequences (ANSI art, some test fixtures) instead of passing it to your terminal:
gh repo read-file:file contains terminal escape sequences; use --allow-escape-sequences to read anywaygh apiraw bodies:the response contains terminal escape sequences; pass --allow-escape-sequences to output it anywaygh pr diffwhen piped:the diff contains terminal escape sequences; pass --allow-escape-sequences to output it anyway
Pass --allow-escape-sequences (also on gh release download --output - and gh gist view) when you need the bytes verbatim, or write to disk with gh repo read-file --output, which always keeps the raw bytes. The check came with the GHSA-3m3g-3wcr-px46 fix; run gh 2.98.0 or later to get it and the other 2.97/2.98 security fixes.
Get directory listing
gh repo read-dir PATH --repo OWNER/REPO # omit PATH to list the repo root
# Or via the API
gh api repos/OWNER/REPO/contents/PATH
gh repo read-dir --json returns an object, not an array - {"entries":[...]} - so filter with .entries[]:
gh repo read-dir src --repo OWNER/REPO --json name,type --jq '.entries[] | select(.type=="file") | .name'
Pin the repo in scripted workflows
gh infers the repository from the current working directory. In agent or CI workflows - where a cd may persist - always pass --repo OWNER/REPO so a stray cwd cannot silently retarget the command. Signatures of a missing --repo:
- Outside a git repo:
failed to run git: fatal: not a git repository - In a repo with no remote:
no git remotes found - Inside a different repo: no error - the command targets that repo, and IDs from elsewhere 404
Search code
gh search code "pattern" --language=typescript
Find trending repos
gh search repos --language=rust --sort stars --order desc
Compare Two Codebases
Systematic workflow for comparing repositories to identify similarities and differences.
Example use: "Compare solana-fm/explorer-kit and tenequm/solana-idls"
Step 1: Fetch directory structures
gh repo read-dir PATH --repo OWNER-A/REPO-A
gh repo read-dir PATH --repo OWNER-B/REPO-B
If comparing a monorepo package, specify the path (e.g., packages/explorerkit-idls).
Step 2: Compare file lists
diff <(gh repo read-dir PATH --repo OWNER-A/REPO-A --json name --jq '.entries[].name') \
<(gh repo read-dir PATH --repo OWNER-B/REPO-B --json name --jq '.entries[].name')
Lines prefixed < exist only in repo A, > only in repo B.
Step 3: Fetch key files for comparison
Compare package dependencies:
gh repo read-file package.json --repo OWNER-A/REPO-A
gh repo read-file package.json --repo OWNER-B/REPO-B
Compare main entry points:
gh repo read-file src/index.ts --repo OWNER-A/REPO-A
gh repo read-file src/index.ts --repo OWNER-B/REPO-B
Add --cache 1h to gh api calls when iterating on the same files repeatedly, to avoid re-spending rate limit.
Comparing two refs of the same repo (or a fork of it) is one call to the compare API, capped at 250 commits and 300 files:
gh api repos/OWNER/REPO/compare/v1.0.0...v2.0.0 --jq '{ahead_by, files: [.files[].filename]}'
Step 4: Analyze differences
Compare the fetched files to identify:
API Surface
- What functions/classes are exported?
- Are the APIs similar or completely different?
Dependencies
- Shared dependencies (same approach)
- Different dependencies (different implementation)
Unique Features
- Features only in repo1
- Features only in repo2
For detailed comparison strategies, see references/comparison.md.
Discover Trending Content
Find trending repositories
# Most starred repos - a bare `--sort` with no query or filter flag fails: Invalid search query ""
gh search repos "stars:>10000" --sort stars --order desc --limit 20
# Trending in specific language
gh search repos --language=rust --sort stars --order desc
# Recently popular (created in the last 30 days; works with BSD and GNU date)
SINCE=$(date -v-30d +%F 2>/dev/null || date -d '30 days ago' +%F)
gh search repos "created:>$SINCE" --sort stars --order desc
# Trending in specific topic
gh search repos "topic:machine-learning" --sort stars --order desc
Discover popular code patterns
# Find popular implementations (code search has no sorting - scope with filters)
gh search code "function useWallet" --language=typescript
# Scope to a known repo (code search can't filter by stars - stars:>N is literal text)
gh search code "implementation" --repo=honojs/hono
# Search specific organization
gh search code "authentication" --owner=anthropics
For complete discovery queries and patterns, see references/discovery.md.
Search Basics
Code search
# Search across all repositories
gh search code "API endpoint" --language=python
# Search in specific organization
gh search code "auth" --owner=anthropics
# Exclude results with negative qualifiers
gh search issues -- "bug report -label:wontfix"
Issue & PR search
# Find open bugs (--state takes only open|closed here; omit it for both)
gh search issues --label=bug --state=open
# Search assigned issues
gh search issues --assignee=@me --state=open
# Meaning-based issue search (gh 2.98.0+): relevance-ranked, issues only, one page, no --sort/--order
gh search issues worktree checkout --repo cli/cli --search-type hybrid
Separate words are ANDed. A single quoted multi-word argument is sent as an exact phrase, so gh search issues "worktree checkout" can return nothing with exit 0.
Search rate limits
Search runs on a much tighter budget than the 5000/hr core API - check with gh api rate_limit:
| Resource | Limit (authenticated) |
|----------|----------------------|
| core (incl. gh api, gh repo read-file) | 5000/hr |
| search (repos, issues, prs, commits) | 30/min |
| code_search | 10/min |
| semantic_search (--search-type semantic\|hybrid) | 10/min - not listed by gh api rate_limit |
For advanced search syntax, see references/search.md.
Special Syntax
Field name inconsistencies
IMPORTANT: GitHub CLI uses inconsistent field names across commands:
| Field | gh repo view | gh search repos |
|-------|----------------|-------------------|
| Stars | stargazerCount | stargazersCount |
| Forks | forkCount | forksCount |
| Topics | repositoryTopics | not available |
| Watchers | watchers ({totalCount}) | watchersCount - actually the star count |
Examples:
# ✅ Correct for gh repo view
gh repo view owner/repo --json stargazerCount,forkCount
# ✅ Correct for gh search repos
gh search repos "query" --json stargazersCount,forksCount
Other fields that do not exist where you expect them: gh pr view has no merged (use mergedAt or state), gh search prs has no mergedAt (use closedAt), and gh run list has no jobs (use gh run view <id> --json jobs). A wrong field fails with Unknown JSON field: "X" followed by the valid list.
One qualifier per argument
gh search (gh 2.97.0+) splits each argument at its first : and wraps the rest in quotes if it contains a space. Packing several qualifiers into one quoted string silently breaks the filter:
# ❌ Sent as language:"go stars:>500" - the language filter is lost
gh search repos "rate limiting middleware language:go stars:>500"
# ✅ Keywords and qualifiers as separate arguments; quote only those containing > or <
gh search repos rate limiting middleware language:go "stars:>500"
gh pr list --search and gh issue list --search pass their string through untouched, so one quoted string is fine there.
Excluding search results
A negative qualifier works as its own argument or at the end of a quoted string ("bug report -label:wontfix"). -- is only required when the query starts with a hyphen, which the shell would otherwise read as a flag.
Put every flag before --. Everything after -- is positional, so trailing flags get swallowed into the query string:
# ✅ Correct - flags first
gh search issues --limit 5 -- -label:wontfix bug
# ❌ Wrong - --limit 5 becomes part of the search query, silently returning 30 results
gh search issues -- -label:wontfix bug --limit 5
For more syntax gotchas, see references/syntax.md.
Scripting Gotchas
-
Every command needs auth - without a token
ghprintsplease run: gh auth loginand exits 4. The exception isgh release downloadon public repos. -
gh apiwrites its error body to stdout. On failure it exits 1, printsgh: Not Found (HTTP 404)to stderr, and prints the error JSON to stdout without applying--jq.$(gh api ...)then captures the error as if it were data, so check the exit status:if sha=$(gh api repos/OWNER/REPO/contents/README.md --jq .sha); then echo "$sha"; else echo "fetch failed" >&2; fi -
-f/-Fswitchgh apito POST, so GET-only endpoints answer 404. Add-X GETto send the fields as a query string:gh api -X GET search/issues -f q='repo:cli/cli is:open worktree' --jq '.total_count' -
--jqtakes one expression and no jq flags (a trailing-rfails withunknown shorthand flag: 'r'); string results already print raw. -
--paginate --slurpcannot combine with--jq/--template. Pipe tojqinstead, or use--paginate --jq, which filters each page. -
Quote endpoints containing
?- zsh treats an unquoted?as a glob and fails withno matches found. -
Exit codes (
gh help exit-codes): 0 success, 1 failure, 2 cancelled, 4 authentication required. Some commands add their own:gh pr checksexits 8 while checks are pending.
Preview Commands
Recent gh releases added preview commands relevant to remote analysis and discovery. They are subject to change without notice.
# Read a repo without cloning (see Quick Operations above)
gh repo read-file PATH --repo OWNER/REPO
gh repo read-dir PATH --repo OWNER/REPO
# GitHub Discussions - often where design rationale lives
gh discussion list --repo OWNER/REPO --search "keyword" --answered
gh discussion view <number> --repo OWNER/REPO --comments
# Agent skills on GitHub
gh skill search <query>
gh skill preview OWNER/REPO <skill> # print SKILL.md without installing
gh skill install OWNER/REPO <skill>
gh discussion list --json returns {"discussions":[...],"next":...,"totalCount":...}, so filter with --jq '.discussions[].title'. gh discussion view also accepts a comment URL (.../discussions/123#discussioncomment-456) to show one full reply thread, and --comments cannot be combined with --json.
gh pr revert <number> (opens a PR reverting a merged PR) is a regular command, not a preview - see pull_requests.md.
Advanced Workflows
For detailed documentation on specific workflows:
Core Workflows:
- remote-analysis.md - Advanced file fetching patterns
- comparison.md - Complete codebase comparison guide
- discovery.md - All trending and discovery queries
- search.md - Advanced search syntax
- syntax.md - Special syntax and command quirks
GitHub Operations:
- repositories.md - Repository operations
- pull_requests.md - PR workflows, CI checks, review threads
- issues.md - Issue management
- actions.md - GitHub Actions, run attempts, reruns
- releases.md - Release management
Setup & Configuration:
- getting_started.md -
gh auth setup-gitcredential helper - other.md - Environment variables, auth traps, aliases, config
- extensions.md - CLI extensions
Resources
- Official docs: https://cli.github.com/manual/
- GitHub CLI: https://github.com/cli/cli
- Search syntax: https://docs.github.com/en/search-github