A Golang TUI application built with Bubble Tea for reviewing local git changes before staging. It's the step before running git add - you review each change, approve what goes to stage, and keep unapproved changes unstaged with optional comments.
- Go — a toolchain that satisfies the version in
go.mod, withgoon yourPATH - Git — the
gitCLI must be available; human-review uses it for diffs and staging
go install github.com/em4go/human-review@latestThis places the human-review binary in $(go env GOPATH)/bin (commonly ~/go/bin). If that directory is not already on your PATH, add it (example for zsh):
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.zshrc
source ~/.zshrcOpen a new terminal if you prefer not to source the file.
git clone https://github.com/em4go/human-review.git
cd human-review
go build -o human-review .Move human-review to a directory on your PATH, or run it via ./human-review. You can also try the app without installing a binary: go run . from the repository root.
human-review # review the Git repo in the current directory
human-review /path/to/repo # review another repositoryPurpose: Review unstaged git changes line-by-line with the ability to stage hunks individually, add comments to changes you want to revisit later, and selectively stage only what you approve.
Workflow:
- List all files with unstaged changes
- Select a file to view its diff
- Move between hunks with
[/]; stage one hunk withs, drop it from the working tree withr, or unstage a hunk from the index when viewing the staged diff (u) - Optional:
eopens the file in an external editor to tweak changes, then the diff reloads (see External editor below) cqueues a review note for the current line or visual selection;:wappends all queued notes toREVIEW.md(the file is not created until then).Kor:reviewopens a draft preview where you can drop notes withDbefore saving
- Vim-style navigation —
j/kto move, Enter / l to open, Space for shortcut help - File list - Shows all modified files with
+X -Yline counts - Diff viewer - Full diff with syntax highlighting via Chroma
- Hunk-level staging - Stage individual hunks instead of whole files
- Comment system - Add comments to hunks you want to revisit
- Persistence - After
:w, review notes are appended toREVIEW.mdat the repo root - Color output - Syntax-highlighted diffs with terminal-friendly colors
┌─────────────────────────────────────────────────────┐
│ human-review │
├─────────────────────────────────────────────────────┤
│ Modified Files (3) │
│ ───────────────── │
│ > src/main.go +10 -3 [staged: 0/3] │
│ src/utils.go +5 -2 [staged: 1/2] │
│ src/config.go +20 -0 │
│ │
│ [j/k] [Enter/l] [Space help] [1/2 sections] [q quit] │
└─────────────────────────────────────────────────────┘
│
▼ (Enter on main.go)
┌─────────────────────────────────────────────────────┐
│ src/main.go │
├─────────────────────────────────────────────────────┤
│ @@ -1,5 +1,7 @@ │
│ func main() { │
│ + // New initialization │
│ + initConfig() │
│ fmt.Println("hello") │
│ - oldWay() │
│ + newWay() │
│ │
│ [[/] hunks] [s stage] [r discard] [c note] [q|h back] [Space help] │
└─────────────────────────────────────────────────────┘
In normal mode (not while typing a comment, an ex command, or a discard confirmation), Space opens the in-app help overlay (same shortcuts as below; Space, Esc, or q closes it). Ctrl+r triggers an immediate rescan from the file tree or from the diff / preview buffers where noted; the app also rescans on a short timer when git is idle.
| Key | Action |
|---|---|
| Space | Toggle keyboard help overlay |
| Tab | Switch focus between file tree and diff / buffer pane |
| : | Open ex command line (see below) |
| K | Open queued review notes as REVIEW.md (draft) preview |
| Ctrl+r | Refresh change list and reload the current preview when git is idle (queues if git is busy) |
| q | From file tree: quit (blocked if there are unsaved queued review notes). From diff pane: return to file tree, except on an error buffer q quits instead—use h / Left to leave an error without quitting |
| Ctrl+c | Same as q (including error-buffer quit vs tree return) |
| h / Left | From diff pane: return to file tree (same idea as q for the buffer) |
| Key | Action |
|---|---|
| j / ↓ | Move selection down (live diff preview updates) |
| k / ↑ | Move selection up |
| Enter / l / Right | On a file: open diff. On a folder row: expand / collapse. On a section header (Staged / Changes): collapse / expand that section |
| 1 | Toggle Staged section collapsed |
| 2 | Toggle Changes section collapsed |
| s | Stage entire file (row must be a file) |
| u | Unstage entire file (or remove untracked file from index as applicable) |
| d | Discard whole file (working tree, staged, or untracked by context); confirmation prompt, default No |
| Ctrl+r | Refresh change list |
| : / K | Same as global |
Applies when viewing a real file diff (not the draft preview, generic preview, or error buffer).
| Key | Action |
|---|---|
| j / ↓ | Move cursor down one line |
| k / ↑ | Move cursor up one line |
| Ctrl+d / Ctrl+u | Half-page down / up |
| g / G | Jump to top / bottom of buffer |
| [ / ] | Previous / next hunk (when the diff has hunks) |
| s | Stage current hunk (working tree / Changes diff only) |
| r | Discard current hunk from working tree (confirm); not used on staged-only diff |
| u | Unstage current hunk (Staged / cached diff only) |
| e | Open file in external editor at inferred line (see External editor below) |
| v / V | Start line-wise visual selection |
| c | Start review comment for cursor line or visual range (see Comment composer) |
| Y | Toggle syntax highlighting on / off (file diffs only) |
| Ctrl+r | Refresh git state and reload this diff |
| : | Ex command line |
| K | Open REVIEW.md (draft) preview |
| Tab / q / h / Left | Focus file tree |
Visual line mode (after v / V): j / k extend selection; c attach comment to range; Esc cancels visual mode.
| Key | Action |
|---|---|
| j / k | Scroll |
| D | Remove the note under the cursor (header or body line) |
| K | Refresh draft from the queue |
| : | Open ex command line; w / wq append notes to REVIEW.md (same as :w / :wq), wq then quits |
| Tab / q / h / Left | Back to file tree |
| Space | Toggle help |
| Key | Action |
|---|---|
| j / k | Scroll |
| Ctrl+r | Refresh |
| Tab / h / Left | Back to file tree |
| q | On Preview buffers: back to file tree. On error buffers: quit the app (if allowed); use h / Left / Tab to return to the tree without quitting |
| Space | Toggle help |
| Key | Action |
|---|---|
| Esc | Cancel without running |
| Enter | Run the command (you may type w / wq / q / … with or without a leading :) |
| Backspace | Delete the previous character in the command line |
| Command | Action |
|---|---|
| :w | Append all queued review notes to REVIEW.md and clear the queue |
| :wq | Same as :w, then quit |
| :q, :quit, :qa | Quit (blocked if there are unsaved queued notes) |
| :q!, :quit!, :qa! | Quit without saving queued notes |
| :review | Open the REVIEW.md (draft) preview |
| :e / :edit | Move focus to the file tree |
| :syntax / :syn | Enable diff syntax highlighting |
| :nosyntax / :nosyn | Disable diff syntax highlighting |
| Key | Action |
|---|---|
| Enter | Queue the note (empty text cancels) |
| Ctrl+j / Alt+Enter | Insert newline in the note |
| Backspace | Delete previous character |
| Alt+Backspace | Delete previous word (terminal must send this sequence) |
| Esc / Ctrl+c | Cancel composer |
| Key | Action |
|---|---|
| y / Y | Confirm discard |
| n / N, Enter, Esc, q, Ctrl+c | Cancel (default is No) |
The TUI runs your editor through sh -c with a command built for your editor family. The editor program is chosen in this order:
HUMAN_REVIEW_EDITOR— use only for human-review (leavesEDITORfor Git and other tools)EDITORVISUAL- Built-in default:
vi
Jump to the cursor line: e maps the diff buffer line under the cursor to a 1-based line on the new-file side of the unified diff (the +++ side). That is the working tree for a normal git diff, and the index line number for git diff --cached (still useful, but it can disagree with the on-disk file if you have mixed staged and unstaged edits).
Goto style is inferred from the basename of the first word in your editor string: hx / helix pass a single quoted path:line:1 argument (Helix’s normal CLI form, not a --goto flag); code / cursor / codium append -g path:line; zed passes a single path:line argument after your flags (for example zed --wait '…/file.go:42'); anything else uses vim-style +line before the quoted path (vi, vim, nvim, …). If you use a wrapper script whose first token is not recognized, it gets vim-style +N—adjust the script or point HUMAN_REVIEW_EDITOR at the real binary.
The status line when you press e shows the target line, the inferred family ([vim] / [helix] / …), the command prefix, and which variable supplied it.
Helix: set EDITOR=hx (or HUMAN_REVIEW_EDITOR=hx). Helix blocks until you quit, which is what human-review expects.
Zed: the CLI usually returns immediately unless you wait on the window. Use for example EDITOR='zed --wait' or HUMAN_REVIEW_EDITOR='zed --wait' (install the zed CLI from Zed’s command palette if needed).
VS Code-style: EDITOR='code --wait' works the same way.
Put an export in your login shell’s startup file so every new terminal has the variable—for zsh, usually ~/.zshrc:
# human-review only (recommended if you want a different EDITOR for Git, etc.)
export HUMAN_REVIEW_EDITOR='hx'
# or: export HUMAN_REVIEW_EDITOR='zed --wait'Or set EDITOR (or VISUAL) there if you want the same editor for human-review and other tools that respect those variables.
Reload without opening a new terminal:
source ~/.zshrcOne-liner to append Helix for human-review only (check the line first so you do not duplicate it):
grep -q HUMAN_REVIEW_EDITOR ~/.zshrc || echo "export HUMAN_REVIEW_EDITOR='hx'" >> ~/.zshrcOn bash, use ~/.bashrc or ~/.bash_profile instead of ~/.zshrc.
| Library | Version | Purpose |
|---|---|---|
| charm.land/bubbletea/v2 | v2.0.2 (Mar 2026) | TUI framework |
| github.com/charmbracelet/bubbles | v1.0.0+ | UI components |
| github.com/charmbracelet/lipgloss | v1.1.0+ | Styling |
| github.com/go-git/go-git/v5 | v5.17.2 (Mar 2026) | Git operations |
| github.com/alecthomas/chroma/v2 | v2.23.1 (Jan 2026) | Syntax highlighting |
human-review/
├── main.go # Entry point
├── cmd/
│ └── root.go # CLI root command
├── internal/
│ ├── models/
│ │ ├── file.go # File with diff info
│ │ ├── hunk.go # Diff hunk struct
│ │ └── comment.go # Comment struct
│ ├── services/
│ │ ├── git.go # Git operations (diff, stage)
│ │ └── comment.go # Comment persistence
│ ├── tea/
│ │ ├── app.go # Main Bubble Tea app
│ │ ├── filelist.go # File list view model
│ │ ├── diffview.go # Diff view model
│ │ └── styles.go # Bubble Tea styles
│ └── utils/
│ └── highlight.go # Chroma integration
└── go.mod
- Initialize Go module and install dependencies
- Create basic Bubble Tea app structure
- Implement git service to get unstaged files
- Build file list view with vim navigation
- Build diff view with hunk-level staging
- Add comment system with persistence
- Add syntax highlighting with Chroma
- Style and polish the UI
- No unstaged changes → Show message "No changes to review"
- File with only additions → Show diff with just + lines
- File with only deletions → Show diff with just - lines
- Binary files → Show "Binary file changed" message
- Merge conflicts → Show warning, don't allow staging
- differ - Another git diff TUI with Bubble Tea
