# Keybindings & cursor mode

> A reference for every key kin binds — in the composer, globally, in cursor (browse) mode, and inside the modals. Press ++f1++ in the app for the same map as an overlay.

A reference for every key kin binds — in the composer, globally, in cursor
(browse) mode, and inside the modals. Press ++f1++ in the app for the same map
as an overlay.

<!-- SOURCE: src/kin/tui/app.py, src/kin/tui/widgets/, src/kin/tui/composer_assist.py, src/kin/tui/turn_lifecycle.py, src/kin/tui/cursor_mode.py, src/kin/tui/clipboard.py, src/kin/tui/modals/, src/kin/tui/_read_only.py, src/kin/tui/agent_panel.py, src/kin/tui/search_overlay.py, src/kin/tui/workflows_modal.py, src/kin/tui/commands/passthrough.py, src/kin/harness/persistence/prompt_history.py -->

## Composer keys

The composer is a multi-line text field. Sending is the dominant action, so
++enter++ sends and ++shift+enter++ inserts a newline.

| Key | Action |
|-----|--------|
| ++enter++ | Send the message (queues if a turn is already running) |
| ++shift+enter++ | Insert a newline for multi-line editing |
| ++ctrl+s++ | Send (an alias of ++enter++ for muscle memory) |
| ++ctrl+v++ | Attach the clipboard image as an `@`-mention (macOS; ordinary terminal text paste is unaffected) |
| ++up++ / ++down++ | ++up++ takes back a queued follow-up for editing; otherwise walk prompt history (and ++down++ returns toward your newer draft) |
| `/` | Open the [slash-command menu](/docs/kin/guide/slash-commands/) |
| `@` | Open the [file-mention picker](/docs/kin/guide/at-mentions/) |
| `!<command>` | Run `<command>` in a real shell instead of sending it to the model — see the tip below |

> **! shell passthrough**
>
> `!git status` (or any shell command) runs immediately in your shell — no
> model turn involved. The output shows up right in the transcript, and a
> summary (command + exit code + output) rides into the model's context on
> your *next* turn, so it can react to what you just ran. Only works while
> kin is idle (a running turn or `/compact` refuses it with a notify); the
> command runs with your full environment (real credentials, so `!gh pr
> list` and `!git push` work) and is never something the model can trigger
> itself — only a literal `!` you type. Bare `!` with nothing after it is
> sent to the model as ordinary text. See
> [`docs/decisions/0075-shell-passthrough.md`](https://github.com/kinra-ai/kin/blob/dev/docs/decisions/0075-shell-passthrough.md)
> for the full design.

> **Tip**
>
> With the composer empty or on a single line, ++up++ and ++down++ recall your
> previous prompts. A queued follow-up is the first ++up++ stop: taking it
> back removes it from automatic sending until you submit it again, so editing
> or deleting it is safe. The recalled text replaces the buffer with the caret
> at the end; a newer unsent draft returns on ++down++. Recall is project-scoped and
> survives restarts: model prompts, slash commands, and literal `!` command
> lines are retained (shell output is not). The default keeps the newest 100;
> configure `prompt_history_cap`, or set it to `0` for memory-only recall.
> Ephemeral launches (`--no-save`, `KIN_NO_SAVE`, or `no_save = true`) also
> keep history only for the current process. Automatic memory-curation prompts
> are internal and never enter this recall history.

> **Ctrl+R: search your history**
>
> ++ctrl+r++ opens a **search** overlay over the same history ↑/↓ recalls
> linearly — type to filter (substring, case-insensitive), ++up++ / ++down++
> move the highlight, ++enter++ copies the match into the composer, ++escape++
> closes without changing anything, and pressing ++ctrl+r++ again while the
> overlay is open steps to the next-older match (readline muscle memory). The
> search uses the same durable, project-scoped history as ↑/↓. See [Slash
> commands](/docs/kin/guide/slash-commands/) for the composer's other overlays.

## Global keys

These work anywhere in the app (a few are suppressed while a modal owns the
screen, so you can't, for example, flip mode behind an open approval prompt).

| Key | Action |
|-----|--------|
| ++shift+tab++ | Toggle the permission mode: auto ↔ strict |
| ++escape++ | Interrupt the running turn; exit cursor mode |
| ++escape++ ++escape++ | On an empty, idle composer (no turn running, no modal open): open the [rewind picker](/docs/kin/guide/sessions/#rewind-retry) — press again within ~1s of the first tap |
| ++ctrl+b++ | Enter cursor / browse mode (composer must be empty) |
| ++ctrl+r++ | Search your prompt history — type to filter, ++up++ / ++down++ move the highlight, ++enter++ inserts, ++escape++ closes, ++ctrl+r++ again steps to the next-older match |
| ++ctrl+n++ | Expand or collapse all reasoning blocks |
| ++ctrl+t++ | Expand or collapse all tool calls |
| ++ctrl+f++ | Search the transcript — opens an overlay; type to filter, ++up++ / ++down++ or `n` / `shift+n` step, ++enter++ jumps to a match, ++escape++ closes |
| ++ctrl+o++ | Open the agent panel (bg shells / **agents** (fg + bg subagents) / tasks); press again or ++escape++ to close |
| ++ctrl+l++ | Open the [session switcher](/docs/kin/guide/sessions/#sessions-the-switcher): running, saved, new, and Outpost sessions |
| ++ctrl+g++ | Toggle the file-tree sidebar (also `/tree`) |
| ++ctrl+j++ | Expand or collapse the pinned todo panel (click the title too) |
| `v` (on the tree) | Peek the focused file in a read-only viewer (also `/view <path>`) |
| ++pageup++ / ++pagedown++ | Scroll the transcript — or, with a modal open, that modal's own content (plan, help, proposal diffs) |
| ++ctrl+home++ / ++ctrl+end++ | Jump to the top / bottom of the same scroll surface |
| ++f1++ | Open the help overlay |
| ++ctrl+c++ | Quit kin |

See [Modes & permissions](/docs/kin/guide/modes-and-permissions/) for what the ++shift+tab++
cycle changes.

## Hover hints (tooltips)

Several chrome widgets advertise their function on hover so screen-reader
users and the curious don't have to chase docs.

| Widget | Hover hint | Opens |
|--------|-----------|-------|
| `WorkflowCard` (running) | "click for the full run — `/workflows`" | `/workflows` modal |
| `BgAgentsBar` | "background agents — ++ctrl+o++ for the panel" | the agent panel |
| `RetryNote` | "re-send the failed request" | the redrive action |
| `TopBar` | "context meter · model · git branch" | — (informational; workspace paths under your home use `~/`) |

The composer keeps a quiet `Message Kin…` placeholder. Guidance is progressive:
the idle status line shows one motionless hint set (starting with `enter sends ·
/ commands · @ files · f1 help` and rotating only after a completed turn), while
F1 is the complete map.

Every overlay's one-line hint follows the same voice: lowercase key names, one
indicative present-tense verb per key (`enter picks`, `esc closes`, `d
revokes`), and ` · ` separators. A hint carries only what the overlay's own
button row doesn't — the `/models` and `/mcp` hubs show `Save & apply [ctrl+s]`
and `Close [esc]` as buttons, so their hint lists row actions only. F1 is the
complete map, so it appends the button-carried keys; it reads each overlay's
declared hint rather than restating it, which is why the two can't drift apart.

## Mouse selection & clipboard

Drag-select any assistant or user message text with the mouse and it copies
automatically on release — no extra key, no toast to dismiss first. A short
toast confirms (`copied — N chars`); ++escape++ clears the selection. Click a
fenced code block (a hover tooltip reads "click to copy") to copy the whole
block in one click, without having to drag-select it precisely.

Copying writes two ways at once:

- **OSC 52** — the terminal escape sequence, fire-and-forget. Works over SSH
  when your terminal honours it (Ghostty does; some VTE-based terminals
  silently drop it). Inside the
  [tmux detach/reattach wrapper](/docs/kin/guide/sessions/#background-sessions-detach-reattach)
  the session sets `set-clipboard on`, so OSC 52 passes through even while
  detached.
- **A local clipboard tool**, as a fallback: `wl-copy` (Wayland), `xclip` /
  `xsel` (X11), or `pbcopy` (macOS) — whichever applies, run in the
  background so it never blocks the UI. When none is available the toast
  says so (`OSC52 required — clipboard-capable terminal`) rather than
  silently claiming success.

There's no config toggle for this — ++escape++ clearing the selection is
the opt-out, and the risk of an unwanted copy is low (you have to have
dragged over text for it to fire).

## Cursor / browse mode

Cursor mode lets you walk the transcript with the keyboard to read, copy, or
expand individual items. Enter it from an empty composer with ++ctrl+b++ (or
++up++ once prompt history is exhausted) — the highlight lands on the last item.
The composer must be empty; with text in it the key is left alone.

| Key | Action |
|-----|--------|
| ++up++ / ++down++ or `j` / `k` | Move between transcript items |
| `g` / `G` | Jump to the first / last item |
| `y` | Copy the focused item to the clipboard |
| ++tab++ | Expand or collapse the focused item (reasoning / tool call) |
| ++escape++ | Exit cursor mode and return to the composer |

Moving past the bottom returns you to the composer. Typing any other printable
character drops you out of cursor mode and back into editing.

## Modal keys

When the harness needs an answer it parks the turn and shows a modal. Each modal
binds its own keys.

| Modal | Keys |
|-------|------|
| Approval | `a` or bare ++enter++ allows once (the focused default) · `s` allows always when offered (this session — the button names the covered scope; fresh-state actions such as force-with-lease omit it) · `d` or ++escape++ denies · `n` reveals the steering-note field, and ++enter++ there denies with that note (esc closes the note first) |
| Sandbox access | ++space++ toggles the requested permissions · `o` grants once · `s` grants for the session · `w` grants for the workspace (offered only when the workspace tier is available) · `d` declines · ++escape++ cancels |
| Git credentials | `o` permits once · `s` trusts this exact route for the session · `h` persists secret-free host-route trust · `d` declines · ++escape++ cancels |
| Confirm (e.g. `/clear`) | `y` confirms · `n` or ++escape++ cancels · bare ++enter++ cancels too (Cancel is the focused default — confirming a destructive action is always explicit) |
| Question (single-select) | ++up++ / ++down++ selects · ++enter++ picks · ++escape++ cancels |
| Question (multi-select) | ++space++ toggles · ++enter++ or Submit · ++escape++ cancels |
| Question (other / free text) | type into the field · ++enter++ sends |
| Plan | ++up++ / ++down++ selects · ++enter++ picks · ++escape++ keeps planning |
| Proposal (artifact-edit diff, `/settings` change) | `a` or ++enter++ applies · `d` or ++escape++ rejects · ++pageup++ / ++pagedown++ scrolls |
| Model configuration (`/models`) | `j` / `c` / `r` switches between Jobs, Connections, and Routes · ++enter++ edits · `a` adds · `m` makes default · ++delete++ removes · `p` checks the connection · ++ctrl+s++ applies the staged draft · ++escape++ discards it |
| MCP manager (`/mcp`) | `a` adds · `e` or ++enter++ edits · `d` / `x` enable-disable · `t` tests · ++delete++ removes · ++ctrl+s++ applies · ++escape++ closes |
| Search provider key (`/search-key`, `/brave-key`) | ++up++ / ++down++ picks the row · type the key into the field · ++enter++ saves · ++escape++ cancels |
| Model picker (bare `/model`) | ++up++ / ++down++ selects · ++enter++ picks · `f` favorites · ++escape++ cancels |
| Effort picker (bare `/effort`) | ++up++ / ++down++ selects · ++enter++ picks · ++escape++ cancels (a change invalidates the prompt cache) |
| Spinner picker (bare `/spinner`) | ++up++ / ++down++ previews live · ++enter++ picks · ++escape++ cancels |
| Nudges (`/nudges`) | ++space++ toggles · ++ctrl+enter++ applies · ++escape++ cancels |
| Beam (`/beam`) | fill the fields, then Review and Beam · one confirmation covers commit, push, and submit · ++escape++ cancels |
| Config hub (`/config`) | ++up++ / ++down++ selects · `v` views current (singletons) · ++enter++ edits · ++escape++ cancels |
| Grants | ++up++ / ++down++ selects · `d` (or `r`) arms a revoke · `y` confirms · `n` / ++escape++ cancels the arm, then ++escape++ closes |
| Workflows (`/workflows`) | ++up++ / ++down++ picks a run · ++escape++ closes |
| Resume picker | ++up++ / ++down++ selects · ++enter++ resumes · ++escape++ cancels |
| Session switcher (`/sessions`, ++ctrl+l++) | ++up++ / ++down++ selects · ++enter++ switches · `x` ends the focused live session · ++escape++ closes |
| Rewind picker (double-++escape++) | ++up++ / ++down++ selects · ++enter++ picks a turn, then a scope · ++escape++ cancels (either stage) |
| Find in transcript (++ctrl+f++) | ++up++ / ++down++ or `n` / ++shift+n++ navigates · ++enter++ jumps · ++escape++ closes |
| Agent panel | ++tab++ switches view (shells → agents → tasks) · `x` stops / dismisses (works on running fg rows too) · `o` opens raw buffered output · ++enter++ opens the agent's rich transcript (TranscriptModal — falls through to read-only output for shell/task rows) · ++escape++ closes |
| Help | ++escape++ or ++f1++ closes · ++pageup++ / ++pagedown++ scrolls |
| Read-only viewer (file peek / agent output) | ++escape++ or `q` closes |
| Transcript modal (agent transcript peek) | ++escape++ or `q` closes |

The approval modal's `s` ("always") remembers the call's signature for the rest
of the session, so calls that match it stop prompting. The sandbox modal's `w`
and the Git credential modal's `h` outlive the session — `w` grants the selected
capabilities for the workspace, and `h` stores a route fingerprint globally,
never a credential. Review and
revoke all of them with `/grants`. Permission decisions are covered in
[Modes & permissions](/docs/kin/guide/modes-and-permissions/).
