packages/coding-agent/docs/keybindings.md
All keyboard shortcuts can be customized via ~/.pi/agent/keybindings.json. Each action can be bound to one or more keys.
The config file uses the same namespaced keybinding ids that pi uses internally and that extension authors use in keyHint() and injected keybindings managers.
Older configs using pre-namespaced ids such as cursorUp or expandTools are migrated automatically to the namespaced ids on startup.
After editing keybindings.json, run /reload in pi to apply the changes without restarting the session.
modifier+key where modifiers are ctrl, shift, alt (combinable) and keys are:
a-z0-9escape, esc, enter, return, tab, space, backspace, delete, insert, clear, home, end, pageUp, pageDown, up, down, left, rightf1-f12`, -, =, [, ], \, ;, ', ,, ., /, !, @, #, $, %, ^, &, *, (, ), _, +, |, ~, {, }, :, <, >, ?Modifier combinations: ctrl+shift+x, alt+ctrl+x, ctrl+shift+alt+x, ctrl+1, etc.
| Keybinding id | Default | Description |
|---|---|---|
tui.editor.cursorUp | up | Move cursor up, browsing older history at the top |
tui.editor.cursorDown | down | Move cursor down, browsing newer history at the bottom |
tui.editor.historyPrevious | (none) | Select the previous prompt history entry |
tui.editor.historyNext | (none) | Select the next prompt history entry |
tui.editor.cursorLeft | left, ctrl+b | Move cursor left |
tui.editor.cursorRight | right, ctrl+f | Move cursor right |
tui.editor.cursorWordLeft | alt+left, ctrl+left, alt+b | Move cursor word left |
tui.editor.cursorWordRight | alt+right, ctrl+right, alt+f | Move cursor word right |
tui.editor.cursorLineStart | home, ctrl+home, ctrl+a | Move to line start |
tui.editor.cursorLineEnd | end, ctrl+end, ctrl+e | Move to line end |
tui.editor.jumpForward | ctrl+] | Jump forward to character |
tui.editor.jumpBackward | ctrl+alt+] | Jump backward to character |
tui.editor.pageUp | pageUp, ctrl+pageUp | Scroll up by page |
tui.editor.pageDown | pageDown, ctrl+pageDown | Scroll down by page |
The dedicated history actions always change history entries, regardless of the cursor position in a multiline prompt. Explicit history bindings take precedence over application actions while the main editor is focused, so binding tui.editor.historyPrevious to ctrl+p overrides model cycling in that context without changing Ctrl+P in selectors.
| Keybinding id | Default | Description |
|---|---|---|
tui.editor.deleteCharBackward | backspace | Delete character backward |
tui.editor.deleteCharForward | delete, ctrl+d | Delete character forward |
tui.editor.deleteWordBackward | ctrl+w, alt+backspace | Delete word backward |
tui.editor.deleteWordForward | alt+d, alt+delete | Delete word forward |
tui.editor.deleteToLineStart | ctrl+u | Delete to line start |
tui.editor.deleteToLineEnd | ctrl+k | Delete to line end |
| Keybinding id | Default | Description |
|---|---|---|
tui.input.newLine | shift+enter, ctrl+j | Insert new line |
tui.input.submit | enter | Submit input |
tui.input.tab | tab | Tab / autocomplete |
| Keybinding id | Default | Description |
|---|---|---|
tui.editor.yank | ctrl+y | Paste most recently deleted text |
tui.editor.yankPop | alt+y | Cycle through deleted text after yank |
tui.editor.undo | ctrl+- | Undo last edit |
| Keybinding id | Default | Description |
|---|---|---|
tui.input.copy | ctrl+c | Copy selection |
tui.select.up | up | Move selection up |
tui.select.down | down | Move selection down |
tui.select.pageUp | pageUp | Page up in list |
tui.select.pageDown | pageDown | Page down in list |
tui.select.confirm | enter | Confirm selection |
tui.select.cancel | escape, ctrl+c | Cancel selection |
These actions apply when interactive mode uses --tui-mode fullscreen and target the primary transcript scroll region. Two-finger trackpad and mouse-wheel input scroll the region under the pointer, falling back to the transcript over the fixed editor/status/footer dock. Clicking an OSC 8 hyperlink opens it in the default handler. Dragging with the primary mouse button selects text and copies it to the clipboard; holding at the transcript's top or bottom edge auto-scrolls into off-screen content.
Fullscreen transcript bindings take precedence over editor bindings. The default unmodified navigation keys therefore control the transcript in fullscreen mode, while their ctrl variants continue to control the editor. Outside fullscreen mode, both variants control the editor.
| Key | Default mode | Fullscreen mode |
|---|---|---|
home, end | Editor | Transcript |
ctrl+home, ctrl+end | Editor | Editor |
pageUp, pageDown | Editor | Transcript |
ctrl+pageUp, ctrl+pageDown | Editor | Editor |
This routing remains configurable through the ordinary action bindings. For example, "tui.altScreen.pageUp": "ctrl+pageUp" makes pageUp control the editor and ctrl+pageUp control the transcript in fullscreen mode. Setting "tui.altScreen.pageUp": [] disables that transcript shortcut entirely. User bindings replace the defaults for that action.
| Keybinding id | Default | Description |
|---|---|---|
tui.altScreen.pageUp | pageUp | Scroll the transcript up by one page |
tui.altScreen.pageDown | pageDown | Scroll the transcript down by one page |
tui.altScreen.previousPrompt | ctrl+shift+up | Jump to the previous marked message |
tui.altScreen.nextPrompt | ctrl+shift+down | Jump to the next marked message |
tui.altScreen.top | home | Scroll to the beginning of the transcript |
tui.altScreen.bottom | end | Scroll to the transcript end and follow new output |
| Keybinding id | Default | Description |
|---|---|---|
app.interrupt | escape | Cancel / abort |
app.clear | ctrl+c | Clear editor |
app.exit | ctrl+d | Exit (when editor empty) |
app.suspend | ctrl+z (none on Windows) | Suspend to background |
app.editor.external | ctrl+g | Open in external editor (externalEditor, $VISUAL, $EDITOR, Notepad on Windows, or nano elsewhere) |
app.clipboard.pasteImage | ctrl+v (alt+v on Windows) | Paste image from clipboard |
| Keybinding id | Default | Description |
|---|---|---|
app.session.new | (none) | Start a new session (/new) |
app.session.tree | (none) | Open session tree navigator (/tree) |
app.session.fork | (none) | Fork current session (/fork) |
app.session.resume | (none) | Open session resume picker (/resume) |
app.session.togglePath | ctrl+p | Toggle path display |
app.session.toggleSort | ctrl+s | Toggle sort mode |
app.session.toggleNamedFilter | ctrl+n | Toggle named-only filter |
app.session.rename | ctrl+r | Rename session |
app.session.delete | ctrl+d | Delete session |
app.session.deleteNoninvasive | ctrl+backspace | Delete session when query is empty |
| Keybinding id | Default | Description |
|---|---|---|
app.model.select | ctrl+l | Open model selector |
app.model.cycleForward | ctrl+p | Cycle to next model |
app.model.cycleBackward | shift+ctrl+p | Cycle to previous model |
app.thinking.cycle | shift+tab | Cycle thinking level |
app.thinking.toggle | ctrl+t | Collapse or expand thinking blocks |
| Keybinding id | Default | Description |
|---|---|---|
app.tools.expand | ctrl+o | Collapse or expand tool output |
app.message.copy | ctrl+x | Copy the last assistant message, or the selected message in /tree |
app.message.followUp | alt+enter | Queue follow-up message |
app.message.dequeue | alt+up | Restore queued messages to editor |
| Keybinding id | Default | Description |
|---|---|---|
app.tree.foldOrUp | ctrl+left, alt+left | Fold current branch segment, or jump to the previous segment start |
app.tree.unfoldOrDown | ctrl+right, alt+right | Unfold current branch segment, or jump to the next segment start or branch end |
app.tree.editLabel | shift+l | Edit the label on the selected tree node |
app.tree.toggleLabelTimestamp | shift+t | Toggle label timestamps in the tree |
app.tree.filter.default | ctrl+d | Set tree filter to default view |
app.tree.filter.noTools | ctrl+t | Toggle tree filter that hides tool results |
app.tree.filter.userOnly | ctrl+u | Toggle tree filter that shows only user messages |
app.tree.filter.labeledOnly | ctrl+l | Toggle tree filter that shows only labeled entries |
app.tree.filter.all | ctrl+a | Toggle tree filter that shows all entries |
app.tree.filter.cycleForward | ctrl+o | Cycle tree filter forward |
app.tree.filter.cycleBackward | shift+ctrl+o | Cycle tree filter backward |
Used inside the scoped models selector (opened via /scoped-models).
| Keybinding id | Default | Description |
|---|---|---|
app.models.save | ctrl+s | Save current model selection to settings |
app.models.enableAll | ctrl+a | Enable all models (or all matching the current search) |
app.models.clearAll | ctrl+x | Clear all models (or all matching the current search) |
app.models.toggleProvider | ctrl+p | Toggle all models for the current provider |
app.models.reorderUp | alt+up | Move the selected model up in the cycle order |
app.models.reorderDown | alt+down | Move the selected model down in the cycle order |
Create ~/.pi/agent/keybindings.json:
{
"tui.editor.historyPrevious": "ctrl+p",
"tui.editor.historyNext": "ctrl+n",
"tui.editor.deleteWordBackward": ["ctrl+w", "alt+backspace"]
}
Each action can have a single key or an array of keys. User config overrides defaults.
On native Windows, app.suspend has no default binding because Windows terminals do not support Unix job control. If you bind it manually, pi shows a status message instead of suspending. In WSL, the normal Linux ctrl+z/fg behavior still applies.
{
"tui.editor.historyPrevious": "ctrl+p",
"tui.editor.historyNext": "ctrl+n",
"tui.editor.cursorLeft": ["left", "ctrl+b"],
"tui.editor.cursorRight": ["right", "ctrl+f"],
"tui.editor.cursorWordLeft": ["alt+left", "alt+b"],
"tui.editor.cursorWordRight": ["alt+right", "alt+f"],
"tui.editor.deleteCharForward": ["delete", "ctrl+d"],
"tui.editor.deleteCharBackward": ["backspace", "ctrl+h"],
"tui.input.newLine": ["shift+enter", "ctrl+j"]
}
{
"tui.editor.cursorUp": ["up", "alt+k"],
"tui.editor.cursorDown": ["down", "alt+j"],
"tui.editor.cursorLeft": ["left", "alt+h"],
"tui.editor.cursorRight": ["right", "alt+l"],
"tui.editor.cursorWordLeft": ["alt+left", "alt+b"],
"tui.editor.cursorWordRight": ["alt+right", "alt+w"]
}