Skip to content
brianschwabauerPublic

About

A fast, minimal terminal UI for managing GitHub.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ghtui

A fast, minimal terminal UI for managing GitHub. Three panes, single-letter commands, instant rendering — inspired by yazi.

A short screen recording of ghtui in action

┌─ Repos ──────────┬─ Pull Requests ──────────┬─ Preview ──────────────┐
│   ✦ All       7  │  #142 Fix login redirect │ Title: Fix login...    │
│   ⚡ Feed     12  │       brian · 2h · ✓     │ +24 −8 in 3 files      │
│                  │  #141 Bump deps          │                        │
│ ▸ ACTIVE         │       dependabot · 1d ✓  │ ## Description         │
│   ● myapp     3  │  #139 New auth flow [D]  │ Adds redirect handler  │
│   ● infra     1  │       alice · 3d · ✗     │ for OAuth callback...  │
│   ○ docs         │                          │                        │
│                  │                          │ ✓ build  ✓ test  ✗ lint│
│ ▸ STARRED        │                          │ 2 reviewers, 4 comments│
│   tokio-rs/tokio │                          │                        │
└──────────────────┴──────────────────────────┴────────────────────────┘
 [tab1] [tab2 +]                              ⏵ synced 4s ago    ●

Why

Switching browser tabs to triage 30 repos is exhausting. gh is great for scripts but slow for browsing. ghtui keeps every repo, PR, issue, release, and README of yours in a local SQLite cache, refreshes in the background, and renders the whole UI from cache so every keystroke feels instant.

  • The cache is the UI. Every screen reads from SQLite, never the network.
  • Stale-while-revalidate. First paint in <50 ms; refreshes happen behind your eyes and redraw when something changes.
  • Predictive prefetch. Pause for 150 ms on a repo, its PRs are already loading. Pause on a PR, its detail is already loading.
  • Conditional requests. READMEs use If-None-Match, so unchanged data costs zero rate-limit quota.
  • Keyboard only. Press ~ (or ?) for the cheatsheet of every binding.

Features

  • Three-pane layout: repos · PRs/Issues/Releases/README · live preview
  • Cross-repo All view (every open PR you can act on, in one list)
  • Cross-repo Feed view (recent releases / tags / commits, with unread badge)
  • Inline diff overlay with split/unified, word-level diff, collapsible files
  • Full-text fuzzy filter (/) and structured filter pills (author, label, …)
  • Command palette (:)
  • New PR / new Issue inline forms
  • Merge / approve / request changes / comment / close / reopen / rename
  • Comments via $EDITOR (just like git commit)
  • Star / unstar repos
  • Pin repos to the top, hide repos you don't want to see
  • cd-style shell integration: jump to the local clone of any repo
  • Tabs (one independent view stack per tab)
  • Yank URLs, open in browser
  • API rate-limit warning in the status bar when remaining < 500
  • Auto-rotated daily logs (7-day retention) under your XDG state dir

Install

ghtui is a single static binary. Right now the install path is cargo install from source.

From source (recommended)

You need a Rust toolchain (1.74+). The easiest install is via rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Then build and install ghtui from the local checkout:

git clone https://github.com/brianschwabauer/ghtui
cd ghtui
cargo install --path .

The binary lands in ~/.cargo/bin/ghtui. Make sure ~/.cargo/bin is on your $PATH.

Build only (no install)

cargo build --release
./target/release/ghtui

Authenticate

ghtui never asks you for a token. It looks for one in this order:

  1. $GITHUB_TOKEN environment variable (if set and non-empty). The variable is removed from the process environment after reading so it doesn't leak into child processes like $EDITOR.
  2. ~/.config/gh/hosts.yml from the GitHub CLI.
  3. gh auth token (calling out to the gh binary directly).

The simplest setup:

gh auth login

After that, ghtui Just Works. If gh isn't installed and $GITHUB_TOKEN isn't set, ghtui prints a one-line message telling you what to do.

Token scopes

If you'd rather create a personal access token by hand, the minimum scopes are:

  • repo — read PRs/issues/code, merge, comment, approve
  • read:org — list org repos / org PRs
  • user — show "you" on cards (optional)

Set it with:

export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
ghtui

First run

The first run takes a few seconds to populate the cache. You'll see repos appear as the sync task finishes, then PRs, then the cross-repo Feed. Subsequent runs paint instantly from the local cache and refresh behind the scenes.

If something looks wrong:

ghtui --diagnose

This prints token source, cache path/size, log directory, last-sync timestamps per resource, and current rate-limit budget — without entering the TUI.

Keybindings

Press ~ (or ?) inside ghtui for the live cheatsheet, filtered to the current context. The most useful keys to start with:

Key Action
j k (or arrows) Navigate
h l Move pane focus / back / forward
Tab Cycle PRs → Issues → Releases → README
Enter / l Open detail (PR, issue, release)
/ Filter the current pane (fuzzy + structured pills)
: Command palette
o Open the focused thing in your browser
y Yank its URL to the clipboard
r Refresh the current view
R Hard refresh (bypass cache)
t / 1–9 / x New tab / switch / close
~ ? Help overlay
q / Ctrl+C Quit

PR-specific:

Key Action
m Merge (squash by default; m again cycles methods in the dialog)
a Approve
A Request changes (with body)
c Comment — opens $EDITOR, like git commit
d Open the inline diff overlay (split/unified, word diff, collapsible)
D Close PR
U Reopen a closed PR
r Rename PR title
n New PR (in the PRs view)

Repo list:

Key Action
, Pin / unpin (pinned repos sort to the top)
s Star / unstar
H Hide a repo from the list
. Toggle visibility of hidden repos (in the repo pane) — and toggle closed PRs/issues elsewhere
cd Print the local checkout path (works with --cd-file for shell integration)

Inside the diff overlay:

Key Action
Tab Switch unified / split
w Toggle word-level diff
] [ Next / previous file
Enter Space Collapse / expand the file at the cursor
z Collapse / expand all files

cd shell integration (jump to local clones)

Bind a shell function to launch ghtui with a path-out file, then cd into whatever ghtui writes there:

# bash / zsh
ghcd() {
    local tmp
    tmp="$(mktemp -t "ghtui-cwd.XXXXXX")"
    ghtui --cd-file "$tmp"
    if [[ -s "$tmp" ]]; then
        cd "$(cat "$tmp")"
    fi
    rm -f "$tmp"
}

Now hit cd (yes, the two letters c then d) on any repo row in ghtui — ghtui exits and your shell jumps into the local clone. ghtui looks under local_repos_root (default $HOME) for clones matching the repo's name, owner-name, or owner/name.

Configuration

Config is optional. ghtui reads ~/.config/ghtui/config.toml if present (macOS: ~/Library/Application Support/ghtui/config.toml, Windows: %APPDATA%\ghtui\config.toml).

# All optional; defaults shown.

# Editor for comment bodies and PR descriptions.
# Honors $VISUAL, then $EDITOR; falls back to vi.
editor = "vi"

# Where to look for local clones for the `cd` shortcut.
# Anything tilde-expandable; checks <root>/<name>, <root>/<owner>-<name>,
# and <root>/<owner>/<name>.
local_repos_root = "~"

# How long the cursor must rest on a row before background prefetch fires.
prefetch_delay_ms = 150

# How often the background sync task ticks (seconds).
sync_interval_secs = 60

# Window for "ACTIVE" classification — repos you've pushed to within
# this many days, plus repos with your open PRs, are considered active.
active_window_days = 90

Keybindings are intentionally not configurable: keeping the surface area small is part of the speed story.

Data, files, and privacy

Path What's in it
~/.config/ghtui/config.toml Your config (above). Optional.
~/.local/state/ghtui/cache.db SQLite cache: repos, PR/issue bodies, READMEs, releases. Mode 0600.
~/.local/state/ghtui/ghtui.YYYY-MM-DD.log Daily-rotated logs, max 7 files.

(macOS: ~/Library/Application Support / ~/Library/Caches. Windows: %APPDATA% / %LOCALAPPDATA%. Exact paths come from the dirs crate.)

The cache contains data from your private repos. ghtui sets the cache file to mode 0600 (Unix only) so other users on the host can't read it. The cache never leaves your machine.

ghtui never writes your token to disk and never logs it. It does keep the token in memory for the lifetime of the process.

Troubleshooting

Symptom Try this
"no GitHub token found, and gh CLI is not installed" Install gh and run gh auth login, or set $GITHUB_TOKEN.
"no GitHub token found — run gh auth login" gh is installed but not logged in.
Repos missing from the list They might be hidden (press . in the repo pane) or in another section. Press R for a hard refresh.
API rate limit warning in the status bar ghtui already uses ETags for READMEs and dedupes in-flight requests; the rest is just usage. The warning shows resets-in time.
Terminal looks broken after a crash Run reset in your shell. ghtui's panic hook should restore the terminal automatically; please open an issue if it didn't.
Build error on cargo install Make sure your Rust toolchain is 1.74+ (rustup update), and you have a working C toolchain (rusqlite is bundled, so no system SQLite needed).
Need a debug log RUST_LOG=ghtui=debug ghtui and check the latest file under the log dir (ghtui --diagnose prints the path).

Architecture in 30 seconds

        ┌──────────────────────┐
        │   Terminal events    │
        │ (key, resize, signal)│
        └──────────┬───────────┘
                   ▼
        ┌──────────────────────┐    reads      ┌───────────────┐
        │   App / UI (ratatui) │ ◀──────────── │  SQLite cache │
        └──────────┬───────────┘               └───────▲───────┘
                   │ SyncRequest                       │ writes
                   ▼                                   │
        ┌──────────────────────┐    GraphQL/REST       │
        │   Sync task (tokio)  │ ────────────────────► GitHub
        └──────────────────────┘                       │
                   │ SyncEvent                         │
                   └──────────────────────────────────►┘

The UI thread only ever reads SQLite. The sync task only ever writes to SQLite. They communicate through one mpsc channel of typed events. SQLite calls run inside tokio::task::block_in_place so disk I/O doesn't stall other tasks on the worker.

Non-goals (so the surface stays small)

  • Inline diff against arbitrary refs (use the diff overlay or shell out to git)
  • Multiple GitHub accounts simultaneously
  • Configurable themes (Catppuccin Mocha, hardcoded)
  • Workflow / Actions management
  • Wiki, projects, discussions
  • Configurable keybindings

If you want any of these, yazi and gh are both excellent and we won't try to be them.

License

MIT — see LICENSE.

About

A fast, minimal terminal UI for managing GitHub.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages