# Keymap


The kli terminal UI binds keys to editor, navigation, and app actions. Bindings are looked up by a key-id string. A key-id is the key's base name, prefixed by any modifiers in `modifier+...+base` form, all lowercase: `ctrl+z`, `alt+left`, `shift+tab`. The base name alone is the key-id for an unmodified key: `enter`, `escape`, `home`.

The tables below list every default binding. Lookups consult a user override layer first, then these defaults, so any binding here can be replaced through the [keybindings setting](#rebinding-keys).

## Session control

Two keys act on the running session rather than the editor. Each takes effect on a second press, with a hint between presses.

| Key | First press | Second press |
| --- | --- | --- |
| `Ctrl+C` | Arms quit, prints `Press Ctrl+C again to quit.` | Quits kli |
| `Esc` | While a turn is running, prints `Press Esc again to interrupt.` At an idle prompt with a turn to step past, prints `Press Esc again to rewind.` | While a turn is running, interrupts the turn. At an idle prompt, opens the rewind menu |

`Ctrl+C` interrupts in every terminal encoding; it is fixed and not part of the rebindable keymap.

`Esc` is bound to the `abort` action, and its effect depends on session state. During a running turn, the first press arms an abort and a second press within about 1.5 seconds interrupts the turn; a later second press re-arms instead, so a single stray press cannot interrupt. At an idle prompt, the first press arms a rewind and a second press within the same window opens the rewind menu over the session's user turns. In the rewind menu, Up/Down move the selection, Enter rewinds to before the chosen turn, and Esc dismisses the menu. On an empty conversation `Esc` does nothing.

## App actions

| Key | Action | Effect |
| --- | --- | --- |
| `Ctrl+L` | `clear-screen` | Clears the screen |
| `Ctrl+O` | `tool-output` | Toggles expansion of tool output, then reprints the transcript |
| `Enter` | `submit` | Submits the prompt |
| `Ctrl+J`, `Shift+Enter` | `newline` | Inserts a newline in the editor |

## Editing

| Key | Action | Effect |
| --- | --- | --- |
| `Backspace` | `backspace` | Deletes the character before the cursor |
| `Delete`, `Ctrl+D` | `delete-char-forward` | Deletes the character after the cursor |
| `Ctrl+U` | `delete-to-line-start` | Deletes from the cursor to the start of the line |
| `Ctrl+K` | `delete-to-line-end` | Deletes from the cursor to the end of the line |
| `Ctrl+W`, `Alt+Backspace` | `delete-word-backward` | Deletes the word before the cursor |
| `Alt+D`, `Alt+Delete` | `delete-word-forward` | Deletes the word after the cursor |
| `Ctrl+Z` | `undo` | Undoes the last edit |
| `Tab` | `insert-tab` | Inserts a tab |
| `Space` | `insert-space` | Inserts a space |

## Navigation

| Key | Action | Effect |
| --- | --- | --- |
| `Left`, `Ctrl+B` | `move-char-left` | Moves the cursor one character left |
| `Right`, `Ctrl+F` | `move-char-right` | Moves the cursor one character right |
| `Up`, `Ctrl+P` | `move-line-up` | Moves the cursor up one line |
| `Down`, `Ctrl+N` | `move-line-down` | Moves the cursor down one line |
| `Home`, `Ctrl+A` | `move-line-start` | Moves the cursor to the start of the line |
| `End`, `Ctrl+E` | `move-line-end` | Moves the cursor to the end of the line |
| `Alt+B`, `Alt+Left`, `Ctrl+Left` | `move-word-left` | Moves the cursor one word left |
| `Alt+F`, `Alt+Right`, `Ctrl+Right` | `move-word-right` | Moves the cursor one word right |

## Inert keys

These keys are recognized so they never insert literal text, and otherwise do nothing.

| Key | Action |
| --- | --- |
| `Insert`, `Shift+Tab` | `swallow` |
| `Page Up`, `Page Down` | `ignore` |

## Rebinding keys

The `keybindings` setting in `settings.json` maps key-id strings to action names. Each entry overrides the default for that key-id; the default keymap is untouched and stays in effect for every key you do not name.

```json
{
  "keybindings": {
    "ctrl+r": "delete-to-line-start",
    "alt+z": "undo"
  }
}
```

Keys are key-id strings as described above (lowercase, `modifier+...+base`). Values are action names. Hyphens and underscores are interchangeable in an action name, so `delete-to-line-start` and `delete_to_line_start` both resolve. An entry whose value is not a known action is skipped with a warning; the rest of the table still applies. A `keybindings` value that is not an object is ignored with a warning.

kli reads `settings.json` from two locations and merges them, project over global:

- Global: `~/.config/kli/settings.json`
- Project: `<repo>/.kli/settings.json`

A project `keybindings` entry overrides the global entry for the same key-id, key by key. See [Settings](/kli/config/settings) for the full settings layering.

### Bindable actions

A `keybindings` value must be one of these action names.

| Action | Effect |
| --- | --- |
| `clear-screen` | Clears the screen |
| `tool-output` | Toggles tool-output expansion |
| `submit` | Submits the prompt |
| `newline` | Inserts a newline |
| `backspace` | Deletes the character before the cursor |
| `move-char-left` | Moves the cursor one character left |
| `move-char-right` | Moves the cursor one character right |
| `move-line-up` | Moves the cursor up one line |
| `move-line-down` | Moves the cursor down one line |
| `move-line-start` | Moves the cursor to the start of the line |
| `move-line-end` | Moves the cursor to the end of the line |
| `move-word-left` | Moves the cursor one word left |
| `move-word-right` | Moves the cursor one word right |
| `delete-char-forward` | Deletes the character after the cursor |
| `delete-to-line-start` | Deletes from the cursor to the start of the line |
| `delete-to-line-end` | Deletes from the cursor to the end of the line |
| `delete-word-backward` | Deletes the word before the cursor |
| `delete-word-forward` | Deletes the word after the cursor |
| `insert-tab` | Inserts a tab |
| `insert-space` | Inserts a space |
| `abort` | Interrupts the running turn, or arms the rewind menu at an idle prompt; takes effect on a second press |
| `undo` | Undoes the last edit |
| `swallow` | Consumes the key with no effect |
| `ignore` | Consumes the key with no effect |
