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

bash
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 ui

If 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] Actions layout 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+Tab cycles which pane is visible (sidebar → main → inspector) and the footer prepends a tab: [sidebar] main inspector switcher. Press v to momentarily peek the sidebar from the main/inspector pane — v or Esc snaps 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.

ViewChordStart flagPurpose
Historyg h--view historyBrowse commits, refs, changed files, and commit metadata.
Statusg s--view statusSelect changed files, stage/unstage, revert with confirmation.
Diffg d--view diffInspect the selected commit or worktree file diff with hunk navigation and staging. Toggle unified ↔ side-by-side with d.
Composeg cFull-screen commit draft editor with summary/body cursors, AI draft, and hook feedback.
Branchesg bList local branches with divergence info; checkout/delete/create-PR via workflow keys. Current branch pinned at row 0.
Tagsg tList tags.
Stashg zStash 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.)
Worktreesg wList linked worktrees with current/dirty markers; remove via W.
Pull requestg pDedicated 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 triageg PMulti-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.
Issuesg iIssue triage list with filter cycling and per-row actions: comment, label, assign, close, reopen. See Issue & PR Triage.
Conflictsg xConflict resolution helper, available during merge / rebase / cherry-pick / revert. Per-row keys s stage / u theirs / U ours / o edit / C continue.
Reflogg rChronological 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.
Bisectg BBisect 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.
Submodulesg MRegistered 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.
ChangelogLFull-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, press Enter. 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:

  1. From branches, tags, or history, press m on 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 show enter compare · m clear.
  2. Navigate to a second ref on any of those three views.
  3. Press Enter to push the diff view in compare mode (git diff <base>..<head>).
  4. < or Esc pops 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.

text
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 back

Selection 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 / k or 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.
  • Enter on a file opens the diff. Enter on a group header fires the batch action: Unstage all staged files, Stage all unstaged files, or Stage all untracked files (the last requires y-confirm).
  • Space stages or unstages the selected file.
  • z asks to revert the selected file. Press y to confirm or n/Esc to cancel.
  • 1 / 2 / 3 toggle the staged / unstaged / untracked visibility mask.

In diff view:

  • j / k move between hunks.
  • PageUp / PageDown scroll the diff.
  • Space stages or unstages the selected hunk.
  • z asks to revert the selected hunk. Press y to confirm or n/Esc to cancel.
  • [ / ] jumps between hunks (or files in stash diffs).
  • Esc or < 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.

KeyAction
eEdit the commit summary/body fields.
TabSwitch fields while editing.
EnterMove from summary to body, or finish body editing.
Ctrl+UClear the active field while editing.
EscLeave edit mode.
cCommit staged changes with the current draft.
IAsk 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.
RAccept 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.

json
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 R to replace your text, or Esc to 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)

KeyAction
j / k, arrowsMove selection in the active list
PageUp / PageDownScroll history / detail / diff
GJump to last visible commit
n / NNext 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)
15Jump to sidebar tab
EnterOpen the diff for the selected commit (history) or file (status), checkout (branches), open diff (stash), drill in (sidebar tab)
SpaceStage/unstage the selected file or hunk (status/diff)
zRevert the selected file or hunk after confirmation
y / YYank cursored identifier (commit hash, branch, tag, stash ref, file path) to the system clipboard
OOpen the cursored entity in the browser (commit on history; PR / repo elsewhere)
rRefresh repository context

History view

History view actions all route through y-confirm or a mode prompt — none fire silently from the keystroke.

KeyAction
cCherry-pick the cursored commit
RRevert the cursored commit (git revert --no-edit)
ZReset to the cursored commit. Opens a soft / mixed / hard mode prompt; recovery instructions surface in the status line for --hard.
iStart an interactive rebase from the cursored commit's parent (git rebase -i <sha>^). Lowercase i so the global I ai-commit-summary stays reachable.
BCreate a branch rooted at the cursored commit (git switch -c <name> <sha>). Prompts for the name and checks out the new branch.
gTCreate a lightweight tag at the cursored commit (git tag <name> <sha>).
CCreate 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.
LGenerate 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

KeyAction
dToggle between unified and side-by-side rendering. Persists per-repo. Falls back to unified on terminals narrower than 120 cols.
SpaceStage/unstage the cursored hunk (worktree diff).
zRevert the cursored hunk (worktree diff).
cCherry-pick the cursored file from a commit-diff or stash-diff explore.
HApply the cursored hunk to the worktree (git apply).
gHApply 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.

KeyAction
mMerge the PR. Prompts for merge / squash / rebase, then y-confirms.
xClose the PR (without merging).
aApprove the PR.
RRequest changes. Opens a multi-line review-body prompt (Enter inserts a newline; Ctrl+D submits).
cComment on the PR. Multi-line prompt as above.
OOpen 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:

text
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.
KeyAction
/ Switch between sidebar tabs (Status / Branches / Tags / Stashes / Worktrees). Header focus carries across switches.
/ , j / kNavigate 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 / FBranches: delete / rename / set-upstream / push / fetch
a / p / XStashes: apply / pop / drop
T / PTags: delete / push
WWorktrees: 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.

KeyAction
[ / ]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

KeyAction
eEdit commit compose fields inline (also pushes compose if you're elsewhere)
EOpen 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).
cCommit staged changes (also pushes compose first so the result lands in view)
SSplit 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.
TabSwitch 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
IGenerate 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:

KeyAction
EnterInsert a newline (does not submit)
Ctrl+DSubmit
EscCancel
BackspaceDelete the previous character (including newlines)
Ctrl+UClear 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 conceptCoco UI equivalentNotes
Commit graphHistory view with \ toggle for full graph modePattern junctions (├╮ / ├╯), per-lane coloring, distinct merge / HEAD glyphs ( / )
File tree / changes panelStatus view (g s)Stage with Space, revert with z, navigate hunks in diff view
Diff viewDedicated diff view (g d) with d to toggle unified ↔ side-by-side120-col fallback to unified on narrow terminals
Stage file / hunkSpace in status / diffHunk-level apply via H (worktree) / gH (index) on commit-diff and stash-diff explores
Commit panelRight-dock commit compose (g c)Edit / commit / AI draft inline; multi-line input prompt for free-form text
AI commit summaryI confirmed draft actionToken cost shown before the call; cancellable from the prompt
PR / provider panelDedicated 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 menuInspector Actions: section + per-key bindings on history viewc cherry-pick, R revert, Z reset (mode prompt), i interactive rebase, B create branch here, gT create tag here, O open in browser
Branches sidebarg b view + sidebar Branches tabCurrent branch pinned at row 0; synced / diverged / no upstream / * current markers; relative timestamps
Stash listg z view + sidebar Stashes tabEnter opens diff; a apply, p pop, X drop
Worktreesg w view + sidebar Worktrees tabW 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 with PageDown
  • Hunk flow: press j/k, Space, then refresh/status check
  • Revert safety: press z, cancel with n and Esc, then confirm only on disposable changes
  • Commit compose: edit summary/body with e, commit staged changes with c
  • 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 ui or set logTui.theme.preset to monochrome.
  • 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 I confirmation and a configured provider.

Plain coco log --format table and coco log --format json remain the stable interfaces for scripts.