lazygh is a Go CLI that aims to make GitHub pull request work less annoying with a lazygit-like TUI with vim-motion.
ghto connect to GitHub. Private image loading uses yourghauth session.- A terminal with kitty graphics protocol support, such as Kitty or Ghostty, if you want inline images.
- If you run
lazyghinsidetmux, enable passthrough withset -g allow-passthrough on. - NerdFont to get the icons.
Installation with mise
Install lazygh globally with mise's Go backend:
mise use -g go:github.com/l-lin/lazygh/cmd/lazygh@latest
lazyghOr run it once without a global install:
mise exec go:github.com/l-lin/lazygh/cmd/lazygh@latest -- lazyghYou can download the binary directly from the release page and use it.
git clone https://github.com/l-lin/lazygh/cmd/lazygh
mise run runmise run install
mise run lazygh
mise run lazygh view https://github.com/acme/widgets/pull/42
mise run lazygh review https://github.com/acme/widgets/pull/42Tagged pushes that match v* publish release archives and checksums.txt.
Use mise run release-check to validate .goreleaser.yaml.
Use mise run release-snapshot to build the release artifacts locally without publishing them.
# Open TUI.
lazygh
# Directly view the PR details.
lazygh view https://github.com/acme/awesome/pull/123
# Directly start reviewing a PR.
lazygh review https://github.com/acme/awesome/pull/123
# Directly start a story review a PR.
# (Must configure `story_review.agent_command` beforehand)
lazygh story-review https://github.com/acme/awesome/pull/123lazygh renders markdown images and supported HTML <img> tags in detail views, only if your terminal support kitty image protocol.
It keeps small images at their natural size. It scales larger images down to fit the detail pane.
If inline graphics are unavailable, or an image download fails, lazygh still shows a visible [Image: …] caption and the resolved URL.
For private repositories, lazygh asks GitHub to render the markdown with repository context, then downloads the resolved image URL with your authenticated gh session when needed.
lazygh looks for $XDG_CONFIG_HOME/lazygh/config.toml. If XDG_CONFIG_HOME is unset, it falls back to ~/.config/lazygh/config.toml.
If the file is missing, lazygh starts with the built-in defaults. If the TOML is malformed, startup fails. Unknown scopes, unknown actions, invalid key strings, invalid keymap value types, invalid theme colors, invalid story-review settings, invalid cache settings, and invalid pull-request search entries are ignored, because apparently survival is preferable to drama.
lazygh uses SQLite as cache storage to improve user experience.
Use [cache] to control the persistent SQLite cache.
- By default,
lazyghstores the cache at$XDG_DATA_HOME/lazygh/cache.sqlite3. - If
XDG_DATA_HOMEis unset, it falls back to~/.local/share/lazygh/cache.sqlite3. lazyghshows cached pull-request lists immediately, then refreshes the active list in the background.- Cached PR detail and review diff entries refresh only when the live list reports a newer
updatedAt, or whenlazyghmutates that PR and invalidates the cached entry.
This example overrides the cache path:
[cache]
path = "/tmp/lazygh/cache.sqlite3"By default, it will use the system preset if not set:
[theme]
preset = "system"Available presets include system, light, and dark, plus the additional presets listed in preset.go.
You can switch presets from inside the app too. Change theme in the actions popup updates the resolved config file immediately.
You can find the list of palette variables in palette.go.
A few palette entries do more than their names suggest:
-
backgroundfills the full TUI background. -
markdown_heading_backgroundcontrols the full-line heading fill. -
pull_request_referencecolors theowner/repo#123prefix in pull-request lists. -
pull_request_titlecolors the pull-request title text in pull-request lists. -
pull_request_status_*_backgroundalso colors thestatus icon in pull-request lists. -
success_backgroundandfailure_backgroundalso fill pull-request rows in view 2 when the Merge Checks summary is fully passing or failing.
This example starts from kanagawa-dark and overrides a few colors.
[theme]
preset = "kanagawa-dark"
# Values must use the `#RRGGBB` format.
background = "#1F1F28"
active_border = "#7E9CD8"
inactive_border = "#54546D"
selected_line_background = "#363646"
pull_request_reference = "#656D76"
pull_request_title = "#DCD7BA"
success = "#98BB6C"
success_background = "#2B3328"
failure = "#E46876"
failure_background = "#43242B"
pending = "#C8C093"
pending_background = "#363646"
muted = "#727169"
warning = "#FFA066"
comment_author_badge = "#7E9CD8"
comment_author_badge_background = "#223249"
markdown_heading = "#7E9CD8"
markdown_heading_background = "#223249"
syntax_keyword = "#957FB8"
syntax_string = "#98BB6C"
pull_request_status_merged = "#957FB8"
pull_request_status_merged_background = "#252535"
diff_addition_highlight_background = "#35513B"
diff_deletion_highlight_background = "#5A2E35"You can open a link either from the actions popup (default keymap a), or by pressing gx.
- By default,
lazyghusesopenon macOS. - By default,
lazyghusesxdg-openon Linux.
[links]
# Example opens links with Firefox on macOS.
# Can be a string or an array of strings. `lazygh` appends the resolved URL as the last argument.
open_command = ["open", "-a", "Firefox"]Story review powers lazygh story mode. It shells out to an external coding agent and expects the final answer to be JSON only.
Configure story_review.agent_command under [story_review]. lazygh writes the generated prompt to a temporary file and replaces {{prompt_file}} in the configured command. If your command does not include {{prompt_file}}, lazygh appends the prompt file path as the last argument.
Examples:
[story_review]
agent_command = ["pi", "--models", "anthropic/claude-sonnet-4-6", "--no-session", "-p", "@{{prompt_file}}"]# Claude Code
[story_review]
agent_command = ["claude", "-p", "@{{prompt_file}}"]By default, lazygh uses the prompt in prompt.go.
You can override the prompt too:
[story_review]
agent_command = ["pi", "--models", "anthropic/claude-sonnet-4-6", "--no-session", "-p", "@{{prompt_file}}"]
prompt = """
Group the changes into a logical, reviewer-friendly story. Use a professional tone. Prefer chapters that reflect one cohesive behavior change, refactor step, or debugging thread. Explain what each chapter is doing, why it exists, and what a reviewer should mentally connect across the listed files. Keep the narrative concise, concrete, and useful for code review.
"""You can find some prompt examples in prompts/story-review/.
You can customize your own pull request searches under [[pull_requests.searches]].
lazygh prepends gh search prs for you, so each entry only needs the flags.
[[pull_requests.searches]]
label = "My PRs"
flags = ["--author", "@me", "--state", "open", "--sort", "updated", "--order", "desc"]
[[pull_requests.searches]]
label = "My reviews"
flags = ["--reviewed-by", "@me", "--limit", "100", "--state", "open", "--sort", "updated", "--order", "desc"]
[[pull_requests.searches]]
label = "Requested"
flags = ["--review-requested", "@me", "--limit", "100", "--state", "open", "--sort", "updated", "--order", "desc"]
[[pull_requests.searches]]
label = "Escalated"
flags = ["--search", "label:escalated state:open", "--sort", "updated", "--order", "desc"]In view 2, press : or choose Custom search from the actions popup. Submitting it creates or updates the Custom tab.
Press Ctrl+V to read a GitHub pull request URL from the clipboard and open it directly in fullscreen view 0.
Choose Open PR from URL from the actions popup when you want to type or paste the URL manually.
Use scoped tables under [keymaps].
A keymap value can be a single key like "q" or a two-key sequence like "za". Arrays still let you keep multiple alternatives.
You can find the default keymaps in default_keymaps.toml