English | 日本語
A gh CLI extension to manage GitHub Projects V2 as a kanban board in your terminal.
gh extension install uzimaru0000/gh-board
gh board # pick from your projects
gh board --owner <org> <num> # open a specific project| gh-board | gh project (official) |
Web UI | |
|---|---|---|---|
| Kanban board view | ✅ | ❌ (list only) | ✅ |
| Card detail with Markdown / comments / reactions | ✅ | ❌ | ✅ |
| Drag & reorder with the keyboard | ✅ (Space) |
❌ | drag-only |
| Roadmap / Iteration timeline | ✅ | ❌ | ✅ |
| Bulk operations | ✅ | ❌ | partial |
| Sub-issue navigation | ✅ | ❌ | ✅ |
| Works fully offline-cached | ✅ | ❌ | ❌ |
| No browser required | ✅ | ✅ | ❌ |
If you live in the terminal but gh project feels too thin and the Web UI breaks
your flow, gh-board fills the gap.
- Board / Table / Roadmap views — switch between kanban board, table, and roadmap layouts
- Card grab & reorder — move cards between columns or reorder within a column
- Card detail — Markdown rendering with table support, comments, emoji reactions, linked PRs, CI/review status
- Sub-issues — parent/child relationship display with navigable sidebar drill-down
- Custom fields — display and edit SingleSelect, Iteration, and other project fields
- Grouping axis — switch the kanban column grouping by SingleSelect or Iteration fields (
Ctrl+g) - Archive / Unarchive — archive cards from the board or restore them from the archive list
- Comments — post new comments and edit your own (
$EDITORintegration) - Filter — free text,
label:,assignee:,milestone:, compoundAND/OR - Fuzzy project selection — quickly find projects with fuzzy matching
- Local board cache — XDG-compliant on-disk cache with stale-while-revalidate for instant startup
- i18n — help screen available in English and Japanese (auto-detects from
LANG/LC_ALL) - Configurable key bindings & theme — customize via
config.toml/theme.toml
gh extension install uzimaru0000/gh-board
To upgrade later:
gh extension upgrade gh-board
gh board --owner <org-or-user> <number>
Running without arguments lets you choose from your accessible projects.
| Flag | Description |
|---|---|
--owner <LOGIN> |
Owner login (org or user). Use @me for the current viewer. |
--no-cache |
Skip the local board cache for this run (forces a fresh fetch). |
Board responses are cached at $XDG_CACHE_HOME/gh-board/board/ (e.g. ~/.cache/gh-board/board/ on Linux, ~/Library/Caches/gh-board/board/ on macOS). On startup, a cached board is rendered immediately while the latest data is fetched in the background. Mutations (move, archive, etc.) invalidate the cache automatically. Cache entries expire after 24 hours. Pass --no-cache to bypass this for a single run.
| Key | Action |
|---|---|
h / l |
Move between columns |
j / k |
Move between cards |
g / G |
First / last card |
Tab / S-Tab |
Next / previous column (wraps) |
Space |
Grab card (reorder with h/j/k/l, release with Space/Esc) |
Enter |
Open card detail |
n |
Create draft issue |
a |
Archive card (with confirmation) |
v |
View archived cards |
Ctrl+g |
Switch grouping axis |
/ |
Filter (C-u to clear) |
p |
Switch project |
r |
Refresh |
? |
Help |
q / Esc |
Quit |
| Key | Action |
|---|---|
j / k |
Scroll vertically |
h / l |
Scroll table horizontally |
c |
Post new comment (Issue/PR only) |
C |
Open comment list |
Enter / o |
Open in browser |
Esc / q |
Back (pops detail stack) |
| Key | Action |
|---|---|
j / k |
Move between comments |
e |
Edit your own comment |
c |
Post new comment |
Esc |
Back to detail |
| Key | Action |
|---|---|
j / k |
Move between cards |
Enter |
Open in browser |
u |
Unarchive card |
r |
Reload |
Esc / q |
Back to board |
- Free text: partial match on card title
label:<name>: filter by labelassignee:<name>: filter by assignee (@prefix supported)milestone:<name>: filter by milestone- Compound:
label:bug AND assignee:me,label:bug OR label:feature
Customize settings in ~/.config/gh-board/config.toml.
Override key bindings in [keys.<mode>] sections.
[keys.board]
move_down = ["n", "Down"] # Use n instead of j to move down
refresh = ["R"] # Use R instead of r to refresh
start_filter = ["/", "f"] # Use f in addition to / for filter
[keys.global]
force_quit = ["C-q"] # Use Ctrl+q instead of Ctrl+c to quitModes: global, board, project_select, detail_content, detail_sidebar, card_grab, confirm, comment_list, status_select, sidebar_edit, etc.
Key notation: j, Enter, Esc, Tab, S-Tab, Space, Up, Down, Left, Right, Backspace, C-c (Ctrl), A-x (Alt)
Place a theme file at ~/.config/gh-board/theme.toml to change the color theme.
See docs/themes/ for preset themes.
# Example: apply Catppuccin Mocha
cp docs/themes/catppuccin-mocha.toml ~/.config/gh-board/theme.toml[[view]]
name = "Bugs"
filter = "label:bug"
[[view]]
name = "My Tasks"
filter = "assignee:@me"
layout = "table"Define shell snippets in ~/.config/gh-board/config.toml and run them against the currently selected card. Trigger a command either by its bound key, or open the palette with : (on the Board or Detail screen) and pick from the list.
[[command]]
name = "Resolve with Claude"
command = "git worktree add ../{repo}.resolve-{number} -b resolve-{number} && cd ../{repo}.resolve-{number} && claude 'https://github.com/{owner}/{repo}/issues/{number} を解決してください'"
post_command = "git worktree remove ../{repo}.resolve-{number} --force"
key = "C-r"
description = "Spawn a worktree and launch claude inside"
[[command]]
name = "Copy issue ref"
command = "printf '#%s' '{number}' | pbcopy"
interactive = false
[[command]]
name = "Show gh status"
command = "gh pr checks {number}"
pause_after = trueAvailable placeholders (resolved from the selected card / current project):
| Placeholder | Description |
|---|---|
{number} |
Issue / PR number |
{title} |
Card title |
{url} |
Card URL |
{body} |
Card body (Markdown) |
{owner} / {repo} / {name_with_owner} |
Parsed from the card URL |
{id} / {content_id} |
Issue / PR node id |
{item_id} |
Project item id |
{card_type} |
issue / pull_request / draft_issue |
{project_number} / {project_title} / {project_url} |
Current project info |
Notes:
commandis executed viash -c, so you can chain with&&,|, etc.interactive = true(default) suspends the TUI like$EDITORdoes — required for tools that take over the terminal (e.g.claude,vim).interactive = falseruns the command in the background and discards its output.post_command(optional) runs aftercommandsucceeds (exit 0), expanded with the same placeholders. Handy for cleanup such as removing a worktree once you are done. With an interactivecommandit runs in the same suspended-TUI session; with a backgroundcommandit is chained ascommand && post_command. Ifcommandfails,post_commandis skipped so you can inspect the leftover state.pause_after = true(defaultfalse, interactive only) waits for a key press after the command (andpost_command) finishes before returning to the board, so you can read the output of commands that don't take over the terminal (e.g.echo,gh pr checks).- Placeholders for fields the card does not have (e.g.
{number}on a draft issue) expand to an empty string. Unknown placeholders are left intact so you can compose your own templates safely. - Commands without a
keyare still reachable via the:palette.
cargo build
schema.graphql (GitHub GraphQL API schema) is automatically downloaded on the first build. Requires gh CLI to be installed and authenticated.
MIT
