Rove

Keybindings

Press F1 inside Rove for the live, localized keymap, including your own overrides. This page is the stable vocabulary and behavior reference.

How keys work

Two things decide what a key does:

  • Where. A key is either Rove-wide, or owned by the focused pane.
  • How. One press, or the prefix followed by a second key.

Which gives you three patterns:

PatternExampleUsed for
Bare lettern, d, rActions in the focused pane
One pressctrl+t, ctrl+wFrequent Rove-wide actions
Prefix sequencectrl+a then iEverything less frequent

Inside the embedded engine terminal, unclaimed keys go straight to the engine; Rove only reserves its explicit chords. The prefix still works there, so the command menu is reachable from every pane. Press ctrl+q to leave the terminal without opening it.

Tap the configured prefix once to open the command layer. By default, Rove opens the complete command guide and also shows shortcuts beside clickable controls already on screen, such as New task, Inbox, Kanban, Routines, Zen, Ask agent to create PR, and Settings. In Settings → Keybindings → Prefix tap, choose Complete guide only to hide the on-screen badges while keeping the guide.

Both the badges and guide read the live keymap. A rebound prefix or action changes immediately, and an unbound or currently unreachable action is not advertised. Clicking a guide row rechecks the current pane and modal scope before it runs. The setting changes only whether the pending command layer also marks controls in place. Both choices use the same prefix, second stroke, timeout, pane scope, and cancellation rules.

The prefix

The default first stroke is ctrl+a. Tap it, then press one more key within 5 seconds. A complete on-screen command map appears after a short pause and shows only actions that can run right now.

SequenceAction
ctrl+a fNew-conversation dialog, preset to "fork a child task": new managed worktree, branched off this Task's branch. Its ATTEMPTS row fans one prompt out to a round of up to 5
ctrl+a cNew-conversation dialog, preset to "continue this chat" in a new tab of the same Task directory
ctrl+a iOpen the Inbox
ctrl+a h / lMove focus left / right across panes
ctrl+a oOpen the Task directory in your editor
ctrl+a mReorder sidebar rows (scope-aware: tab / task / project)
ctrl+a nNew task
ctrl+a wClose the active split, otherwise the tab — the prefix twin of ctrl+w
ctrl+a 1 / 2 / 3Kanban / Routines / Issues
ctrl+a zToggle zen mode
ctrl+a ,Open Settings
ctrl+a p (P is the same key)Create a PR — for the active task, or for the row under the cursor while the sidebar has focus (the focus rule)
ctrl+a kPull the failing PR checks' logs into the task's engine (proposed — awaiting owner sign-off)
ctrl+a uMerge the base branch into the task's worktree (proposed — awaiting owner sign-off)
ctrl+a rRedraw the screen — erase and fully repaint. While the DAEMON OUT OF DATE banner is up it instead refreshes Rove onto the installed build: restarts the daemon and relaunches the TUI, after a confirm (proposed — awaiting owner sign-off)

ctrl+a c picks an engine first. Claude and Codex can fork their own conversations natively. Copilot and Kimi use a transcript handoff even for a same-engine continuation; a built-in source can also hand off to a different built-in or custom target. A custom source has no readable transcript, so its continuation is refused. See Engines.

ctrl+a r is the escape hatch for a screen that has gone wrong: it erases the display and repaints every cell, so leftover glyphs from a terminal that reflowed, a resize Rove did not see, or another program writing over Rove all clear in one frame. It touches nothing but pixels — no task, tab, or engine state changes — so it is always safe to press. Rove already repaints itself after a resize, so reach for this only when something slipped past that.

The same stroke changes meaning while the two builds differ (the amber DAEMON OUT OF DATE banner): then ctrl+a r refreshes Rove onto the installed build — it stops the daemon and relaunches the TUI, after one confirm — and the redraw is not bound. Engine sessions live in the PTY host and survive the relaunch; the tabs reattach to the same sessions. On a current Rove the refresh is absent from the keymap and the command guide, and r redraws.

The sequence cancels on timeout, esc, an invalid second key, or a change of focus or dialog.

Hold ctrl to reveal follow-up keys

Hold either Ctrl key for at least 400 ms to open a guide to the keys you can press next. The guide includes each available direct ctrl+<key> shortcut and the current prefix entry. Each row omits the ctrl+ prefix because Ctrl is already held. Bare keys, F-row keys, multi-modifier chords, and individual prefix commands stay out of this guide. The current Binding Stack determines the rows, so changing focus or opening a page or dialog narrows the list to the shortcuts that work there.

The guide also opens while an embedded engine terminal has focus. It does not take ownership of the next key. Releasing Ctrl or pressing any other key closes the guide, and that other key continues through normal shortcut or terminal dispatch.

This guide needs a terminal that reports modifier press and release events through the kitty keyboard protocol. Unsupported terminals ignore Rove's request, so the guide stays unavailable and existing typing and shortcuts keep their old behavior. See Troubleshooting for terminal support.

One-press keys

KeyAction
F1The live keymap; works from every pane, including inside the terminal (not while a dialog or a full-page view — Settings / Worktrees / Update — is open)
ctrl+qFocus the sidebar; pressed again there, quit immediately (q in the sidebar quits with a confirm)
ctrl+tNew engine tab
ctrl+eNew-conversation dialog with the engine/shell picker; inside it, ←/→ (or h/l) pick the engine and enter confirms, tab cycles the destination (new tab here → fork a child task → new Scratch task in $HOME) and ctrl+f the context (fresh ⇄ continue this chat). A Scratch destination starts the picked engine, or a bare shell when shell is picked
ctrl+wClose the active split, otherwise the tab — closing the last tab leaves the task open with no session
enterReopen a session in a task whose tabs are all closed (only while that empty pane is showing; ctrl+e does the same there)
ctrl+[ / ctrl+]Previous / next tab (ctrl+[ needs kitty — see below)
ctrl+1 … ctrl+9Tab N of the current task — the number the folded rail prints on it (needs kitty — see below)
ctrl+\Split right
ctrl+=Split down
F2Rename the active split, otherwise the tab
F3Focus the next split
F4Cycle focus forward
F5Confirm and reset the active terminal
F7Jump to the next Inbox item across all projects

Overlap resolves by context: ctrl+w closes the innermost split when a tab is split, otherwise the tab. F2 follows the same rule. enter is bound only by the "no sessions here" pane, which has no input and no tab of its own — everywhere else in the workspace it reaches the terminal as usual.

New task is ctrl+a n from anywhere (the sidebar also answers to bare n). It is a prefix sequence, not a direct ctrl+n, so the embedded engine and shell terminals keep ctrl+n for readline/emacs next-history.

Both split chords need a terminal speaking the kitty keyboard protocol (legacy terminals can't encode ctrl+=, and ctrl+\ would be SIGQUIT); reserving ctrl+\ also costs the embedded shell its SIGQUIT. If a split chord does nothing, rove doctor says whether your terminal answers the protocol query — that is the first thing to check.

ctrl+[ needs kitty for the same reason. Without the protocol it is not a chord at all: it sends the same byte as Escape, so the terminal cannot tell the two apart and Rove reads it as Escape — cycling backwards is simply unavailable there, with no second binding to fall back on. ctrl+] has its own byte and works everywhere. The consolation is that the ambiguity resolves in favour of the engine: ctrl+[ keeps working as Escape inside the embedded terminal, which is what vim and every CLI's cancel key need.

ctrl+<digit> needs kitty too. Legacy terminals send nothing for ctrl+1 and ctrl+9, and ctrl+2…ctrl+8 arrive as control bytes that read as other keys (ctrl+3 is Escape, ctrl+8 is Backspace), so those keep their old meaning there and tab N is reached with ctrl+] instead.

Horizontal keys: h / l

Wherever ← / → move you sideways — a dialog's engine picker, effort or status row, the new-task selector fields, the automation schedule segments, the Kanban board's card cursor — h and l do the same thing, vim-style. They are always an alternative, never a replacement: the arrow keys keep working.

h/l are live only where ←/→ already are, and only while nothing is being typed. A focused text field, the composer, or the sidebar's / search keeps h and l as ordinary letters. The one deliberate exception is the sidebar itself: right focuses the engine pane, but l there already means "open the row under the cursor", so the sidebar keeps right alone. In the Files pane h/l already fold and unfold the tree, which is the same shape.

Bare letters work only while that pane has focus and no dialog or text input is active.

Sidebar

KeyActionKeyAction
nNew taskrRename
enterOpenbRename branch
l / spaceOpen the row under the cursorvChange engine
oOpen Task directory in your editorsSettings
uUpdate page — version check + release notes/Search
dDelete Task / forget projectxWorktrees page
gg / shift+gTop / bottom
shift+pPin / unpin managed TaskrightFocus the current engine pane
shift+mEnter reorder mode (scope-aware: tab / task / project)tCycle task sort (default → recent → attention → name)

Sidebar row verbs (b, v, o, d, r, shift+p, shift+m) act on the row under the cursor — the highlight j/k moves, which may differ from the active task until you press enter. Prefix chords (ctrl+a o, ctrl+a p) are global and act on the active task, except while the sidebar has focus — both of them then follow the row under the cursor.

In reorder mode, j/k moves the highlighted project and enter or esc finishes. Project headings themselves aren't cursor rows; the move routes through a Task row in that project.

Files

KeyAction
j / k (or arrows)Move
h / l (or ←/→)Collapse / expand
enterOpen in your configured editor; changed files use a Vim/Nvim diff when available, otherwise Rove falls back to its read-only preview
dOpen a read-only diff in a workspace tab without moving focus. On a directory row it opens everything under it as one combined diff
shift+DOpen the whole worktree's diff in one tab (proposed, awaiting sign-off — the Changes tab's [D] diff everything chip does the same with no chord)
rRefresh the current file tab
bOn Changes, switch working-tree changes ⇄ branch vs base
oOpen audio, video, or PDF files in the system application
aInsert an @path mention into the engine pane
[ / ]Switch file tabs

h / l fold directories on both tabs — on Changes they also expand and collapse untracked-directory rows.

The sidebar is a tree (project → Task → Terminal Tab) and it never folds, so everything is always visible. Search (/) matches titles, repos, branches, and live tab titles, and keeps matching rows' parents so a hit is never orphaned.

Right-click any row for its context menu; j/k and ⏎ drive it, and a press anywhere else, or esc, dismisses it. Common row actions also have direct chords. A Task or tab row also offers New conversation (the ctrl+e engine/shell picker) and New shell (a bare shell tab) for that worktree, both enter the Task first, exactly as pressing the chord there would. These entries have no chord of their own. Set status opens a picker over the six Task statuses and writes the one you choose. Copy branch name and Copy path put the Task's branch or recorded worktree path on the system clipboard (local clipboard command plus OSC 52, so it also works over SSH); copying never creates the worktree, and a project-main or directory row, whose stored branch is empty, offers only Copy path. Run again re-fires the Task's stored brief as a new Task, and appears only on a row that recorded one. Land into base branch runs the same land the Worktrees page's l does, and appears only on a managed Task row that has a branch — a project-main or directory row owns no Rove branch to land. On a project header, Field notes reads the repo's durable notes (rove api note). Open in editor, Rename branch, and Change engine are the o, b, and v chords for the row you clicked; the engine entry opens a picker over your available engines instead of cycling, and Rename branch follows the same empty-branch rule as Copy branch name. (If right-click opens your terminal's menu instead, see Troubleshooting.)

Terminal scrollback

These chords are trapped by Rove while the embedded terminal has focus; the engine or shell does not receive them.

KeyAction
ctrl+pageupScroll one page up through buffered terminal output
ctrl+pagedownScroll one page down; reaching the bottom resumes following live output

The mouse wheel uses the same scrollback. Buffer size is configured in Settings → General → Terminal and applies to newly opened terminals.

While scrolled back, click first beside the line count to jump to the earliest retained line, or latest to return to the bottom and follow new output. These buttons do not send input to the engine or shell.

Copying from it

Drag with the mouse to select, and the selection is copied when you release. The chords below copy the same selection without the mouse.

KeyAction
ctrl+cCopy when text is selected; interrupt the engine or shell when nothing is
cmd+cCopy the selection (macOS)
ctrl+shift+cCopy the selection

ctrl+c is selection-aware on every platform. Rove draws the selection itself, so no terminal emulator knows it is there to claim the chord first — without this, ctrl+c interrupted the engine while text sat highlighted. Copying clears the selection, so the next ctrl+c is an interrupt again, and ctrl+c with nothing selected is always an interrupt.

cmd+c and ctrl+shift+c only reach Rove on terminals that forward them. Most emulators keep cmd+c for their own copy, which is equivalent; kitty, Ghostty and iTerm2 can be configured to pass it through. ctrl+shift+c requires a terminal that speaks the kitty keyboard protocol — without it a terminal sends ctrl+shift+c and ctrl+c as the same byte, and the selection-aware ctrl+c above is what answers.

Searching it

ctrl+a / opens a query row in the pane footer. Typing filters as you go, and each new query parks on the newest occurrence — a scrollback is read backwards, so the hit you want is nearly always the last one.

Pasting while the query row is focused adds to the query; it does not send input to the terminal child. Line breaks in pasted queries become spaces. Switching terminal sessions clears the query, selection and local scroll position. The session's process and buffered output stay available when you return. Resizing the same session keeps its query open.

KeyAction
ctrl+a /Open the query row
return or upWalk to the older match (wraps at the top)
downWalk to the newer match
escapeClose the row and return to the scroll position you opened it at

Every hit on screen is highlighted; the one you are parked on is painted in the accent colour. The search covers Rove's own scrollback only — while a full-screen app (an engine, vim, less) is on the alternate screen it owns its buffer, and the row says so instead of searching one screen of somebody else's redraw.

Inbox

ctrl+a i opens it. What the sections mean and how items clear: The TUI → Inbox.

KeyAction
j / k (or arrows)Select
enterOpen the target task and its exact tab when present, then clear the item
dClear an ATTENTION item without navigating
escClose

Diff review

In the read-only diff tab, with the workspace focused:

KeyAction
j / k (or arrows)Move the line cursor
vAnchor a range (v again cancels)
cWrite a note
xDrop the note the cursor sits inside
sSend all unsent notes to the engine
rReload the diff from disk

These are fixed and can't be rebound. A send with no engine session in the Task leaves the notes unsent and says so. The workflow: The TUI → Diff review.

Workspace pages

All of these pages close with q, esc, or ctrl+c. Their bare letters are active only while the page has focus.

PageKeys
Kanbanarrows (or h/l) move between cards; tab changes project; enter opens details; n creates; d deletes; r refreshes
Routinesj/k select; n creates; e pauses/resumes; s runs now; d deletes; r refreshes; enter opens the latest run's Task
GitHub Issuesj/k select; tab changes repo; a toggles "assigned to me"; r refreshes; enter starts a Task
Worktreesj/k or arrows select; l lands; d starts removal; see Managing worktrees
Updatej/k selects an action; u updates; r opens the release page; enter runs the selected action

In the Kanban story drawer, tab / shift+tab walks fields and ctrl+enter activates its primary action: start an unlinked story or open the linked Task. While creating a story, ctrl+s saves without starting and esc cancels. In an existing story, esc saves edits and closes, while ctrl+c discards them.

Customizing

Edit ~/.rove/settings/keybindings.yaml. Changes reload live, no restart.

prefix:
  key: ctrl+a          # or a list: [ctrl+a, ctrl+s]; null disables prefix bindings
  timeoutMs: 5000
  bindings:
    chat.fork.new: f

bindings:
  chat.tab.new: ctrl+t
  chat.tab.chooseEngine: ctrl+e
  sidebar.select: [enter]
  files.createPR: null   # null or [] unbinds

darwin:                  # platform overlays win per chord
  bindings:
    files.openExternal: cmd+o
  • A direct override replaces that binding's whole chord list.
  • prefix.key may list several first strokes. Each opens the same command layer; hints and the guide print the first one.
  • Prefix overrides set second-stroke keys and keep the binding's pane scope.
  • Uppercase is a distinct chord: shift+p (or just P) can be bound apart from p. Shift with another modifier on a letter (ctrl+shift+p) is invalid; legacy terminals send the same byte either way.
  • Unknown ids and invalid entries are ignored with a warning in Settings → Keybindings. A typo never breaks the default keymap.

Plugin chords

Rove ships none; every plugin chord is your own choice:

plugins:
  ctrl+g: pane:examples.lazygit.git
  f6: action:examples.notify.test

See design/plugins.md.


Why each chord sits where it does, and which ones are still open questions: design/keybinding-decisions.md.

On this page