GitHub CLI for fetching issues, PRs, comments, reviews, code, and activity — with built-in caching.
A token-efficient GitHub client tailored for AI workflows. Parses issue/PR URLs or raw numbers, fetches bodies + comments + reviews, and outputs clean Markdown or JSON. Responses are cached in a local SQLite DB so repeated reads don't burn rate limit.
| Feature | Description |
|---|---|
| URL or number | Auto-detects owner/repo from the git remote when you pass just a number |
| PR review threads | review pulls unresolved threads grouped by file |
| Comment filters | Limit by count, recency, reactions, or exclude bots |
| Search | Issues, PRs, and code search with state/type filters |
| Notifications & Activity | Inbox triage and event feeds without leaving the terminal |
| SQLite cache | Every fetched object is reusable; status shows cache stats |
| Interactive mode | Run tools github with no args for a guided menu |
# Fetch an issue or PR (URL or number if repo is inferrable)
tools github issue https://github.com/owner/repo/issues/123
tools github pr 456
# Include comments with filters
tools github pr 456 --no-bots --min-reactions 1 --last 20
# Review threads for a PR (only unresolved, grouped by file)
tools github review 456 --unresolved-only --group-by-file
# Search issues / PRs
tools github search "flaky test" --type pr --state open
# Code search
tools github code-search "TODO(matt)"
# Notifications (unread in last 7 days)
tools github notifications --state unread --since 7d
# Activity feed
tools github activity --since 1d --type pr
# Fetch a raw file
tools github get https://github.com/owner/repo/blob/main/src/index.ts --clipboard
# A repository's releases as one Markdown document
tools github releases oven-sh/bun --limit 3
tools github releases facebook/react --since 2026-01-01 --no-prereleases -o react-releases.md
# Safe stack-aware merge (retargets dependents before optional branch delete)
tools github merge 123 --rebase # stack-safe: restack+FF (preserves SHAs); children only retargeted
tools github merge 123 --rebase --restack-children # also rebase + force-push each direct child now
tools github merge 123 --rebase --no-restack # legacy GitHub rewrite rebase (breaks cascades)
tools github merge 123 --ff-only # true FF (base ref → head SHA; keeps commit SHAs)
tools github merge 123 --squash --delete-branch # subject = PR title (#123), body = one bullet per commit
tools github merge 123 --squash --dry-run # print the generated squash message, merge nothing
tools github merge 123 --squash --subject "feat: ship (#123)" --body "* one\n* two" # explicit values win
tools github merge https://github.com/owner/repo/pull/123 --merge --delete-remote
# Cache + rate limit status
tools github status| Command | Description |
|---|---|
issue <url-or-num> |
Fetch an issue with optional comments |
pr <url-or-num> |
Fetch a PR with optional comments and review comments |
merge <url-or-num> |
Merge a PR; retarget stack dependents before optional branch delete |
comments <url-or-num> |
Fetch only the comment thread |
review <url-or-num> |
Review threads (unresolved-only, group-by-file, JSON/MD) |
search <query> |
Search issues and PRs |
code-search <query> |
GitHub code search |
get <file-url> |
Fetch raw file contents |
notifications |
List inbox notifications with filters |
activity |
Personal activity / events feed |
releases <repo> |
A repository's releases as one Markdown document |
status |
Auth, rate limit, and cache stats |
GitHub only auto-retargets dependent PRs when a branch is deleted via the web UI "Delete branch" button. Deleting via gh pr merge --delete-branch, the REST API, or git push --delete closes child PRs instead (cli/cli#1168).
tools github merge always:
- Merges without deleting the head branch via the merge API:
--merge/--squash→ GitHubpulls.merge--rebase(default stack-safe) → local restack head onto base if needed,git push --force-with-lease, then true FF of base → head (preserves SHAs). The restack starts from the tip the PR's base had before its last force push (GitHub records it on the PR as a base force push), so a child whose parent a previous merge rebased drops exactly the parent's old commits.--restack-childrenalso rebases each direct child right away (git rebase --onto <base> <old-parent-tip>, thegh stackcascade). It is off by default: it force-pushes branches outside this merge and strands their own children (2026-10-13: #496 showed 635 files instead of 62). Pass--no-restackfor legacy GitHubmerge_method=rebase(rewrites SHAs; breaks cascading children).--ff-only(alias--ff) → true fast-forward: move base ref to head SHA (git.updateRefwithforce=falseafter a compare preflight). Preserves commit SHAs; fails if base is not an ancestor of head.
- Finds open PRs whose
baseis the merged head branch - Retargets each onto the merged PR's base. GitHub native stacks reject REST
PATCH /pulls/{n}{base}with "Cannot change the base branch because the pull request is part of a stack." The client thenPOST /repos/{owner}/{repo}/stacks/{n}/unstackand retries the PATCH. That keeps the child PR number and conversation. Close-and-reopen is never the happy path. If retarget still fails after the parent SHA is already on the base, stdout printsMERGEDplus recovery commands (non-zero exit). It does not throw as if the merge never happened. - (
--rebase --restack-childrenonly) Restacks dependents so the next PR is FF-able. Without it a child is rebased at its own merge. - Only then, if
--delete-branch/--delete-remote, deletes the remote head ref
Exactly one of --merge, --rebase, --squash, or --ff-only is required.
All progress logs go to stdout.
--squash writes a real commit message by default. Without --subject the
subject is the PR title, a space, and (#N) (not doubled when the title already
ends with it). Without --body the body is every PR commit subject, oldest
first, as * <subject> bullets separated by single newlines. The PR-commits
endpoint stops at 250 commits, so past that the compare endpoint is walked
page by page; if the collected count still differs from the PR's own commit
count the merge refuses rather than write a short body. The CLI prints the message and says
which part was generated and which came from a flag. Commit text is passed to
the GitHub API as data, never through a shell. --merge is unchanged: no
default message is generated there. --dry-run resolves the PR, lists
dependents, prints the squash message and stops before any write.
tools github releases <owner/repo> lists a repository's releases (a github.com URL works too) and writes them as one Markdown document: a title, then for each release a heading with the tag, the name and the date, the release body, and the release URL. The newest release comes first.
tools github releases oven-sh/bun --limit 3 # the three newest, to stdout
tools github releases facebook/react --since 2026-01-01 --no-prereleases -o react-releases.md
tools github releases https://github.com/vercel/next.js --since 1m --clipboard
tools github releases oven-sh/bun --limit 5 --json # machine-readable| Option | Description |
|---|---|
-L, --limit <n> |
Keep only the newest N releases |
--since <date> |
Only releases published since a date: ISO 8601, or relative (7d, 2w, 3m) |
--no-prereleases |
Skip pre-releases. Drafts are always skipped |
--oldest |
List the oldest first instead of the newest first (applied after --limit) |
--json |
Emit { tag, name, publishedAt, prerelease, url, body } per release instead of Markdown |
-o, --output <file> |
Write to a file instead of stdout |
-c, --clipboard |
Copy to the clipboard instead of stdout (with -o, both happen) |
The listing is read page by page (100 per page) until it runs out, the limit is met, or, with --since, a whole page is older than the date. GitHub orders that listing by creation time, not publication time, so a release published long after its commit date, more than a page away from where its date puts it, can be missed by --limit and --since; reading every page would cost one request per 100 releases. Dates are UTC days. Headings inside a release body move down two levels so they nest under that release's own heading. It only reads. A token (GITHUB_TOKEN or gh auth) raises the rate limit and lets it read private repositories.
Run any subcommand with --help for its full option list. The most common flags (--format ai|json, --limit, --last, --no-bots, --min-reactions, --stats, --clipboard) work across multiple subcommands.
Uses gh auth credentials when available, otherwise works unauthenticated with public rate limits. tools github status tells you which mode you're in and how many calls remain.
genesis-tools:githubskill — read-only GitHub browsing for agentsgenesis-tools:github-prskill — PR review feedback workflow