Coco UI
coco ui is Coco's full-screen Git workstation. It builds on the interactive log work and moves toward a terminal-native alternative to GitKraken, lazygit, and gitui for daily repository work.
coco log -i remains supported. Internally it opens coco ui --view history, so existing muscle memory keeps working while new workflows grow around the broader coco ui entrypoint.
Start The Workstation
1# Open the history browser
2coco ui
3
4# Start on worktree status
5coco ui --view status
6
7# Start on the focused diff surface
8coco ui --view diff
9
10# Keep using the existing interactive log entrypoint
11coco log -i
12
13# Browse all refs or filter history before opening
14coco ui --all
15coco ui --branch feature/ui --path src --limit 500
16
17# Force a theme preset
18coco ui --theme catppuccin
19NO_COLOR=1 coco uiIf coco ui is not running in a TTY, Coco falls back to non-interactive output so automation and packaged CLI smoke checks do not hang.
Layout
The UI uses a persistent Git workstation layout:
- Header: command label (
coco), repository, branch, dirty state, provider/PR state, search text, and a›-separated breadcrumb showing where you are in the navigation stack (e.g.history › diff). - Sidebar: status, branches, tags, stashes, and worktrees tabs. When focused, items inside the active tab are cursor-navigable and per-entity actions (checkout / apply / pop / drop / etc.) fire without leaving the workstation view.
- Main panel: the active view — history graph, status, diff, compose, branches, tags, stash, worktrees, or pull-request.
- Right dock (inspector): contextual metadata + per-entity action list. Narrow at rest (~22% of width) so the commit graph dominates; expands to ~40% when focused via
tab. On short terminals, collapses into a tabbed[Inspector] Actionslayout so neither section gets clipped. - Footer: two slots — view-specific contextual hints on the left, and persistent global affordances anchored to the right (
g jump · < back · ? help · : cmds · q quit). - Narrow terminals (single-pane): below ~100 columns the three-pane layout folds to a single full-width pane (the old 8-cell icon rails are retired).
Tab/Shift+Tabcycles which pane is visible (sidebar → main → inspector) and the footer prepends atab: [sidebar] main inspectorswitcher. Pressvto momentarily peek the sidebar from the main/inspector pane —vorEscsnaps you back. This makes an 80×24 tmux split or SSH session first-class.
The goal is calm density. Common actions stay visible; advanced or destructive actions move behind help, the command palette, or confirmation prompts.
Views
coco ui has sixteen top-level views. Each is a first-class destination — reachable from any other view through the navigation stack.
| View | Chord | Start flag | Purpose |
|---|---|---|---|
| History | g h | --view history | Browse commits, refs, changed files, and commit metadata. |
| Status | g s | --view status | Select changed files, stage/unstage, revert with confirmation. |
| Diff | g d | --view diff | Inspect the selected commit or worktree file diff with hunk navigation and staging. Toggle unified ↔ side-by-side with d. |
| Compose | g c | — | Full-screen commit draft editor with summary/body cursors, AI draft, and hook feedback. |
| Branches | g b | — | List local branches with divergence info; checkout/delete/create-PR via workflow keys. Current branch pinned at row 0. |
| Tags | g t | — | List tags. |
| Stash | g z | — | Stash list with rich rows (branch · files · age). a/A apply, p pop, R rename, b branch-from-stash, X drop, u undo-drop; : palette adds staged-only / keep-index variants. (gs is reserved for status, so stash uses gz. gZ creates a stash from any view.) |
| Worktrees | g w | — | List linked worktrees with current/dirty markers; remove via W. |
| Pull request | g p | — | Dedicated PR action panel for the current branch: header, checks table, reviews summary, body preview. Action keys: m merge, x close, a approve, R request changes, c comment, O open in browser. |
| PR triage | g P | — | Multi-PR triage list with filter cycling and per-row actions. Distinct from g p (single, current branch); capital P targets the cursored PR by number. See Issue & PR Triage. |
| Issues | g i | — | Issue triage list with filter cycling and per-row actions: comment, label, assign, close, reopen. See Issue & PR Triage. |
| Conflicts | g x | — | Conflict resolution helper, available during merge / rebase / cherry-pick / revert. Per-row keys s stage / u theirs / U ours / o edit / C continue. |
| Reflog | g r | — | Chronological recovery log — every HEAD movement (commit, checkout, merge, reset, …) with relative time, action, hash, and message. Enter drills into the diff for the entry's hash. |
| Bisect | g B | — | Bisect workflow surface. Shows the current candidate, the parsed decision log, and the action keys: g good / b bad / s skip / x reset. The title bar shows a BISECTING badge whenever a bisect is in progress. |
| Submodules | g M | — | Registered submodules with name, pinned sha, tracking branch, and clean/modified/uninitialized/conflicted state. Enter drills into the cursored submodule (see Submodule navigation below). Capital M disambiguates from g m which is the compare-base mark. |
| Changelog | L | — | Full-screen AI-generated changelog for the current branch. Reached via L from history or branches (not a g-chord). Per-branch cache; r regenerates, y yanks, E opens in $EDITOR, c kicks off create-PR seeded with the content. |
The --view flag accepts history, status, or diff for the historical command-line surface. The other views are reached interactively via chords or by pressing Enter on a contextual selection.
History mode starts in compact graph mode. Press \ to toggle to full graph mode when you want higher topology fidelity — pattern junctions (├╮ / ├╯), per-lane coloring, distinct merge / HEAD glyphs (◆ / ◉), and graph continuation rows.
Submodule navigation
coco ui treats every nested submodule as a first-class repository. Pressing Enter on a submodule drills into it — git, history, branches, status, diff, and every other view re-scope to the submodule's working directory, as if you had run coco ui from inside it. Pressing Esc or < pops back to the parent.
Two entry points:
- Submodules view (
g M). Cursor a row, pressEnter. The frame opens on the submodule's history view. - Commit diff (any commit that touches a submodule). Open the diff, cursor onto the submodule's file row, press
Enter. The frame opens with the(oldPin, newPin)range captured from the diff — useful for seeing exactly what changed between two pinned commits.
Once inside, the breadcrumb in the title bar shows where you are (coco › vendor/lib ← esc). Selection state per view is preserved; popping back lands the cursor exactly where you left it on the parent. Drill-ins can stack — coco › vendor/lib › vendor/lib/inner ← esc works the same way, with each Esc walking back one level.
The drill-in is read-only by design. Stage / commit / push workflows still run against the parent until follow-up work on per-frame mutation lands.
Compare two refs
Diff any two refs (branches, tags, or commits) without leaving the workstation. The flow:
- From branches, tags, or history, press
mon a row to mark the cursored ref as the compare base. The status banner sticks ("Compare base: <label> — press enter on another ref to diff") and the footer adapts to showenter compare · m clear. - Navigate to a second ref on any of those three views.
- Press
Enterto push the diff view incomparemode (git diff <base>..<head>). <orEscpops the diff and clears the base automatically.
m again on the same ref toggles the base off without leaving the view. Outside the compare flow, m is unbound on those views.
Navigation
coco ui shares a chord-driven navigation model with coco log -i. Press g and then a second key to jump anywhere; < or Esc pops the navigation stack to where you came from.
1g h history g c compose g b branches g t tags g w worktrees
2g s status g d diff g z stash g p PR g x conflicts
3g i issues g P PR triage g r reflog g B bisect g M submodules
4< back esc backSelection state per view is preserved across navigation, and a ›-separated breadcrumb in the header shows your current path.
For the full navigation reference — chord rules, contextual transitions (Enter on a commit, e/c from status), the interactive command palette (:), the / global search across views, the help overlay, the per-context [/] and ←/→ keys, theming, and accessibility — see TUI Navigation.
Worktree And Diff Workflow
In status view:
- Files render under
▾ Staged (n)/▾ Unstaged (n)/▾ Untracked (n)headers in canonical order. j/kor arrows move through files within the active group.←/→jumps between groups (lands on the group's first file).↑at the first file of a group promotes the cursor onto the group header.↓from the header re-enters the group.Enteron a file opens the diff.Enteron a group header fires the batch action:Unstage all staged files,Stage all unstaged files, orStage all untracked files(the last requires y-confirm).Spacestages or unstages the selected file.zasks to revert the selected file. Pressyto confirm orn/Escto cancel.1/2/3toggle the staged / unstaged / untracked visibility mask.
In diff view:
j/kmove between hunks.PageUp/PageDownscroll the diff.Spacestages or unstages the selected hunk.zasks to revert the selected hunk. Pressyto confirm orn/Escto cancel.[/]jumps between hunks (or files in stash diffs).Escor<pops the navigation stack so you return to whichever view pushed diff (status or history).
Stash diffs render multiple files in one scrolling stream. Each diff --git row paints as a compact ▾ <path> header (> <path> in ASCII mode), and the file the cursor is currently scrolled inside gets selection styling so the active context is always visible.
Real diffs stay in the main panel so long diffs do not overflow the right dock. The right dock stays focused on metadata and commit actions.
Commit Compose
Status and diff views show a commit box in the right dock.
| Key | Action |
|---|---|
e | Edit the commit summary/body fields. |
Tab | Switch fields while editing. |
Enter | Move from summary to body, or finish body editing. |
Ctrl+U | Clear the active field while editing. |
Esc | Leave edit mode. |
c | Commit staged changes with the current draft. |
I | Ask for an AI commit draft after explicit confirmation. |
Esc (while drafting) | Cancel the in-flight AI draft. Hard cancel via AbortController — the LLM request tears down, the spinner stops, the status line reads "AI draft cancelled." Works from any view (not just compose) while the draft is loading. |
R | Accept a pending AI draft when one is staged (see below). |
Manual commits require staged changes and a non-empty summary. Hook failures are shown in the TUI status/commit feedback instead of breaking terminal state.
AI drafts are opt-in. Pressing I shows a confirmation panel with an estimated token impact before Coco calls a provider. Confirmed AI drafts populate editable summary/body fields; they do not commit automatically.
Live streaming preview (opt-in)
When service.streaming.enabled: true is set in .coco.config.json, the AI draft renders a live preview pane below the loader as the model generates. Content builds up character-by-character so you can read the draft forming instead of staring at an opaque spinner. The final draft still goes through the same schema validator and commitlint retry as the non-streaming path; streaming is preview-only.
1{
2 "service": {
3 "streaming": { "enabled": true }
4 }
5}Default is false. Some providers stream poorly (one-shot blob disguised as a stream) and the preview just blinks in those cases.
Pending AI draft confirmation
When you've already typed content in the summary or body and then fire I, the AI draft does not silently replace your typing. It stages in pendingAiDraft and the compose surface shows a message:
AI draft ready. Press
Rto replace your text, orEscto keep what you have.
Press R to swap the AI draft in (your typing is lost). Press Esc to dismiss the AI draft (your typing is preserved). The empty-fields case still replaces directly as before — no extra step when there's nothing to lose.
Surface keybindings
For the navigation model (chords, back, palette, search, help, breadcrumb, themes, context-routed [/] and ←/→), see TUI Navigation. The keys below are specific to the surfaces coco ui exposes — they're available alongside the global navigation set.
Generic (every view)
| Key | Action |
|---|---|
j / k, arrows | Move selection in the active list |
PageUp / PageDown | Scroll history / detail / diff |
G | Jump to last visible commit |
n / N | Next or previous visible search result |
[ / ] | Previous or next sidebar tab (sidebar focused) / inspector tab (inspector focused, short terminals) / diff hunk or file (diff view) |
← / → | Switch sidebar tabs (sidebar focused) |
1–5 | Jump to sidebar tab |
Enter | Open the diff for the selected commit (history) or file (status), checkout (branches), open diff (stash), drill in (sidebar tab) |
Space | Stage/unstage the selected file or hunk (status/diff) |
z | Revert the selected file or hunk after confirmation |
y / Y | Yank cursored identifier (commit hash, branch, tag, stash ref, file path) to the system clipboard |
O | Open the cursored entity in the browser (commit on history; PR / repo elsewhere) |
r | Refresh repository context |
History view
History view actions all route through y-confirm or a mode prompt — none fire silently from the keystroke.
| Key | Action |
|---|---|
c | Cherry-pick the cursored commit |
R | Revert the cursored commit (git revert --no-edit) |
Z | Reset to the cursored commit. Opens a soft / mixed / hard mode prompt; recovery instructions surface in the status line for --hard. |
i | Start an interactive rebase from the cursored commit's parent (git rebase -i <sha>^). Lowercase i so the global I ai-commit-summary stays reachable. |
B | Create a branch rooted at the cursored commit (git switch -c <name> <sha>). Prompts for the name and checks out the new branch. |
gT | Create a lightweight tag at the cursored commit (git tag <name> <sha>). |
C | Create a pull request for the current branch. Generates the PR title + body via coco changelog against the default branch, opens a multi-line review prompt pre-filled with the seeded content (Ctrl+D submits, Esc cancels). Auto-detects the head + base branches; surfaces a pointer if a PR is already open for the branch. |
L | Generate a changelog for the current branch in a full-screen view. Per-branch cache so re-entry is instant; r regenerates, y yanks to clipboard, E opens in $EDITOR, c kicks off create-PR seeded with this content, < / Esc exits. |
After a successful split-apply or single commit, the freshly created commits get a ▎ accent marker next to their short hash for ~5 seconds so you can see at a glance which rows the operation just created.
Diff view
| Key | Action |
|---|---|
d | Toggle between unified and side-by-side rendering. Persists per-repo. Falls back to unified on terminals narrower than 120 cols. |
Space | Stage/unstage the cursored hunk (worktree diff). |
z | Revert the cursored hunk (worktree diff). |
c | Cherry-pick the cursored file from a commit-diff or stash-diff explore. |
H | Apply the cursored hunk to the worktree (git apply). |
gH | Apply the cursored hunk to the index (git apply --cached). |
[ / ] | Jump to previous / next hunk (or file in stash-diff). |
Pull-request view
Reachable via g p. Each action routes through a confirmation gate or an input prompt.
| Key | Action |
|---|---|
m | Merge the PR. Prompts for merge / squash / rebase, then y-confirms. |
x | Close the PR (without merging). |
a | Approve the PR. |
R | Request changes. Opens a multi-line review-body prompt (Enter inserts a newline; Ctrl+D submits). |
c | Comment on the PR. Multi-line prompt as above. |
O | Open the PR in the browser. |
Sidebar (focused)
When the sidebar is focused (via tab or by opening it directly), navigation runs on two axes plus an "escape upward" to the active tab's title:
1 ┌─ Status (3) ──┐
2 │ Branches (12)│ ←/→ between tab titles
3 │ Tags (4) │ (header focus carries
4 │ Stashes (1) │ across switches)
5 └─ Worktrees (2)┘
6 ↑↓
7 ──────────────
8 [Branches (12)] ← Header is its own cursor target.
9 ↑ from item index 0 lands here.
10 Enter drills to the dedicated view (`g b`).
11 ──────────────
12 * main ← Items list. ↑/↓ navigates;
13 feat/x Enter performs per-entity action;
14 feat/y ↓ from header re-enters here.| Key | Action |
|---|---|
← / → | Switch between sidebar tabs (Status / Branches / Tags / Stashes / Worktrees). Header focus carries across switches. |
↑ / ↓, j / k | Navigate items within the active tab's list. ↑ at index 0 promotes the cursor onto the active tab's header. |
Enter (on items) | Per-entity primary action — checkout for branches, open diff for stashes, drill into the dedicated view for tabs without a primary action. |
Enter (on header) | Drill into the dedicated view for the active tab (g b / g t / g z / g w / g s) regardless of items. |
D / R / u / +P / F | Branches: delete / rename / set-upstream / push / fetch |
a / p / X | Stashes: apply / pop / drop |
T / P | Tags: delete / push |
W | Worktrees: remove |
The cursor only ever appears in one place. When the header is focused, the items render without their selection highlight; when an item is focused, the header renders as the normal active title. The per-entity ops fire via the same dispatch as the dedicated views, so muscle memory carries across.
Inspector (focused)
When focused (via tab), the inspector expands to ~40% of width. Two tabs share the panel: [Inspector] shows commit metadata and the changed-file list; [Actions] shows the per-entity action cheat-sheet and is itself a cursor-navigable list.
| Key | Action |
|---|---|
[ / ] | Toggle between [Inspector] and [Actions] tabs |
↑ / ↓ (Inspector tab) | Move the file-list cursor |
↑ / ↓ (Actions tab) | Move the cursor through the action list |
Enter (Inspector tab) | Open the diff for the cursored file |
Enter (Actions tab) | Fire the cursored action (cherry-pick / revert / reset / yank / open in browser, etc.) |
The Actions tab dispatches the same events the matching keystroke would fire, so the action list stays in sync with the keyboard. The active row gets the same selection styling sidebar headers and status group headers use, so the visual cue is consistent across surfaces.
Compose
| Key | Action |
|---|---|
e | Edit commit compose fields inline (also pushes compose if you're elsewhere) |
E | Open the commit draft in $EDITOR / $VISUAL. Round-trips the current draft through a temp file so you get the full power of your editor (markdown highlighting, paste, multi-line nav). On save, the content gets re-split into summary + body. A crashed editor preserves the existing draft. Also works from status and diff views (pushes into compose first). |
c | Commit staged changes (also pushes compose first so the result lands in view) |
S | Split the staged set into multiple commits. Opens an overlay with the LLM-generated plan (y apply / r regenerate / < cancel). Each group's title respects your conventional-commits + commitlint config. See Commit Split for the full flow. |
Tab | Switch summary/body fields while editing |
Enter (in editor) | Move from summary to body, or finish body editing |
Ctrl+U (in editor) | Clear the active field |
Esc (in editor) | Leave edit mode |
I | Generate an AI commit draft from the staged changes |
After a successful operation (commit / split / etc.), the status line surfaces a directive next-step hint: "Created N commits — press gh to view them in history. 6 unstaged + 3 untracked remaining — press gs to stage, I to draft AI commit message."
Input prompts
Most prompts are single-line (Enter submits). Two are multi-line: pr-comment and pr-request-changes. In multi-line mode:
| Key | Action |
|---|---|
Enter | Insert a newline (does not submit) |
Ctrl+D | Submit |
Esc | Cancel |
Backspace | Delete the previous character (including newlines) |
Ctrl+U | Clear the entire buffer |
Workflow actions
D (delete branch), T (delete tag), X (drop stash), W (remove worktree), A (abort operation), M (AI conflict help), and C (create PR) launch workflow actions, gated behind confirmation prompts where destructive. All workflow actions are also reachable from the command palette (:).
GitKraken Migration Notes
| GitKraken concept | Coco UI equivalent | Notes |
|---|---|---|
| Commit graph | History view with \ toggle for full graph mode | Pattern junctions (├╮ / ├╯), per-lane coloring, distinct merge / HEAD glyphs (◆ / ◉) |
| File tree / changes panel | Status view (g s) | Stage with Space, revert with z, navigate hunks in diff view |
| Diff view | Dedicated diff view (g d) with d to toggle unified ↔ side-by-side | 120-col fallback to unified on narrow terminals |
| Stage file / hunk | Space in status / diff | Hunk-level apply via H (worktree) / gH (index) on commit-diff and stash-diff explores |
| Commit panel | Right-dock commit compose (g c) | Edit / commit / AI draft inline; multi-line input prompt for free-form text |
| AI commit summary | I confirmed draft action | Token cost shown before the call; cancellable from the prompt |
| PR / provider panel | Dedicated pull-request view (g p) | Header, checks table, reviews summary, body preview; m merge / x close / a approve / R request changes / c comment |
| Right-click commit menu | Inspector Actions: section + per-key bindings on history view | c cherry-pick, R revert, Z reset (mode prompt), i interactive rebase, B create branch here, gT create tag here, O open in browser |
| Branches sidebar | g b view + sidebar Branches tab | Current branch pinned at row 0; ≡ synced / ↕ diverged / ◌ no upstream / * current markers; relative timestamps |
| Stash list | g z view + sidebar Stashes tab | Enter opens diff; a apply, p pop, X drop |
| Worktrees | g w view + sidebar Worktrees tab | W remove (current worktree refused) |
Manual QA Checklist
Use this checklist when validating changes to the TUI:
- Clean repo:
coco ui - Dirty repo with staged, unstaged, and untracked files:
coco ui --view status - Long diff file: open status, press
Enter, scroll withPageDown - Hunk flow: press
j/k,Space, then refresh/status check - Revert safety: press
z, cancel withnandEsc, then confirm only on disposable changes - Commit compose: edit summary/body with
e, commit staged changes withc - AI draft: press
I, cancel once, then confirm in a test repo/provider setup - Narrow terminal: verify minimum-size messaging
- Wide terminal: verify panels remain stable
NO_COLOR=1 coco ui- Non-TTY fallback:
coco ui < /dev/null
Troubleshooting
- The UI does not open: verify the command is running in a real TTY.
- Colors look wrong: try
NO_COLOR=1 coco uior setlogTui.theme.presettomonochrome. - A commit is blocked by hooks: the commit box shows hook output. Fix or restage the affected files, then commit again.
- AI draft is not running: AI calls require explicit
Iconfirmation and a configured provider.
Plain coco log --format table and coco log --format json remain the stable interfaces for scripts.