Rove

The TUI

A tour of the complete user-facing interface: the three panes, tasks and sessions, files and review, Settings and the Inbox, full-window pages, updates, attachments, and narrow terminals.

This page explains what the features are for. The key tables live in Keybindings; the mental model behind tasks and sessions lives in Concepts.

Workspace, focus, and mouse

The normal workspace has three columns:

  • Tasks is a tree of project → Task/worktree → terminal tab. Every level is always expanded. Selecting a tab opens that exact session.
  • Workspace shows the active engine, shell, plugin, or read-only file tab. A Task can have several tabs, and a terminal tab can contain several splits.
  • Files has All and Changes views for the selected Task's worktree.

Click a pane or row to focus it. F4 moves focus forward, ctrl+a h / l moves left or right, and ctrl+q returns from the workspace to Tasks. From the Tasks pane, right arrow enters the current engine tab. Mouse clicks select rows and tabs; right-clicking a sidebar row opens the same common actions available from the keyboard, including New conversation and New shell for that Task's worktree, plus Copy task ID, Copy branch name and Copy path, which put the Task's id, branch or worktree path on the system clipboard for a rove api call, a git checkout or a cd in another shell, and Open in editor, Rename branch, and Change engine for the row you clicked. Right-clicking a project header offers New task, Field notes (the notes agents filed on that repo with rove api note, newest first, each with its author and time — ↑↓ walks them and d retires the one under the cursor, which is how a note whose fact has stopped being true stops being injected into new sessions) and Remove project. Clicking anywhere else dismisses that menu.

The Tasks rail sizes itself from the terminal — wider terminals get a wider rail, so branch names stop truncating. Drag its right edge to set the width yourself; the edge lights up when the cursor is on it, and double-clicking it goes back to the width Rove picks. A width you drag to is remembered across restarts, and it is an override rather than a replacement: a terminal too narrow to honour it squeezes the rail down to what fits and pays the full width back when the window grows again. The fold has no draggable edge — its fixed width is the point of folding. Each folded project section starts with its first letter and a rule, such as r── or w──; scratch tasks share an s── section. The fold shows the same projects the expanded rail shows and hides the same ones — a project you have closed down to nothing is absent from both, and so are the routine sessions the expanded rail folds behind its count row — and a section's letter comes from the header the expanded rail prints, so two repos whose folders share a name (work/api, oss/api) fold to w── and o── rather than to a── twice.

Under a section, a task with open tabs folds to one cell per tab: the tab's number, coloured by that tab's own state, so a second chat that finishes or waits shows up folded. The numbering starts again at 1 for the next task, and the number is the key that reaches it: ctrl+1…ctrl+9 switch to tab N of the current task, with a · past the ninth. The hairline fold has no room for a digit and keeps one cell per task.

Zen mode (ctrl+a z) hides Files and lets the workspace use the freed width. The Tasks rail remains visible. Below 70 columns, the separate narrow-terminal layout takes over instead.

With no tasks at all (a first launch, or all tasks deleted), the workspace column shows a welcome panel instead of an empty pane: the keys to create a task and open help (read from your live keymap, so rebinds show correctly), and one line about the engines. That line reads one of three ways, because "installed" and "usable" are different questions: the engines that can run a task (installed and signed in), or — when none can — the ones that are installed but whose login Rove cannot find, or, failing that, none at all. It is the same probeEngines check rove doctor runs, so the two surfaces cannot reach opposite verdicts. When something is missing (no engine CLI, no git) the panel also says what to install, with rove doctor as the full diagnosis. Creating your first task replaces it with the normal workspace.

Status glyphs in the sidebar

Task rows carry worktree-level facts:

MarkMeaning
▴Pinned Task
+N / −NChanged and deleted files in the worktree
↑N / ↓NCommits this worktree has that its base does not, and the ones the base has that it does not
≠ / ✗ / ✓The pull request conflicts with its base, has failing checks, or has passing checks. One mark: a conflict outranks a check result

↑N is the one mark that outlives a commit: committing empties +N / −N, so without it a worker that committed its work and one that reported success and delivered nothing render the same blank row — a difference you would otherwise meet at land time, as EMPTY_BRANCH. ↑N and ↓N are absent rather than zero when no base branch resolves, so a repo with no remote reads as it always did.

The PR mark drains to grey when the last PR poll failed — the reading stands, but nothing is confirming it any more. Pending checks, review state, and a merged or closed PR draw nothing. The Task's board status (in_review, done, …) does not appear on the row either: you set it, so you already know it. Set it from the row's right-click menu (Set status) or with rove api update --status; it is a label, and changing it leaves the worktree, the branch, and every running session alone.

Whose turn is it

Rove derives one more fact per Task — does this need me — and groups every Task by it:

GroupYour move
needs you — a permission prompt, a quota wall nothing will clear on its own, a settled error, an engine that died having delivered nothing, a failed worktree deletionanswer it
ready to land — the pull request is open and approvedmerge it
needs review — a worker filed a report, or a turn finished, and nobody has acted on itread the diff
working — an engine is producing output, or Rove will resume it when its quota window rollsnothing
quiet, or nothing has reportednothing

This group is derived, not declared. A task's board status is something a person or a worker typed; a worker that crashed leaves it reading in_progress forever. The group instead comes from what has owners: the worker's report, the pull request as Rove last polled it, the engine's arbitrated activity, and whether its session is still alive. Which is why two of these are invisible to any tab's engine state — a task whose worker reported and went quiet, and one whose PR was approved an hour ago.

It reaches you through the attention sort and the Kanban card badge, not as a glyph on the sidebar row: the rail's glyph column already carries per-tab engine state, and a second vocabulary in the same column turns the rail into a legend.

Two debounces keep the top group honest: an errored turn must stand 20 seconds (an engine that fails and retries itself would otherwise summon you into the gap), and a dead session 60 seconds (one sweep of the check that can see an engine die inside a live terminal). A permission prompt and a quota wall are marked at once — nothing but you clears either.

Sort the task list by this ordering with the sidebar's attention sort (rove api context --repo . --text prints the same ranking in a shell): the top of the list is what needs you next. rove api context also reports the group for scripts and agents — see the API reference.

A plugin may add its own short label to the right-hand cluster, with a deadline: it fades when the plugin stops refreshing it, so a plugin that dies cannot leave stale words on your rows. See Plugin authoring.

Session state belongs to the engine tab that runs it, so the state glyph sits on the tab rows underneath. There are four states, and only one asks anything of you:

GlyphMeaning
spinnerEngine is working (also shown while a worktree materializes or deletes)
!Needs you: a permission prompt, a rate limit, an errored turn, a dead engine process, or a failed worktree deletion. Open the tab to see which
●Turn finished, and you haven't looked yet
○Quiet: idle, not yet observed, a finished turn you've already seen, a shell tab, or a custom engine without activity tracking

A tab labelled ⚠ <name> is a live hosted session that was missing from the saved tab list. Rove exposes it instead of hiding a running process and adopts it back into the Task's tab state when possible.

Seen means consumed. A ● clears the moment you actually open that tab, select the task, with that tab active. Moving the sidebar cursor over the row doesn't count. Once seen, the badge drops back to ○; there is no lingering checkmark. Rove saves the completion timestamp per task and tab, so restarting or reattaching does not relight a completion you already read. A later completion has a later timestamp and appears unread as usual.

Each tab row reports its own activity, not the task's roll-up. Tab 2 can spin while tab 1 rests. The tab strip at the top of the workspace and the attention inbox keep a finer vocabulary (◷ rate limited, † exited, ? needs input, ! error) over the same saved timestamps, because there the glyph sits next to a word that explains it; a ✓ you have already read settles back to ○, and stays settled after a restart.

Managing Tasks in the sidebar

Press / to fuzzy-search task titles, repositories, branches, and live tab titles. Parent rows stay visible around a match. Arrow keys move through the results, enter opens one, and esc restores the previous selection.

The selected Task answers these actions whether the cursor is on its worktree row or one of its tab rows:

  • r changes the Task title. b opens a filterable local-branch picker and can also rename the current branch by accepting a new name. Project-main rows do not rename the repository's checked-out branch.
  • v cycles through detected and custom engines. The new engine applies when the Task's engine session is reopened; it does not replace a running turn.
  • o opens the Task directory in the configured GUI/workspace editor.
  • shift+p pins or unpins a managed Task. shift+m, followed by j/k, reorders the row under the cursor at its own level: a tab moves within its Task, a Task moves within its repo group, and a project-main row moves the whole project. Moves stop at the edges (no wrap-around), and the new order persists across restarts.
  • d is kind-aware: it forgets a project-main row, removes only the Rove record for a directory Task, or removes a managed Task and its worktree after the dirty-worktree safety check.

Right-clicking a row opens the same verbs as a menu, plus Run again on any Task that still has its brief. Rove stores the prompt a Task was created with, so the entry re-runs those exact words in a NEW Task with its own branch and worktree, leaving the original untouched. The confirm shows the brief in full before anything is created. A Task created without a prompt has no brief to re-run, and the entry does not appear for it.

The confirmation dialogs state the exact deletion boundary before anything is changed. See Concepts → Task for the three Task kinds and Sessions for session teardown.

Inbox

ctrl+a i opens it. The Inbox answers two questions, what needs me? and where was I?, with one section for each:

  • ATTENTION. Pending items, blocked ones first. An item appears when a turn completes, a session asks for input, hits a rate limit, errors, or when the engine process exits. Everything that is stopped until you act — a permission prompt, a rate limit, an error, a dead engine, a failing routine — sorts ahead of plain finished turns, and within each of those two groups the oldest is first. A completed turn is the only item nobody is waiting on, so it never queues in front of an agent that cannot move. Most items target one task-and-tab; events without a tab identity target the whole task instead. A newer event for the same target replaces the older one, and starting a new turn clears it. A rate-limited item also names when its automatic resume is due (resumes 3:14 PM), so you can tell a wait from a dead end.
  • RECENT. The last handful of tabs you visited, most recent first. These aren't pending work, just jump targets; a spinner marks the ones still running.

enter opens the task and, when the episode names one, its exact tab; a task-level episode leaves that task's current tab active. It also clears the item. d clears without navigating (ATTENTION rows only; RECENT rows have nothing to drop). You rarely need d: visiting a target clears its item anyway, since visiting any tab resolves a task-level episode, and stale items whose tab or task is gone get cleaned up in the background.

F7 jumps straight to the first pending item across all projects — the oldest blocked one, or the oldest finished turn when nothing is blocked — without opening the Inbox, and cycles on repeated presses. It works even while you're typing inside an engine session. With nothing pending it just says so.

Diff review

The files pane shows what changed; diff review lets you respond. Press d on a file to open its read-only diff, then:

  1. j / k move the line cursor.
  2. v anchors a range. Move to the other end with j/k; v again cancels. Skip this for a single-line note.
  3. c writes a note for the current line or range. x drops the note the cursor sits inside — the way out of a typo that isn't sending it.
  4. s sends all unsent notes, across all files of the task, to the engine as one prompt, and submits it.

The prompt the engine receives is just file, line numbers, and your words, no code excerpt. The engine reads the worktree itself. Notes are stored per task and survive restarts; the footer counts notes · unsent so you always know what's pending. Sending doesn't switch tabs, so keep reviewing while the engine works, and r reloads the diff when the engine has changed the file under you.

A Task with no engine session has nowhere to send to — ctrl+w on the last tab leaves one in exactly that state. s then leaves every note unsent and says so, rather than reporting a delivery that did not happen.

Notes anchor to the file path and the line number displayed at the time you wrote them; they don't re-anchor when the diff changes underneath. These keys are fixed and not rebindable.

Files pane

All shows the worktree's tracked and unignored files as a navigable tree. Changes starts with uncommitted changes against HEAD. If the working tree is clean and Rove can resolve a base branch, it automatically switches to the whole branch-versus-base view so committed agent work does not disappear. Press b to choose the scope manually; the header always names the active scope.

The base is the task's PR base when it has one, then origin/HEAD, origin/main, or origin/master, then a local main or master — so a repo with no remote still gets a branch view instead of an empty pane beside a sidebar row reporting commits. When none of those resolve, the scope line names that as the reason rather than leaving b as a silent no-op.

Combined diffs. d on a directory row opens everything under it as one diff in one tab, and the Changes tab's [D] diff everything chip does the same for the whole worktree — reviewing a twelve-file attempt is one keypress and one tab instead of twelve of each. A combined diff is read-only: a review note anchors to a single path, so a diff spanning files carries none, and its footer says so. Per-file notes are unchanged. A directory with nothing changed in the active scope says so rather than opening blank.

What a diff states instead of drawing nothing. A renamed file's diff shows the rename and the same +N −M as its list row, not the whole file as added. A changed binary and a mode-only change each state what changed — in the single-file view and in a combined diff's section — because a patch git wrote entirely in its preamble has no hunks to draw. Pure renames name the original path; additions and deletions of empty files state the change. These states have no review cursor or line comments. Filenames are literal, including brackets, *, ?, and leading :; selecting one cannot include another file's hunks. Directory and whole-worktree diffs still combine their files. If git itself refuses (a pruned remote, a renamed base branch), the pane shows git's own error and r retries; a failed read is never reported as an absence of changes. An unreadable or missing text file shows a read error and the same retry action; a valid empty file says "empty file". While a different path, worktree, or comparison base loads, the preview shows a loading state instead of the previous file's text. A late result cannot replace the current preview or reopen a closed tab.

Open a text file with enter. Rove uses the configured terminal editor; for a changed file it requests that editor's diff mode when Vim or Neovim is available, otherwise it opens Rove's read-only preview. d always opens the read-only diff in a workspace tab — for a directory row, the combined diff of everything under it — a pastes an @path mention into the active engine without submitting it, and o sends audio, video, or PDF files to the system application. Remote files cannot use a local system viewer.

The pane watches local worktrees for changes and also supports r for an explicit refresh. Set ROVE_FILETREE_WATCH=0 to turn the watcher off and leave r as the only way to repopulate the list. See Keybindings for the complete navigation table.

Create a pull request with the active agent

Choose Ask agent to create PR above Files or press ctrl+a p. This is an agent workflow, not a direct GitHub API action: Rove inspects the current branch, target branch, upstream, and dirty-file count, then submits a prompt to the active engine. The prompt asks the agent to review the diff, commit remaining changes, push the branch, and run gh pr create.

Watch the engine tab for progress, failures, or questions. The action is unavailable on the target branch and requires an active engine session. A project's main row is that repo's own checkout rather than a task branch, so the chip is not offered there; ctrl+a p still answers with the reason. A repo can replace the prompt with .rove/pr-instructions.md; see Per-repo init. Because the engine performs the work, its own skills and approval rules still apply. The default prompt expects an authenticated gh CLI and a pushable origin remote.

Creating a task

Every dialog in Rove is built from the same pieces — a bold title with esc opposite it, capitalised field labels that light up when focused, rounded wells around the inputs, chip buttons for choose-one rows, a key legend, and a bottom-right [ Action ] where one applies. On a terminal under 34 rows those borders drop away so the action button is never pushed off the bottom. docs/design/dialogs.md is the rule and the components that carry it.

Focus the sidebar and press n. The New task dialog starts on a mode selector and an engine selector; tab walks every field and the bottom-right Create button — except in the two path fields (the repo, and the Clone tab's parent directory), where it first completes the highlighted suggestion in place, exactly as a shell would, and only moves on once there is nothing left to complete. ctrl+e cycles the detected engines from anywhere in the dialog. Use ctrl+[ / ctrl+] to move between its three modes, or focus the mode selector and use the left/right arrows (or h/l).

  • For Existing picks a local repository and the ref to branch from. Rove creates a new task branch and worktree, then opens it ready for the first prompt. The current repository and its checked-out branch are the defaults. Type a name to filter your saved repositories, or a path (/ or ~/) to browse directories instead; ↑/↓ moves the highlight. tab completes the highlighted row without leaving the field — a browsed directory keeps its trailing slash, so the next tab walks one level deeper — while enter takes the highlighted row and moves on to the branch field. For a repository Rove already tracks as a project, an extra opens row appears: leave it on "a new task worktree" for the behaviour above, or choose "the project itself" to open that repository's own checkout instead of branching off it. That is how you return to a project that left the sidebar after you closed its last tab — the branch field disappears, because opening a checkout forks from nothing.
  • For New Repo clones a Git URL into a chosen parent directory, derives an available folder name, then creates a task from the requested base branch. The parent-directory field browses the same way the repo field does, tab included. The parent directory is remembered for the next clone.
  • Adopt Worktree imports existing git worktrees that are not already tasks. The path-glob field filters by absolute path or basename; enter toggles the highlighted row and ctrl+a selects or clears all filtered rows. Adoption does not copy the directory or create a branch. Dirty and externally-created worktrees are allowed and labelled.

The chosen repository and engine become defaults for later task creation. Adopting several worktrees is item-by-item: successful imports remain even if another row fails, and Rove reports the result count.

Tabs and terminal splits

ctrl+t starts a fresh engine tab immediately; ctrl+e opens the full engine, shell, and plugin picker. Tabs share the Task's worktree but keep separate processes, scrollback, titles, and engine conversations. ctrl+[ / ctrl+] switch tabs, F2 renames one, and ctrl+w closes it — including the last one, which leaves the Task open with no session. Re-entering that Task from the sidebar reopens the kind of tab that was there; from the empty pane itself, enter or ctrl+e does the same.

Inside a terminal tab, ctrl+\ splits right and ctrl+= splits down. New leaves run your login shell in the same worktree. F3 cycles split focus; F2 and ctrl+w operate on the active split before falling back to the whole tab. Split layouts and custom names survive a Rove restart, but which split had focus does not. If a split process exits, its leaf disappears and the remaining layout collapses naturally.

Who owns the mouse inside a terminal decides who owns selection. At a plain shell prompt — a git log, a cat, anything that never asks for the mouse — drag to select and Rove copies the text to your system clipboard on release. Inside an app that tracks the mouse itself (Claude Code, Codex, vim, less, htop) the click goes to the app instead, so its own selection and copy work as they do in any terminal, and Rove paints nothing over them. Launching such an app clears a selection Rove was still showing. Hold shift while dragging to select out of a mouse-aware app anyway, the way iTerm2 and kitty do.

On Windows, ctrl+c copies a Rove selection and clears its highlight without interrupting the embedded app. With no Rove selection, ctrl+c reaches the app normally. A selection owned by the embedded app still uses that app's copy behavior.

The optional horizontal tab strip can be always visible, visible only for multiple tabs, or hidden. The sidebar tree still lists every tab in all three modes. Persistence and process-lifetime details live in Sessions.

Worktree audit and cleanup

Focus the sidebar and press x to open the full-window Worktrees page. It audits every non-main local worktree for saved projects, including directories created outside Rove, and shows dirty state, remote-branch state, PR/merge signals and age. l lands a tracked task branch; d starts the guarded worktree-removal flow.

Deleting a worktree is not the same as deleting a task, branch, or engine history. Dirty deletion requires a second, explicit force confirmation. Read Managing worktrees before using either mutation.

Settings

Open Settings with ctrl+a ,, or press s while the sidebar is focused. Use j/k to choose a section, l or right arrow to enter its rows, h or left arrow to return to the section list, and enter to activate a row.

  • General controls theme, light/dark mode, language, transparency, focus and split styles, notifications, keyboard hints, zen startup, editor choice, worktree location, terminal scrollback and the optional horizontal tab strip. It also shows available engine quota snapshots.
  • Engines lists every engine Rove can launch: built-ins, the contrib catalog, plugin-registered and your own, each with what local detection found under it: where its binary is, and for engines with an account detector whether you are logged in (login itself still happens in each engine's own CLI). On an engine row, space switches it on or off (off keeps its settings, it just stops being offered when picking an engine for a task), enter edits the launch command, r renames, x resets a built-in or removes a custom engine, and d makes it the default. An engine you added yourself also shows the protocol it borrows — the built-in adapter that gives it a transcript reader, account detection and resume, or generic for none. You pick it when adding the engine; to change it later, x the engine and add it again. A third line under each engine says how it reports to Rove: the hooks Rove installs into that engine's own config (and whether what is on disk is the current shape), whether the engine leaves completion markers Rove can read back, and whether it declares screen rules. A missing layer is not a fault — an engine whose hooks report every state needs no screen rules. The row at the bottom of the section installs the missing or outdated hooks for every engine at once; Rove also runs that install on every launch, so the row is for engines that arrived after Rove started.
  • Plugins enables or disables registered plugins live and edits settings declared by their manifests. Update, link and remove plugins from the shell.
  • Marketplace lists plugins published under the rove-plugin topic on GitHub, most-starred first, and installs one without leaving the TUI. enter on a row clones the repo and shows what it declares — every build command, startup hook, action and event handler — and installs only after you confirm; nothing the plugin authored runs before that. A row already in the registry is tagged installed and refuses a second copy. The listing is re-queried each time you open the section, which is also how you retry after GitHub was unreachable (it falls back to the first-party plugins).
  • Keybindings shows the active prefix, loaded YAML overrides and warnings. Edit the displayed YAML path; changes reload live.
  • Feedback submits a GitHub Discussion through an authenticated gh CLI.
  • Dev contains reset, a backend-exit action and experimental switches. Reset clears UI and task-index state after confirmation, but leaves worktrees and engine history on disk. The current Restart backend action exits only this TUI window; other attached windows and hosted sessions remain connected. Use rove daemon restart from a shell when you need to restart the daemon itself.

Zen mode always keeps the Tasks rail visible — it carries the affordance for leaving zen. There is no setting for this.

Starting sessions: the new-session dialog

ctrl+e is the one dialog for starting anything. It lists your detected engines, a shell, and any plugin panes. Two toggles set what happens:

  • tab flips the destination: a new tab in this worktree ⇄ a forked child task in a fresh worktree.
  • ctrl+f flips the context: a fresh conversation ⇄ continue this one.

Flip either toggle and the list narrows to engines only; a shell can't continue a conversation, and a plugin pane isn't a task.

Continue uses a native conversation fork only when the selected engine is the source engine and supports one, currently Claude and Codex. Copilot and Kimi use a transcript handoff even when continuing to the same engine. A built-in source can also hand off to a different built-in or custom target. A custom source has no readable transcript, so Rove refuses to continue it instead of opening a context-free tab.

Fork a child task opens the quick composer (prompt, attempts, engine, branch). The child branches from your task's current branch, so committed work carries over. Uncommitted changes stay behind; commit first if the child needs them.

Attempts fans the same prompt out to a round of up to 5 siblings, the keyboard path to rove api add --count N --prompt …. They share one round id, so rove api collect --group <id> reports them together. A round does not move you: the siblings appear in the sidebar and start working while you stay on the task you fired from — only a single attempt still carries you into the child, because that one is "carry on from here". The chip stops at 5 where the CLI allows 10; Orchestration calls 3-4 the sweet spot, and past five the shell command is the better tool.

ctrl+a c (continue in a new tab) and ctrl+a f (fork a child task) open the same dialog with the toggles pre-set.

To resume a conversation that is not already represented by a tab, use the engine's own picker (e.g. claude-code's /resume) inside a fresh engine tab. Availability and restart behavior vary by engine; see Resuming a conversation.

Pages: ctrl+a 1 / 2 / 3

Three pages replace the workspace pane while the sidebar stays put. esc or q closes a page; selecting a task in the sidebar also returns you to the workspace. The chords stay live, so you can hop between pages directly.

Kanban (ctrl+a 1)

The Kanban board: Backlog, In progress and Done for one project, with the card cursor on an in-progress story

The issue store as a board, one project at a time (tab cycles projects). A project gets a section if it can hold a backlog at all — the issue store has a record for it, you saved it as a project, or a live task runs in it — so deleting the task you finished leaves its stories on the board. Four columns:

  • Backlog. Status open, not linked to a task.
  • In progress. Status doing, or linked to a task. Either route works: agents move cards with rove api issue-update --task, and a session started from the drawer sets doing. A linked card wears its task's derived group as a badge — needs you, ready to land, needs review, working (the same reading as the sidebar mark, above) — and the cards that need a person float to the head of the column, counted in the header as N need you. A parked card keeps its badge but never floats: its engine being blocked is often why somebody parked it.
  • Parked. Status hold, linked or not; sits between In progress and Done.
  • Done. Status done.

enter opens the detail drawer: edit the title and description, then start a real session from the card: pick an engine, pick where it runs (the story's own worktree, or the project checkout), and choose to follow it or stay on the board. Starting links the issue and flips it to doing. n creates a story, d deletes one (the issue record only; a linked task and its worktree are never touched). The board refreshes every few seconds, so cards moved by agents move on screen too.

The drawer's STATUS field is how a human moves a card between columns: tab to it and ←/→ (or h/l) steps through open · doing · hold · done. The board's own keys steer the cursor and d deletes the story outright, so without this the only way to mark work finished was an agent running rove api issue-update --status — "I finished this" and "this never existed" were the same keypress.

For a linked story, the drawer also shows an EVENTS snapshot with up to the 12 most recent engine lifecycle events Rove still holds for that task. It is a point-in-time diagnostic view: reopen the drawer to fetch it again. A daemon restart clears the in-memory event ring, so an empty list does not mean the linked Task never ran.

The story detail drawer: editable title and description above the engine, workspace and after-start choices a session would launch with

The board in motion: walking the cards, opening a story, filing a new one with n, and an agent picking it up (rove api issue-update --task) while the page is open, which moves the card into In progress on its own:

Filing a story from the board, then an agent moving its card into In progress

Routines (ctrl+a 2)

The Routines page: three scheduled prompts with their repo, cron expression and next run, and the selected routine's prompt, precheck and run history below

Daemon-owned scheduled prompts on five-field cron expressions. Each row shows the repo, the schedule, and the next run; the detail box below shows the prompt, the precheck if any, and the last few runs with their outcomes.

n creates a routine (name, repo, prompt, schedule), e pauses or resumes, s runs one now, enter opens the task created by the latest run. There is no in-page editing. Recreate the routine, or use rove api routine-update (which also sets prechecks; see rove api). An enabled routine keeps the daemon alive so schedules fire with no TUI attached.

Walked through end to end, with the page pictured and the cron and precheck rules spelled out: Routines.

GitHub Issues (ctrl+a 3)

A read-only view of the repo's GitHub issues, fetched through the gh CLI. Open it from the sidebar rail or with ctrl+a 3; use rove api workitem-* to browse and start work from issues when the TUI is not open.

When the page opens, a filters to issues assigned to you, tab switches repos, and r refreshes past the cache. enter starts a Rove task from the selected issue: the issue body arrives as the first prompt (fenced, and explicitly marked as an untrusted report), and the task keeps a linkedWorkItem pointer back to the issue. An issue that already has a task shows that task's title on its detail line, and enter opens the task instead of creating a second one. Nothing is imported into the local issue store and nothing is written back to GitHub.

What's new after an upgrade

The first launch on a newly installed version opens a What's New dialog listing the release notes for every version between the one you were running and the one you just started. q, esc or ctrl+c dismisses it, and it does not come back until the next upgrade — a relaunch on the same build goes straight to the workspace.

It is a modal over your workspace, not a screen instead of it: the task list you were coming back to stays visible behind it, which is the point. Long ranges scroll — ↑↓ a line, ⇞⇟ a screen, home/end to either end — so an upgrade that crossed six releases is readable all the way down.

A fresh install never sees it; there is no earlier version to have changed from. Neither does a downgrade.

The dialog chrome follows your UI language (Settings → Appearance → Language). The release notes themselves are whatever was published to the GitHub release, which today is English only. They render as markdown — headings, nested bullets, code and emphasis all come through, and a link shows its label without its address, so the sentence starts at the left edge instead of behind two GitHub URLs. The Update page and rove update list render them the same way. If GitHub is unreachable the dialog says so and shows the release URL instead — it never blocks startup, and it is dismissible before the notes finish loading.

Rove remembers what it has shown in app.whatsNewSeenVersion in ~/.rove/state.json. Deleting that key replays the dialog once.

Updates and version warnings

When a newer release is available, the sidebar shows an update affordance and u opens the Update page. It compares the installed and latest versions, shows release notes for the versions in between, and offers three actions:

  • u runs the displayed self-update command, leaves the TUI, and reports the result in the terminal.
  • r opens the latest release page in the system browser.
  • q or esc closes the page without changing anything.

For a specific release or a browsable list of the latest 20 releases, use rove update <version> or rove update list; see CLI reference. Releases that cross a known breaking version show a warning before installation.

An amber DAEMON OUT OF DATE banner means this TUI and the already-running daemon are different builds — the ordinary result of rove update, since the daemon is a long-lived process that keeps running the code it booted with.

The banner names the chord that fixes it: ctrl+a r restarts the daemon and relaunches this Rove on the installed build, after one confirmation. Finish any immediate interaction first — the window goes away and comes back. Running engine sessions are not at risk: they live in the separate PTY host, which outlives both the daemon and the TUI, so open tabs reattach to the same sessions. Settings → Dev → Restart backend does the same thing, and rove daemon restart from a shell still works if you would rather.

Rove offers the refresh, never takes it: there is no auto-restart, and the chord is only bound while the two builds actually differ.

Narrow terminals (phone SSH)

Below 70 columns the TUI switches to one panel at a time, made for phone-sized SSH sessions. Nothing changes at 70 columns or wider, and there is no setting: it follows the terminal width.

  • The task list and the workspace alternate: opening a task shows the workspace full-width. A ‹ Tasks row at its top-left returns to the list, by click or by the key it shows (ctrl+q by default). No new chords.
  • The first sidebar row is ↩ Recent: <task>; enter drops you back into the task you were last working in, and it survives reconnects.
  • The files pane is hidden; the pane-cycle keys skip it.
  • The tab strip always shows, compressed to the active tab plus a 2/3 counter. The usual tab chords still switch.
  • The footer keeps one quota chip per engine (CLAUDE 42%) and shrinks the hints to bare keycaps.
  • Dialogs center themselves with tighter padding, and Settings drops the inline hint beside each switch so the switch's own label fits. The paragraph above each group still explains what it does.
  • Dialogs with a picker (new task, branch picker) shrink the picker's visible rows on a short terminal instead of pushing their own Create button off the bottom. The list still scrolls, so every entry stays reachable.

Attachments: drag and drop, paste

Drop an image (.png, .jpg, .jpeg, .gif, .webp) or a .pdf from your file manager:

  • Onto an engine session. The path lands in the engine's input, pasted but not submitted, so you can keep typing around it. The visible session catches the drop even when your keyboard focus is elsewhere.
  • Into the quick-task composer or an issue drawer. The file becomes an images[N]: /path attachment line sent along with the first prompt.

ctrl+v in those dialogs does the same with the clipboard: a copied file attaches by path, a raw screenshot is saved under ~/.rove/attachments/ first. Rove only ever passes paths; the engine reads the file itself.

Pasting text with newlines stays one paste. Rove asks the terminal for bracketed paste, so a multi-line block arrives framed and is handed to the engine as a paste rather than as typing that submits on the first newline, the engine shows it as a pasted block and you decide when to send.

For engines with a quota probe (Claude Code and Codex today), the footer shows each usage window the vendor reports, e.g. CLAUDE 5h 42% → 14:00 · 7d 12%, with the percentage colored green below 75%, yellow from 75%, red from 95%. The same numbers, same thresholds, appear in Settings → General.

The daemon refreshes quota roughly every 15 minutes, so treat the figure as approximate, not live. When Claude hits its subscription window, Rove schedules an automatic resume for the affected task and continues it once the window resets.

On this page