Neovim integration for GitHub CLI (gh).
Neovim utilities for working with the GitHub CLI, focusing on basic issue/PR viewing and editing with quick access from your editor.
Features:
- Issue/PR browsing: Buffer-based lists with Oil-style editing
- Issue editing: Text buffer editing with seamless sync via
ghCLI - Async command execution via GraphQL or JSON
- In-memory caching with TTL
- Command completion
Inspired by: Snacks.nvim rendering style
gh.nvim supports two modes for fetching GitHub data:
-
"json"(default): UsesghCLI with JSON output- More reliable, works everywhere
ghworks - Slightly slower due to JSON parsing
- More reliable, works everywhere
-
"api": Uses GraphQL API viagh api graphql- Faster data fetching
- More efficient for large datasets
Configuration Example:
vim.g.gh_opts = {
data_source = "json", -- or "api"
issue_detail = {
reuse_window = true,
split_direction = "auto", -- "auto", "horizontal", or "vertical"
}
}See Configuration section for all available options.
:Gh issue list " Open issues for current repo (open issues only, default)
:Gh issue list --state all " All issues (open + closed)
:Gh issue list --state closed " Closed issues only
:Gh issue list --limit 50 " Limit to 50 issues
:Gh issue list --repo owner/repo " Issues from specific repo
:Gh issue list --state all --limit 100 " Combine flagsNote: Like gh CLI, :Gh issue list shows open issues only by default. Use --state all to see both open and closed issues.
The issue list buffer displays issues in an editable table format:
# GitHub Issues (edit and :w to save)
# Format: #number │ STATE │ title
#123 │ OPEN │ Fix the navigation bug
#124 │ CLOSED │ Add dark mode support
Actions:
- Edit titles directly in the buffer
- Change state between OPEN/CLOSED
- Press
<CR>on an issue to open detail view - Press
Rto refresh the list - Press
Mto load more issues - Press
Sto open interactive sort menu - Quick sort with
sprefix:sn- Sort by numberst- Sort by titless- Sort by statesa- Sort by authorsc- Sort by created datesu- Sort by updated datesl- Sort by label count
- Press same sort key again to toggle ascending/descending order
- Save with
:wto sync changes to GitHub
:Gh issue view 123 " Open issue #123 for current repo
:Gh issue view 123 --repo owner/repo " Open issue from specific repoThe issue detail buffer shows the full issue with editable content:
# Issue title here
Issue body in markdown format.
You can edit both the title and body,
then save with :w to sync to GitHub.Metadata Display (Virtual Text): After the title, you'll see issue metadata rendered as virtual text with badges and colors:
- State (OPEN/CLOSED) with badges
- Author
- Labels with color-coded badges
- Assignees
- Created/Updated dates (relative time)
- URL
Actions:
- Edit title (first line after
#) - Edit body (everything after the blank line)
- Press
qto close - Press
gxto open issue in browser - Save with
:wto sync changes to GitHub
The :Gh command has tab completion for subcommands and flags:
:Gh <TAB> " Shows: issue, pr, repo, run, workflow
:Gh issue <TAB> " Shows: list, view, create, close, reopen
:Gh pr <TAB> " Shows: list, view, create, checkout, status
" Flag completion
:Gh issue list -<TAB> " Shows: --state, -s, --limit, -L, --repo, -R
:Gh issue list --state <TAB> " Shows: open, closed, all
:Gh issue view 123 -<TAB> " Shows: --repo, -RIn-memory cache with TTL support (session-scoped):
local cache = require("gh.cache")
-- Check if cache is valid
if cache.is_valid("my_key", 300) then -- 300 seconds TTL
local data = cache.read("my_key")
end
-- Write to cache
cache.write("my_key", { some = "data" })
-- Get cache statistics
local stats = cache.get_stats()Note: Cache is cleared when Neovim restarts.
Async GitHub CLI wrapper:
local cli = require("gh.cli")
-- List issues
cli.list_issues(nil, function(success, issues, error)
if success then
vim.print(issues)
end
end)
-- Get issue details
cli.get_issue(123, nil, function(success, issue, error)
if success then
vim.print(issue)
end
end)Pull request commands:
:Gh pr list " List pull requests (not yet implemented)
:Gh pr view 456 " View/edit PR #456 (not yet implemented)Note: PR support is planned for a future release.
The :Gh command also acts as a passthrough to the GitHub CLI:
:Gh pr checks
:Gh repo view
:Gh run listThese commands execute asynchronously and populate the quickfix list with results.
- plenary.nvim - For async job execution
- gh - GitHub CLI
- Neovim 0.8+ (tested with 0.10+)
- GitHub CLI (
gh) installed and authenticated - plenary.nvim
{
"e-roux/gh.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
},
}Add to your init.lua:
vim.g.gh_opts = {} -- Use defaults, or pass custom config (see Configuration)use {
"e-roux/gh.nvim",
requires = {
"nvim-lua/plenary.nvim",
},
}Add to your init.lua:
vim.g.gh_opts = {} -- Use defaults, or pass custom config (see Configuration)-
Clone this repository to your Neovim config directory:
git clone https://github.com/e-roux/gh.nvim ~/.config/nvim/pack/plugins/start/gh.nvim -
Install plenary.nvim if not already installed
-
Add to your
init.lua:vim.g.gh_opts = {} -- Use defaults, or pass custom config (see Configuration)
-
Ensure
ghCLI is installed and authenticated:gh auth login
Configuration is passed via vim.g.gh_opts and automatically merged with defaults when the plugin loads.
vim.g.gh_opts = {
-- Data source for GitHub API calls
-- "json" (default): Uses gh CLI with JSON output (reliable)
-- "api": Uses GraphQL API via gh api (faster)
data_source = "json",
-- Issue detail view options
issue_detail = {
reuse_window = true, -- Reuse existing issue detail window
-- When true: opening a new issue replaces the current issue detail window
-- When false: each issue opens in a new split
split_direction = "auto" -- Split direction for issue detail windows:
-- "auto" (default): Automatically choose based on window width
-- (vertical if width >= 120 columns, horizontal otherwise)
-- "horizontal": Always split horizontally
-- "vertical": Always split vertically
}
}Important:
- Set
vim.g.gh_optsbefore the plugin loads (e.g., in yourinit.lua) - PR support is planned for a future release
This approach follows modern Neovim plugin patterns and provides:
- Faster startup (no
setup()function call required) - Simpler configuration
- Compatibility with lazy loading
- Alignment with Neovim 0.12+ conventions
You can access the resolved configuration at runtime:
local config = require("gh.config")
print(vim.inspect(config.opts))- Issue list viewing and editing
- Issue detail viewing and editing
- Virtual text metadata display with badges
- Pull request support
- Issue creation from template
- Better error messages
- More tests
VIM License - see Vim License