2026-07-28 14:54:19 +02:00
2026-07-27 14:04:08 +02:00
2026-07-27 14:04:08 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 12:00:44 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 09:52:23 +02:00
2026-07-28 09:52:23 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00
2026-07-28 14:54:19 +02:00

gh-threads

A terminal UI for people receiving GitHub pull-request reviews. It shows open PRs and a scrollable PR dashboard with the description, branches, review state, merge conflicts and affected files, checks, people, labels, milestone, activity, change statistics, thread totals, submitted reviews, and the PR conversation. Review threads and comments are paginated rather than silently stopping at the first page. The thread viewer includes highlighted diff hunks, comment authors, and read-only reaction counts on individual comments. Resolved threads start folded. GitHub suggestion blocks are shown as syntax-highlighted remove/add previews. Comments and PR descriptions render GitHub Flavored Markdown, including quoted replies, inline and fenced code, lists and tasks, links, tables, emphasis, strikethrough, emoji, and GitHub alerts. The current PR is refreshed in the background.

Install and run

Requires Go 1.24+, Git 2.38+, and an authenticated GitHub CLI:

go install .
gh auth login
gh-threads

To run directly from a source checkout instead:

go run .

Use go run ., not go run main.go: the latter compiles only main.go and omits the other files in the package.

For automation, GH_TOKEN or GITHUB_TOKEN can still be provided and takes precedence over the GitHub CLI credential. Enterprise token environment variables are also supported.

By default the PR picker searches all repositories for open PRs assigned to the authenticated user and groups the results by repository. Use --repo to limit the picker to one repository:

gh-threads --repo owner/repository

With a repository selected, pass --all to include every open PR in that repository:

gh-threads --repo owner/repository --all --poll 15s

GitHub Enterprise Server can be used after authenticating that host:

gh auth login --hostname github.example.com
gh-threads --repo owner/repository \
  --endpoint https://github.example.com/api/graphql

Configuration

The optional TOML configuration is loaded from $GH_THREADS_CONFIG, $XDG_CONFIG_HOME/gh-threads/config.toml, or the operating system's user configuration directory at gh-threads/config.toml. On Linux this is normally ~/.config/gh-threads/config.toml. On macOS, ~/Library/Application Support/gh-threads/config.toml is preferred, with ~/.config/gh-threads/config.toml automatically used as a fallback when it exists.

theme = "dark"                 # dark, light, high-contrast, or no-color
refresh_interval = "10s"
repository = ""               # optional owner/repository default
show_all = false              # requires repository
limit = 50
endpoint = "https://api.github.com/graphql"

[display]
fold_resolved = true
thread_list_width_percent = 33 # 20-60
dashboard_mode = "hotkey"      # "hotkey" or "intermediate"
compact_reviews = true         # aggregate submitted review history

[paths]
scroll = false
scroll_interval = "350ms"      # minimum 50ms

[threads]
# Each status must occur exactly once. "outdated" means unresolved and outdated;
# resolved threads remain in "resolved" even when they are also outdated.
status_order = ["unresolved", "outdated", "resolved"]
within_status = "file"          # "file" or "timestamp" (oldest first)

[cache]
enabled = true                 # instant stale view plus offline fallback
max_age = "168h"               # 7 days; 0 means no age limit
directory = ""                 # defaults to the OS user cache directory

[editing]
mode = "vim"                   # "vim" or "standard"; description field only for now

When cached data exists, the picker and PR details are rendered immediately from that snapshot while a live GitHub refresh runs in the background. Cached screens are labelled with their save time and are replaced automatically when fresh data arrives. Check annotations are fetched separately only for failed checks so they do not inflate the initial PR query.

The cache uses separate JSON files for the picker and each visited PR. Cache content is hashed before writing: unchanged responses do not rewrite their files. Their modification time is touched at most once per day (or half the configured maximum age, when shorter) so recently validated snapshots remain usable without writing on every poll. Changed files are replaced atomically.

GitHub's public APIs report whether a PR conflicts but do not expose its conflicting file paths. For conflicting PRs only, gh-threads performs a read-only git merge-tree analysis in a temporary bare repository. It never touches or inspects the current checkout, so Git, Jujutsu (jj), and directories without a local repository behave identically. The analysis fetches the exact remote base branch and pull-request head ref using the existing GitHub credential. Results are memoized by the base and head commit, and failed scans are retried after one minute.

Command-line flags override the configuration. GH_REPO overrides the configured repository when --repo is not provided. The corresponding flags include --config, --theme, --poll, --fold-resolved, --thread-list-width, --dashboard-mode, --compact-reviews, --path-scroll, and --path-scroll-interval, plus --cache, --cache-max-age, --cache-dir, and --editor-mode. Boolean settings can be disabled explicitly, for example --compact-reviews=false.

With the default dashboard_mode = "hotkey", opening a PR goes directly to its review threads and d opens the dashboard only when requested. Set dashboard_mode = "intermediate" to follow picker → dashboard → review threads instead.

Compact reviews aggregate submission counts by state and author. Reviews with a written summary retain a compact one-line body, while timestamps and commit SHAs are omitted. Set compact_reviews = false to restore the complete review history and metadata.

Keys

Key Action
h / l Focus the thread list / thread detail
j / k Move between items or scroll the dashboard/focused detail
? Show contextual keybinding help
d Open the current pull request dashboard
e Edit the current PR title, target branch, and description from its dashboard
/ Fuzzy-search paths and filter with status:, author:, updated:true
F Clear active thread filters
n / N Next / previous thread with a new update
c Compose a reply to the selected thread
R Resolve or unresolve the selected thread
/ Choose a fuzzy-search match
g / G First / last item
enter / l Open the selected PR dashboard or its review threads
enter Toggle the selected review thread
za Toggle the selected thread
ctrl-d / ctrl-u Scroll thread detail or page through lists
tab Hide or reveal the thread list
b / esc Return to the previous screen
r Refresh now
q Quit

The reply composer appears inline beneath the selected thread so its code and comments remain visible while writing. It supports multiple lines: enter inserts a newline, ctrl-s opens the rendered confirmation preview, and esc cancels. Replies and resolution changes require an explicit y confirmation. Write keys remain disabled for cached snapshots, during refreshes, and whenever GitHub does not grant the corresponding capability.

The dashboard editor works with raw Markdown so template checklists can be updated directly. The active line is highlighted without inserting a layout-changing block character. It opens with the description focused; tab and shift-tab move between the description, title, and target branch. When the target branch is focused, repository branches are recommended using the typed text, likely branch names, the current/default branch, and each branch's latest commit time. The list updates as you type. Use ctrl-p and ctrl-n to select the previous or next result, then tab or enter to complete it; pressing tab again moves to the description.

With the default editing.mode = "vim", the description starts in Normal mode. It supports hjkl, 0, ^, $, gg, G, w/W, b/B, e/E, f/F/t/T with ; and ,, i/a/I/A, o/O, s, and x/X. s removes the character under the cursor and enters Insert mode. Soft-wrapped rows behave as visual editor lines for vertical and line-local motions, but do not add newlines to the Markdown submitted to GitHub. ctrl-d and ctrl-u move the cursor and viewport down or up by half a page, including while extending a Visual selection. v starts character-wise Visual mode and V starts visual-line selection; d or x deletes the selection, y copies it to the system clipboard, and p pastes from the system clipboard. Normal mode uses a block cursor, while Insert mode uses the terminal's hardware bar cursor at the boundary between characters without hiding or shifting either character. The description retains its raw Markdown while headings, emphasis, inline code, links, quote markers, and HTML comments receive syntax highlighting. Highlighting consists only of zero-width terminal styling and cannot alter wrapping, selection, clipboard contents, cursor offsets, or submitted text. esc returns from Insert to Normal mode; a second esc cancels the editor. Set editing.mode = "standard" for direct insertion with arrow, home, and end navigation. Title and target branch remain standard inputs in either mode. ctrl-s opens an explicit confirmation. If the title, description, or target branch changes remotely while the editor is open, submission is blocked rather than overwriting the newer metadata.

Current scope

The application can reply to review threads, resolve or unresolve them, and update the PR title, target branch, and description. Comment reactions remain read-only. Other write operations remain disabled. The dashboard shows the capability gate, including why each action is unavailable. Read state persists beside the configuration, and recent PR data is cached for offline fallback. Check contexts and annotations are paginated. GitHub features which depend on server-side context, such as unfurling issue references or displaying uploaded images, are represented textually in the terminal. See TODO.md for remaining read-only work and write-support preparation.

Description
No description provided
Readme 2 MiB
Languages
Go 100%