Skip to main content

Read-only GitHub reviewer CLI: triage, re-review diffs, comment threads

Project description

twgh

Read-only GitHub reviewer CLI — for humans and agents.

Triage what needs you, re-review only what changed since you last looked (force-push robust), and see which of your comments were addressed. Works across github.com and GitHub Enterprise.

twgh never writes to GitHub — no approvals, no comments, no merges. It is the read side of reviewing; for write actions it hands you off to the browser.

Every command produces clean, pipeable output: --json where the data is tabular, unified diff hunks where the data is code. No wrapper needed — twgh status --json | jq and twgh diff 42 | llm "summarise" work out of the box. This makes twgh equally useful as a human terminal tool and as a building block in agent and CI pipelines.

GitHub concepts

Understanding these GitHub primitives helps clarify what twgh surfaces and why.

Comment types on a PR

┌─────────────────────────────────────────────────────────────┐
│                    GitHub PR Comments                        │
├──────────────────────────┬──────────────────────────────────┤
│  Issue Comments          │  Review Threads                  │
│  (PR-level / timeline)   │  (inline / code-level)          │
├──────────────────────────┼──────────────────────────────────┤
│ • Not tied to a file     │ • Anchored to a file + line     │
│ • No commit reference    │ • Tied to a specific commit OID │
│ • Cannot be "resolved"   │ • Can be resolved/unresolved    │
│ • Cannot be "outdated"   │ • Can become outdated when the  │
│ • General conversation   │   underlying code changes       │
│   on the PR timeline     │ • Groups into a thread (replies)│
└──────────────────────────┴──────────────────────────────────┘
Level What it is GitHub API source In twgh
Issue comment Free-form message on the PR timeline ("LGTM!", "please rebase") pr.comments.nodes comments command only
Review thread A conversation anchored to a specific line of code, started during a review pr.reviewThreads.nodes threads command (one row per thread)
Review comment An individual reply within a review thread comments inside a thread node comments command (one row per comment)

A review itself (the APPROVE / REQUEST_CHANGES / COMMENT submission) is a separate entity — twgh uses it for anchor calculation but does not surface its body in either threads or comments.

Reviews and review state

When you submit a review on GitHub, it records:

  • State: APPROVED, CHANGES_REQUESTED, or COMMENTED
  • Commit OID: the HEAD commit at the time you submitted

twgh uses this commit OID as the anchor — the baseline for twgh diff. Everything between anchor and current HEAD is "what changed since you last looked". Because the anchor is a commit hash (not a branch pointer), it survives force-pushes and rebases.

Resolved vs outdated threads

  • Resolved: someone explicitly clicked "Resolve conversation". The feedback loop is considered closed.
  • Outdated: the code the comment points at has moved (the diff hunk no longer exists at that position). GitHub marks the thread but does not auto-resolve it.

twgh's classify_thread maps these states plus the commit history into a verdict: ✓ resolved, ~ changed (code moved but not resolved), or · untouched (nothing happened).

Force-push safety

Many review tools break when authors force-push because branch history is rewritten. twgh avoids this by anchoring on commit OIDs returned by the GraphQL API — these point at the original commits your review was filed against. GitHub's compare endpoint (/compare/<oid>..<oid>) works with any reachable object, including pre-rebase commits that are no longer on any branch.

PR references

<ref> is a PR URL, host/owner/repo#N, owner/repo#N, owner/repo/N, or a bare number inside a repo checkout. status prints URLs — paste them straight back.

Host selection

-H/--gh-host overrides $GH_HOST, which defaults to github.com. Works as a global option (twgh -H ghe.example.com status) or per command.

Installation, Configuration

Optional TOML config at ~/.config/twgh/config.toml (override with --config). Bootstrap a starter with:

# Installlation
uv tool install twgh      # or: pipx install twgh
make install      # uv tool install -e . + bash completion

# Configuration
twgh edit --init

Hide PRs from bots/service accounts in status and threads (inbox mode):

[exclude]
authors = ["dependabot[bot]", "self-hosted-renovate[bot]"]

# Per-host additions (extends the global list):
[exclude."github.enterprise.com"]
authors = ["renovate-enterprise[bot]"]

Override the exclusion list to show a specific author:

twgh status --author "dependabot[bot]"

Prerequisites

  • The GitHub CLI gh, authenticated for each host you review on: gh auth login --hostname <host>. twgh borrows gh for auth, host routing, and transport; it never handles your token.

Commands

twgh status — which PRs need me right now?

twgh status
twgh status --json
twgh status --author "dependabot[bot]"

Cross-repo rollup of every open PR you are review-requested on, commented on, or have reviewed. Per PR: author, your review state against the current head (needs_review / needs_re_review / up_to_date), and open-thread count. ★ marks PRs that moved since your last status run — new head or new comments, tracked via a local snapshot (the first run deliberately flags nothing).

Option Description
--json Machine-readable JSON array — one object per PR
--author <user> Show only PRs by this author (overrides exclusion list)

twgh diff — what changed since I last looked?

twgh diff <ref>
twgh diff <ref> --all
twgh diff <ref> --comments
twgh diff <ref> --web

The author pushed again; you don't want to re-read the whole PR. diff anchors on your last submitted review (else your last inline comment) and shows anchor→HEAD, restricted to the files you commented on. The anchor is a commit OID, so the diff survives force-pushes and rebases. With no prior review there is nothing to re-diff — it points you at twgh open instead.

Output is unified diff text, paged through delta / $PAGER / less -R. When stdout is not a TTY (piped or redirected), the pager is skipped and raw diff text goes to stdout — ready for grep, patch, or an LLM.

Option Description
--all Show the whole-PR diff, not just files you commented on
--comments / -C Weave your inline review comments into the diff as # -prefixed context above each file's patch
--web Open the anchor→HEAD range in GitHub's compare view instead of the pager. A force-push-safe deep-link the web UI can't construct by hand. File-scoping and --all do not apply

twgh compare — diff between two arbitrary commits

twgh compare <base> <head>
twgh compare <base> <head> --web
twgh compare abc123 def456 --repo LOS/my-repo

Show the diff between any two commits. Both short and long SHA forms are accepted — GitHub's API and web UI resolve them server-side. Repo context comes from the current directory's git remote; use --repo to override.

Same output behaviour as diff: paged for humans, raw text when piped.

Option Description
--web Open the compare view in the GitHub web UI (/compare/<base>..<head>)
--repo / -R <owner/repo> Repository override — use instead of CWD git context

twgh threads — were my comments addressed?

twgh threads <ref>
twgh threads                    # global inbox (no ref)
twgh threads <ref> --mine
twgh threads <ref> --by alice
twgh threads <ref> --all

An action-oriented triage view: one row per review conversation with a verdict showing whether it still needs attention:

  • ✓ resolved — the thread is marked resolved
  • ~ changed — the anchored code moved since the comment (inspect with twgh diff)
  • · untouched — the point still stands as written

Omit <ref> for a global inbox of open threads across all PRs that involve you.

Option Description
--mine Only threads you opened
--by <user> Filter by thread author
--all Include resolved threads

twgh comments — what was said while I was away?

twgh comments <ref>
twgh comments <ref> --mine
twgh comments <ref> --by carol
twgh comments <ref> --all

A chronological reading view of everything said on a PR: top-level comments and inline review comments, sorted by time. Think of it as a chat log — you read top to bottom to understand the full conversation timeline. Comments in resolved threads are hidden by default.

Option Description
--mine Only your comments
--by <user> Filter by comment author
--all Include inline comments in resolved threads

threads vs comments

Both draw from the same PR data but serve different workflows:

threads comments
Unit one row per conversation one row per comment
Order by file path by time
Key info actionable verdict full message body
Question answered "what needs my response?" "what was said?"
Scope single PR or global inbox single PR only

twgh open — hand off to the browser

twgh open <ref>

Opens the PR in the browser for the write actions twgh stays out of: approving, replying, resolving, merging.

twgh edit — configure twgh

twgh edit --init    # create starter config and open in $EDITOR
twgh edit           # open existing config in $EDITOR

Opens the config file in $EDITOR. Use --init to bootstrap a starter config if one doesn't exist yet.

twgh summary — reviewer activity report

twgh summary                          # your reviews, last 30 days
twgh summary --days 7                 # last week only
twgh summary --reviewer alice         # someone else's activity
twgh summary --json                   # machine-readable output

Produces a three-tier report measuring a reviewer's effort, engagement depth, and responsiveness over a time window.

Volume metrics

Metric What it measures
PRs reviewed Unique PRs where the reviewer submitted at least one review
Lines reviewed Sum of additions + deletions across all reviewed PRs (proxy for review scope)
Files reviewed Sum of changed files across all reviewed PRs
Total comments Inline review comments authored by the reviewer across all threads
Comments / PR Average comments per PR — measures thoroughness
Unique repos How many distinct repositories the reviewer covers — breadth of impact

Engagement metrics

Each time a reviewer submits a review on GitHub, it carries one of three states:

State Meaning
Approved Reviewer signed off — could range from rubber-stamp to thorough
Changes requested Reviewer took a position and authored specific feedback
Comment only Reviewer engaged but did not commit to a verdict

A single PR can accumulate multiple reviews (e.g., request changes → author pushes fixes → re-review and approve = 2 reviews on 1 PR). This is why "total reviews" can exceed "PRs reviewed".

Quality signals

Engagement score — a weighted average that rewards deeper review effort:

score = (changes_requested × 3  +  comment_only × 2  +  approvals × 1)
        ─────────────────────────────────────────────────────────────────
                              total_reviews
Score Interpretation
1.0 All approvals — potential rubber-stamping
1.5 Mix of approvals and substantive comments
2.0 Balanced engagement with meaningful discussion
2.5–3.0 Heavy change-request activity — very hands-on reviewer

The weights reflect effort: requesting changes requires the reviewer to articulate specific problems and defend a position, while approving can be as simple as clicking a button.

Median turnaround — median time from PR creation to the reviewer's first review submission on that PR. Measures responsiveness — how long authors wait for initial feedback. This is a rough proxy: it penalises the reviewer for PRs created before they were assigned, or PRs they picked up late from a queue.

Output conventions

twgh uses two output formats, chosen by what the data naturally is:

Data shape Format Commands
Tabular (PR list, thread verdicts, metrics) Rich table (human) or --json (machine) status, threads, comments, summary
Code delta Unified diff text with @@ hunks diff, compare

Diff output is already structured — @@ markers delimit hunks, +/- prefix changed lines, file headers name each path. Any downstream tool, LLM, or agent can parse it without a JSON wrapper. Piping works naturally: the pager is skipped when stdout is not a TTY.

Agent skill

The skill/ directory contains twgh-review-check, a ready-made agent skill (for GitHub Copilot CLI, Claude Code, or any compatible harness) that orchestrates twgh to answer: "Can I approve this PR, or do we need another round?"

The skill runs twgh threads, twgh diff --comments, and twgh comments, then synthesises a per-thread assessment table with a final APPROVE or DISCUSS verdict. It handles the nuances: ✓ resolved threads are done, ~ changed threads need the diff inspected to distinguish real fixes from coincidental churn, and · untouched threads need the comment stream checked for author replies that GitHub didn't mark as resolutions.

Install it by copying skill/ to your agent's skill directory, or point your agent at it directly. The skill has no dependencies beyond twgh and gh.

Development

make test              # pytest + coverage (floor 85%)
make static-analysis   # ruff lint-fix, format, ty

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

twgh-0.6.2.tar.gz (35.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

twgh-0.6.2-py3-none-any.whl (33.0 kB view details)

Uploaded Python 3

File details

Details for the file twgh-0.6.2.tar.gz.

File metadata

  • Download URL: twgh-0.6.2.tar.gz
  • Upload date:
  • Size: 35.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for twgh-0.6.2.tar.gz
Algorithm Hash digest
SHA256 6742314d2373617d03e8871dbbef220a94ce0b3a820b26d0136112d23c40c3dd
MD5 4077ec1d0ec25a7eed38e5755556afdf
BLAKE2b-256 0cb05760c1789b6ac2126c57a21e2f30b5f0116f5b3322c4c7a3b852d57118a4

See more details on using hashes here.

File details

Details for the file twgh-0.6.2-py3-none-any.whl.

File metadata

  • Download URL: twgh-0.6.2-py3-none-any.whl
  • Upload date:
  • Size: 33.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for twgh-0.6.2-py3-none-any.whl
Algorithm Hash digest
SHA256 177e3e8d97805690070465ba925ed2a83d02b115fc2a7e1d0c623392a9a0ce5f
MD5 1aff1fb024f5b085d44ca6aaf71feec4
BLAKE2b-256 1c1328c5eadeb2cc9a5ca9da4086237065c87b5ecf0b58349318a9fee4564251

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page