Metadata-Version: 2.4
Name: twgh
Version: 0.3.0
Summary: Read-only GitHub reviewer CLI: triage, re-review diffs, comment threads
Author-email: sysid <sysid@gmx.de>
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: typer>=0.15.1
Requires-Dist: rich>=13.0.0

# 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.

## Install

From PyPI (requires Python ≥ 3.12):

```sh
uv tool install twgh      # or: pipx install twgh
```

From source:

```sh
make install      # uv tool install -e . + bash completion
```

### Prerequisites

- The GitHub CLI [`gh`](https://cli.github.com), 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?

```sh
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?

```sh
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

```sh
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?

```sh
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?

```sh
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

```sh
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

```sh
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.

## 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 bmw.ghe.com status`) or per command.

## Configuration

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

```sh
twgh edit --init
```

### Author exclusion

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

```toml
[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:

```sh
twgh status --author "dependabot[bot]"
```

## Output conventions

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

| Data shape | Format | Commands |
|------------|--------|----------|
| Tabular (PR list, thread verdicts) | Rich table (human) or `--json` (machine) | `status`, `threads`, `comments` |
| 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.

## Development

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