Reference
~1.8k tokens

Keybindings

On macOS, some bindings use Option or Fn keys instead (run /help for exact keybindings).

General

KeyAction
Ctrl+CQuit / clear input / copy selection
Ctrl+HShow keybindings
Ctrl+FSearch messages
Ctrl+SFile picker
Ctrl+OOpen plan in editor
Ctrl+TToggle plan panel
Ctrl+LModel picker

Editing

KeyAction
EnterSubmit prompt
Shift+Enter / Ctrl+Enter / Ctrl+J / Alt+EnterNewline
TabSwitch between plan and build
/commandOpen command palette
Ctrl+WDelete word backward
Alt+← / Alt+→Move word left / right
Shift+motionSelect text
Ctrl+AJump to start of line
Home / EndJump to start/end of line
Ctrl+U / Ctrl+DScroll half page up / down
PageUp / PageDownScroll page up / down
Ctrl+EJump to end of line
Ctrl+GScroll to top
Ctrl+Alt+GScroll to bottom
Ctrl+QPop queue
Esc EscRewind
Alt+OEdit input in external editor
Ctrl+VPaste clipboard image or text
Alt+ZUndo typing / paste

macOS-specific

KeyAction
Ctrl+Del / ⌥DelDelete word forward
Ctrl+KDelete to end of line

While Streaming

KeyAction
↑ / ↓Navigate input history
Esc EscCancel agent
Ctrl+BMove running calls to the background (tmux: press twice)

Form

KeyAction
↑ / ↓Navigate options
EnterSelect option
EscClose

Pickers

KeyAction
↑ / ↓Navigate
EnterSelect
EscClose
TypeFilter
PageUp / PageDownScroll page up / down
Ctrl+U / Ctrl+DScroll half page up / down

Context-Specific

Some pickers add extra bindings on top of the defaults:

ContextKeyAction
Rewind PickerCtrl+FFork up to the selected prompt and switch to it
QueueEnterRemove item
CommandsTabComplete command
Model Picker!/@/#/$Set tier (strong/medium/weak/compaction)
Model Picker%Fold or unfold provider
Session PickerCtrl+NNew session
Session PickerCtrl+RRename session
Session PickerCtrl+DDelete session (press twice)
Thinking Picker↑/↓Move between effort levels
Thinking Picker0-9Type a token budget
Thinking PickerEnterApply and close
Thinking PickerEscClose without changing anything
Task PickerCtrl+DDelete finished task (press twice)

Plugins

Built-in plugins register these themselves, and your own plugins can add more with maki.keymap.set:

KeyAction
Ctrl+PBrowse sessions
Ctrl+XOpen tasks
Alt+TThinking effort

Context Inheritance

Child contexts inherit their parent's bindings and add their own.

  • Pickers is the base for: Rewind Picker, Theme Picker, Model Picker, Queue, Commands, Search, File Picker

Overriding Keybindings

Rebind a single key from your init.lua with maki.keymap.set. To move a built-in to another key, call its action. This opens the file picker on Alt+S, for terminals that take Ctrl+S:

maki.keymap.set({ "n", "i" }, "<M-s>", function()
  maki.ui.action("file_picker")
end)

{ "n", "i" } lists both modes. Your binding replaces the default on that key, and the default comes back when the binding is removed. When two bindings share a key, the last set wins, and the log names both.

Some keys stay out of reach (the full order is in Key resolution):

  • Ctrl+C and Ctrl+Z, so quit and suspend always work.
  • Esc while the agent runs, so Esc Esc can cancel the turn.
  • The keys of an open modal, picker or prompt, which it handles first.
  • Keys a plugin binds while it loads, like Ctrl+P. Plugins load after your global and project init.lua, so their set comes last.
  • Multi-key combinations and rows like Type to filter. Only single keys can be bound.

/help and the splash show the default labels, even for a key you rebound. Below them, /help lists every key that plugins and your init.lua bound, with its modes and owner, and every action you can call.

Upgrading old keymaps

Older versions fired "n" bindings while you typed in the chat input. Now they fire only in normal mode, so a shortcut set in "n" alone stops working while you type. List both modes:

-- Before:
maki.keymap.set("n", "<M-n>", open_notes)
-- After:
maki.keymap.set({ "n", "i" }, "<M-n>", open_notes)

At startup, the status bar warns about a plugin or init.lua that binds a key in "n" alone. One that also binds a key in "i", like a vim mode, handles modes itself and gets no warning.

Composing keys

Tab switches between plan and build. To make it accept a file completion first while the completion menu is open, bind it in your init.lua and try each action in turn. ev.fallback() hands the key back to Maki when none of them took it:

maki.keymap.set("i", "<Tab>", function(ev)
  if maki.ui.action("completion.accept") then return end
  if maki.ui.action("ghost.accept") then return end
  ev.fallback()
end)

ghost.accept stands for another plugin's action. An action returns true when it handled the key. If its plugin is not installed or is disabled, it returns nil and the binding moves on.

completion.accept writes file paths into the input, so calling it needs fs_read, input and session_read in ~/.config/maki/plugin.toml (see plugin permissions):

[permissions]
fs_read = true
input = true
session_read = true

Without them the action returns nil, Tab switches between plan and build, and the status bar names the missing permission.

Recovering from a bad keymap

If an override leaves Maki stuck (a modal that does not close, a plugin that throws on load), boot without user init.lua:

maki --no-plugins

Skips user init.lua files (global and project) but keeps the Lua host and builtin plugins running, so tools still work. Custom commands and skills still load, and the project permission rules and env file follow folder trust.

The default keymap lives in Rust, not Lua, so --no-plugins never drops it.

Shell and images

These are input conventions, not remappable key rows:

  • Prefix a line with ! to run a shell command yourself (5 minute timeout). Use !! to hide the command and its output from the agent.
  • Pasting image file paths attaches the images.
  • Right-click pastes clipboard text on Windows.
  • Middle-click inserts the PRIMARY selection, the same text middle-click pastes in a shell. Linux only, since other platforms have no PRIMARY selection. On Wayland the compositor must support it.